mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-06-20 20:23:36 +08:00
* feat: s01-s14 docs quality overhaul — tool pipeline, single-agent, knowledge & resilience Rewrite code.py and README (zh/en/ja) for s01-s14, each chapter building incrementally on the previous. Key fixes across chapters: - s01-s04: agent loop, tool dispatch, permission pipeline, hooks - s05-s08: todo write, subagent, skill loading, context compact - s09-s11: memory system, system prompt assembly, error recovery - s12-s14: task graph, background tasks, cron scheduler All chapters CC source-verified. Code inherits fixes forward (PROMPT_SECTIONS, json.dumps cache, real-state context, can_start dep protection, etc.). * feat: s15-s19 docs quality overhaul — multi-agent platform: teams, protocols, autonomy, worktree, MCP tools Rewrite code.py and README (zh/en/ja) for s15-s19, the multi-agent platform chapters. Each chapter inherits all previous fixes and adds one mechanism: - s15: agent teams (TeamCreate, teammate threads, shared task list) - s16: team protocols (plan approval, shutdown handshake, consume_inbox) - s17: autonomous agents (idle polling, auto-claim, consume_lead_inbox) - s18: worktree isolation (git worktree, bind_task, cwd switching, safety) - s19: MCP tools (MCPClient, normalize_mcp_name, assemble_tool_pool, no cache) All appendix source code references verified against CC source. Config priority corrected: claude.ai < plugin < user < project < local. * fix: 5 regressions across s05-s19 — glob safety, todo validation, memory extraction, protocol types, dep crash - s05-s09: glob results now filter with is_relative_to(WORKDIR) (inherited from s02) - s06-s08: todo_write validates content/status required fields (inherited from s05) - s09: extract_memories uses pre-compression snapshot instead of compacted messages - s16: submit_plan docstring clarifies protocol-only (not code-level gate) - s17-s19: match_response restores type mismatch validation (from s16) - s17-s19: claim_task deps list handles missing dep files without crashing * fix: s12 Todo V2 logic reversal, s14/s15 cron range validation, s18/s19 worktree name validation - s12 README (zh/en/ja): fix Todo V2 direction — interactive defaults to Task, non-interactive/SDK defaults to TodoWrite. Fix env var name to CLAUDE_CODE_ENABLE_TASKS (not TODO_V2). - s14/s15: add _validate_cron_field with per-field range checks (minute 0-59, hour 0-23, dom 1-31, month 1-12, dow 0-6), step > 0, range lo <= hi. Replace old try/except validation that only caught exceptions. - s18/s19: add validate_worktree_name() to remove_worktree and keep_worktree, not just create_worktree. * fix: align s16-s19 teaching tool consistency * fix pr265 chapter diagrams * Add comprehensive s20 harness chapter * Fix chapter smoke test regressions * Clarify README tutorial track transition --------- Co-authored-by: Haoran <bill-billion@outlook.com>
138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
s01_agent_loop.py - The Agent Loop
|
|
|
|
The entire secret of an AI coding agent in one pattern:
|
|
|
|
while stop_reason == "tool_use":
|
|
response = LLM(messages, tools)
|
|
execute tools
|
|
append results
|
|
|
|
+----------+ +-------+ +---------+
|
|
| User | ---> | LLM | ---> | Tool |
|
|
| prompt | | | | execute |
|
|
+----------+ +---+---+ +----+----+
|
|
^ |
|
|
| tool_result |
|
|
+---------------+
|
|
(loop continues)
|
|
|
|
This is the core loop: feed tool results back to the model
|
|
until the model decides to stop. Production agents layer
|
|
policy, hooks, and lifecycle controls on top.
|
|
|
|
Usage:
|
|
pip install anthropic python-dotenv
|
|
ANTHROPIC_API_KEY=... python s01_agent_loop/code.py
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
try:
|
|
import readline
|
|
# macOS 的 libedit 在处理中文输入时有退格问题,这四行修复它
|
|
readline.parse_and_bind('set bind-tty-special-chars off')
|
|
readline.parse_and_bind('set input-meta on')
|
|
readline.parse_and_bind('set output-meta on')
|
|
readline.parse_and_bind('set convert-meta off')
|
|
except ImportError:
|
|
pass
|
|
|
|
from anthropic import Anthropic
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv(override=True)
|
|
|
|
if os.getenv("ANTHROPIC_BASE_URL"):
|
|
os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)
|
|
|
|
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
|
|
MODEL = os.environ["MODEL_ID"]
|
|
|
|
SYSTEM = f"You are a coding agent at {os.getcwd()}. Use bash to solve tasks. Act, don't explain."
|
|
|
|
# ── Tool definition: just bash ────────────────────────────
|
|
TOOLS = [{
|
|
"name": "bash",
|
|
"description": "Run a shell command.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {"command": {"type": "string"}},
|
|
"required": ["command"],
|
|
},
|
|
}]
|
|
|
|
|
|
# ── Tool execution ────────────────────────────────────────
|
|
def run_bash(command: str) -> str:
|
|
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
|
|
if any(d in command for d in dangerous):
|
|
return "Error: Dangerous command blocked"
|
|
try:
|
|
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
|
|
capture_output=True, text=True, timeout=120)
|
|
out = (r.stdout + r.stderr).strip()
|
|
return out[:50000] if out else "(no output)"
|
|
except subprocess.TimeoutExpired:
|
|
return "Error: Timeout (120s)"
|
|
except (FileNotFoundError, OSError) as e:
|
|
return f"Error: {e}"
|
|
|
|
|
|
# ── The core pattern: a while loop that calls tools until the model stops ──
|
|
def agent_loop(messages: list):
|
|
while True:
|
|
response = client.messages.create(
|
|
model=MODEL, system=SYSTEM, messages=messages,
|
|
tools=TOOLS, max_tokens=8000,
|
|
)
|
|
|
|
# Append assistant turn
|
|
messages.append({"role": "assistant", "content": response.content})
|
|
|
|
# If the model didn't call a tool, we're done
|
|
if response.stop_reason != "tool_use":
|
|
return
|
|
|
|
# Execute each tool call, collect results
|
|
results = []
|
|
for block in response.content:
|
|
if block.type == "tool_use":
|
|
print(f"\033[33m$ {block.input['command']}\033[0m")
|
|
output = run_bash(block.input["command"])
|
|
print(output[:200])
|
|
results.append({
|
|
"type": "tool_result",
|
|
"tool_use_id": block.id,
|
|
"content": output,
|
|
})
|
|
|
|
# Feed tool results back, loop continues
|
|
messages.append({"role": "user", "content": results})
|
|
|
|
|
|
# ── Entry point ──────────────────────────────────────────
|
|
if __name__ == "__main__":
|
|
print("s01: Agent Loop")
|
|
print("输入问题,回车发送。输入 q 退出。\n")
|
|
|
|
history = []
|
|
while True:
|
|
try:
|
|
query = input("\033[36ms01 >> \033[0m")
|
|
except (EOFError, KeyboardInterrupt):
|
|
break
|
|
if query.strip().lower() in ("q", "exit", ""):
|
|
break
|
|
history.append({"role": "user", "content": query})
|
|
agent_loop(history)
|
|
# Print the model's final text response
|
|
response_content = history[-1]["content"]
|
|
if isinstance(response_content, list):
|
|
for block in response_content:
|
|
if getattr(block, "type", None) == "text":
|
|
print(block.text)
|
|
print()
|