套壳不丢人!我用Go+AI搓了一个Agent统一编排框架,ClaudeCode-Codex-Pi全被我包了
本机暂存
<p>去年我还在折腾 langchain/langgraph 开发智能体,弄了个 langgraphgo 项目,把 langgraph 往 Go 生态圈里搬。那会儿网上做智能体的,十个有八个用 langchain/Crew AI。</p><p>一个阶段有一个阶段的玩法。</p><p>现在我看到了另一种路子:大家直接用 Claude Code、Codex、OpenCode、Pi 这些 coding agent "套壳"来实现智能体。</p><p>先说两个很多人搞混的点。</p><p>别觉得这些工具只能写代码。Claude Code、Codex 的架构走的是通用智能体模式,早就不止 coding 了。</p><p>也别把"套壳"当贬义词。Manus 刚火那阵,就有同事撇嘴说"这不就是 Claude 的套壳"。但你看,Claude、Codex、Antigravity 一个个都在推 SDK,巴不得你基于它们二次开发。牛顿怎么说的,站在巨人肩膀上不丢人。</p><span id="more"></span><p>百度厂内突然蹿红了一个叫 dodo 的应用比龙虾都火。就是靠"套壳"快速出产品原型,再慢慢长成大家离不开的工具。百度公众号和前几天的百度大会上也推了。</p><p>OpenClaw 是在 Pi coding agent 上搭起来的智能体产品,上半年火得不成样子。</p><p>这些东西免费(CC),有的还开源(Codex CLI、OpenCode、Pi),甚至设计了 harness engineering 这套东西。等于送你一个结实的产品基座,剩下的精力全押在产品和创意上。</p><p>![image-20260603075823837.png<img src="/2026/06/11/go-ai-agent-orchestration-framework/image-20260603075823837.png" class=""></p><p>去年我还在从零手搓智能体。今年,我负责的 LLM 训推故障分析产品已经改成跑在其中一个智能体上了。我只需要管诊断逻辑、知识库、对外 API——那些真正跟业务相关的事。</p><p>但问题来了:这么多智能体,到底用哪个?</p><p>小孩子才做选择题,成年人我都要。</p><p>那就自己动手。套一个壳。向上给统一调用层,向下接各种智能体。</p><p>于是 agent-wrapper 出来了。你可以拿它的命令行测试,也可以直接嵌到 Go 项目里,把精力放在你该忙的地方:扩大用户和赚钱。</p><blockquote><p>有人会说 acp 不就能干这个?我实践下来 acp 不好用,从零搓了一个。具体分析见项目 README。</p></blockquote><h2 id="你到底要套什么"><a href="#你到底要套什么" class="headerlink" title="你到底要套什么"></a>你到底要套什么</h2><p>市面上的 Coding Agent CLI,单个拎出来都很能打——Claude Code 审代码,Codex 写脚本,Pi 做任务编排,OpenCode 跑自动化。但它们各自为政。五个操不同方言的装修师傅挤在一个工地,你每次想换人干活都得重新翻译需求。</p><p>胶水代码糊了一堆:启动子进程、解析各自的输出协议(NDJSON、SSE、JSONL、JSON-RPC……)、管生命周期、处理超时和错误、做重试和上下文压缩。全跟业务没关系,但不写系统就不稳。</p><p>agent-wrapper 干的就是这一层。一行代码注册 provider,一行代码切 agent:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">registry := agentwrapper.NewRegistry()</span><br><span class="line">claude.RegisterIn(registry)</span><br><span class="line">codex.RegisterIn(registry)</span><br><span class="line">pi.RegisterIn(registry)</span><br><span class="line"></span><br><span class="line">agent, _ := registry.Get(<span class="string">"codex"</span>, <span class="literal">nil</span>) <span class="comment">// 切 Claude? 改成 "claude-code"</span></span><br><span class="line">orch := agentwrapper.NewOrchestrator(agent)</span><br><span class="line">result, _ := orch.RunSync(context.Background(), types.RunInput{</span><br><span class="line"> Prompt: <span class="string">"帮我重构这段代码"</span>,</span><br><span class="line">})</span><br><span class="line">fmt.Println(result.Text)</span><br></pre></td></tr></table></figure><p>换 agent = 换一个字符串。胶水代码归零。</p><p>![image-20260603080124183.png<img src="/2026/06/11/go-ai-agent-orchestration-framework/image-20260603080124183.png" class=""></p><h2 id="不只是包一层,是给-Agent-装上刹车"><a href="#不只是包一层,是给-Agent-装上刹车" class="headerlink" title="不只是包一层,是给 Agent 装上刹车"></a>不只是包一层,是给 Agent 装上刹车</h2><p>套壳没技术含量?我不认。</p><p>调一次 Agent 谁都会。管住它难得多。一个 AI Agent 在你的代码库里自由调用工具——写文件、删代码、执行命令——你敢不加约束?</p><p>![image-20260603080302277.png<img src="/2026/06/11/go-ai-agent-orchestration-framework/image-20260603080302277.png" class=""></p><p>agent-wrapper 的 Orchestrator 不是简单的 for 循环。内置了三道闸门(harness engineering 的标准做法):</p><p><strong>审批拦截。</strong> 每次 tool_call 先过你的 ApprovalHandler。读文件放行,写文件拦住。按工具名、参数、上下文做决策,拒绝时注入合成 ToolResult,Agent 收到 "DENIED" 继续对话,不会崩:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">orch := agentwrapper.NewOrchestrator(agent,</span><br><span class="line"> agentwrapper.WithApprovalHandler(<span class="function"><span class="keyword">func</span><span class="params">(ctx context.Context, call agentwrapper.ToolCall)</span></span> (*agentwrapper.Decision, <span class="type">error</span>) {</span><br><span class="line"> <span class="keyword">switch</span> call.Name {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"read"</span>, <span class="string">"ls"</span>, <span class="string">"grep"</span>:</span><br><span class="line"> <span class="keyword">return</span> &agentwrapper.Decision{Action: agentwrapper.ActionAllow}, <span class="literal">nil</span></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> &agentwrapper.Decision{Action: agentwrapper.ActionDeny, Reason: <span class="string">"只读模式"</span>}, <span class="literal">nil</span></span><br><span class="line"> }</span><br><span class="line"> }),</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><strong>预算控制。</strong> Agent 烧 token 跟烧钱似的。每个 turn 结束回调 BudgetHandler,超阈值直接掐断:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">agentwrapper.WithBudgetHandler(<span class="function"><span class="keyword">func</span><span class="params">(ctx context.Context, usage types.TokenUsage)</span></span> <span class="type">error</span> {</span><br><span class="line"> <span class="keyword">if</span> usage.TotalTokens > <span class="number">50000</span> {</span><br><span class="line"> <span class="keyword">return</span> fmt.Errorf(<span class="string">"预算超支: %d tokens"</span>, usage.TotalTokens)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">}),</span><br></pre></td></tr></table></figure><p><strong>上下文压缩 + 自动重试。</strong> LLM 报 "context length exceeded",Orchestrator 自动压消息历史(滑动窗口→摘要→链式策略,最多重试三次),调用方完全无感。</p><p>以上这三样,哪个 Agent CLI 原生都不给你。agent-wrapper 给了。</p><h2 id="会话恢复:让-Agent-记住你的项目"><a href="#会话恢复:让-Agent-记住你的项目" class="headerlink" title="会话恢复:让 Agent 记住你的项目"></a>会话恢复:让 Agent 记住你的项目</h2><p>Coding Agent 最烦的就是每次开新聊天都失忆。</p><p>![image-20260603080456247.png<img src="/2026/06/11/go-ai-agent-orchestration-framework/image-20260603080456247.png" class=""></p><p>agent-wrapper 支持 session resume。第一次 RunSync 拿回一个 SessionID,下次传进去,Agent 就知道你上次改了哪些文件、聊了什么架构、卡在哪个 bug 上:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一轮</span></span><br><span class="line">r1, _ := orch.RunSync(ctx, types.RunInput{Prompt: <span class="string">"这个项目的目录结构是什么?"</span>})</span><br><span class="line">fmt.Println(r1.SessionID) <span class="comment">// ← 存起来</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 下一轮,上下文全在</span></span><br><span class="line">r2, _ := orch.RunSync(ctx, types.RunInput{</span><br><span class="line"> Prompt: <span class="string">"刚才你提到有一个潜在的性能问题,展开说说"</span>,</span><br><span class="line"> SessionID: r1.SessionID,</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>CI 里跑重构,下班跑了一半,明天把 SessionID 传进去接着搞。跨天、跨进程、跨机器。</p><h2 id="效果展示"><a href="#效果展示" class="headerlink" title="效果展示"></a>效果展示</h2><p>![image-20260603065821797.png<img src="/2026/06/11/go-ai-agent-orchestration-framework/image-20260603065821797.png" class=""></p><p>一行命令就能跑:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 流式输出(文本→stdout,元数据→stderr)</span></span><br><span class="line">agent-wrapper run --provider claude-code <span class="string">"解释这段代码"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># JSON 聚合输出(CI/脚本友好)</span></span><br><span class="line">agent-wrapper run --provider codex <span class="string">"fix the bug"</span> --json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 带审批+预算</span></span><br><span class="line">agent-wrapper run --provider claude-code <span class="string">"重构本项目"</span> --approve-all --budget-tokens 50000</span><br><span class="line"></span><br><span class="line"><span class="comment"># NDJSON 管道输出</span></span><br><span class="line">agent-wrapper run --provider claude-code <span class="string">"hello"</span> --json --stream | jq .</span><br><span class="line"></span><br><span class="line"><span class="comment"># 恢复会话</span></span><br><span class="line">agent-wrapper run --provider claude-code --session-id abc123 <span class="string">"继续"</span></span><br></pre></td></tr></table></figure><p>但更核心的用法是当 Go 库用。go get 一下,import 进去,调 orch.Run。零额外进程,零协议开销。Claude Code、Codex、Pi、OpenCode,五个 provider,一套接口。</p><h2 id="套壳的真正价值"><a href="#套壳的真正价值" class="headerlink" title="套壳的真正价值"></a>套壳的真正价值</h2><p>套壳不丢人。</p><p>丢人的是换一次工具重写一遍胶水代码。丢人的是用着 AI Agent 还得手工管子进程生命周期。丢人的是一晚上 API 烧了几十美刀第二天看账单才后悔。</p><p>agent-wrapper 就是个壳。但解决的不是"怎么调一次 Agent",是"怎么让 Agent 安全地、长期地、按预算地、跨 provider 为你工作"。把"玩一下"变成"能上线"。</p><p>项目地址:<a href="https://github.com/smallnest/agent-wrapper">https://github.com/smallnest/agent-wrapper</a></p><h2 id="最后说几句"><a href="#最后说几句" class="headerlink" title="最后说几句"></a>最后说几句</h2><p>大多数人用 Agent 还停留在"命令行输一个 prompt"。这没什么不对,但不够。</p><p>十年前我们从手写 SQL 走到满世界 ORM,从手动部署走到 k8s 编排。每一轮变化里,有人站在第一层骂套壳,有人站在第二层埋头套壳,还有人站在第三层——让所有想套壳的人套得更快。</p><p>Agent 时代,把壳套好,就是最好的手艺。</p>
同分类推荐文章
- 00 卷首语:当 Karpathy 说他半年没写一行代码 (2026-06-21 21:20:27)
- LLM 究竟是如何工作的? (2026-06-21 11:09:44)
- Loop Engineering 实践:一次批量实现 8 个 issue,完成夔牛工具的开发 (2026-06-17 04:00:24)
建议继续学习
- Go Reflect 性能 (累计阅读 14,121)
- 面向“接口”编程和面向“实现”编程 (累计阅读 13,886)
- 一种基于长连接的社交游戏服务器程序构架 (累计阅读 7,471)
- 从Go看,语言设计(一) (累计阅读 6,146)
- go-kit 入门(一) (累计阅读 4,740)
- 分布式存储Seaweedfs源码分析 (累计阅读 4,722)
- 为什么我们要使用Go语言以及如何使用它的 (累计阅读 4,561)
- Go 语言初步 (累计阅读 4,478)
- 程序员的“横向发展” (累计阅读 4,119)
- ZeroMQ 的模式 (累计阅读 4,041)