解构 Claude Code:从 Agent Loop 开始,构建一个真正能工作的智能体

如果你正在学习 Agent 开发,最容易陷入的误区是:把大量精力放在 Prompt 上,却没有理解 Agent 的运行时机制。本文基于 learn-claude-code 项目从 s01 到 s20 的递进实现,围绕 Claude Code 类编码 Agent 的核心机制,完整拆解一个 Agent 应该如何从零构建。

引言:Agent 不是“会调用函数的聊天机器人”

普通聊天应用的流程通常只有一次请求和一次响应:

用户输入 → LLM → 文本回答

Agent 则不同。它不仅要回答问题,还要观察环境、制定计划、调用工具、读取结果、修正策略,并持续执行到目标完成:

用户目标
   ↓
模型判断下一步
   ↓
调用工具改变或观察环境
   ↓
读取工具结果
   ↓
继续判断下一步
   ↓
完成任务或请求用户介入

以 Claude Code 为代表的 Coding Agent,本质上是一个由大语言模型驱动、由宿主程序控制的运行时系统。模型提供推理和决策能力,程序提供工具、状态、权限、上下文和执行保障。

因此,构建 Agent 的关键问题不是“如何写一个万能 Prompt”,而是如何设计以下几个部分:

  1. Agent 如何循环运行;
  2. 模型如何认识并调用工具;
  3. 如何限制高风险行为;
  4. 如何规划和推进长任务;
  5. 如何管理有限的上下文窗口;
  6. 如何在多轮会话之间保留知识;
  7. 如何应对 API 和工具执行失败;
  8. 如何拆分子任务并组织多个 Agent;
  9. 如何隔离并行代码修改;
  10. 如何动态接入外部系统。

learn-claude-code 项目的价值,正是把这些问题拆成 20 个递进模块,让我们能够看到一个 Agent 从 100 多行代码逐渐演变为完整运行时的过程。

一、先建立正确的心智模型

一个可工作的 Agent,可以抽象成五个相互配合的部分。

flowchart LR
    U["用户目标"] --> O["编排器 Orchestrator"]
    O --> L["LLM 决策"]
    L --> T["工具系统"]
    T --> E["外部环境"]
    E --> T
    T --> O
    O --> S["状态与上下文"]
    S --> L
    O --> G["权限与安全"]
    G --> T

1. LLM 是决策器

模型负责理解目标、分析上下文,并决定下一步应该回答、读取文件、执行命令还是委派任务。

2. Orchestrator 是控制器

宿主程序负责调用模型、保存消息、分发工具、处理异常,并判断循环何时结束。项目中的 agent_loop 就承担了这个角色。

3. Tools 是 Agent 的手和眼睛

模型本身不能直接读取本地文件或执行 Shell。它只能输出结构化的工具调用请求,再由宿主程序执行。

4. State 是持续工作的基础

对话消息、Todo、Task、Memory、后台任务和团队邮箱都属于状态。没有外部状态,Agent 很难稳定完成长任务。

5. Guardrails 决定 Agent 的能力边界

权限检查、路径限制、危险命令拦截、用户确认和 Worktree 隔离,共同决定 Agent 可以做什么,以及在什么条件下可以做。

下面按照实际开发顺序逐层构建这些能力。

二、第一层:实现最小 Agent Loop

项目的 s01_agent_loop 展示了最核心的 Agent 循环。其逻辑可以简化为:

def agent_loop(messages):
    while True:
        response = client.messages.create(
            model=MODEL,
            system=SYSTEM,
            messages=messages,
            tools=TOOLS,
        )

        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":
                continue

            output = execute_tool(block.name, block.input)
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": output,
            })

        messages.append({
            "role": "user",
            "content": results,
        })

这个循环包含四个关键协议动作。

第一步:把工具描述交给模型

工具不是只在 Python 中注册一个函数。模型还需要看到工具名称、用途和参数 Schema:

TOOLS = [{
    "name": "bash",
    "description": "Run a shell command",
    "input_schema": {
        "type": "object",
        "properties": {
            "command": {"type": "string"}
        },
        "required": ["command"],
    },
}]

工具描述会影响模型是否选择这个工具、何时使用,以及生成什么参数。它既是接口文档,也是模型行为提示。

第二步:完整保存模型响应

一次响应中可能同时存在文本块和多个 tool_use 块。宿主程序应保存完整的 response.content,而不是只提取文本。

第三步:通过 ID 配对调用和结果

每个 tool_result 必须携带对应的 tool_use_id。这让模型知道某个结果属于哪个调用,也是后续上下文压缩必须维护的重要协议关系。

第四步:由模型决定何时停止

Agent 不应该在执行一次工具后立即结束。工具结果要重新交给模型,由模型判断任务是否完成、是否需要验证,或者是否需要继续行动。

这就是 Agent 与普通 Function Calling 的主要差异:Function Calling 可能只调用一次函数,Agent Loop 则会围绕目标持续运行。

三、第二层:设计可靠的工具系统

只有 Bash 工具的 Agent 可以工作,但它很难被精细控制。s02_tool_use 将能力拆分为:

  • bash:执行命令;
  • read_file:读取文件;
  • write_file:创建或覆盖文件;
  • edit_file:精确替换文本;
  • glob:查找文件。

这种拆分有三个好处。

1. 工具语义更清晰

模型可以明确表达“读取文件”而不是自己拼接 cat 命令。宿主程序也能区分只读操作与写操作。

2. 权限可以按工具分类

读取文件可以自动允许,写入文件可以检查路径,危险 Bash 命令可以要求确认。

3. 参数和输出更容易标准化

专用工具可以控制编码、分页、输出长度和错误格式,不必依赖不同操作系统的 Shell 行为。

工具系统需要四层结构

一个成熟的 Tool Runtime 不应只有函数映射,而应包含:

Tool Schema
    ↓
参数校验与兼容转换
    ↓
权限判断
    ↓
Handler 执行与结果规范化

可以用统一注册表描述:

TOOL_REGISTRY = {
    "read_file": {
        "schema": READ_FILE_SCHEMA,
        "handler": run_read_file,
        "permission": "read",
        "timeout": 30,
    },
}

项目早期模块直接使用:

output = handler(**block.input)

这种写法简单,但模型一旦生成 Schema 之外的参数,Python 就可能抛出 TypeError。项目中 read_fileoffset 问题就是一个真实案例。

模型有时会根据训练知识生成 offset 参数,即使早期 Schema 没有声明它。s19_mcp_plugin 最终采用了更稳健的分发方式:

params = inspect.signature(handler).parameters
safe_input = {
    key: value
    for key, value in block.input.items()
    if key in params
}

try:
    output = handler(**safe_input)
except TypeError as exc:
    output = f"Tool error: {exc}"

对于生产实现,还应该使用 Pydantic 或 JSON Schema Validator 在进入 Handler 前完成严格校验。

需要记住一条原则:模型生成的工具参数属于外部输入,不能因为它来自 LLM 就默认可信。

四、第三层:把安全控制放在执行路径上

模型能调用工具之后,下一个问题不是如何让它做得更多,而是如何让它只做被允许的事情。

s03_permission 将操作分为三类:

明确安全 → 自动执行
明确危险 → 直接拒绝
无法确定 → 请求用户确认

例如:

  • 读取工作区内文件通常可以直接允许;
  • rm -rf /shutdown 等命令应该拒绝;
  • 删除普通项目文件、部署或覆盖配置可能需要用户确认。

权限检查必须发生在工具执行之前

正确顺序应当是:

模型生成 tool_use
    ↓
校验参数
    ↓
检查权限
    ↓
执行 Handler
    ↓
记录和返回结果

如果只在系统提示词里告诉模型“不要执行危险操作”,并不能构成真正的安全边界。Prompt 是行为引导,权限层才是执行约束。

用 Hooks 保持主循环稳定

s04_hooks 把权限、日志、输出处理和上下文注入从 Agent Loop 中抽离出来:

register_hook("PreToolUse", permission_hook)
register_hook("PostToolUse", log_hook)
register_hook("UserPromptSubmit", context_inject_hook)
register_hook("Stop", summary_hook)

这样可以形成类似中间件的扩展结构:

flowchart LR
    A["tool_use"] --> B["PreToolUse Hooks"]
    B -->|允许| C["Tool Handler"]
    B -->|拒绝| D["Blocked tool_result"]
    C --> E["PostToolUse Hooks"]
    E --> F["tool_result"]

Hook 机制的意义不仅是代码整洁。它还让权限、审计、可观测性和策略控制能够独立演进,而不必不断修改 Agent Loop。

五、第四层:让 Agent 学会规划和管理任务状态

模型可以直接连续调用工具,但面对复杂任务时容易出现三个问题:

  • 忘记原始目标;
  • 执行到一半遗漏步骤;
  • 用户无法判断当前进度。

s05_todo_write 引入结构化 Todo:

[
  {"content": "分析项目结构", "status": "completed"},
  {"content": "修改核心代码", "status": "in_progress"},
  {"content": "运行测试", "status": "pending"}
]

Todo 并不是装饰性的 UI,而是 Agent 的外部工作记忆。它把模型当前的执行计划变成宿主程序可以检查的状态。

运行时可以据此实现:

  • 多步骤任务开始前要求创建计划;
  • 限制同时只能有一个 in_progress 项;
  • 工具调用多轮后仍未更新计划时提醒模型;
  • 结束前检查是否仍有未完成任务。

Todo 和 Task 的区别

项目在 s12_task_system 又引入了持久化 Task,两者承担不同角色:

类型 生命周期 作用
Todo 当前会话或当前任务 描述执行步骤和进度
Task 可跨会话持久化 描述可领取、可依赖的工作单元

Task 包含 ID、状态、Owner 和 blockedBy

@dataclass
class Task:
    id: str
    subject: str
    description: str
    status: str
    owner: str | None
    blockedBy: list[str]

有了依赖关系,Agent 才能判断某项工作是否可以开始。这是后续多智能体调度的基础。

六、第五层:通过子 Agent 隔离复杂子任务

当主 Agent 的任务越来越复杂,把所有探索过程都塞进同一个上下文会产生大量噪声。

s06_subagent 增加了 task 工具。主 Agent 可以把一个子问题交给新的 Agent Loop:

主 Agent
  │
  ├── 发送任务描述
  ▼
子 Agent:使用全新 messages 和受限工具集
  │
  ├── 独立读取、分析、执行
  ▼
只返回最终摘要

为什么子 Agent 要使用独立上下文

如果子 Agent 继承主会话全部消息,它仍然会受到大量无关信息影响。全新上下文可以让它专注于一个具体目标。

为什么只返回摘要

文件搜索、命令输出和中间推理可能非常长。主 Agent 通常只需要结论、证据和建议,而不需要复制完整过程。

为什么要禁止无限递归

项目没有给子 Agent 暴露 task 工具,并设置最大运行轮次。否则子 Agent 可以不断生成新的子 Agent,造成成本和资源失控。

因此,一个好的委派工具至少应限制:

  • 子 Agent 可使用的工具;
  • 最大模型调用轮次;
  • 最大 Token 或成本;
  • 最长执行时间;
  • 返回结果的格式;
  • 是否允许继续委派。

七、第六层:用 Skills 扩展能力,而不是无限膨胀系统提示词

随着 Agent 支持的任务越来越多,把所有知识和操作流程都写进系统提示词会迅速消耗上下文。

s07_skill_loading 使用 skills/*/SKILL.md 保存技能。每个技能通过 Frontmatter 描述名称和用途:

---
name: code-review
description: Review code changes and identify correctness risks.
---

# Code Review Workflow
...

启动时,系统只把技能目录放进 Prompt:

- code-review: Review code changes and identify correctness risks.
- agent-builder: Build a minimal coding agent.
- mcp-builder: Create an MCP integration.

当模型确认需要某项技能后,再调用 load_skill 读取正文。

这是一种渐进式披露机制:

先暴露能力索引
    ↓
模型判断相关性
    ↓
按需加载完整说明

Skills 适合承载领域知识、操作步骤、质量标准和工具使用规范,但不应该取代代码层的安全控制。

八、第七层:管理上下文,而不是被上下文窗口管理

长时间运行的 Agent 会不断积累:

  • 用户消息;
  • 模型回答;
  • Tool Use;
  • Tool Result;
  • 大段文件内容;
  • Shell 输出;
  • 子 Agent 摘要。

如果不做处理,请求最终会超过模型上下文限制,或者因为无关历史过多而降低决策质量。

s08_context_compact 实现了四层处理策略。

1. 大型输出外置

超长工具结果写入磁盘,上下文中只保留摘要和文件路径:

[Tool result stored at .task_outputs/tool_results/tool_123.txt]
Summary: test output, 18 failures...

2. Micro Compact

保留消息结构,但用占位内容替换较早的长工具结果:

[Earlier tool result compacted. Re-run if needed.]

3. Snip Compact

保留对话头部和尾部,删除中间低价值消息。

4. Summary Compact

让模型总结历史,只保留目标、已完成工作、关键决策、用户约束和剩余事项。

压缩时最容易踩的坑

tool_usetool_result 是协议配对,不能从中间切断:

assistant: tool_use(id=abc)
user: tool_result(tool_use_id=abc)

如果只保留其中一条,API 会认为消息序列非法。因此压缩器必须识别消息结构,而不是简单按数组下标删除。

成熟的 Agent 应同时具备主动压缩和响应式压缩:前者在接近阈值时提前执行,后者在 API 返回 Prompt Too Long 后作为恢复措施执行。

九、第八层:区分会话上下文和持久记忆

上下文解决的是“本轮模型能看到什么”,记忆解决的是“下次会话还应该知道什么”。

s09_memory 把记忆保存为 .memory 中的 Markdown 文件,并用 MEMORY.md 建立索引。记忆被分为:

  • user:用户偏好;
  • feedback:用户对 Agent 行为的纠正;
  • project:项目结构、命令和约定;
  • reference:外部参考资料。

记忆系统需要解决三个问题

写什么

不是所有对话都值得持久化。适合写入的内容通常具有跨任务复用价值,例如项目测试命令、用户明确偏好和重要架构决策。

什么时候写

可以在用户明确要求、任务完成、收到纠正反馈或检测到稳定项目事实时触发,但应避免模型未经确认大量写入推测性内容。

什么时候读

不应把全部记忆塞入每次请求。系统应该根据当前任务检索相关记忆,再注入动态系统提示词。

因此,Memory 的正确形态不是不断增长的聊天记录,而是可检索、可更新、带来源的长期知识库。

十、第九层:动态组装系统提示词

早期模块使用固定 Prompt:

SYSTEM = "You are a coding agent. Act, don't explain."

但真实 Agent 的系统提示词通常由多个部分组成:

身份与行为准则
+ 当前工作目录
+ 可用工具说明
+ 项目约束
+ 当前任务状态
+ 相关 Skills
+ 相关 Memories
+ 团队角色
+ Worktree 信息

s10_system_prompt 将这些内容拆成 Section,再根据运行时 Context 组装:

def assemble_system_prompt(context):
    sections = [
        PROMPT_SECTIONS["identity"],
        PROMPT_SECTIONS["tools"],
        PROMPT_SECTIONS["workspace"],
    ]

    if context.get("memories"):
        sections.append(
            f"Relevant memories:\n{context['memories']}"
        )

    return "\n\n".join(sections)

动态 Prompt 的核心不是拼接字符串,而是确保模型看到的环境描述与实际运行时保持一致。例如切换 Worktree 后,系统提示词中的工作目录也必须更新。

十一、第十层:为失败设计恢复机制

一个只在理想条件下运行的 Agent 很容易实现。真正困难的是处理以下情况:

  • API 限流或临时不可用;
  • 请求超时;
  • 模型输出达到最大 Token;
  • Prompt 超过上下文窗口;
  • 工具超时或返回异常;
  • 模型调用未知工具;
  • Handler 参数不兼容。

s11_error_recovery 加入了指数退避、最大重试次数、备用模型和输出续写。

不同错误需要不同策略

错误类型 推荐策略
429 / 临时服务错误 指数退避并重试
连续服务过载 切换备用模型或停止重试
max_tokens 注入续写指令继续生成
Prompt Too Long 执行响应式压缩后重试
工具参数错误 转成 tool_result 让模型修正
危险操作 拒绝或请求用户批准

尤其要避免工具异常直接终止 Agent 进程。工具失败本身也是一种环境观察,应该尽可能结构化地反馈给模型,让模型选择修正参数或更换方案。

十二、第十一层:支持后台任务和时间调度

有些操作不适合同步阻塞 Agent Loop,例如启动服务器、运行大型测试或等待构建结果。

s13_background_tasks 把耗时操作放到后台线程,立即向模型返回任务 ID:

[Background task bg_123 started]

任务完成后,运行时把结果作为通知重新注入消息:

[Background task bg_123 completed]
Exit code: 0
Output: 128 tests passed

这样模型可以在等待期间继续处理其他工作。

s14_cron_scheduler 又加入了定时任务,使 Agent 可以被时间事件唤醒。调度系统需要管理:

  • Cron 表达式校验;
  • 一次性与重复任务;
  • 持久化;
  • 避免同一分钟重复触发;
  • Agent 忙碌时排队;
  • Agent 空闲时自动消费。

从架构上看,用户输入、后台任务完成和 Cron 到期都可以统一为事件,再交给 Agent Loop 处理。

十三、第十二层:从子 Agent 进化为多智能体系统

子 Agent 更像一次性的函数调用:接收任务,返回摘要。多智能体系统中的 teammate 则具有更长的生命周期,需要持续通信、领取任务和等待反馈。

s15_agent_teams 引入 MessageBus,为每个 Agent 建立邮箱:

Lead Agent ──message──> Reviewer
Lead Agent <──message── Reviewer

Lead Agent ──message──> Backend Agent
Lead Agent <──message── Backend Agent

只有消息总线还不够

如果消息只是自然语言文本,系统很难判断一条回复对应哪个请求。s16_team_protocols 因此增加:

  • 消息类型;
  • request_id
  • 计划提交;
  • 计划批准或驳回;
  • Shutdown 请求;
  • 协议状态。

例如计划审批流程:

sequenceDiagram
    participant L as Lead
    participant T as Teammate
    L->>T: request_plan(request_id, task)
    T->>L: submit_plan(request_id, plan)
    L->>T: review_plan(request_id, approve/feedback)
    T->>T: 执行或修改计划
    T->>L: 汇报结果

s17_autonomous_agents 进一步允许 teammate 自主领取 Task、推进状态并汇报结果。

多智能体的核心不是并发,而是协调

构建多 Agent 系统时,至少需要回答:

  1. 谁负责分配工作;
  2. 如何避免两个 Agent 重复领取任务;
  3. 如何表达任务依赖;
  4. 如何把响应匹配到请求;
  5. 如何暂停或终止 Agent;
  6. 如何处理 Agent 崩溃;
  7. 多个 Agent 修改代码时如何避免冲突。

线程只是执行载体,Task、Mailbox、Protocol 和 Isolation 才是协作系统的核心。

十四、第十三层:用 Git Worktree 隔离代码修改

多个 Agent 如果在同一个工作目录修改文件,会出现非常实际的冲突:

  • 一个 Agent 覆盖另一个 Agent 的修改;
  • git status 无法区分改动归属;
  • 测试结果受到其他未完成修改影响;
  • Agent 删除或重写彼此正在使用的文件。

s18_worktree_isolation 使用 Git Worktree 为任务建立独立目录和分支:

主仓库
├── 当前工作区
└── .worktrees/
    ├── backend-task/   → branch: wt/backend-task
    └── review-task/    → branch: wt/review-task

Teammate 的系统提示词、文件工具和 Bash cwd 都绑定到自己的 Worktree。

Worktree 生命周期需要保护

创建 Worktree 只是第一步,还应处理:

  • 名称和分支冲突;
  • Task 与 Worktree 的绑定;
  • 未提交改动检查;
  • 保留现场供人工检查;
  • 明确授权后才能丢弃修改;
  • 最终合并或 Cherry-pick。

对于 Coding Agent,文件系统隔离和 Git 状态隔离是并发安全的重要组成部分。

十五、第十四层:通过 MCP 动态扩展工具

如果所有工具都静态写进 Agent,接入数据库、文档平台、部署系统和企业内部服务会变得越来越困难。

MCP 的作用,是让外部服务以统一方式向 Agent 暴露工具和资源。

s19_mcp_plugin 演示了一个简化流程:

Agent 调用 connect_mcp("docs")
    ↓
MCP Client 获取工具列表
    ↓
将工具 Schema 合并到模型可见工具池
    ↓
将 Handler 合并到本地分发表
    ↓
后续轮次可以调用新工具
def assemble_tool_pool():
    tools = list(BUILTIN_TOOLS)
    handlers = dict(BUILTIN_HANDLERS)

    for client in mcp_clients.values():
        tools.extend(client.tool_schemas())
        handlers.update(client.tool_handlers())

    return tools, handlers

项目中的 docs 和 deploy MCP 服务是教学 Mock,但已经体现出动态工具发现的关键思想:Agent 的能力集合不必在启动时完全确定。

真实 MCP 接入还需要处理连接方式、鉴权、工具命名冲突、超时、断线重连、返回内容大小和高风险工具审批。

十六、把所有部分组合成完整 Agent Runtime

最终的 s20_comprehensive 把前面的机制组合到同一个运行时中。

flowchart TD
    EVENT["用户输入 / Cron / 后台通知 / 团队消息"] --> CTX["准备运行时上下文"]
    CTX --> COMPACT["上下文预算与压缩"]
    COMPACT --> PROMPT["组装动态系统提示词"]
    PROMPT --> CALL["调用 LLM"]
    CALL --> DECISION{"是否包含 tool_use"}
    DECISION -->|否| DONE["输出结果并更新状态"]
    DECISION -->|是| VALIDATE["参数校验"]
    VALIDATE --> PERMISSION["Hooks 与权限检查"]
    PERMISSION --> DISPATCH["工具分发"]
    DISPATCH --> BUILTIN["文件 / Bash / Todo / Task"]
    DISPATCH --> AGENTS["Subagent / Teammate"]
    DISPATCH --> ASYNC["后台任务 / Cron"]
    DISPATCH --> MCP["MCP 工具"]
    BUILTIN --> RESULT["标准化 tool_result"]
    AGENTS --> RESULT
    ASYNC --> RESULT
    MCP --> RESULT
    RESULT --> CTX
    CALL -->|API 异常| RECOVERY["重试 / 降级 / 响应式压缩"]
    RECOVERY --> CALL

从这个结构可以看出,一个完整 Agent Runtime 可以划分为八层:

职责 项目对应实现
模型层 推理并生成下一步行动 Anthropic Messages API
循环层 维护 tool-use 循环 s01
工具层 Schema、Handler 与结果 s02、s19
治理层 权限、Hooks、审计 s03、s04
状态层 Todo、Task、Memory s05、s09、s12
上下文层 Skill、Prompt、压缩 s07、s08、s10
可靠性层 重试、后台任务、调度 s11、s13、s14
协作层 子 Agent、团队、Worktree、MCP s06、s15–s19

十七、Agent 开发者应该遵循的实现顺序

如果你准备从零实现自己的 Agent,不建议一开始就开发多智能体平台。更合理的路线是:

阶段一:跑通最小闭环

  • 接入一个模型;
  • 实现 tool_use / tool_result 循环;
  • 提供一个只读工具和一个执行工具;
  • 保存完整消息历史。

完成标准:Agent 能自己读取信息、调用工具并继续推理,直到给出结果。

阶段二:建立工具治理

  • 工具 Schema 与 Handler 统一注册;
  • 参数严格校验;
  • 路径限制;
  • 权限分级;
  • 超时和输出长度限制;
  • 所有异常转成结构化结果。

完成标准:错误工具调用不会导致主循环崩溃,高风险操作无法绕过权限层。

阶段三:支持复杂任务

  • Todo;
  • Task 和依赖;
  • 子 Agent;
  • Skills;
  • 动态系统提示词。

完成标准:Agent 能拆解任务、委派子问题,并保持清晰进度。

阶段四:解决长时间运行问题

  • 上下文预算;
  • 大结果外置;
  • 压缩和摘要;
  • 持久记忆;
  • API 重试和降级;
  • 后台任务。

完成标准:Agent 可以连续工作较长时间,不因上下文或临时错误轻易中断。

阶段五:扩展为协作系统

  • MessageBus;
  • Agent 生命周期;
  • 协议和请求匹配;
  • Worktree 隔离;
  • MCP 动态工具。

完成标准:多个 Agent 能在隔离环境中分工,并把结果可靠地交还给协调者。

十八、当前项目给出的工程启示

learn-claude-code 是一个教学项目。它通过复制上一阶段代码并增加新能力,使读者能够直观看到每一步变化,但这种结构不适合直接作为生产框架。

如果继续工程化,建议从 s20 中拆出以下组件:

agent_runtime/
├── loop.py              # Agent Loop
├── model_client.py      # 模型调用与错误恢复
├── tools/
│   ├── registry.py      # 工具注册
│   ├── validation.py    # 参数校验
│   ├── permissions.py   # 权限策略
│   └── executor.py      # 工具执行
├── context/
│   ├── prompt.py        # 动态 Prompt
│   ├── compaction.py    # 上下文压缩
│   ├── memory.py        # 长期记忆
│   └── skills.py        # Skills
├── tasks/
│   ├── todo.py
│   ├── task_store.py
│   ├── background.py
│   └── scheduler.py
├── agents/
│   ├── subagent.py
│   ├── team.py
│   ├── protocol.py
│   └── mailbox.py
├── isolation/
│   └── worktree.py
└── integrations/
    └── mcp.py

还应补充自动化测试和可观测性,重点覆盖:

  • tool_usetool_result 的配对;
  • 多工具调用顺序;
  • 未知参数和未知工具;
  • 权限拒绝和用户确认;
  • 压缩边界;
  • Task 依赖和并发领取;
  • API 重试;
  • 后台通知注入;
  • Agent 协议状态转换;
  • Worktree 脏状态保护。

总结:真正的 Agent 能力来自运行时,而不只来自模型

通过这个项目可以看到,最初的 Agent Loop 很短,真正不断增长的是围绕模型建立的工程系统。

一个成熟 Agent 的构建逻辑可以概括为:

用 Agent Loop 建立行动闭环
    ↓
用 Tools 连接真实环境
    ↓
用 Permission 和 Hooks 约束行为
    ↓
用 Todo、Task 和 Memory 管理状态
    ↓
用 Skills、Prompt 和 Compact 管理上下文
    ↓
用 Retry、Background 和 Cron 提升可靠性
    ↓
用 Subagent、Team 和 Protocol 扩展协作
    ↓
用 Worktree 和 MCP 实现隔离与开放扩展

对于 Agent 开发学习者,最重要的不是一次性复刻所有功能,而是理解每一层为什么出现:

  • 没有循环,模型只能回答一次;
  • 没有工具,模型无法作用于环境;
  • 没有权限,工具能力会变成风险;
  • 没有状态,长任务会失控;
  • 没有压缩和记忆,Agent 无法长期工作;
  • 没有恢复机制,临时错误会终止任务;
  • 没有协议和隔离,多智能体只会制造更多冲突。

Claude Code 类 Agent 的核心,并不是一个神秘的超级 Prompt,而是一套让模型能够安全、持续、可观察地行动的运行时机制。理解这套机制之后,你才真正具备了从零构建 Agent 的能力。

暂无评论

发送评论 编辑评论


				
上一篇