diff --git a/s05_todo_write/code.py b/s05_todo_write/code.py index 9c88de35..0529cba3 100644 --- a/s05_todo_write/code.py +++ b/s05_todo_write/code.py @@ -232,10 +232,8 @@ register_hook("Stop", summary_hook) # agent_loop — same as s04 + nag reminder counter # ═══════════════════════════════════════════════════════════ -rounds_since_todo = 0 - def agent_loop(messages: list): - global rounds_since_todo + rounds_since_todo = 0 while True: # s05: nag reminder — inject if model hasn't updated todos for 3 rounds if rounds_since_todo >= 3 and messages: diff --git a/s06_subagent/code.py b/s06_subagent/code.py index 2732246b..901a6368 100644 --- a/s06_subagent/code.py +++ b/s06_subagent/code.py @@ -312,10 +312,8 @@ register_hook("Stop", summary_hook) # agent_loop — same as s05 + nag reminder, task auto-dispatches # ═══════════════════════════════════════════════════════════ -rounds_since_todo = 0 - def agent_loop(messages: list): - global rounds_since_todo + rounds_since_todo = 0 while True: # s05: nag reminder if rounds_since_todo >= 3 and messages: diff --git a/s07_skill_loading/code.py b/s07_skill_loading/code.py index b4506fc5..45c04313 100644 --- a/s07_skill_loading/code.py +++ b/s07_skill_loading/code.py @@ -356,10 +356,8 @@ register_hook("Stop", summary_hook) # agent_loop — same as s05-s06 + nag reminder # ═══════════════════════════════════════════════════════════ -rounds_since_todo = 0 - def agent_loop(messages: list): - global rounds_since_todo + rounds_since_todo = 0 while True: if rounds_since_todo >= 3 and messages: messages.append({"role": "user", diff --git a/s08_context_compact/README.en.md b/s08_context_compact/README.en.md index 15199c37..b35b7125 100644 --- a/s08_context_compact/README.en.md +++ b/s08_context_compact/README.en.md @@ -136,7 +136,7 @@ def compact_history(messages): Sometimes the API still returns `prompt_too_long` (413) — when context grows faster than compression triggers. -This triggers **reactive_compact**: more aggressive than compact_history, it retreats from the tail, but still avoids leaving an orphaned `tool_result`. +This triggers **reactive_compact**: more aggressive than compact_history in trigger (emergency response to a 413 error), but more conservative in what it removes, keeping ~5 recent messages and only summarizing earlier history. Still avoids an orphaned `tool_result`. ```python def reactive_compact(messages): diff --git a/s08_context_compact/README.ja.md b/s08_context_compact/README.ja.md index da174d0a..9d8ba810 100644 --- a/s08_context_compact/README.ja.md +++ b/s08_context_compact/README.ja.md @@ -136,7 +136,7 @@ def compact_history(messages): API がまだ `prompt_too_long`(413)を返すことがある。コンテキストの増加速度が圧縮のトリガー速度を上回る場合。 -この時 **reactive_compact** がトリガーされる:compact_history よりもさらに積極的だが、末尾を残す際も孤立した `tool_result` を残さないようにする。 +この時 **reactive_compact** がトリガーされる。トリガー方式は compact_history より積極的(413 エラー後の緊急対応)だが、圧縮方針はより温和で、末尾約 5 件のメッセージを保持し、早期の履歴だけを要約する。孤立した `tool_result` を残さないよう配慮する。 ```python def reactive_compact(messages): diff --git a/s08_context_compact/README.md b/s08_context_compact/README.md index ab5dae54..7f63b49f 100644 --- a/s08_context_compact/README.md +++ b/s08_context_compact/README.md @@ -136,7 +136,7 @@ def compact_history(messages): 有时候 API 还是返回 `prompt_too_long`(413),上下文增长速度快于压缩触发速度时。 -这时触发 **reactive_compact**:比 compact_history 更激进,从尾部回退,但仍要避免留下孤立 `tool_result`。 +这时触发 **reactive_compact**:触发方式比 compact_history 更激进(API 报错后的应急手段),但压缩策略更温和,保留最近约 5 条原始消息,只总结较早历史。同样避免留下孤立 `tool_result`。 ```python def reactive_compact(messages): diff --git a/s10_system_prompt/README.en.md b/s10_system_prompt/README.en.md index fcaa99c9..bcf1811a 100644 --- a/s10_system_prompt/README.en.md +++ b/s10_system_prompt/README.en.md @@ -68,9 +68,6 @@ Split the monolithic string into a dictionary, each key is a topic: ```python PROMPT_SECTIONS = { "identity": "You are a coding agent. Act, don't explain.", - "tools": "Available tools: bash, read_file, write_file.", - "workspace": f"Working directory: {WORKDIR}", - "memory": "Relevant memories are injected below when available.", } ``` @@ -86,8 +83,12 @@ def assemble_system_prompt(context: dict) -> str: # Always loaded sections.append(PROMPT_SECTIONS["identity"]) - sections.append(PROMPT_SECTIONS["tools"]) - sections.append(PROMPT_SECTIONS["workspace"]) + + # Dynamic — tools and workspace from context + tools = ", ".join(context.get("enabled_tools", [])) + if tools: + sections.append(f"Available tools: {tools}.") + sections.append(f"Working directory: {context.get("workspace", WORKDIR)}") # On-demand — based on real state, not keywords memories = context.get("memories", "") diff --git a/s10_system_prompt/README.ja.md b/s10_system_prompt/README.ja.md index 50f8f95f..cfe8f95b 100644 --- a/s10_system_prompt/README.ja.md +++ b/s10_system_prompt/README.ja.md @@ -68,9 +68,6 @@ s10 は prompt アセンブリ機構に焦点を当てる。s08-s09 の能力を ```python PROMPT_SECTIONS = { "identity": "You are a coding agent. Act, don't explain.", - "tools": "Available tools: bash, read_file, write_file.", - "workspace": f"Working directory: {WORKDIR}", - "memory": "Relevant memories are injected below when available.", } ``` @@ -86,8 +83,12 @@ def assemble_system_prompt(context: dict) -> str: # 常にロード sections.append(PROMPT_SECTIONS["identity"]) - sections.append(PROMPT_SECTIONS["tools"]) - sections.append(PROMPT_SECTIONS["workspace"]) + + # context から動的に tools と workspace を取得 + tools = ", ".join(context.get("enabled_tools", [])) + if tools: + sections.append(f"Available tools: {tools}.") + sections.append(f"Working directory: {context.get("workspace", WORKDIR)}") # オンデマンド — 実際の状態に基づく、キーワードではない memories = context.get("memories", "") diff --git a/s10_system_prompt/README.md b/s10_system_prompt/README.md index 2ed0d6a5..0cd95930 100644 --- a/s10_system_prompt/README.md +++ b/s10_system_prompt/README.md @@ -68,9 +68,6 @@ s10 聚焦 prompt 组装机制。以 s08-s09 的能力为背景,但不重复 ```python PROMPT_SECTIONS = { "identity": "You are a coding agent. Act, don't explain.", - "tools": "Available tools: bash, read_file, write_file.", - "workspace": f"Working directory: {WORKDIR}", - "memory": "Relevant memories are injected below when available.", } ``` @@ -86,8 +83,12 @@ def assemble_system_prompt(context: dict) -> str: # 始终加载 sections.append(PROMPT_SECTIONS["identity"]) - sections.append(PROMPT_SECTIONS["tools"]) - sections.append(PROMPT_SECTIONS["workspace"]) + + # 从 context 动态获取 tools 和 workspace + tools = ", ".join(context.get("enabled_tools", [])) + if tools: + sections.append(f"Available tools: {tools}.") + sections.append(f"Working directory: {context.get("workspace", WORKDIR)}") # 按需加载 — 基于真实状态,不是关键词 memories = context.get("memories", "") diff --git a/s10_system_prompt/code.py b/s10_system_prompt/code.py index 723adc6d..be1d5f3d 100644 --- a/s10_system_prompt/code.py +++ b/s10_system_prompt/code.py @@ -41,9 +41,6 @@ MODEL = os.environ["MODEL_ID"] PROMPT_SECTIONS = { "identity": "You are a coding agent. Act, don't explain.", - "tools": "Available tools: bash, read_file, write_file.", - "workspace": f"Working directory: {WORKDIR}", - "memory": "Relevant memories are injected below when available.", } @@ -51,10 +48,14 @@ def assemble_system_prompt(context: dict) -> str: """Select and join prompt sections based on current context.""" sections = [] - # Always loaded — identity, tools, workspace + # Always loaded — identity sections.append(PROMPT_SECTIONS["identity"]) - sections.append(PROMPT_SECTIONS["tools"]) - sections.append(PROMPT_SECTIONS["workspace"]) + + # Dynamic — tools and workspace from context + tools = ", ".join(context.get("enabled_tools", [])) + if tools: + sections.append(f"Available tools: {tools}.") + sections.append(f"Working directory: {context.get("workspace", WORKDIR)}") # Conditional — memory loaded when MEMORY.md exists and has content memories = context.get("memories", "") diff --git a/s11_error_recovery/code.py b/s11_error_recovery/code.py index 8a4862b8..f54fe28c 100644 --- a/s11_error_recovery/code.py +++ b/s11_error_recovery/code.py @@ -272,10 +272,10 @@ def agent_loop(messages: list, context: dict): # ── LLM call: with_retry handles 429/529, outer handles rest ── try: response = with_retry( - lambda mt=max_tokens, mdl=state.current_model: - client.messages.create( - model=mdl, system=system, messages=messages, - tools=TOOLS, max_tokens=mt), + lambda: client.messages.create( + model=state.current_model, system=system, + messages=messages, tools=TOOLS, + max_tokens=max_tokens), state) except Exception as e: # Path 2: prompt_too_long -> reactive compact (once) diff --git a/s17_autonomous_agents/README.en.md b/s17_autonomous_agents/README.en.md index 2d676faf..d229194b 100644 --- a/s17_autonomous_agents/README.en.md +++ b/s17_autonomous_agents/README.en.md @@ -195,15 +195,15 @@ Two teammates claim and work in parallel. Lead only creates tasks and spawns tea | Component | Before (s16) | After (s17) | |-----------|-------------|-------------| | Task assignment | Lead manually assigns | Teammates auto-claim (can_start checks deps) | -| Teammate state | WORK or exit | WORK → IDLE (60s poll) → SHUTDOWN | +| Teammate state | WORK → IDLE (1s inbox poll) → WORK / SHUTDOWN | WORK → IDLE (5s inbox + task board poll, 60s timeout) → WORK / SHUTDOWN | | claim_task | No owner check | Rejects tasks that already have an owner | -| IDLE phase shutdown | Doesn't handle shutdown_request | Dispatches shutdown immediately and exits | -| Lead inbox | Prints only, not in context | consume_lead_inbox injects into history | -| New functions | — | idle_poll, scan_unclaimed_tasks, consume_lead_inbox | +| IDLE phase shutdown | Exits after receiving shutdown_request | Dispatches shutdown immediately and exits | +| Lead inbox | consume_lead_inbox routes protocol responses and injects into context | Reuses consume_lead_inbox mechanism | +| New functions | consume_lead_inbox already exists | idle_poll, scan_unclaimed_tasks (reuses consume_lead_inbox) | | Identity persistence | System prompt only | Auto re-inject after compression | -| Lead tools | 14 (s16) | 14 (unchanged) | +| Lead tools | 14 | 14 (unchanged) | | Teammate tools | 5 | 8 (+ list_tasks, claim_task, complete_task) | -| Teammate exit | Exit after task done | Exit only after 60s idle timeout | +| Teammate exit | WORK ends → enters IDLE, waits for shutdown_request (no timeout) | Exits after 60s idle timeout or receiving shutdown_request | --- diff --git a/s17_autonomous_agents/README.ja.md b/s17_autonomous_agents/README.ja.md index a88642d8..14177872 100644 --- a/s17_autonomous_agents/README.ja.md +++ b/s17_autonomous_agents/README.ja.md @@ -195,15 +195,15 @@ if len(messages) <= 3: | コンポーネント | 変更前 (s16) | 変更後 (s17) | |--------------|------------|------------| | タスク割り当て | Lead が手動 assign | チームメイトが自動認領(can_start で依存確認) | -| チームメイト状態 | WORK または終了 | WORK → IDLE(60s ポーリング) → SHUTDOWN | +| チームメイト状態 | WORK → IDLE(1s 間隔で inbox をポーリング)→ WORK / SHUTDOWN | WORK → IDLE(5s 間隔で inbox + タスクボードをポーリング、60s タイムアウト)→ WORK / SHUTDOWN | | claim_task | owner チェックなし | 既に owner があるタスクを拒否 | -| IDLE フェーズシャットダウン | shutdown_request を処理しない | 即座にシャットダウンをディスパッチして終了 | -| Lead inbox | 印刷のみ、コンテキストに入らない | consume_lead_inbox で history に注入 | -| 新規関数 | — | idle_poll, scan_unclaimed_tasks, consume_lead_inbox | +| IDLE フェーズシャットダウン | shutdown_request を受信後に終了 | 即座にシャットダウンをディスパッチして終了 | +| Lead inbox | consume_lead_inbox がプロトコル応答をルーティングしコンテキストに注入 | consume_lead_inbox 機構を踏襲 | +| 新規関数 | consume_lead_inbox は既存 | idle_poll, scan_unclaimed_tasks(consume_lead_inbox を踏襲) | | 身份保持 | system prompt のみ | 圧縮後に自動再注入 | -| Lead ツール | 14 (s16) | 14(変更なし) | +| Lead ツール | 14 | 14(変更なし) | | チームメイトツール | 5 | 8(+ list_tasks, claim_task, complete_task) | -| チームメイト終了条件 | タスク完了後即終了 | 60s アイドルタイムアウト後のみ終了 | +| チームメイト終了条件 | WORK 完了後 IDLE に入り、shutdown_request を待って終了(タイムアウトなし) | 60s アイドルタイムアウトまたは shutdown_request 受信で終了 | --- diff --git a/s17_autonomous_agents/README.md b/s17_autonomous_agents/README.md index b3192c2d..0370cdbc 100644 --- a/s17_autonomous_agents/README.md +++ b/s17_autonomous_agents/README.md @@ -195,15 +195,15 @@ if len(messages) <= 3: | 组件 | 之前 (s16) | 之后 (s17) | |------|-----------|-----------| | 任务分配 | Lead 手动 assign | 队友自动认领(can_start 检查依赖) | -| 队友状态 | WORK 或退出 | WORK → IDLE(轮询 60s) → SHUTDOWN | +| 队友状态 | WORK → IDLE(每 1s 轮询 inbox)→ WORK / SHUTDOWN | WORK → IDLE(每 5s 轮询 inbox + 任务板,60s 超时)→ WORK / SHUTDOWN | | claim_task | 无 owner 检查 | 拒绝已有 owner 的任务 | -| IDLE 阶段关机 | 不处理 shutdown_request | 直接 dispatch shutdown 并退出 | -| Lead inbox | 只打印,不进上下文 | consume_lead_inbox 统一注入 history | -| 新函数 | — | idle_poll, scan_unclaimed_tasks, consume_lead_inbox | +| IDLE 阶段关机 | 收到 shutdown_request 后退出 | 直接 dispatch shutdown 并退出 | +| Lead inbox | consume_lead_inbox 路由协议响应并注入上下文 | 沿用 consume_lead_inbox 机制 | +| 新函数 | 已有 consume_lead_inbox | idle_poll, scan_unclaimed_tasks(沿用 consume_lead_inbox) | | 身份保持 | 仅 system prompt | 压缩后自动重注入 | -| Lead 工具 | 14 (s16) | 14(不变) | +| Lead 工具 | 14 | 14(不变) | | 队友工具 | 5 | 8(+ list_tasks, claim_task, complete_task) | -| 队友退出条件 | 完成任务即退出 | 60s 无新任务才退出 | +| 队友退出条件 | WORK 完进入 IDLE,等待 shutdown_request 后退出(无超时) | 60s 无新任务或收到 shutdown_request 后退出 | ---