针对独立开发者,GitHub 项目 `solo-agent-agile-template` 迎来了第二版更新。该模板基于 Claude Agent SDK 构建,旨在解决初版在实战中遇到的问题。新版通过精简命令体系、完善技术文档以及集成 Claude Agent SDK,进一步优化了开发流程。用户利用此模板,可以快速构建拥有 Web 界面的自主品牌 AI Agents,极大地降低了全栈开发与 AI 应用落地的技术难度,让“人人都是全栈工程师”更进一步。
原文链接:Linux.do
针对独立开发者,GitHub 项目 `solo-agent-agile-template` 迎来了第二版更新。该模板基于 Claude Agent SDK 构建,旨在解决初版在实战中遇到的问题。新版通过精简命令体系、完善技术文档以及集成 Claude Agent SDK,进一步优化了开发流程。用户利用此模板,可以快速构建拥有 Web 界面的自主品牌 AI Agents,极大地降低了全栈开发与 AI 应用落地的技术难度,让“人人都是全栈工程师”更进一步。
原文链接:Linux.do
极简框架把复杂度从框架搬到了你身上。循环本身只有 1445 行,它不替你做日志、不替你做预算、不替你做回滚、不替你做评测。它只保证一件事:模型做了什么、失败了什么,都变成事件发出来。剩下的账,得自己付。
20 条失败用例覆盖四类:调用不存在的工具、参数不合 schema、工具抛异常、工具超时。每条独立跑一次完整循环,统计失败信号是否送达模型、循环是否崩溃。
20 条用例,四类失败各 5 条。结果很干净:20 条全部产生了明确的错误结果,带 isError 标记和可读原因;循环零崩溃,每次都能继续或正常收尾。
四类的处理路径不一样。工具不存在时,循环直接生成「Tool xxx not found」的结果,不让模型空等;参数不合 schema 时,校验在执行前失败,工具根本没被调用;工具抛异常时,异常被捕获成错误结果;超时同理。
这个一致性是设计出来的,不是碰巧。它意味着模型永远知道「刚才那步为什么没成」,而不是收到一个空结果或者一个被吞掉的异常。上一代系统最缺的就是这一层。
| 失败类型 | 用例数 | 信号送达 | 循环崩溃 | 错误信息平均长度 |
|---|---|---|---|---|
| 工具不存在 | 5 | 5 | 0 | 74 字符 |
| 参数不合 schema | 5 | 5 | 0 | 90 字符 |
| 工具抛异常 | 5 | 5 | 0 | 77 字符 |
| 工具超时 | 5 | 5 | 0 | 88 字符 |
四类失败各 5 条,全部把错误如实送给模型,循环没有一次崩溃。
注意:但「信号送达」不等于「问题解决」。框架保证模型看见错误,不保证它看见之后能改对。实测里能跑通是因为策略是脚本化的、确定会换策略;真实模型可能在同一处反复试,这时候需要的是预算闸门和人工接管,不是更多的重试。
循环没有内置预算。模型可以一直调工具,直到上下文爆掉或者你手动停。必须在钩子里计次、在上层计 token,两道闸门都要有。
计次容易,计 token 麻烦——用量在 assistant 消息的 usage 字段里,要自己累加。累加的地方应该是 afterToolCall 或每轮结束,别放在界面层,界面层可能在重连后丢掉。
这不是理论风险。我让一个脚本化模型在同一个错误上反复重试:权限被拒,它继续读同一个路径。没有闸门时,30 轮上限内它把 30 次调用全用完了;加一条「8 次预算 + 拦下后 terminate」的规则,第 9 轮就收尾。

同一个死循环任务:无预算闸门时 30 轮上限内把 30 次调用全部用尽,加 8 次预算后 9 轮收尾。差的不只是次数,还有这 22 轮里每一个 token。
更值得记的是那 30 轮不是终点,是我给实验设的上限。没有 terminate,模型会在这个错误上继续试下去,直到上下文撑爆或者你手动 kill。脚本编排时代这个由进程超时兜着,循环里没有进程生命周期,只能靠钩子。
所以预算闸门要做两件事:到点拦住,并且让这一轮直接结束。只拦不终止,模型下一轮还会再请求同一个工具,你只是把频率降低了。
pi 生态里有独立的 pi-telemetry 包,但那是可选项。真正必须做的是把事件落盘:每个事件带会话 id、轮次、时间戳、工具名、耗时、结果摘要。有了这份结构化记录,任何一次线上异常都能重放出来。
反例是只记日志。日志是自由文本,事后要靠正则去猜当时发生了什么;事件是结构化的,可以直接算工具成功率、平均轮次、按失败类型分组。
循环会改东西——改文件、写库、重启服务。要能回滚,前提是每个有副作用的工具都留下「改之前是什么」。这件事框架不管,工具的作者必须管。
务实做法是给工具加一个 replay 策略字段:声明为 safe 的工具可以重放,声明为 never 的工具出错后必须人工确认。循环本身支持这个语义,但具体哪些工具是 safe,只有你知道。
真实模型做回归测试几乎不可能:温度、采样、上游版本都会让结果漂移。可行的做法是分层——循环用脚本化模型做单测,锁死「钩子是否拦截」「队列是否消费」「错误是否送达」这类行为;模型效果用固定用例集做抽查,允许答案措辞不同但要求关键结论一致。
评测集不要超过 30 条。超过就没人维护了。每条用例写清三件事:输入、期望的关键结论、不可接受的结果。
把这一篇和前九篇压缩成一张表。每一条都对应一个具体的失败模式,不是泛泛而谈。
| 检查项 | 失败模式 | 对应实验 |
|---|---|---|
| 工具描述能区分用途 | 模型选错工具、反复试同一个 | 04 |
| 失败带 code 与原因 | 模型不知道发生了什么,原样重试 | 10 |
| 危险动作有钩子 | 提示词约束失效,危险命令被执行 | 07 |
| 有调用预算 | 同一错误上无限重试,烧成本 | 07 |
| 上下文有剪枝策略 | 20 轮后上下文线性膨胀 | 06 |
| 长任务有进度事件 | 用户以为卡死,提前退出 | 05 |
| 支持插话 | 方向错了只能等跑完或杀进程 | 08 |
| 事件落盘可重放 | 线上异常无法还原 | — |
| 有回归集 | 换模型或改提示词后无据可依 | 10 |
回头看这十篇,pi-agent-core 做的事情可以一句话概括:它只实现循环,把所有判断留给你和模型。工具只有几个,没有 MCP,没有子 Agent,没有沙箱,没有计划模式。
这套设计的代价很明确,就是这一篇列的四件事都要自己做。收益也很明确:每个决定都是你做的,所以每个失败你都能定位。不会出现「框架内部在某处重试了三次」这种查不下去的情况。
从 LangGraph 迁过来最大的变化不是代码少了,是心态变了。以前你在设计图的形状,试图把所有分支提前想到;现在你在设计工具和闸门,接受模型会走弯路。前者追求确定性,后者追求可恢复性——面对开放的任务,可恢复比确定更值钱。
十篇的实验代码都在一个仓库里,每个数字都能自己跑一遍。如果你的场景是流程固定、分支可穷举,状态机仍然是更好的选择;如果你的场景是排障、调研、修复这类「下一步取决于这一步看到了什么」的任务,循环的性价比会高得多。
上一代框架最容易劝退人的地方是脚手架:配置文件、目录约定、中间件注册,跑通一个 hello world 要读半小时文档。pi-agent-core 反过来——核心就是 new Agent() 加一个 streamFn,剩下都是你自己决定。这一篇把最小可运行版本拆开,顺便看清一次 prompt 到底发生了什么。
为了不依赖任何 API 密钥且可复现,实验注入了一个确定性 streamFn 取代真实模型。循环、工具调度、事件发射全部是真实运行时,只有模型输出是脚本化的。
一个能跑起来的 Agent 只需要三样:初始状态(系统提示、模型、工具列表)、一个 streamFn(怎么把上下文变成模型的输出流)、以及一次 prompt 调用。没有 registry,没有 pipeline,没有 chain 组合器。
streamFn 是关键设计。它的签名是拿到模型对象和上下文,返回一个异步事件流。这意味着循环不关心你用什么模型、走什么协议——真实 provider 用它,测试用假的也用它,两者走同一条路径。
这个设计带来的直接好处是可测性:你可以把模型行为完整脚本化,然后精确断言循环在每种情况下做了什么。后面九篇的实验能复现,靠的就是这一点。
import { Agent } from '@earendil-works/pi-agent-core'
import { createAssistantMessageEventStream } from '@earendil-works/pi-ai'
const agent = new Agent({
initialState: {
systemPrompt: '你是一个排障助手。',
model,
tools: [],
},
streamFn: (m, context) => {
const stream = createAssistantMessageEventStream()
const text = '收到。'
stream.push({ type: 'start', partial: partialMessage() })
stream.push({ type: 'text_delta', contentIndex: 0, delta: text, partial: partialMessage(text) })
stream.push({ type: 'done', reason: 'stop', message: finalMessage(text) })
return stream
},
})
agent.subscribe((e) => {
if (e.type === 'message_update') process.stdout.write(e.assistantMessageEvent.delta ?? '')
})
await agent.prompt('帮我看看服务状态')
背景:注意事件顺序里的一个细节:先 push start,再 push 增量,最后 push done。done 事件里带的是完整消息,而不是“结束标记”。循环拿这个完整消息继续判断要不要调工具、要不要再来一轮。
我跑了一次带两个工具调用的会话:模型先决定同时读日志和查负载,两个工具并行执行,然后基于结果给出结论。整个过程发出 27 个事件,总耗时 314.7 毫秒。
事件顺序是:agent_start → turn_start → 用户消息开始结束 → 助手消息开始 → 若干 text_delta → 助手消息结束 → 两个 tool_execution_start → 工具结束(按完成顺序)→ 工具结果消息 → turn_end → 下一轮 turn_start → 助手消息 → turn_end → agent_end。
关键观察有两点。第一,单个 turn 内可以有多次工具调用,但只有一次模型请求;第二轮请求是因为工具结果需要模型解读,不是循环自己加的。第二,工具结束事件的顺序是完成顺序,不是声明顺序——180 毫秒的那个先结束,300 毫秒的后结束。

一次 prompt 的耗时构成:两个工具并行执行占 300 毫秒,框架自身调度开销不到 5 毫秒。所谓 Agent 慢,慢在模型和工具,不在循环。
这个数据推翻了一个常见直觉:以为 Agent 框架本身很重。实际上循环的开销是毫秒级,你感知到的所有延迟都来自模型往返和工具执行。
推论也很直接:优化 Agent 性能,不要去优化循环。要么并行化工具,要么减少轮次,要么换更快的模型。循环本身没有可优化的空间。
Agent 实例上挂着一个 state 对象,里面是当前模型、思考等级、工具列表、完整消息历史,以及几个只读标记:是否正在流式、正在流式的消息、待完成的工具调用、错误信息。
这个 state 是可变的,而且可以直接改。想临时换模型,改 state.model;想在两轮之间塞一条消息,push 进 state.messages。这在很多框架里是禁忌,在这里是设计。
代价是责任:你改了 state,就要保证它仍然是一段合法上下文。宽松的接口换来的是自由,不是零成本。
agent.subscribe((e) => {
if (e.type === 'turn_start') {
console.log('轮次', agent.state.messages.length, '待办工具', agent.state.pendingToolCalls.size)
}
if (e.type === 'agent_end') {
console.log('会话结束,共', e.messages.length, '条消息')
}
})
还有一个容易被忽略的方法:waitForIdle。prompt 返回不代表界面已经稳定,因为订阅 agent_end 的异步监听器可能还在跑。需要“确定一切结束”的时候,等它,而不是等 prompt。
这个系列所有实验都不需要 API 密钥,原因是 streamFn 可以整个替换。把模型行为写成脚本,你就得到了一个完全确定的 Agent——同样的输入永远产生同样的工具调用、同样的事件序列。
这在工程上价值很大。真实模型做回归测试几乎不可能:温度、采样、上游版本变化都会让结果漂移。但把模型换成脚本之后,你可以断言“第三次工具调用必须被钩子拦住”“队列里的消息必须在下一轮被消费”这类循环级行为。
结论:把模型当外部依赖,把循环当自己的代码。前者用集成测试抽查,后者用单元测试锁死。
下一篇讲工具。这是整个系列里最容易被低估的部分——大多数人以为工具就是“注册一个函数”,实际上工具的 schema 设计直接决定了模型能不能用对,执行模式决定了任务要跑多久。
很多人第一次看 pi 的仓库会困惑:为什么一个 Agent 框架要拆成三个包?装一个不行吗?答案藏在一个数字里——真正的循环层只占整个体系代码量的 1.7%。拆开不是因为讲究,是因为不拆就没法只替换其中一层。
统计口径:各包 dist 目录下 .js 文件的物理行数(含空行与注释),版本 1.4.0。这不是有效代码行数,但用来比较相对体量足够稳。
最底下是 pi-ai,管模型接入。它把 OpenAI、Anthropic、Gemini、Bedrock、本地 Ollama 这些不同的 API 抹平成一套接口:统一的流式协议、统一的工具调用格式、统一的用量统计。上层代码不需要知道背后是哪家模型,换模型只换一个 provider 对象。
中间是 pi-agent-core,管循环。它拿一段上下文发给模型,模型说要调工具,它执行,把结果塞回去,再来一轮。它还管状态、事件流、消息队列、钩子——但不管“你要做什么业务”。
最上面是 pi-coding-agent,是一个完整产品:命令行界面、会话管理、主题、扩展加载、斜杠命令。你在终端里用的那个 pi,是这一层。
大多数 Agent 框架把这三件事揉在一个包里。后果是:你想换模型,得改框架内部;你想换界面,得 fork 整个仓库;你只想用循环,被迫装进一整套 CLI 依赖。
切成三层之后,每一层的替换成本是独立的。做后端服务只需要 pi-ai 加 pi-agent-core,不用拖进 25MB 的终端界面。做终端工具就三层全要。想做嵌入式或浏览器端,中间那层是纯 TypeScript,没有任何 Node 原生依赖。
这个思路可以直接搬到自己项目里:接入层、循环层、交互层分开,界面永远不参与决策。
我把三个包的 dist 目录全量扫了一遍,数物理行数。结果比预想的更极端:核心循环 1445 行,模型接入层 20551 行,产品层 64769 行。
这个比例说明一件事:Agent 的复杂度不在循环,在模型适配和交互。循环本身是十几行伪代码就能说清的东西——发请求、收工具调用、执行、回填、再来一轮。真正吃掉工程量的是各家 API 的差异、流式解析的边界、终端界面的渲染。
所以那些“用框架写了三个月还在调”的项目,时间大概率花在了产品层,而不是循环层。这个认知很重要:换掉循环,省不下多少代码;换掉循环,省下的是决策权的归属。

三层代码量对比:核心循环只占 1.7%,产品层占了近四分之三。想改行为,改的是那 1445 行;想改体验,改的是那 6 万行。
| 包 | 职责 | 一级依赖 | 导出符号 | 占比 |
|---|---|---|---|---|
| pi-ai | 模型接入 | 10 | 36 | 23.7% |
| pi-agent-core | Agent 循环 | 2 | 5 | 1.7% |
| pi-coding-agent | CLI 产品 | 22 | 44 | 74.6% |
循环层只有 2 个一级依赖,这是刻意的。依赖越少,越容易嵌进别人的运行时——Cloudflare Worker、浏览器、你自己的后端服务。
导出符号只有 5 个,也说明它的 API 面很小。小的 API 面意味着升级不容易炸:你依赖的东西少,别人改的时候波及你的概率就低。
在 pi-ai 这层,你打交道的是 provider 和 model 对象。日常操作是选模型、配 baseUrl、处理密钥轮换。如果要做私有化部署,这层是唯一需要改的地方。
在 pi-agent-core 这层,你打交道的是 Agent 实例、工具定义、事件订阅、钩子。这一层是你写业务逻辑的地方,也是这个系列后面八篇的主战场。
在 pi-coding-agent 这层,你打交道的是扩展、命令、主题、会话存储。如果你只是要用它当工具,这层是配置问题;如果你要把它嵌进自己产品,这层大概率整个不要。
npm install @earendil-works/pi-agent-core @earendil-works/pi-ai
# 不需要 pi-coding-agent,省下 25MB 和 22 个依赖
背景:一个常见的误判:以为装了 Agent 框架就自动获得“工具生态”。pi 的官方态度是不内置 MCP、不内置子 Agent、不内置沙箱。这些能力在 pi-mcp、pi-codemode 这些独立包里,或者由你自己实现。选型时把这块算进成本。
如果你正在维护一个内部 Agent 平台,这套切分可以直接借。接入层只负责“把请求变成统一格式”,循环层只负责“决定下一步”,交互层只负责“把事件渲染出来”。三层之间只通过事件和消息通信。
这样做最直接的好处是可观测。所有决策都发生在循环层,所有决策都会发出事件;交互层不参与决策,所以你能确定“看到的”就是“发生的”。反过来,如果界面层能偷偷改工具参数,你的日志就永远对不上现场。
第二个好处是可替换。模型换代时只换接入层,界面改版时只改交互层。循环层的代码一年可能都不动一次——因为它要做的事本来就没变过。
下一篇进入实操:不装 CLI、不配真实模型,用 40 行代码把循环跑起来,看看一次 prompt 到底发出了哪些事件、按什么顺序发。这是后面所有中级技巧的地基。
Linux.do社区用户发布开源项目,将社区第三方客户端FluxDo移植为鸿蒙原生版本。据帖子介绍,作者此前一直通过卓易通在鸿蒙设备上运行安卓版FluxDo,在发现社区有开发者编写了原生应用后,考虑到自身不擅长UI设计,遂选择移植已有成熟应用而非从零开发。该项目已在GitHub开源,地址为github.com/54iceblock/fluxdo的ohos-port分支,采用GPL-3.0协议。安装过程仅需三条命令,真机安装需通过DevEco工具进行自动签名,项目同时提供了适配文档供开发者参考。帖子遵循Linux.do社区开源推广规范,声明项目完整开源无未公开部分,已链接认可社区,涉及AI生成或润色的介绍内容已按社区要求以截图方式发布,并承诺接受社区长期监督。该移植版本使鸿蒙用户无需依赖安卓兼容层即可原生使用FluxDo客户端,目前话题已有社区成员参与讨论。
核心观点:操作系统生态的成色不取决于头部大厂适配,而在于社区开发者能否自发填补长尾应用空缺。
原文链接:Linux.do
开发者在 V2EX 发布开源工具 AgentSafe,用于在分享截图和调试日志前,在本地排查可能泄露的敏感内容。该工具针对的场景是:向开源项目提交 issue、展示功能或分享 AI 编程助手生成的截图时,画面中可能包含内部项目名、邮箱、内部域名,调试日志中也可能带有认证字段和 Cookie。AgentSafe 运行在用户本地电脑上,安装需通过终端完成,后续主要通过本地浏览器界面操作。使用流程为:选择 PNG/JPEG 截图或 UTF-8 格式的 .log/.txt 文件;填写需查找的敏感名称、词语或域名,留空则按内置规则检查邮箱、部分认证字段和 Cookie;查看检测结果;对图片手动框选遮盖并另存副本,原图不会被修改。整个检查过程不向外部服务发送文件内容,仅安装时需联网下载依赖。该项目采用 MIT 许可证,当前版本为 v0.1.0a1,属实验性 Alpha 阶段,建议按 macOS 说明体验,暂无独立安装包,Windows 尚未验证。图片 OCR 目前仅支持英文,中文配置词语可用于文本检查;工具不会自动判断业务内容是否机密,也不检测任意 API key 或密码。作者希望了解安装体验、检测结果可读性,以及用户在何种场景下会选择该工具而非图片编辑器。
核心观点:AI 编程让晒截图成为日常,敏感信息防线却仍靠肉眼,本地化检查工具折射开发者对云端隐私风险的警惕升级。
原文链接:V2EX 分享发现
据Linux.do论坛用户反馈,智谱GLM 5.3模型在Claude Code中的实际使用体验不及预期。该用户使用智谱start套餐每日赠送的300万tokens额度,在Claude Code中执行一个Plan任务时,任务尚未完成额度即已耗尽。用户还实测了推理速度:GLM 5.3约为75 token/s,GLM 5.3 Flash约为43 token/s,并指出定位轻量高速的Flash版本速度反而低于旗舰版,称这一现象“倒反天罡”。Claude Code是Anthropic推出的AI编程工具,支持接入第三方模型,国内开发者常通过智谱等国产大模型的编程套餐驱动该工具,以降低使用门槛和成本。值得注意的是,智能体编程工作流中,由于每轮请求需重传完整上下文,叠加工具调用、代码读写与多步规划,token消耗远超普通对话场景,单个复杂任务消耗数十万甚至百万级tokens并不罕见。智谱GLM系列近期迭代较快,GLM 5.3定位旗舰模型,Flash定位轻量版本,但此次实测显示其在agent场景下的吞吐表现与套餐额度设计仍有优化空间。该帖已引发社区对国产模型承载Claude Code的实际能力与套餐定价合理性的讨论。
核心观点:Agent编程的token消耗量级正在重塑大模型定价逻辑,吞吐与额度跟不上,就接不住Claude Code外溢的生态流量。
原文链接:Linux.do







评论前必须登录!
立即登录 注册