Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e96eef1
feat(memory): add PersonalReflection + TeamDebriefResult models and s…
Apr 11, 2026
b343731
feat(memory): Rewrite reflect.py with Individual Review + Team Debrie…
Apr 11, 2026
5d18e08
feat(memory): Integrate Phase 5+6 into experiment runner (Layer 2 S4)
Apr 11, 2026
fb0c075
feat(memory): Inject personal reflection into agent prompt (Layer 2 S5)
Apr 11, 2026
53c8c9d
docs: Add detailed git commit workflow to CLAUDE.md
Apr 11, 2026
d444860
feat(agents): Add ReAct tool system with agent memory
Apr 13, 2026
bb9d4d3
feat(llm): Add chat_with_tools() and thinking model support
Apr 13, 2026
2e695fa
feat(simulation): Enhance finance engine and game loop with market re…
Apr 13, 2026
500cd5a
feat(runner): Extend context building and decision models
Apr 13, 2026
ad3685f
feat(prompts): Add pricing rules and beliefs injection to ERPsim prompts
Apr 13, 2026
b23a0c3
refactor(agents): Add ReAct loop and long_term_memory to ERPSim agents
Apr 13, 2026
c3542b1
feat(memory): Add memory module init (models + store + reflect)
Apr 13, 2026
42970df
feat(platform): Add multi-user competition platform
Apr 13, 2026
2380edb
feat(frontend): Refactor frontend with i18n, protected routes, and ne…
Apr 13, 2026
0ab9514
docs(plan): Add hindsight memory and competition platform plans
Apr 13, 2026
5044c98
fix: Minor cleanup and test fixes
Apr 13, 2026
f46a649
chore: Add backend/data/ to .gitignore
Apr 13, 2026
3da15d1
feat(frontend): iMessage-style discussion UI with team sidebar
Apr 13, 2026
2e56881
fix(frontend): Detect LLM teams from message data instead of hardcoding
Apr 14, 2026
e774685
feat(frontend): Replace experiment table with card layout
Apr 14, 2026
86abb3a
style(frontend): Increase content area horizontal padding
Apr 14, 2026
6e5a62e
refactor(prompts): Split ERPsim agent prompts into homogeneous/hetero…
Apr 14, 2026
0619852
fix(simulation): Fix sold=0 bug — aggregate sales before building tea…
Apr 14, 2026
b6b894c
feat(erpsim): Add round feedback system with sales, banking, and mark…
Apr 14, 2026
882104d
refactor(storage): Persistent SQLite connection, optimized queries, a…
Apr 14, 2026
5a9461c
feat(frontend): Add team selector, pricing chart, and performance opt…
Apr 14, 2026
4bc16d0
test(erpsim): Add feedback context tests and fix integration test imp…
Apr 14, 2026
6a3050a
feat(erpsim): Add token usage tracking across discussion, reflection,…
Apr 14, 2026
dee0e8d
fix(simulation): Fix raw material shortage and make round count confi…
Apr 15, 2026
4e328fb
fix(demand): Normalize marketing term and scale market sizes 2x
Apr 16, 2026
224fcf1
fix(simulation): Fix raw material shortage and make round count confi…
Apr 16, 2026
87a8cae
feat(frontend): Detect LLM teams from config and add keyboard round n…
Apr 16, 2026
1f38ab6
docs(experiments): Add 5 experiment PRDs, roadmap, and pilot runner s…
Apr 16, 2026
11644c4
docs(claude): Add frontend lint command and default LLM config section
Apr 16, 2026
dadd6fd
feat(frontend): Redesign landing page with brutalist layout and geome…
Apr 16, 2026
77b62eb
style(frontend): Center landing hero vertically and clean up unused i…
Apr 16, 2026
ed91c6a
feat(simulation): Add demand volatility for dynamic market condition
May 15, 2026
db48361
feat(erpsim): Enrich agent context with sustainability ROI and AR mat…
May 15, 2026
6efde8f
feat(prompts): Add production scheduling rules to all agent prompts
May 15, 2026
a32ccf7
feat(kelly): Add Kelly Personal Construct Theory system
May 15, 2026
d9a0b57
style(frontend): Increase landing page vertical spacing
May 15, 2026
9dd6a45
docs: Add experiment analysis docs and AAAI plan, remove obsolete pro…
May 15, 2026
445d99c
docs: Reorganize and rename docs to unified Chinese naming convention
May 16, 2026
c591faf
chore: Remove docs/ from version control, add gitignore rules
May 16, 2026
97c664c
feat(simulation): Enhance demand model, packaging system, and game en…
May 17, 2026
f55feda
refactor(prompts): Extract shared templates, add solo mode, and impro…
May 17, 2026
1a13739
feat(runner): Redesign CLI team allocation, add solo support and expe…
May 17, 2026
a75f91e
feat(frontend): Enhance ERPSim views, charts, and project documentation
May 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# LLM API Keys
# LLM API Keys (fill in whichever providers you use)
KIMI_API_KEY=your_kimi_api_key_here
DEEPSEEK_API_KEY=your_deepseek_api_key_here
DASHSCOPE_API_KEY=your_dashscope_api_key_here
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
DEEPSEEK_API_KEY=

# LLM defaults
# LLM defaults (used when --provider/--model not specified)
DEFAULT_LLM_PROVIDER=kimi
DEFAULT_LLM_MODEL=kimi-k2.5
DEFAULT_LLM_MODEL=kimi-k2.6

# Storage
DATABASE_PATH=data/orgconstruct.db
Expand Down
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ data/archives/*.json
# Logs
*.log
logs/
!logs/*.md

# OS
.DS_Store
Expand All @@ -55,5 +56,18 @@ Thumbs.db
.agents/
.claude/

# Docs (not published, kept locally)
docs/

# Papers and copyrighted materials
*.pdf

# Experiment scripts (local only, not published)
scripts/

# Research scratch
erp_extract.txt
backend/data/

# paper drafts
paper/
208 changes: 191 additions & 17 deletions CLAUDE.md

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions backend/src/orgconstruct/agents/discussion.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ async def run(self, context: GameContext) -> Decision:
for round_num in range(1, self.max_rounds + 1):
for agent in self.agents:
response = await agent.respond(context, self.last_discussion_history)
self.last_discussion_history.append({
"round": round_num,
"role": agent.display_name,
"content": response,
})
self.last_discussion_history.append(
{
"round": round_num,
"role": agent.display_name,
"content": response,
}
)
logger.info(
"Round %d %s: %s...",
round_num,
Expand Down
171 changes: 160 additions & 11 deletions backend/src/orgconstruct/agents/erpsim_discussion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

Structurally identical to RoundRobinDiscussion but typed for ERPSimContext
and ERPSimDecision. Separate class to avoid breaking the simplified path.

Supports two modes:
1. Legacy: plain discussion → CEO final decision (backward compatible)
2. ReAct: each agent independently gathers data → discussion → CEO ReAct decision
"""

import asyncio
import logging

from orgconstruct.agents.erpsim_executive import ERPSimExecutiveAgent
from orgconstruct.models.erpsim_decision import ERPSimContext, ERPSimDecision
from orgconstruct.agents.tools.executor import ToolRegistry
from orgconstruct.models.erpsim_decision import (
ERPSimContext,
ERPSimDecision,
PrivateContext,
PublicContext,
)

logger = logging.getLogger(__name__)

Expand All @@ -25,7 +35,7 @@ class ERPSimDiscussion:
def __init__(
self,
agents: list[ERPSimExecutiveAgent],
max_rounds: int = 2,
max_rounds: int = 3,
) -> None:
self.agents = agents
self.max_rounds = max_rounds
Expand All @@ -36,34 +46,173 @@ def __init__(
def ceo(self) -> ERPSimExecutiveAgent:
return self._agent_by_role["CEO"]

async def run(self, context: ERPSimContext) -> ERPSimDecision:
"""Run discussion rounds and return the CEO's final decision."""
async def run(self, context: ERPSimContext) -> tuple[ERPSimDecision, dict]:
"""Run discussion rounds and return the CEO's final decision.

Legacy mode: uses ERPSimContext (backward compatible).

Returns:
Tuple of (ERPSimDecision, token_usage dict with prompt/completion/total tokens).
"""
self.last_discussion_history = []
total_prompt = 0
total_completion = 0

for round_num in range(1, self.max_rounds + 1):
for agent in self.agents:
response = await agent.respond(context, self.last_discussion_history)
result = await agent.respond(context, self.last_discussion_history)
usage = result.get("usage", {})
pt = usage.get("prompt_tokens", 0)
ct = usage.get("completion_tokens", 0)
total_prompt += pt
total_completion += ct
self.last_discussion_history.append(
{
"round": round_num,
"role": agent.role,
"content": response,
"content": result["content"],
"token_count": pt + ct,
}
)
# Throttle to avoid rate limits on fast sequential calls
await asyncio.sleep(_INTER_AGENT_DELAY)

# Brief pause before final decision
await asyncio.sleep(_INTER_AGENT_DELAY)
decision = await self.ceo.make_final_decision(
decision, final_usage = await self.ceo.make_final_decision(
context,
self.last_discussion_history,
)
total_prompt += final_usage.get("prompt_tokens", 0)
total_completion += final_usage.get("completion_tokens", 0)

logger.info(
"Discussion complete: %d messages, decision has %d prices, tokens=%d",
len(self.last_discussion_history),
len(decision.pricing),
total_prompt + total_completion,
)

token_usage = {
"prompt_tokens": total_prompt,
"completion_tokens": total_completion,
"total_tokens": total_prompt + total_completion,
}
return decision, token_usage

async def run_react(
self,
public_ctx: PublicContext,
private_ctx: PrivateContext,
tools: ToolRegistry,
) -> tuple[ERPSimDecision, dict]:
"""ReAct-enhanced discussion: agents gather data independently → discuss → CEO decides.

Flow:
1. Each agent independently runs react_loop to gather data
2. Agents share their assessments in N discussion rounds
3. CEO runs react_decision for the final structured decision

Returns:
Tuple of (ERPSimDecision, token_usage dict).
"""
self.last_discussion_history = []
total_prompt = 0
total_completion = 0

# Phase 1: Independent ReAct (each agent gathers data)
agent_assessments: dict[str, str] = {}
for agent in self.agents:
task = self._get_agent_task(agent.role)
assessment = await agent.react_loop(public_ctx, private_ctx, tools, task)
agent_assessments[agent.role] = assessment
await asyncio.sleep(_INTER_AGENT_DELAY)

# Phase 2: Discussion rounds (agents share assessments)
for round_num in range(1, self.max_rounds + 1):
for agent in self.agents:
# Build discussion prompt including previous assessments
history_text = self._format_history_for_discussion()

other_assessments = {
role: assessment[:500]
for role, assessment in agent_assessments.items()
if role != agent.role
}
others_text = "\n".join(
f"{role}: {text}" for role, text in other_assessments.items()
)

prompt = (
f"You are in a team discussion for Round {public_ctx.round_number}.\n\n"
f"Your team members have shared their analyses:\n{others_text}\n\n"
f"Discussion so far:\n{history_text}\n\n"
f"As {agent.display_name}, provide your perspective. "
f"Keep your response concise (2-3 paragraphs)."
)

messages = [
{"role": "user", "content": prompt},
]
response = await agent.llm_client.chat(
messages, temperature=0.3, max_tokens=4096
)
usage = response.usage
pt = usage.get("prompt_tokens", 0)
ct = usage.get("completion_tokens", 0)
total_prompt += pt
total_completion += ct
self.last_discussion_history.append(
{
"round": round_num,
"role": agent.role,
"content": response.content,
"token_count": pt + ct,
}
)
await asyncio.sleep(_INTER_AGENT_DELAY)

# Phase 3: CEO final ReAct decision
discussion_summary = self._format_history_for_discussion()
decision = await self.ceo.react_decision(
public_ctx, private_ctx, tools, discussion_summary
)

logger.info(
"Discussion complete: %d messages, decision has %d prices",
"ReAct discussion complete: %d messages, decision has %d prices, tokens=%d",
len(self.last_discussion_history),
len(decision.pricing),
total_prompt + total_completion,
)

token_usage = {
"prompt_tokens": total_prompt,
"completion_tokens": total_completion,
"total_tokens": total_prompt + total_completion,
}
return decision, token_usage

def _get_agent_task(self, role: str) -> str:
"""Return a role-specific task description for ReAct."""
tasks = {
"CEO": (
"Analyze the overall business situation. Check financial health, "
"production capacity, and market conditions. Identify key risks and opportunities."
),
"CFO": (
"Analyze the financial situation. Check cash position, loan status, "
"interest payments, and revenue trends. Identify cost optimization opportunities."
),
"CMO": (
"Analyze the market situation. Check sales performance by SKU and DC, "
"competitor pricing, market share, and demand patterns."
),
}
return tasks.get(
role, "Analyze the current situation and prepare your assessment."
)

return decision
def _format_history_for_discussion(self) -> str:
"""Format discussion history as text."""
lines = []
for msg in self.last_discussion_history:
lines.append(f"{msg['role']}: {msg['content'][:300]}")
return "\n".join(lines)
Loading