这周末快速浏览了一遍
learn-claude-code项目。Agent 本身并没有涉及太多复杂算法,重点主要在于整体架构和能力逐步演进的设计思路。下面按章节简单记录一下。
s01:经典 Agent 循环
s02:工具路由
对工具调用逻辑进行封装。
原本是根据工具名称通过一系列 if 判断选择执行逻辑,现在改为从 tool_handlers 工具路由表中查找对应的处理函数。
s03:权限控制
在工具调用前加入权限判断,大致包含三道闸门:
- 始终拒绝执行的命令;
- 命中特定规则时,需要询问用户;
- 由用户进行最终审批。
s04:Hook 机制
引入 Hook,将权限判断、日志记录和结果后处理等逻辑拆分到不同的生命周期节点中,例如:
PreToolUsePostToolUseStop
这样可以在不侵入核心 Agent Loop 的情况下扩展各种处理逻辑。
s05:Todo List
加入待办事项列表。
本质上是维护一份由 LLM 自己生成和更新的任务列表,帮助模型记录任务步骤及当前进度。
s06:Sub-agent
加入子 Agent。
除了不与主 Agent 共享上下文、不能继续创建子 Agent 之外,子 Agent 的能力与主 Agent 基本一致。
值得注意的是,Todo List 和 Sub-agent 都是以 Tool 的形式提供给模型的。
s07:Skill 加载
引入 Skill 机制。
它的本质是将"启动时加载全部上下文"改为"按需加载上下文"。系统需要通过一个 Skill Loader 扫描指定目录,整理出可用的 Skill 列表,并在启动时将这份列表提供给 LLM。
模型需要使用某项能力时,再加载对应 Skill 的详细内容,从而减少初始上下文占用。
s08:上下文压缩
上下文通常主要被大量工具调用结果占用,因此压缩策略会优先处理工具输出。
每轮对话会依次进行多层压缩:
- 当工具调用结果的总大小超过阈值时,将体积最大的结果写入本地文件,上下文中只保留索引;
- 当消息数量超过阈值时,只保留头部和尾部消息的完整内容,中间消息替换为索引;
- 仅保留最近 3 条消息的完整内容,其余消息显示为占位符;已经落盘的内容继续保留文件路径。
如果经过上述处理后,上下文仍然超过阈值,才会调用 LLM 对历史内容生成摘要,并用摘要替换此前的上下文。
与此同时,完整的历史消息仍会写入本地文件,避免信息永久丢失。
s09:记忆机制
实现记忆的存储、召回、提取和整理。
它解决的问题与 s07 类似:每次开启新会话时,不必一次性加载全部历史上下文。
在对话过程中,模型会定期从上下文中提取值得长期保留的信息,并写入 .memory/*.md 文件。下一次开启会话时,系统只会将 MEMORY.md 记忆索引加入 System Prompt,由模型根据需要加载具体记忆。
主要包含以下四类记忆:
| 类型 | 保存内容 | 示例 |
|---|---|---|
user |
用户的长期偏好 | "使用 Tab 缩进" |
feedback |
后续仍然适用的工作反馈 | "不要 Mock 数据库" |
project |
相对稳定的项目事实 | "认证模块重写是由合规要求驱动的" |
reference |
外部资料或信息查找线索 | "流水线问题记录在 Linear 的 INGEST 项目中" |
s10:Task 机制
加入 Task 机制。
它与 s05 中 Todo List 的区别在于:Task 的抽象层次更高,而且能够跨会话保存。
系统需要通过 .task/*.json 文件持久化维护任务状态;Todo List 更偏向单次执行过程中的步骤管理,而 Task 更接近长期任务或项目级工作的管理机制。
后续章节
后续几个章节主要介绍:
- 定时任务;
- Multi-agent 协作;
- MCP,即外部工具和服务的接入。