← All posts

你以为自己在做 Agent,其实你在做 Harness

March 24, 2026AIagentsLLMs

从GitHub项目learn-code-claude 学习 coding agent 的实现。

如果你第一次看到 Claude Code、Codex、Cursor Agent 这类 coding agent,脑子里很容易冒出一种感觉:这东西肯定很复杂,背后一定藏着一整套神秘架构。

这感觉不奇怪。因为今天互联网上关于 agent 的讨论,常常会把它描述成一种近乎玄学的系统:提示词工程、工作流编排、路由逻辑、记忆模块、工具调用、状态机、多 agent 协作,最后再套上一层产品包装,整个东西就显得像一个无法拆解的黑箱。

但如果你真的去看代码,会发现事情没有那么神秘。

对大多数程序员来说,所谓“做一个 agent”,本质上并不是造出一个新的大脑。大脑通常已经由 Anthropic、OpenAI、Google 这些模型厂商提供了。你真正做的,是给这个大脑搭建一套能工作的环境。换句话说,你真正构建的,并不是 agent 本身最核心的智能,而是 agent 的 harness。

这篇文章想讲清楚的,就是这个认知转变:

Agent = LLM + Harness

其中,LLM 是大脑;Harness 是除了大脑之外的一切。工具是 harness,任务系统是 harness,上下文压缩是 harness,知识加载是 harness,子 agent、后台任务、团队协作、worktree 隔离,也全都是 harness。

一旦你用这个角度回头去看,一个 coding agent 就不再神秘了。它不是某种无法理解的“AI 魔法产品”,而是一个很朴素的结构:先有一个最小 agent loop,然后围绕这个 loop,一层一层补齐模型在真实世界工作所需要的能力。

这个仓库最适合拿来讲这件事,因为它不是在抽象谈论 agent,而是把这个生长过程直接写成了代码。从 agents/s01_agent_loop.pyagents/s12_worktree_task_isolation.py,你能看到一个 coding agent 如何从最小闭环,长出工具系统、任务板、上下文管理、团队协作,最后长成一个更接近 Claude Code 的雏形。

如果你过去一直觉得 coding agent 神秘,那这篇文章的目标就是把这层神秘感拆掉。更重要的是,当你看完这些代码后,也许会第一次真正意识到:以后你自己做 agent,真正要做的,几乎都是 harness。

为了避免这篇文章写成一篇平铺直叙的“功能介绍”,你可以带着一个固定问题往下读:

每一章到底在补齐模型缺失的哪一种工作能力?

用这个问题去看 agents/s01s12,很多设计会一下子变得非常清楚。它们不是在堆 feature,而是在一层层补齐模型工作的环境。

1. 先把误解掰正:程序员平时做的,几乎不是 LLM,而是 Harness

我们先把一句最重要的话放在最前面:

大多数程序员平时并不训练自己的基础模型。

你不会自己训练一个 Claude,也不会自己造一个 GPT-5。你调用的是别家提供的模型 API。也就是说,那个负责理解自然语言、推理问题、规划步骤、决定下一步行动的大脑,通常不是你写出来的。

那你到底在写什么?

你在写的是:

  • 模型可以调用哪些工具
  • 每个工具的输入输出长什么样
  • 工具执行结果如何回填给模型
  • 模型能看到多少历史上下文
  • 任务状态存在哪里
  • 长任务如何跨越多轮对话持续进行
  • 多个 agent 之间如何通信
  • 并行改代码时如何避免相互污染

这些东西合起来,就是 harness。

这也是为什么我更喜欢用这样一句话来理解 agent engineering:

你不是在制造大脑,你是在给大脑造身体、造工作台、造工具箱、造记事本、造邮箱、造隔离工作目录。

如果不先把这层认知理顺,后面的很多设计都会看歪。你会误以为 agent 的核心在“提示词怎么写”,在“工作流怎么编排”,在“节点图怎么拖”。但这类系统真正稳定下来,靠的并不是外面那层流程图,而是 harness 是否把模型在真实环境里工作需要的一切补齐了。

接下来,我们就顺着 agents/ 目录往下看。

2. s01_agent_loop.py:Agent 的底座,其实只是一个循环

先看最小形式的 agent。在 agents/s01_agent_loop.py 里,整个系统真正不可再简化的部分,其实就两个东西:一个 bash 工具,一个 agent_loop()

这个文件里最关键的部分,大致就是下面这个模式:

def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        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":
                output = run_bash(block.input["command"])
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output
                })

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

这是整篇文章最重要的一段代码,因为它把 coding agent 的最小闭环全部暴露出来了。

这个闭环里发生的事情其实非常朴素:

  1. 用户给出一个任务,进入 messages
  2. 模型读到这些消息和可用工具定义
  3. 模型决定是直接回答,还是调用工具
  4. 如果它调用了工具,harness 就负责执行工具
  5. 工具结果以 tool_result 的形式重新喂回模型
  6. 模型基于新观察继续推理下一步
  7. 直到模型不再调用工具,循环结束

很多人第一次看懂 agent,往往就是在这一刻。因为这说明了一件事:

agent 不是“一次模型调用”,而是“模型在一个循环里持续观察和行动”。

这里的 LLM 负责决定何时调用工具、调用哪个工具、读到结果后下一步做什么;而 harness 负责把这些决定变成真实执行。两者一旦接起来,一个最小 agent 就成立了。

从工程角度看,这个 while True + tool_use + tool_result 就是地基。后面所有章节,其实都不是在改写这个地基,而是在它外围加结构。

3. s02_tool_use.py:模型不是突然变强了,而是你给了它更好的手脚

有了 s01 之后,模型已经可以通过 bash 触达现实环境。但如果你真的想让它做 coding 工作,很快就会发现一个问题:全靠 shell,太粗糙了。

于是 agents/s02_tool_use.py 做了第一件非常关键的事:不是改 agent loop,而是扩展工具层。

这个文件里有几个非常重要的实现:

  • safe_path():把路径约束在工作区里
  • run_read():专门读文件
  • run_write():专门写文件
  • run_edit():做精确文本替换
  • TOOL_HANDLERS:把工具名映射到处理函数

最关键的代码,不是某一个工具本身,而是这个 dispatch map:

TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
}

这段代码看起来很普通,但它其实体现了一个非常重要的 agent 设计原则:

增加能力,不要改大脑;尽量只扩展 harness 的动作接口。

换句话说,s02 的进步不是“模型更聪明了”,而是“模型被允许以更合适的方式行动了”。

为什么这很重要?因为 coding agent 的能力,很大程度上不是靠 prompt 变长,而是靠动作接口变好。

举个最简单的例子。一个 agent 如果只能用 bash 去读文件,那它可能会反复拼 catsedawk,甚至在 shell 转义上犯错。但如果你直接给它 read_file(path),你实际上是把“读取文件”这个动作从一团脆弱的命令行技巧,提升成了一个明确、稳定、带边界的能力。

同样,safe_path() 也不是一个小细节,而是 harness 的边界定义。它决定了 agent 的手能伸到哪里,不能伸到哪里。换句话说,权限也是 harness 的一部分。

所以从 s01s02,你看到的不是一个“更复杂的 agent”,而是一个“动作空间更合理的 agent”。

4. s03_todo_write.py:光会用工具还不够,Agent 还得会管理自己的过程

agents/s03_todo_write.py,这个仓库开始补齐一个很多人容易忽视的能力:过程管理。

很多人第一次做 agent,注意力会放在“它会不会调用工具”“它能不能改文件”上。但真实任务一旦超过两三步,问题马上就变了:它会不会跑偏?会不会做着做着忘记自己原本要完成什么?会不会跳步骤?

s03 给出的答案不是写一套硬编码工作流,而是把任务状态本身做成一个可写的工具。

这里的核心实现是 TodoManager

class TodoManager:
    def __init__(self):
        self.items = []

    def update(self, items: list) -> str:
        ...
        if in_progress_count > 1:
            raise ValueError("Only one task can be in_progress at a time")
        self.items = validated
        return self.render()

这段代码看起来像一个很普通的状态容器,但它背后的想法非常重要:

agent 不只是要“思考下一步”,还要能把整个任务过程显式地外部化。

也就是说,模型不只是在脑子里想“我接下来该做什么”,而是通过 todo 这个工具,把自己的任务列表写进 harness 维护的结构化状态里。这样一来,过程就不再是漂浮在上下文里的自然语言碎片,而是一个可以被读取、更新、检查的对象。

更有意思的是 s03 还做了一个小机制:

if rounds_since_todo >= 3:
    results.insert(0, {"type": "text", "text": "<reminder>Update your todos.</reminder>"})

这说明什么?

说明 harness 不只是被动执行,它还会在模型跑偏时轻轻拉它一把。

这个提醒机制没有替模型做规划,也没有强行规定执行流程,它做的只是维持一个“你应该持续同步任务状态”的工作纪律。换句话说,harness 在这里扮演的角色不是“替代智能”,而是“稳定智能的工作方式”。

这也是做 agent 时一个特别值得记住的点:

很多看起来像“模型不够聪明”的问题,实际是 harness 没有给它一个足够稳的过程结构。

5. s04_subagent.py:复杂任务为什么一定会长出子 Agent

当任务再复杂一点,只靠一个长长的 messages[] 就会开始吃力。此时最容易出现的误解是:“是不是应该再给主 agent 更多上下文?”

agents/s04_subagent.py 走的是相反方向:不是继续把东西往主上下文里塞,而是直接生成一个新的上下文。

最关键的实现是这个:

def run_subagent(prompt: str) -> str:
    sub_messages = [{"role": "user", "content": prompt}]
    ...

子 agent 拿到的是一套全新的 messages=[]。它和父 agent 共享文件系统,也共享基础工具,但不共享原来的对话历史。子 agent 做完事之后,返回给父 agent 的也不是完整工作痕迹,而只是最后的摘要。

这背后其实是在解决一个很工程化的问题:

主上下文太宝贵了,不应该被所有中间推理过程污染。

这也是很多人第一次做多 agent 时容易看反的地方。子 agent 的意义首先不是“多开几个模型很高级”,也不是“并行执行更快”,而是:

用进程边界换上下文边界。

父 agent 负责维持整体目标和主线;子 agent 负责在一个干净、短命、可丢弃的上下文里把局部问题做完。这个模式一旦理解了,你会发现几乎所有成熟的 coding agent 系统,最后都会长出某种形式的 subagent。

6. s05_skill_loading.py:知识不是一次性塞满,而是按需加载

到了 agents/s05_skill_loading.py,仓库开始补另一类很关键的能力:领域知识注入。

很多人做 agent 时,第一反应是把所有规则、规范、最佳实践、产品文档全塞进 system prompt。但这种做法很快就会遇到两个问题:

  1. prompt 会越来越长
  2. 大部分知识在当前任务里根本用不上

s05 给出的解法非常优雅:两层知识注入。

第一层,只把 skill 的元信息放进 system prompt,让模型知道“有什么知识可用”。

第二层,当模型真的需要某个知识时,再通过 load_skill(name) 把完整内容注入进来。

对应的关键实现是 SkillLoader

def get_descriptions(self) -> str:
    ...

def get_content(self, name: str) -> str:
    ...
    return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"

这个设计背后的思想非常值得记住:

Harness 不只是动作接口的集合,也是知识接口的集合。

模型不需要一开始就背下所有东西。它只需要知道有哪些能力、有哪些知识仓库,然后在需要的时候去取。这跟人类程序员工作其实非常像。我们也不是把所有文档一次性背在脑子里,而是在遇到具体问题时再查。

这一步非常重要,因为它让 agent 从“有工具能动手”进一步长成了“知道何时补充专门知识”的系统。

7. s06_context_compact.py:上下文管理不是优化技巧,而是生存条件

agents/s06_context_compact.py,仓库开始正面处理一个迟早绕不过去的问题:上下文会满。

很多人刚开始做 agent 时,容易有一种朴素想法:历史上下文越多越好,记得越全越强。但现实是,过长的上下文会让模型逐渐迟钝、昂贵、注意力分散,最后甚至在关键目标上失焦。

所以 s06 干的事不是“尽量多记”,而是“有策略地忘”。

这个文件用了一个三层压缩策略:

  • micro_compact(messages):把较老的 tool_result 替换成占位符
  • auto_compact(messages):把完整对话转存到 .transcripts/,再让模型生成摘要
  • compact 工具:允许模型主动触发手动压缩

其中最值得注意的,不是某个具体函数,而是整个思路:

  1. 原始痕迹不一定要一直留在主上下文里
  2. 关键状态要保住,但可以换一种更紧凑的表达
  3. 对话历史可以外部化到文件里,而不是永远挤在窗口里

也就是说,harness 在这里做的,不是“替模型记忆”,而是“替模型管理记忆的存放方式”。

这会带来一个很深的启发:

agent 的记忆,不应该只理解成 token window;它更应该被理解成“窗口内摘要 + 窗口外存档 + 必要时再回取”的组合。

一旦你这样看,上下文压缩就不再只是性能优化,而是 agent 架构的基本能力。

8. s07_task_system.pys08_background_tasks.py:任务和执行都必须外部化

到这里为止,agent 已经能做很多事了。但还有两个问题没有解决:

  • 如果任务需要跨越很多轮对话,状态存在哪?
  • 如果某个动作很慢,agent 是不是就只能一直卡住等?

agents/s07_task_system.py 解决的是第一个问题。

它把任务状态直接持久化到了 .tasks/task_*.json 文件里,并用 TaskManager 管理任务的创建、更新、依赖关系和状态流转。这里尤其关键的是 blockedByblocks,因为这意味着任务之间不再只是松散列表,而是一个依赖图。

这一层不是抽象概念,而是很具体的落盘实现。比如 TaskManager.create() 直接把任务写成 JSON 文件,而 update() 在任务完成时会清理其他任务的依赖:

def create(self, subject: str, description: str = "") -> str:
    task = {
        "id": self._next_id, "subject": subject, "description": description,
        "status": "pending", "blockedBy": [], "blocks": [], "owner": "",
    }
    self._save(task)
    self._next_id += 1
    return json.dumps(task, indent=2)
if status == "completed":
    self._clear_dependency(task_id)

这一步非常重要,因为它让“长期目标”从对话文本里搬到了磁盘上。只要任务状态在文件里,哪怕上下文被压缩、哪怕 agent 会话重启,任务板仍然是稳定存在的。

agents/s08_background_tasks.py 解决的是第二个问题。

这里的 BackgroundManager 用后台线程执行耗时命令,把结果放进通知队列,再在下一次 LLM 调用前通过 drain_notifications() 注入结果。

它的核心思路也非常直白:agent 发起任务,harness 立刻返回 task id,真正的等待发生在后台线程里。

def run(self, command: str) -> str:
    task_id = str(uuid.uuid4())[:8]
    self.tasks[task_id] = {"status": "running", "result": None, "command": command}
    thread = threading.Thread(
        target=self._execute, args=(task_id, command), daemon=True
    )
    thread.start()
    return f"Background task {task_id} started: {command[:80]}"

等到后台线程结束后,结果并不是偷偷存在某个内部状态里,而是被放进通知队列,等下一次主循环调用模型之前再注入:

notifs = BG.drain_notifications()
if notifs and messages:
    messages.append({"role": "user", "content": f"<background-results>\n{notif_text}\n</background-results>"})

这说明 harness 开始接管一个新的维度:时间。

不是所有动作都要同步完成。一个成熟的 agent 需要知道,有些事情可以先发出去跑,自己继续做别的,等结果回来再汇总。这听起来像操作系统或任务调度器的能力,但在 agent 世界里,它也属于 harness。

s07 + s08 这一步,你会明显感觉到:系统已经不只是一个“会调工具的聊天机器人”了。它开始有任务板、有长期状态、有异步执行模型,这才逐渐接近真正的软件工作流。

9. s09s11:多 Agent 不是多开几个模型,而是给它们共享的协作环境

很多人一提到 multi-agent,第一反应是“多开几个模型实例”。但如果只是多开窗口,没有共享任务板、没有通信协议、没有状态同步,那根本不叫团队,只是几个彼此失联的线程。

agents/s09_agent_teams.pyagents/s11_autonomous_agents.py 做的,就是把“多 agent”从概念变成组织结构。

s09 里,最核心的两个对象是:

  • MessageBus
  • TeammateManager

MessageBus 用 JSONL inbox 做每个 teammate 的收件箱,TeammateManager 则负责把 agent 作为持久成员启动、挂起、恢复,而不是像 s04 那样一次性派出去、做完就销毁。

最能体现这个设计的,是 MessageBus.send()read_inbox() 这两个函数。它们没有玩任何花哨协议,本质上就是“往某人的 jsonl 收件箱追加一条消息”和“把收件箱读完再清空”:

def send(self, sender: str, to: str, content: str,
         msg_type: str = "message", extra: dict = None) -> str:
    ...
    inbox_path = self.dir / f"{to}.jsonl"
    with open(inbox_path, "a") as f:
        f.write(json.dumps(msg) + "\n")
def read_inbox(self, name: str) -> list:
    ...
    inbox_path.write_text("")
    return messages

这意味着系统从“子 agent”进化到了“队友 agent”。

到了 s10,系统又进一步补了协议层。它不只是能发普通消息,还定义了 shutdown_requestshutdown_responseplan_approval_response 这类结构化消息类型。也就是说,agent 之间的沟通开始从随意自然语言,变成了带语义约束的协作协议。

这一步很像分布式系统里的协议演化。最开始大家都能互相发消息,但如果没有约定哪些消息意味着“请停机”、哪些消息意味着“计划待审批”,系统就会开始混乱。s10 做的其实就是把这些团队动作变成明确的消息语义。

再到 s11,关键变化是自治。agent 不再只能等 lead 指派,而是能:

  • 扫描未认领任务
  • claim_task()
  • 空闲时进入 idle
  • 收到新任务后自行恢复工作

s11 里有两段很值得看,因为它们直接把“自治”写成了代码。

第一段是扫描公共任务板:

def scan_unclaimed_tasks() -> list:
    ...
    if (task.get("status") == "pending"
            and not task.get("owner")
            and not task.get("blockedBy")):
        unclaimed.append(task)

第二段是抢占任务:

def claim_task(task_id: int, owner: str) -> str:
    ...
    task["owner"] = owner
    task["status"] = "in_progress"

这一步的含义非常大。因为它说明一个 agent team 的核心,不在于“lead 特别会调度”,而在于 harness 是否提供了一个足够稳定的公共工作环境:任务板、邮箱、身份、协议、claim 规则、idle 机制。

换句话说,多 agent 的本质不是“更多智能”,而是“更好的协作基础设施”。

10. s12_worktree_task_isolation.py:真正进入工程现实之后,任务和目录都要隔离

如果说 s09-s11 解决的是“谁来做事、怎么协作”,那 agents/s12_worktree_task_isolation.py 解决的就是“在哪里做事”。

这是一个非常容易被低估的点。

在真实代码库里,多任务并行最大的风险之一,就是不同 agent 在同一工作目录里互相覆盖、互相污染、互相踩状态。仅仅有任务 ID、任务 owner、消息邮箱是不够的。你还需要目录级隔离。

所以 s12 引入了几个关键对象:

  • EventBus
  • TaskManager 的 worktree 绑定能力
  • WorktreeManager

这里最重要的思想,可以压缩成一句话:

任务是控制平面,worktree 是实际执行时使用的独立工作目录和分支。

任务板决定“要做什么”;worktree 决定“在哪个独立目录里做”。WorktreeManager.create() 会创建一个绑定到特定分支的独立工作目录,把它记录到 .worktrees/index.json,还可以和具体任务绑定。之后 agent 不只是“执行命令”,而是“在某个命名 worktree 对应的独立目录里执行命令”。

这一点在代码里写得非常明确。任务先和 worktree 绑定:

def bind_worktree(self, task_id: int, worktree: str, owner: str = "") -> str:
    task = self._load(task_id)
    task["worktree"] = worktree
    ...

然后 WorktreeManager.create() 再真正创建这个独立工作目录和分支:

self._run_git(["worktree", "add", "-b", branch, str(path), base_ref])
...
if task_id is not None:
    self.tasks.bind_worktree(task_id, name)

这时候 task id 不再只是看板上的编号,它开始和一个真实目录、一条真实分支、一套真实生命周期绑定起来。keep()remove()worktree_events() 这些接口,本质上都是在给并行执行加收尾机制和可观测性。

这一步一出来,整个系统的工程感就完全不一样了。

因为这意味着 agent 不再只是一个会调用命令的推理器,而是一个能在真实 repo 中基于多个独立工作目录并行工作的执行系统。它开始具备了真正接近 Claude Code 这类 coding agent 的工作形态。

11. s_full.py:把前面的机制装进同一个驾驶舱

写到这里,还可以再看一眼 agents/s_full.py

这个文件本身不是教学章节,而是一个“把前面机制全部拼起来”的参考实现。它的价值不在于引入新概念,而在于帮助你建立一个整体视角:前面那些看似分散的机制,最后并不是彼此孤立地存在,而是会一起进入同一个主循环。

s_full.py 的文件头部,其实已经把这个组合关系画得很直白了:

  • Microcompact / Auto-compact
  • Drain bg notifications
  • Check inbox
  • 同一个 tool dispatch
  • Subagent
  • Teammate
  • Shutdown
  • Plan gate

这恰好说明了 harness engineering 的一个现实特点:

当系统长大之后,你维护的已经不再是“一个函数”,而是“一整套围绕主循环运转的前置检查、状态注入、工具分发和生命周期机制”。

也就是说,Claude Code 这类系统真正复杂的地方,不是某个神秘 prompt,也不是某个神奇模型调用,而是这些机制如何被拼成一个整体驾驶舱。

这也是为什么 s_full.py 值得作为文章末尾的对照阅读。它会让你意识到,前面 12 个章节其实不是 12 个散点知识,而是 12 个可以重新装回一台机器里的部件。

12. 回头再看:从 s01s12,长出来的全都是 Harness

现在我们可以回到最开始那句话了:

Agent = LLM + Harness

如果你把 agents/ 目录重新扫一遍,会发现这个仓库做的几乎没有一项是在“训练 LLM”,也没有一项是在“发明新的智能”。

它做的是:

  • s01 里提供最小 agent loop
  • s02 里定义动作接口
  • s03 里定义过程状态
  • s04 里做上下文隔离
  • s05 里做按需知识注入
  • s06 里做记忆压缩
  • s07 里把目标持久化到任务板
  • s08 里把慢操作变成异步执行
  • s09-s11 里给多个 agent 造团队基础设施
  • s12 里把执行环境升级成目录级隔离

这些全都不是 LLM 本身。这些全都是 harness。

所以,如果你以后想做自己的 agent,一个特别重要的认知变化就是:

不要先问“我该怎么造一个像 Claude 一样聪明的东西?”

先问:

  • 我手上的模型已经够聪明了吗?
  • 我有没有给它合适的工具?
  • 我有没有给它稳定的任务结构?
  • 我有没有设计好知识加载方式?
  • 我有没有处理长上下文和长期状态?
  • 我有没有给多个 agent 提供协作环境?
  • 我有没有给并行执行提供独立工作目录?

你会发现,这些问题几乎全部都指向 harness engineering。

13. 结语:程序员真正要学的,不是神秘的 Agent Magic,而是 Harness Engineering

这篇文章想完成的,不是把 Claude Code 再神化一遍,而是把它拆回工程问题。

一旦你沿着 agents/s01s12 看过这些实现,就很难再把 coding agent 理解成某种不可解释的黑箱。它当然依赖强模型,但模型之外的工程结构同样重要,而且这部分恰恰是大多数程序员真正能掌控、也真正应该去打磨的部分。

某种意义上,做 agent 和做传统软件的区别,并没有想象中那么大。你依然在设计接口、管理状态、控制权限、处理并发、搭建协作机制、约束执行边界。区别只是,这一次坐在系统中心的不再是一个写死逻辑的程序,而是一个会自己决定下一步行动的模型。

这也是为什么我觉得 learn-claude-code 这个项目真正有价值的地方,不在于“教你复刻一个产品”,而在于它把一个 coding agent 的生长路径完整地摊开给你看了。

从一个最小循环开始,慢慢长出工具、知识、记忆、任务、协作和隔离环境。你看见的不只是 Claude Code 的影子,更是一种通用的 agent engineering 方法论。

而一旦你真的理解了这一点,很多新的可能性就自然打开了。

因为 coding agent 只是其中一个场景而已。

只要某个领域里存在“观察、推理、行动”的闭环,只要那个领域可以被工具化、知识化、任务化、状态化,那么你就可以为那个领域设计自己的 harness。研究 agent 可以这样做,运维 agent 可以这样做,客服 agent、销售 agent、垂直行业 copilot 也都可以这样做。

所以,程序员以后再说“我要做 agent”,如果想说得更准确一点,也许应该说:

我要围绕现成的 LLM,做一个足够好的 harness。

这句话听起来没有那么神秘,但它更接近工程现实。

而且,它也更有力量。

附:阅读这篇文章时建议对照的代码文件

建议的阅读顺序不是随机跳读,而是:

  1. 先看 s01s02,建立“loop + tools”的最小心智模型
  2. 再看 s03s06,理解 process、knowledge、memory 这些中层机制
  3. 然后看 s07s12,进入长期状态、异步执行、团队协作和目录隔离
  4. 最后回头看 s_full.py,把这些机制重新拼成一个整体
  • agents/s01_agent_loop.py
  • agents/s02_tool_use.py
  • agents/s03_todo_write.py
  • agents/s04_subagent.py
  • agents/s05_skill_loading.py
  • agents/s06_context_compact.py
  • agents/s07_task_system.py
  • agents/s08_background_tasks.py
  • agents/s09_agent_teams.py
  • agents/s10_team_protocols.py
  • agents/s11_autonomous_agents.py
  • agents/s12_worktree_task_isolation.py
  • agents/s_full.py