:第一次实现——先做一个最小 Agent)
发布时间2026-09-1标签AI Agent工程实践MVP最小实现架构画得再漂亮也只是纸上的东西。上一篇我画了四个节点Task Router、Planner、Analyzer、Reviewer。听起来很完整对吧但我决定一个都不实现。这一篇我只写最窄的一条通路用户问 → 调一个工具 → 读结果 → 回答。原因很简单我想看看这个最小可跑的版本到底会怎么死。系列导航上一篇AI Agent 工程实践38从需求到 Agent 架构——为什么需要这些节点下一篇AI Agent 工程实践40第一次失败——Agent 为什么会做错问题背景这是第五阶段的第四篇也是第一次真正写代码。很多人会犯一个错架构图里画了四个节点就非得把四个都写出来才罢休。但我的经验是——先做一个故意很蠢的最小版本让它跑起来然后用它去暴露问题。这个最小版本有个学名叫 MVPMinimum Viable Product但在这里它的意义不是证明能跑而是用最快的速度暴露它会怎么死。为什么暴露死亡这么重要因为 Agent 项目最大的风险不是写不出来而是写了很多但里面的假设全是错的。你架构图里画的 Planner、Reviewer可能根本解决不了真实问题——而这些问题只有让最小版本跑起来、撞上真实问句才会显现。所以我这一篇砍掉 Task Router、砍掉 Planner、砍掉 Reviewer只保留一个 LLM 两个工具grep 和 read_file让整条链路能跑通。错误尝试我差点犯了两个反方向的错。第一个错想一步到位。想把四个节点、六个工具、Memory、评估集全部写完再跑。结果就是写了一个月一行能跑的代码都没有还积累了一堆我以为对的假设。这一堆以为对的假设是最贵的。比如我以为LLM 会自己选对工具直到真实跑起来才发现它经常选错我以为读到文件就能定位 bug直到真实跑起来才发现它会编造不存在的函数。这些假设只有让代码真的跑起来才会被证伪——而一步到位的写法让你把所有假设都攒到最后一起爆。第二个错觉得太简单不值得跑。一个 LLM 两个工具这有什么好跑的 但我告诉你恰恰是这个最简单的版本暴露了后面整整十篇要解决的问题。你只有真的跑起来才能看到它选错工具、读错文件、凭空编造结论的样子。两个错误殊途同归都推迟了第一次看到真实失败的时间点。这里我要特别强调一个心态在 Agent 开发里先跑起来的优先级高于架构正确。因为 Agent 的行为极度依赖真实执行环境你画在纸上的架构有一半会在第一次真实运行时被推翻。与其花一个月搭一个看起来正确的架构不如花两天搭一个一定能跑的最小版然后用真实失败去修正架构。关键观察所以这篇的核心动作就一个用最短的路径让 Agent 第一次真正跑起来然后诚实地记录它为什么不能直接上线。最小版本长这样没有任何花哨的东西。一个循环LLM 决定调工具 → 工具执行 → 结果塞回上下文 → LLM 继续直到它觉得该回答了。核心洞察MVP 的意义不是证明能跑而是用最快的速度暴露它会怎么死。这个最小版的价值不在于它能做什么而在于它把Agent 运行的每一个环节都摊开在你面前LLM 决策、工具选择、参数传递、结果解析、终止判断——每一个环节都可能出问题而这些问题只有最小版能让你一个一个看清楚。最终方案100 行的最小 Agent下面是 Repo Doctor v0 的完整实现用 Python 手写一个 tool-calling loop不依赖任何框架就是为了看清每一环# repo_doctor/v0/main.py —— 最小可跑版本约 100 行 import subprocess, json from openai import OpenAI client OpenAI(base_urlhttps://api.deepseek.com, api_key...) SYSTEM 你是仓库诊断助手。你可以调用工具来调查代码库。 可用工具 - grep(keyword): 在仓库中搜索关键字返回匹配的文件和行 - read_file(path): 读取指定文件内容 调查充分后直接输出结论。 TOOLS [ {type: function, function: { name: grep, description: 在仓库中搜索关键字返回匹配的文件和行, parameters: {type: object, properties: { keyword: {type: string}}, required: [keyword]}}}, {type: function, function: { name: read_file, description: 读取指定文件内容, parameters: {type: object, properties: { path: {type: string}}, required: [path]}}}, ] def call_tool(name, args, repo): if name grep: r subprocess.run([grep, -rn, args[keyword], repo], capture_outputTrue, textTrue, timeout10) return r.stdout[:3000] or (无匹配) if name read_file: with open(f{repo}/{args[path]}, encodingutf-8, errorsignore) as f: return f.read()[:3000] return (未知工具) def run_agent(query, repo, max_steps6): messages [{role: system, content: SYSTEM}, {role: user, content: f仓库路径 {repo}问题{query}}] for _ in range(max_steps): resp client.chat.completions.create( modeldeepseek-chat, messagesmessages, toolsTOOLS) msg resp.choices[0].message if msg.tool_calls: messages.append(msg) for tc in msg.tool_calls: result call_tool(tc.function.name, json.loads(tc.function.arguments), repo) messages.append({role: tool, tool_call_id: tc.id, content: result}) else: return msg.content return 达到最大步数仍未完成 if __name__ __main__: print(run_agent(这个项目里支付相关的逻辑在哪, /path/to/hello-agents))跑一次输出大概长这样结论支付相关逻辑在 payment.py 中核心函数是 payment_process() 它负责订单支付和回调处理。建议查看该函数附近的 payment_callback()。这一版能跑。但先别急着高兴我们仔细看这个结论——它有一个致命的问题payment_process()这个函数仓库里根本不存在。它是 Agent 编出来的。下一篇会专门解剖这个失败。在进入为什么不能上线之前先把这 100 行代码的每个关键环节点一遍你就知道最小版到底暴露了哪些环节代码片段环节潜在问题最小版就埋着SYSTEMTOOLS提示与工具描述工具描述含糊LLM 会选错工具第 41 篇修call_tool工具执行参数不校验、结果截断 3000 字符第 44 篇修for _ in range(max_steps)终止控制最大 6 步可能没查完就停或烧光第 41 篇修messages.append上下文管理无限增长长任务爆上下文第 45 篇修return msg.content输出结论无证据校验幻觉直接进答案第 40、42 篇修这段 100 行代码每一行都对应着后面一篇要解决的问题。这就是最小版的价值——它不是最终产品的阉割版而是问题清单的具象化。架构图 / 流程图把上面代码的执行流程画出来你会看到它的朴素看着很正常对吧但问题就藏在最后一步——那个payment_process()到底存不存在Agent 根本没验证。第二张图这个循环的隐患标注版发布提示可用 draw.io 重画成正式图与 Mermaid 图形成双图组合用户问句 │ ▼ ┌────────────┐ ① 工具描述含糊 → 可能选错工具40/41 篇 │ Agent(LLM) │──────────────────────────────┐ └─────┬──────┘ │ │ ② 参数不校验 → 可能传错参数41 篇 │ ▼ │ ┌────────────┐ ③ 结果截断 3000 字符 │ │ call_tool │ 可能丢关键信息44 篇 │ └─────┬──────┘ │ │ ④ 结果塞回上下文无校验 │ ▼ │ ┌────────────┐ ⑤ 结论无证据检查 │ │ Agent(LLM) │ 幻觉直接进答案40/42 篇 │ └─────┬──────┘ │ │ ⑥ 输出结论 │ ▼ │ 答案 ◄───────────────────────────────────┘ payment_process() 可能是编的这张图把最小版的每一个薄弱环节都标了出来。后面整整十篇就是逐个把这些隐患标注换成已修复。为什么不能直接上线这一版跑通了但我把它能跑和能上线分得很清。下面这张清单就是我故意留的技术债也是后面整整十一篇的伏笔#技术债后果后面哪篇解决1没有 Task Router三种任务混在一起该定位时去解释472没有 Planner查够了没全凭感觉草率下结论 / 烧 Token40、413工具描述太模糊参数没约束选错工具、传错参数41、424没有 Reviewer结论无证据链幻觉成灾payment_process()可能不存在40、425没有 State调查过程不记录无法回溯为什么这样想426没有评估集改完好坏不知道越改越玄学437上下文无上限读文件会撑爆长文件直接溢出44、498出错就死无兜底工具报错整个流程崩449无法复现模型/Prompt/工具都没锁版本同一个问题两次答案不同4510没有成本/延迟监控烧多少 Token 全靠猜46这一篇的价值就是把上面这十个坑提前摆在明面上。它们不是以后可能会出问题而是现在就已经埋下了只等真实问句来引爆。设计权衡候选方案优点缺点为什么不选一步到位写完整架构一次成型一个月跑不起来积累一堆未验证假设推迟了看到真实失败的时间用 LangGraph 框架搭省事、规范掩盖底层细节出了问题看不懂先手写 loop看清每一环100 行手写最小版快、透明、暴露问题功能残缺最快暴露它会怎么死关于为什么不用 LangGraph值得单独说不是 LangGraph 不好而是在这个阶段你需要的不是框架的便利而是对每一环的可见性。手写 loop你能精确看到LLM 这次调了什么工具、传了什么参数、工具回了什么。等这套东西你想清楚了第 49 篇再谈要不要换成框架。常见误区FAQQ1MVP 越少越好是不是连工具都只留一个最少两个。一个工具比如只留 read_file无法暴露工具选择这个环节的问题——而工具选错恰恰是 Agent 最常见的失败之一第 40 篇的 Tool Selection Error。Q2为什么不用 LangGraph 搭 MVP对初学者来说框架会掩盖每一环发生了什么。手写 100 行 loop你被迫面对工具调用、参数解析、结果回填这些最底层的问题——这些问题在框架里被封装了你直到出 bug 才知道它们存在。Q3这 100 行代码最后会被扔掉吗不会全扔。它的工具调用循环骨架会被保留后续的 Task Router、Planner、Reviewer 都是在这个骨架上加节点而不是推倒重来。MVP 不是一次性用品是后续版本的可运行的基线。Q4怎么判断 MVP够了一个简单标准它能完整跑完一次端到端任务哪怕结果错误。能跑 会错就是最好的 MVP 状态——因为下一步就是去解剖它怎么错的第 40 篇。总结✅ 先做最小可跑版本故意砍掉所有高级节点。✅ 100 行手写 loop就是为了看清每一环。✅ 这 100 行里每一行都对应一篇后续要解决的问题。✅ 这一版能跑但埋了 10 个技术债。✅ 铁律MVP 的意义不是证明能跑而是最快暴露它会怎么死。✅ 下一篇让这个最小版跑真实案例看它第一次做错。参考资料OpenAI Function Calling 文档 → 为什么引用tool-calling loop 的 API 用法是这 100 行的基础。《The Pragmatic Programmer》tracer bullet 概念 → 为什么引用先打通一条最小链路再扩展正是本篇的方法论来源。系列导航上一篇AI Agent 工程实践38从需求到 Agent 架构——为什么需要这些节点下一篇AI Agent 工程实践40第一次失败——Agent 为什么会做错本文是 [AI Agent 工程实践] 系列的第 39 篇。