#!/usr/bin/env python3 """ s01_agent_loop.py - Agent 循环 AI 编码 Agent 的核心秘密可以浓缩成一个模式: while stop_reason == "tool_use": response = LLM(messages, tools) 执行工具 追加结果 +----------+ +-------+ +---------+ | User | ---> | LLM | ---> | Tool | | prompt | | | | execute | +----------+ +---+---+ +----+----+ ^ | | tool_result | +---------------+ (循环继续) 这就是核心循环:把工具结果喂回给模型,直到模型决定停止。 生产级 Agent 会在这个基础上叠加策略、Hooks 和生命周期控制。 用法: 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"你是位于 {os.getcwd()}. 使用 bash 解决任务。直接行动,不要只解释。" # ── 工具定义:只有 bash ──────────────────────────── TOOLS = [{ "name": "bash", "description": "运行一条 shell 命令。", "input_schema": { "type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"], }, }] # ── 工具执行 ──────────────────────────────────────── def run_bash(command: str) -> str: dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"] if any(d in command for d in dangerous): return "错误:危险命令已被拦截" 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 "(无输出)" except subprocess.TimeoutExpired: return "错误:执行超时(120 秒)" except (FileNotFoundError, OSError) as e: return f"错误:{e}" # ── 核心模式:while 循环持续调用工具,直到模型停止 ── def agent_loop(messages: list): while True: response = client.messages.create( model=MODEL, system=SYSTEM, messages=messages, tools=TOOLS, max_tokens=8000, ) # 追加 assistant 轮次 messages.append({"role": "assistant", "content": response.content}) # 如果模型没有调用工具,就结束 if response.stop_reason != "tool_use": return # 执行每个工具调用并收集结果 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, }) # 将工具结果喂回去,循环继续 messages.append({"role": "user", "content": results}) # ── 入口 ────────────────────────────────────────── if __name__ == "__main__": print("s01: Agent 循环") 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) # 打印模型最终文本回复 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()