- SignalDesk1小时前
本文系统分享了关于 agent 开发的思考,全文近 一万字,阅读需要 15 分钟,对于想要学习 agent 开发的同学也许大有裨益。 我开源了一个极简 Agent 框架,叫 Kiso 。 最初其实来源于一个很朴素的念头:我能不能做出一个比 Pi 更简单、更稳定、更省 token 的 Agent 框架。对于一个多少有点技术理想的 Agent 研究者来说,如果最后没能在开源 Agent 这件事上留下点自己的痕迹,实在是手痒难耐。 所以即便是重复造轮子,我也便造了。 但“简单、稳定、省 token”是一开始写在草稿纸上的三个愿望。而愿望和工程现实之间,隔着巨大的鸿沟。一旦真正去拆解像 Pi 这样的参考实现,就会发现它已经把“极简”逼到了一个很难继续压缩的位置:默认工具极少,系统提示词非常克制,Loop 也没有多少多余的控制逻辑。在这样的前提下,如果所谓创新只是再少两个工具、再少几百个 prompt token ,工程空间其实已经非常有限。 做着做着,我发现自己真正想解决的问题变了。 我开始意识到,一个 Agent 真正难以被托付的原因,往往并不是它不会调用某个工具,也不是缺少 Memory 、Plan 、Multi-Agent 之类的组件。模型能力会继续上涨,这些组件也会不断变化,但只要 Agent 开始通过文件系统、Shell 、Git 、浏览器或者网络接口真正改变外部世界,就会出现一个更底层的问题: 当一个概率模型开始改变现实以后,系统凭什么知道什么真的发生过? 这个问题后来几乎决定了 Kiso 的整个方向。 我最开始也试图给 Agent 下一个很“第一性原理”的定义,比如:Agent 是一个在不完全信息下,通过持续观察世界、采取行动、读取反馈,让现实状态逐渐向目标状态收敛的闭环系统。后来我觉得,这个定义作为思考工具有用,但没必要把文章变成一篇“什么才是真正 Agent”的理论争论。Kiso 也不需要证明一个适用于所有 Agent 的终极定义。它只需要关心一类非常具体的系统: 一个会读取外部世界、会调用有副作用的工具、而且可能长时间运行甚至中途崩溃的 Agent 。 把范围缩到这里以后,有几件事情几乎无法绕开: Observed ≠ Current Intent ≠ Effect Started ≠ Succeeded Process Completion ≠ Goal Satisfaction Memory ≠ Durable Fact 模型看见过,不代表现实现在还是那个样子;模型产生了一个工具意图,不代表外部世界已经发生变化;一个操作开始了,不代表它已经成功;整个 Loop 正常结束,也不代表真正的任务目标已经被满足;而进程此刻记得的东西,只要还没有成为 durable state ,崩溃之后就没有资格继续被当作事实。 所以做到后来,我越来越觉得 Kiso 虽然对外仍然可以叫一个 Agent 框架,但它真正想守住的是更底层的一层东西。严格一点说,它越来越像一个 Agent Runtime 。 我给它最后收敛出来的分工只有一句话: 模型负责扩大系统能够处理的问题空间,而 Runtime 负责限制什么才有资格被称为“事实”。 这就是 Kiso 后面绝大部分工程决定的起点。 01. 控制流闭环,不等于现实闭环 最常见的 Agent Loop 其实非常简单:模型接收上下文,生成工具调用; Runtime 执行工具,把结果重新放回上下文;模型继续推理,直到最后停止。从程序控制流来看,箭头已经漂亮地绕了一圈,又回到了模型,看上去闭环成立了。 但只要真正写过 Coding Agent ,并且让它跑过稍微长一点的任务,就会发现: 控制流闭环,不代表现实闭环。 因为模型面对的并不是一个静态状态机。它面对的是一个持续变化的外部世界。文件可能被人类同时修改,Git 分支可能发生变化,Shell 命令可能执行到一半,HTTP 请求可能已经发出去但响应还没有回来,进程更可能在任何一个 CPU 指令之间被 kill -9 。 这里面其实存在几道完全不同的语义边界。 第一道是认识论边界。模型所有输入,本质上都只是现实世界某个历史切片的投影。即使今天给它 100 万 token 的上下文,它能够保存的仍然只是“过去看见过什么”,而不是一个正在持续变化的现实本身。几十秒前读过的文件,可能已经被 IDE 、Git 、后台进程或者另一个 Agent 改过了。 第二道是因果边界。模型流里出现一个 tool_use ,只代表它提出了一个行动意图。从这个 token 被生成出来,到磁盘真正写入一个字节,中间还隔着完整的 Runtime 、权限判断、工具执行以及操作系统。把“模型说要做”直接等同于“现实已经做了”,在正常情况下可能看不出问题,一旦进入崩溃恢复,整个因果关系就会立刻变得混乱。 第三道是持久化边界。进程当前知道一个工具已经执行成功,并不意味着重新启动以后还能证明这件事。如果这份确定性只存在于一个 Promise 、一个对象字段或者调用栈里,断电之后,它就跟从来没有存在过一样。 最后还有评价边界。一个工具返回成功,只能说明局部系统调用完成了。 write_file 成功不代表代码正确, npm test 返回 0 也不总能证明用户真正想解决的问题已经被解决。最终目标是否完成,需要来自新的观察、测试、验收或者人类判断,而不是让负责行动的模型自己顺手宣布“任务完成”。 这几道边界看起来很普通,但它们最后几乎决定了 Kiso 的整体结构。因为接受这些区别以后,就不能再把 Agent Runtime 理解成一个负责“LLM ↔ Tool”的 while loop 。它必须维护的是一条能够在崩溃以后仍然解释得清楚的因果链: Model Intent ↓ Turn Commit ↓ Authorization ↓ Durable STARTED ↓ Real-world Effect ↓ Durable Receipt ↓ Observation / Verification 模型输出当然可以是概率的,但从什么时候开始允许现实发生变化,到现实发生变化以后系统究竟知道了什么,这些边界必须尽量确定。 02. Fail-Stop 中断与执行账本 这套思路遇到的第一个问题,就是进程崩溃。 假设 Agent 已经修改完第一个文件,接下来开始执行一条耗时的 Shell 命令。命令刚运行到一半,用户关掉终端,机器突然掉电,OOM Killer 杀掉进程,或者我们干脆在外面直接给它一个 SIGKILL 。重新启动以后,它应该从哪里继续? 很多轻量 Agent 框架保存的是一份模型交互历史:User 、Assistant 、Tool Call 、Tool Result 。正常使用当然没有问题,但现实世界恰好存在一个非常麻烦的窗口: Tool Call ↓ 真实副作用正在发生 ↓ Tool Result 如果进程刚好死在中间,磁盘上可能只有“我要执行这个操作”,没有最终结果。但外部世界并不是一个可以整体回滚的数据库事务。文件可能已经写完了,Git commit 可能已经产生了,远程 HTTP 请求也可能已经被服务端接受。这个时候如果 Runtime 简
- 情报分类:开源项目与落地
- 分类依据:内容涉及项目实践、创业、副业或变现
- 信息来源:服务器 / V2EX
- 发布时间:2026/9/21 08:57:14
- 暂无回复