如何实现一套零依赖Agent

2026/03/15

我最近基于 Learn Claude Code 这个项目,按章节把它的 Python 教程实现迁到了 TypeScript。 项目地址: https://github.com/ckvv/learn-claude-code

和教程首页强调的路线一致:先理解最小 loop,再逐层叠加 tool use、task system、background tasks、agent teams 和 worktree isolation。

一切都从最小 Agent Loop 开始

整个项目里,s01_agent_loop.ts。把一个零依赖 agent 的底层模式压缩到了几行逻辑里:

  1. 把当前消息历史发给模型
  2. 如果模型返回 tool call,就执行工具
  3. 把工具结果作为 tool 消息继续喂回模型
  4. 如果没有 tool call,就结束本轮

这也是原教程反复强调的核心模式:模型本身不会“行动”,真正让 agent 动起来的是“模型决策 + 工具执行 + 结果回灌”这个闭环。

TypeScript 版本里,我没有引入额外 SDK 去包这层流程,而是尽量保留最直白的结构。原因很简单:如果你的目标是理解 agent 到底怎么工作,最不该做的事情,就是一开始先把 loop 藏进库里。

这样做有两个很直接的好处:

如果你自己也想做 agent,我很建议先亲手写一遍这种版本。很多所谓“高级能力”,其实都只是给这个循环加约束、加状态、加治理。

零依赖的关键,不是“不用库”,而是不提前把问题抽象错

这里说的“零依赖”,不是一种姿态,也不是为了刻意追求极简。它真正重要的地方在于:你可以先把运行时边界想清楚,再决定哪些东西值得抽象。

很多 agent 项目一上来就会先做这些事:

这些东西当然都可能有价值,但问题在于,如果你还没亲手写过最小 loop,其实并不知道自己是在抽象真实问题,还是在抽象想象中的问题。

所以这次迁移里,一个很明确的决定是:不把教程代码过度封装成库。

仓库里只有一个真正被抽出来的公共模块:src/simple_fetch_client.ts。它做的事情非常少,只是:

这个取舍看起来有点克制,但对“零依赖 agent”这件事很重要。因为一旦你把 tool registry、message adapter、runtime orchestration 全部抽成框架层,文章就会从“讲清楚机制”变成“讲如何使用你的封装”。

这也是为什么 TypeScript 版本仍然坚持“章节单文件、自包含”的风格。比如从 s01s03,你能很直观地看到能力是怎么一点点加上去的:

这条演进路径很重要,因为它揭示了一个经常被忽略的事实:agent 的能力不是靠 prompt 一次性“提示出来”的,而是靠运行时里一层层补出的状态和工具能力建立起来的。

零依赖实现里,最值得认真设计的是协议层,不是框架层

这套教程的原始版本是 Python + Anthropic 风格接口,而我在 TypeScript 里改成了 OpenAI 兼容调用。这个改动不只是换个客户端那么简单,它实际上逼着我重新想清楚一个更关键的问题:

教程里的“agent 概念”,哪些是模型接口无关的,哪些又是强绑定某家 SDK 的?

迁移后我越来越确定,真正稳定的是这几层:

相对不稳定、需要适配的反而是 API 细节,比如消息格式、tool call 返回结构,以及不同模型供应商对内容块的表达方式。

这也是为什么我越来越倾向于把 agent 实现分成两部分:

从 Todo 到 Task System,agent 才开始像一个真正的执行体

我觉得这个项目的分水岭在 s03_todo_write.tss07_task_system.ts

s03 里,模型第一次有了显式的过程状态。todo 工具不是为了让输出更好看,而是为了让 agent 在多步任务里不至于失忆。你会发现,一旦任务不是“一问一答”,而是“先读代码、再改文件、再验证结果”,仅靠对话上下文并不够,模型需要一个外部化的任务面板。

再往后到 s07,任务状态不再只存在内存里,而是落成 .tasks/task_*.json。这一步很关键,因为它意味着:

换句话说,agent 从“会调用工具的聊天机器人”,变成了“带控制平面的执行系统”。

这也是为什么我很认同教程后半段的方向。真正可扩展的零依赖 agent,重点不是继续往 prompt 里塞规则,而是把这些规则沉到外部状态机、任务系统和生命周期管理里。

Worktree 隔离说明:Agent 工程化的重点从“会不会改代码”转向“如何安全地改代码”

到了 s12_worktree_task_isolation.ts,这套教程终于从“会做事”走到了“能在真实团队里安全做事”。

这个章节里我最喜欢的一句注释是:

Tasks are the control plane. Worktrees are the execution plane.

它几乎可以当成整个工程化阶段的纲领。

前面的章节解决的是:

而 worktree 这一层解决的是另一类问题:

所以 s12 里除了任务管理,还引入了 worktree 索引、事件日志、repo root 检测等机制。这些实现不一定复杂,但它们已经非常接近真实 coding agent 产品会面对的问题。

很多人做 agent demo 时,最关心的是“模型能不能自己改一个文件”。但如果你真的想把它变成一个长期可用的开发工具,更关键的问题其实是:

这也是我做完这次 TypeScript 迁移后最大的感受:agent 的难点,后期越来越不像“提示词工程”,而像“运行时系统设计”。

s_full.ts 让我确认了一件事:Claude Code 不是一个功能点,而是一组协同机制

看完整个仓库后,再回头看 s_full.ts 会很有意思。它并没有发明新的魔法,而是把前面章节里的机制组合起来:

这就是我理解这类 agent 产品的正确方式:它们不是一个“超级 prompt”,而是一套多层机制叠加出来的操作系统雏形。

换句话说,如果你想复刻 Claude Code 的能力,不应该上来就问“系统 prompt 怎么写”,而应该先问:

这套教程最好的地方,就是把这些问题拆成了 12 个连续可运行的实验。

结语

Learn Claude Code 这套内容最有价值的地方,在于它没有把 agent 神秘化。它把一个看起来复杂的 AI coding system,还原成了一个个可以单独理解的机制层。

当你把最小 loop、tool dispatch、todo、task、background task、team protocol、worktree isolation 一层层接起来之后,你会发现,所谓“AI Coding Agent”,本质上是一个围绕模型构建出来的可执行 runtime。

而如果你真想把这件事学明白,最好的起点往往不是先找一个最强框架,而是先亲手实现一套零依赖版本。因为只有这样,你才会真正知道哪些东西是核心,哪些只是后来为了规模化协作才加上的壳。

参考

Edit this page on GitHub