AI Agent 工程实践(37):需求分析——一个 Agent 项目到底应该怎么拆
发布时间:2026-08-30
标签:AI Agent|工程实践|需求分析|Agent 架构
- 上一篇:AI Agent 工程实践(36):不要再写 Agent 教程——先定义一个真实问题
- 下一篇:AI Agent 工程实践(38):从需求到 Agent 架构——为什么需要这些节点
上一篇我定了方向:仓库诊断这个任务,值得用 Agent 做。
但"值得做"离"能开工"之间,还隔着一道很深的沟。
朋友问我:那你到底要做什么?
我说:做一个能诊断代码仓库的 Agent。
他追问:它接收什么?产出什么?中间能碰哪些系统?哪些东西它得记住?哪些事它绝对不能做?
我张了张嘴,发现一个都答不利索。
那一刻我明白了:"我有一个想法"和"我理解了需求",是两件完全不同的事。
而"把需求想清楚"这件事,在 Agent 项目里,比传统软件项目难得多——因为你要面对的,不是一个"输入→输出"的函数,而是一个会"自己决定下一步干什么"的大模型。
问题背景
这是第五阶段的第二篇。上一篇解决了"选对问题",这一篇解决"选对之后,怎么把一句模糊的需求,翻译成 Agent 能执行的确定性"。
先说清楚一件事:这里的"拆需求",不是画产品原型,也不是写接口文档,而是把需求拆成 Agent 能理解的八个槽位。这是 Agent 项目和传统软件项目在需求阶段最大的不同。
传统软件的需求分析,产出的是功能清单和页面流程;而 Agent 的需求分析,产出的是八个"能力槽"——因为你最终要面对的是一个大模型,它不像代码那样有确定的输入输出,你必须先想清楚:喂给它什么、允许它碰什么、要求它记住什么、禁止它做什么。
为什么传统需求分析在这套不了?因为传统软件的"需求"是写给代码看的——代码不自由发挥,你写清楚"输入 A 输出 B",它就不会输出 C。但 Agent 的需求是写给模型看的——模型会自由发挥,你的需求里少定义一个"绝对不能做的事",它就真的可能去做。所以 Agent 需求分析的核心,不是"定义功能",而是定义边界:哪些能做、哪些不能做、边界在哪。
错误尝试
第一次尝试:一句话需求,直接开写
我最初的做法很天真:直接开写。
我的第一版"需求文档"只有一句话——"做一个能帮我分析仓库的 Agent,用 LangGraph 实现"。然后就跑去搭环境、装依赖、写第一个工具。
结果写到一半就卡住了:用户问"这个 bug 在哪",我到底该让它读几个文件?读到什么程度算找到?它能不能执行git checkout?它给出的结论要不要附带证据?
每一个问题都让我停下来重新想,而每重新想一次,前面写好的代码就得推倒一块。需求没拆清楚就开始写代码,就像没画图纸就盖楼——每一堵墙都在返工。
更糟的是,因为需求是模糊的,我对"做完没有"也完全没有标准,只能靠感觉。这种感觉驱动的开发,在 Agent 项目里尤其危险,因为 Agent 的输出本身就是不确定的,你连一个"对的输出长什么样"都说不清。
第二次尝试:以为"想清楚了"就够了,没写下来
第一次失败后,我吸取了"要拆需求"的教训,但犯了第二个错:只在脑子里拆,没落到纸上。
我想:"嗯,输入是用户问题,输出是诊断结论,工具就是那几个,边界是只读不改。"——自认为已经想清楚了,于是又开始写代码。
结果写到一半发现:我想的"那几个工具"只有三个,但写的时候发现还需要读 git 历史;我以为"只读不改"就够了,但用户实际会问"帮我修这个 bug",那"改代码"到底算不算?这些模糊点,因为没写下来,每次都是写到那个位置才被迫面对,又变成了返工。
"以为想清楚了"和"真的写清楚了",差距巨大。脑子里的需求会自动"补全"那些你没定义的槽——你以为你想过,其实你只是默认了。
关键观察:八个问题
转折点来自一个很土的办法:我把需求"拆"成了八个问题,逼自己逐个回答。回答不出来的,就说明我还没想清楚。
这八个问题后来成了我拆所有 Agent 项目的固定框架:
- Input:进来的是什么?
- Task:它要干哪几类事?
- State:干的过程中,它要记住哪些中间状态?
- Tools:它能调用哪些外部能力?
- Knowledge:它需要哪些静态知识?
- Memory:跨会话,它要长期记住什么?
- Policy:哪些事它绝对不能做?
- Output:出来的是什么?
为什么是这八个?因为它们恰好覆盖了 Agent 项目里"必须提前想清楚"的八个边界:输入边界(Input)、任务边界(Task)、状态边界(State)、能力边界(Tools)、知识边界(Knowledge)、记忆边界(Memory)、行为边界(Policy)、输出边界(Output)。任何一个没定义清楚,Agent 就会在运行期用"自由发挥"替你补一个错误答案。
核心洞察是:
拆需求的过程,就是把你脑子里的模糊,翻译成 Agent 能执行的确定性。八个槽填满的那一刻,"做完"才有了定义。
最终方案:把 Repo Doctor 拆进八个槽
拿我们的贯穿项目 Repo Doctor 来实操,逐个槽填满。每个槽我都会给"是什么 + 一个反例(不定义清楚的后果)":
1. Input(输入)
- 用户的自然语言问句("这个项目里支付逻辑在哪")
- 一个本地仓库的路径(Agent 的操作范围锚点)
- 反例:如果只定义"用户问句",不定义"仓库路径"锚点,Agent 就会在多个仓库间乱窜,甚至去读用户的整个磁盘。
2. Task(任务,三种)
- bug 定位:从现象反查代码,找到根因
- 代码解释:讲清某段逻辑是干嘛的
- PR review:评估一次改动的风险
- 反例:如果只定义"诊断"一个大类,不拆成三种子任务,Agent 就会把"解释代码"和"审查 PR"混为一谈——该解释时去挑毛病,该审查时去背文档。
3. State(中间状态)
- 已读过的文件列表
- 已确认的线索("支付入口在 order.py")
- 当前假设("怀疑是回调没注册")
- 待验证的疑点队列
- 反例:如果不定义 State,Agent 就会重复读同一个文件、忘记自己查过什么——这正是第 40 篇"State Error"的高发区。
4. Tools(工具)
grep(搜代码)、read_file(读文件)、git_log(看历史)、run_test(跑测试)- 反例:如果不限定工具集,Agent 可能去调用"写文件""删文件"这类危险工具——所以 Tools 槽的另一个作用是"能力白名单"。
5. Knowledge(静态知识)
- 目标框架的 API 用法(如 FastAPI 的路由、依赖注入)
- 常见报错模式库("这个报错通常是 xx 原因")
- 反例:如果没有 Knowledge,Agent 面对不熟悉的框架时会瞎猜 API 用法,产生幻觉。
6. Memory(跨会话记忆)
- 这个仓库的目录结构、核心模块分布
- 之前诊断过的历史结论("上次这个 bug 是 xx 修的")
- 反例:如果没有 Memory,同一个仓库每次诊断都要从零摸结构,浪费时间;上周修过的 bug,这周又当新问题查。
7. Policy(红线)
- 只读不写:绝不修改任何文件
- 不碰
.git内部 - 不读取、不输出
.env、密钥等敏感内容 - 反例:这是最不能省的槽。没有 Policy,用户说一句"帮我看看 .env 里有什么",Agent 可能真的读出来给你——这是真实发生过的事。
8. Output(输出)
- 诊断结论 +证据链(每个结论都要指向具体的文件行)
- 修复建议(可选,但不直接改代码)
- 反例:如果不强制"证据链",Agent 就会给出没有根据的结论——"可能是数据库问题"这种话,你无法验证它是对是错。
八个槽填完,你会发现一个神奇的变化:"做完没有"第一次有了客观标准——它答对了没有,就看它的结论有没有证据、证据对不对得上文件。
架构图 / 流程图
八个槽的关系不是平铺的,它们之间有一条数据流向。画出来是这样:
关键在中间那个闭环:State → 调用 Tools → 回到 State,这是 Agent 区别于普通函数的核心——它在执行中不断更新自己的认知,直到认为证据够了,才走向 Output。这个"状态驱动的调查闭环",就是第 39 篇要动手实现的第一个东西。
第二张图:八槽的"生命周期视角"(发布提示:可用 draw.io 重画成正式图,与 Mermaid 图形成双图组合):
┌───────────────────────────────────────┐ │ 一次任务的生命周期 │ └───────────────────────────────────────┘ 进来: Input(问句+仓库) ─→ Task(识别类型) ─→ Policy(红线过滤) │ 执行: ┌──────────── 循环 ────────────┐ │ │ State(当前认知) │ │ │ │ 调用 │ │ │ ▼ │ │ │ Tools / Knowledge ─→ 新证据 │◄──────────┘ │ │ 更新 │ │ └──→ 回到 State │ └───────────────────────────────┘ │ 出来: State 证据够 → Memory 沉淀 → Output(结论+证据)这张图强调的是:Policy 是整个循环的"外圈护栏"——它不参与循环,但约束循环里的每一步。八槽里最容易漏的,就是这个"外圈护栏"(Policy),因为它不产生输出、只防止错误输出。
代码或配置示例
八个槽不是纸面概念,我会把它落成一份真实的配置骨架,作为整个项目的"需求底座":
# repo_doctor/requirements.yaml —— 需求拆解的唯一事实源 agent: name: Repo Doctor input: - query: string # 用户问句 - repo_path: string # 仓库根路径 tasks: - bug_locate # 定位 bug - code_explain # 解释代码 - pr_review # PR 审查 state: - files_read: [] # 已读文件 - clues: [] # 已确认线索 - hypothesis: null # 当前假设 - pending: [] # 待验证疑点 tools: - grep - read_file - git_log - run_test knowledge: - framework_api: fastapi - error_patterns: true memory: - repo_structure # 记住仓库结构 - past_diagnostics # 记住历史诊断 policy: - read_only: true # 只读 - no_dot_git: true # 不碰 .git - no_secrets: true # 不泄露敏感信息 output: - conclusion: string - evidence: [] # 证据链(必填) - suggestion: optional这份 YAML 的价值在于:它把"我大概知道要做什么",钉成了"每个槽是什么、边界在哪"。后面所有代码,都是对这份需求的翻译。
为了让"八槽"不流于形式,我还会给每槽加一条"验收标准"——填槽时问自己"这条写清楚了没有":
# 八槽验收清单(填槽时自问) acceptance: input: "能举出 3 种典型输入,并知道边界(什么不该收)" task: "能列全任务类型,并说清每类的判定特征" state: "能列出运行中必须记住的所有中间信息" tools: "能列出能力白名单,并标注哪些是危险项" knowledge: "能列出 Agent 必须提前知道、不能靠猜的东西" memory: "能区分'会话内'与'跨会话'各记什么" policy: "能列出 3 条以上绝对红线" output: "能定义'结论必须带证据'这类硬约束"设计权衡
| 候选方案 | 优点 | 缺点 | 为什么不选 |
|---|---|---|---|
| 只写一句需求就开干 | 快 | 写到一半反复返工,"做完"没标准 | 需求模糊是 Agent 返工的根因 |
| 写 50 页 PRD | 详尽 | 太重,Agent 需求大多 8 个槽就够 | 过度设计,拖慢启动 |
| 八槽拆解法 | 边界清晰、够用 | 槽与槽之间有耦合需再梳理 | 正好卡在"够用"和"清晰"之间 |
一个诚实的边界:八个槽不是银弹。对于特别复杂的 Agent(比如要对接几十个系统),你可能需要更细的拆解;但对于"从零做一个项目"这个阶段,八个槽是性价比最高的框架——它逼你想清楚,又不至于把你拖进文档泥潭。
另一个提醒:槽与槽之间有耦合。比如 Policy 会限制 Tools("只读"意味着删掉所有写工具)、Output 依赖 State(证据链来自中间状态)。填槽时不要孤立地填,要顺着数据流走一遍,确认八个槽能串成一条自洽的链路。
常见误区(FAQ)
Q1:八槽和写接口文档有什么区别?
接口文档定义"输入输出类型",八槽额外定义"行为边界(Policy)""中间状态(State)""跨会话记忆(Memory)"——这些都是传统接口文档没有、但对 Agent 至关重要的槽。
Q2:一定要把八个槽全写成文档吗?
写下来至少一次。哪怕只写在自己的笔记里。关键是"写"这个动作——它逼你面对"脑子里默认但没定义的槽"。(我第二次失败就是栽在这:以为想清楚了,其实只是默认了。)
Q3:需求拆完,后面需求变了怎么办?
改 requirements.yaml,然后顺着改动重新检查关联槽(比如加了新 Task,State/Tools/Policy 都可能要跟着动)。八槽是"唯一事实源",改动都从它发起,就不会散落各处。
Q4:Policy 槽到底该多严?
参考"最少权限"原则:只给完成任务所需的最小能力。Repo Doctor 只需要读,就绝不配写工具;宁可在需求阶段多删一条能力,也不要在运行期多一个风险面。
总结
✅ 需求拆解的目标:把模糊翻译成 Agent 能执行的确定性。
✅ 固定框架:Input / Task / State / Tools / Knowledge / Memory / Policy / Output 八个槽。
✅ 八个槽本质是八条边界:输入/任务/状态/能力/知识/记忆/行为/输出。
✅ 核心闭环:State → Tools → State,这是 Agent 区别于函数的地方。
✅ 产出物是一份 YAML 需求底座,作为后续所有代码的"唯一事实源"。
✅ "做完没有"第一次有了客观标准:结论有没有证据、证据对不对得上。
参考资料
- OpenAI《A Practical Guide to Building Agents》→ 为什么引用:它把 Agent 的关键要素(tools、state、guardrails)体系化,是八槽框架的参照。
- LangGraph 的 State 设计文档 → 为什么引用:State 是八槽里最关键的一环,这里提前确认了它的工程形态。
系列导航
- 上一篇:AI Agent 工程实践(36):不要再写 Agent 教程——先定义一个真实问题
- 下一篇:AI Agent 工程实践(38):从需求到 Agent 架构——为什么需要这些节点
本文是 [AI Agent 工程实践] 系列的第 37 篇。
