mirror of
https://github.com/shareAI-lab/analysis_claude_code.git
synced 2026-06-21 04:33: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>
This commit is contained in:
137
s01_agent_loop/code.py
Normal file
137
s01_agent_loop/code.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user