AI Agent 工程实践(37):需求分析——一个 Agent 项目到底应该怎么拆

发布时间:2026-08-30

标签:AI Agent|工程实践|需求分析|Agent 架构


上一篇我定了方向:仓库诊断这个任务,值得用 Agent 做。

但"值得做"离"能开工"之间,还隔着一道很深的沟。

朋友问我:那你到底要做什么?

我说:做一个能诊断代码仓库的 Agent。

他追问:它接收什么?产出什么?中间能碰哪些系统?哪些东西它得记住?哪些事它绝对不能做?

我张了张嘴,发现一个都答不利索。

那一刻我明白了:"我有一个想法"和"我理解了需求",是两件完全不同的事。

而"把需求想清楚"这件事,在 Agent 项目里,比传统软件项目难得多------因为你要面对的,不是一个"输入→输出"的函数,而是一个会"自己决定下一步干什么"的大模型。


问题背景

这是第五阶段的第二篇。上一篇解决了"选对问题",这一篇解决"选对之后,怎么把一句模糊的需求,翻译成 Agent 能执行的确定性"。

先说清楚一件事:这里的"拆需求",不是画产品原型,也不是写接口文档,而是把需求拆成 Agent 能理解的八个槽位。 这是 Agent 项目和传统软件项目在需求阶段最大的不同。

传统软件的需求分析,产出的是功能清单和页面流程;而 Agent 的需求分析,产出的是八个"能力槽"------因为你最终要面对的是一个大模型,它不像代码那样有确定的输入输出,你必须先想清楚:喂给它什么、允许它碰什么、要求它记住什么、禁止它做什么。

为什么传统需求分析在这套不了?因为传统软件的"需求"是写给代码 看的------代码不自由发挥,你写清楚"输入 A 输出 B",它就不会输出 C。但 Agent 的需求是写给模型 看的------模型会自由发挥,你的需求里少定义一个"绝对不能做的事",它就真的可能去做。所以 Agent 需求分析的核心,不是"定义功能",而是定义边界:哪些能做、哪些不能做、边界在哪。


错误尝试

第一次尝试:一句话需求,直接开写

我最初的做法很天真:直接开写。

我的第一版"需求文档"只有一句话------"做一个能帮我分析仓库的 Agent,用 LangGraph 实现"。然后就跑去搭环境、装依赖、写第一个工具。

结果写到一半就卡住了:用户问"这个 bug 在哪",我到底该让它读几个文件?读到什么程度算找到?它能不能执行 git checkout?它给出的结论要不要附带证据?

每一个问题都让我停下来重新想,而每重新想一次,前面写好的代码就得推倒一块。需求没拆清楚就开始写代码,就像没画图纸就盖楼------每一堵墙都在返工。

更糟的是,因为需求是模糊的,我对"做完没有"也完全没有标准,只能靠感觉。这种感觉驱动的开发,在 Agent 项目里尤其危险,因为 Agent 的输出本身就是不确定的,你连一个"对的输出长什么样"都说不清。

第二次尝试:以为"想清楚了"就够了,没写下来

第一次失败后,我吸取了"要拆需求"的教训,但犯了第二个错:只在脑子里拆,没落到纸上。

我想:"嗯,输入是用户问题,输出是诊断结论,工具就是那几个,边界是只读不改。"------自认为已经想清楚了,于是又开始写代码。

结果写到一半发现:我想的"那几个工具"只有三个,但写的时候发现还需要读 git 历史;我以为"只读不改"就够了,但用户实际会问"帮我修这个 bug",那"改代码"到底算不算?这些模糊点,因为没写下来,每次都是写到那个位置才被迫面对,又变成了返工。

"以为想清楚了"和"真的写清楚了",差距巨大。 脑子里的需求会自动"补全"那些你没定义的槽------你以为你想过,其实你只是默认了。

关键观察:八个问题

转折点来自一个很土的办法:我把需求"拆"成了八个问题,逼自己逐个回答。回答不出来的,就说明我还没想清楚。

这八个问题后来成了我拆所有 Agent 项目的固定框架:

  1. Input:进来的是什么?
  2. Task:它要干哪几类事?
  3. State:干的过程中,它要记住哪些中间状态?
  4. Tools:它能调用哪些外部能力?
  5. Knowledge:它需要哪些静态知识?
  6. Memory:跨会话,它要长期记住什么?
  7. Policy:哪些事它绝对不能做?
  8. 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 工程实践 系列的第 37 篇。

相关推荐
tachibana235 分钟前
AI Agent 的记忆机制
人工智能·ai·大模型·llm·agent
ovO1 小时前
DeepSeek Harness 源码解读(三):七个核心服务怎样拼成一次 Agent 运行
开源·agent·deepseek
小羊432 小时前
从Prompt到Skill:专家经验的标准化封装指南
agent
一个处女座的程序猿2 小时前
Agent之Harness:WorkBuddy的简介、安装和使用方法、案例应用之详细攻略
agent·workbuddy·harness
乌拉布拉乌2 小时前
用 agents-md-writer 优化你的 AGENTS.md
人工智能·agent
GoCoding3 小时前
DeepSeek Harness 插件
agent·deepseek
Csvn3 小时前
第 11 章 路由 Routing
人工智能·aigc·agent
GoCodingInMyWay3 小时前
DeepSeek Harness 插件
agent·deepseek·harness
charles_he3 小时前
Agent 写操作超时后,最危险的动作是“再试一次”
人工智能·架构·agent