← 全部极客知识库

工程化 / 大模型应用工程化

2. 需求与规格——把“帮我优化一下”变成明确任务

原文备份 · 第 4 / 21 篇 · 查看极客时间原知识库 ↗

本篇目录 · 9 节

来源:大模型应用工程化

笔记助手用了几天,你记得问过一个关于RAG的问题,却在首页翻不到,于是对Kimi Code说:“帮我加个历史问答搜索。”

它可以很快做出一个输入框。但这个框到底搜索问题、答案还是笔记?会不会只能搜索首页已经显示的50条?如果换一个浏览器,能不能找到同一台电脑上的记录?这些选择即使都写成了能运行的代码,做出来的也可能不是你想要的功能。

本篇先把这些选择确定下来。交付结果是一份小而明确的需求规格、一组能够区分对错的案例,以及交给Kimi Code的规划任务。应用源码保持第01篇完成后的状态,搜索功能尚未实现。先把“要做什么、怎样判断”稳定下来,再进入后面的版本管理和任务拆分。

下载本篇材料 · 参考需求规格 · 验收案例 · 上一篇:可验证的开发

核实日期:2026年10月4日。本机Kimi Code CLI仍为2.0.2,仅执行版本和帮助检查。本文规格与案例由作者编写,不是实际Kimi生成记录。配套脚本只检查材料完整性,没有运行尚未实现的搜索功能,也没有调用Kimi或DeepSeek。

一、需求、规格和实现,分别回答什么

先不用记缩写,看三个层次的例子:

层次 本次例子 解决的问题
用户需求 我记得过去问过什么,希望快速找回来 为什么要做
行为规格 输入关键词,只搜索本会话历史问题,按明确规则展示结果 用户操作后应该发生什么
实现方案 哪个路由接收请求,哪个函数查记录,页面怎样渲染 代码具体怎样做

“加一个SQL查询”是实现线索,不能代替需求。反过来,“让搜索更智能”表达了愿望,也没有给出可以检查的行为。

本课使用的Spec就是一份行为约定,不要求套用庞大的文档模板。所谓规格驱动开发,在这里是先写下关键规则与例子,让实现和测试围绕它们展开;遇到真正改变需求的决定,再同步更新规格。它不保证Agent一定正确,也不意味着需求从此不能修改。

对第01篇那个统计行,几条清楚的验收条件已经足够。这次搜索涉及可见范围、字段、排序、空输入和结果上限,值得单独保存一份文档。文档的多少取决于判断难度,不取决于功能名字听起来是否高级。

二、先看项目事实,别让AI从想象中设计

解压练习包,进入lesson-02目录。project是第01篇reference的原样快照,包含已选证据统计;其余文件是本篇的规格与案例。没有复制你的历史数据库,不需要为了写需求先启动网页。

先打开三个文件,寻找这些位置:

  • project/web_app.py中的home函数:首页从当前会话读取历史。
  • project/storage.py中的history函数:按owner过滤,再取最近50条。
  • project/templates/web.html中的历史区域:显示问题、时间、模式与操作状态,链接进入已有预览或结果。

这里的owner可以先理解成“当前浏览器会话的内部标识”。项目还没有登录账号,清除Cookie会进入新会话;同一台电脑上的两种浏览器,也不因此共享一个身份。搜索需求必须承接这个事实,不能只加一个输入框就默认拥有跨设备账号历史。

还有一个容易被忽略的事实:历史不全是已经答完的问题。刚建立的预览是ready,处理中可能为running,结束后是done,结果未确认可能为uncertain。它们都有问题文本,但不一定都有完整答案。

现在把想法改写得更准确一些:让本人在同一浏览器会话里,通过记得的关键词找到过去提交的问题,再打开它的预览或结果。 这句话已经排除了不少不必要的猜测。

三、先回答五个最影响结果的问题

你不必一开始回答几十项问卷。先处理会明显改变实现和验收的五个决定。

需要决定 本课第一版选择 取舍理由
搜什么字段? 只搜问题文本,入口叫“搜索历史问题” 行为容易解释;答案全文搜索留待明确需要时增加
搜谁的记录? 当前会话的全部操作 延续现有权限,不新增账号与找回机制
怎样算匹配? 忽略大小写的连续子串 本地即可完成,不引入模型、向量服务或拆词规则
返回多少、怎样排列? 最新优先,最多50条;先匹配再截取 不漏掉较旧但相关的记录,限制页面规模
空、长、无结果怎么办? 空输入回最近列表;超过80字符报错;无结果明确提示 不静默截断,不把未命中伪装成模型回答

这些是本课的产品选择,不是所有搜索系统的统一规定。你完全可以选择搜索答案,但必须连带改变文案、案例和实现。不能一边告诉用户“全文搜索”,一边只过滤问题标题。

本次不会因为“智能”二字就调用模型。找一个已知关键词,确定性的本地匹配足够清楚。之后若真的需要“记不住原词也能找回”,那是新需求,应另外比较同义词、全文索引或语义检索,而不是偷偷改变第一版的匹配含义。

四、给容易含糊的词补上可检查的含义

现在把五个选择收紧到实现者不用猜的程度。

输入。 只接收一个关键词字符串,先用Python的strip()去掉两端空白,再检查长度。去空白后0到80字符合法;空字符串表示不加关键词过滤。81个字符就显示错误,不截成前80个。长度在大小写归一化前计算,避免转换后长度变化导致口径不一致。

匹配。 将关键词和问题分别做casefold()后,判断前者是否是后者的连续子串。它比只比较英文字母原样更适合不区分大小写的匹配;它不是中文分词、同义词检索或语义理解。内部空格保留,RAG 流程不会自动匹配没有那个空格的RAG流程。Python字符串方法

%和_也只是普通字符。某种数据库写法可能把它们解释成通配符,但这不能反过来改变用户约定。先规定行为,后面再选择能正确实现它的查询方式。

范围。 匹配所有属于当前会话的操作,四种状态都包括,演示和真实模式记录也都包括。别的会话即使有相同文字,也不能返回它的问题、数量或结果。身份由后端会话决定,不能通过表单里传一个owner就切换成别人。

顺序。 先完成可见范围与关键词过滤,再按创建时间从新到旧排序。时间相同时按操作ID降序,保证规则确定;它不表示随机ID本身代表真实先后。最后取最多50条,有更多时提示用户缩小关键词。这里的80和50都是教学应用的限制,不是服务商上限。

副作用。 已建立会话后,搜索、刷新和清空不会新建问答或计划,不调用模型,也不修改旧业务记录。原程序在首次访问时可能建立会话,响应可能更新Cookie,所以规格写的是“不修改业务记录”,没有声称HTTP访问绝不产生任何状态。

完整规则保存在SEARCH_SPEC.md,用R1到R7编号。后续发现问题,可以直接说“实现违反R5的先过滤后限制”,比反复转述一长段聊天更容易定位。

五、用反例检验规格,而不只是举成功例子

先看材料中六条精简记录。A和B代表两个不同会话;这些都是作者构造的数据,没有真实个人内容。

ID 会话 问题 说明
A01 A RAG流程是什么? 较早,已有答案
A02 A 怎样检查引用? 问题不含RAG,答案含RAG
A03 A rag失败怎样排查? 比A01新,仍是ready
B01 B RAG相关的私有问题 属于另一个会话
A04 A 100%覆盖是否可信? 用来检查百分号是否按文字匹配
A05 A 下划线_是什么? 用来检查下划线是否按文字匹配

当前会话为A,输入RAG,预期依次返回A03、A01。

这个例子一次区分了几种常见错误:不去两端空白会漏结果;只匹配大写会漏A03;擅自搜索答案会多出A02;不检查身份会泄漏B01;只搜索done状态会漏掉尚未生成答案的A03。

再看一个更难察觉的反例:会话C有51条记录,最旧的C01包含“旧资料针”,其余50条都不包含。查询“旧资料针”必须返回C01。如果实现先取首页最近50条,再在那50条里找,就会错误地显示无结果。

这不是要为个人应用提前搭大型搜索引擎。只是把两个看似接近的操作顺序区分开:

要求:本会话全部记录 → 关键词匹配 → 排序 → 最多50条
错误捷径:本会话最近50条 → 关键词匹配

写验收案例时,可以使用“给定条件—当用户操作—应出现结果”的形式。例如:

给定:会话C已有51条记录,只有最旧的C01包含“旧资料针”。
当:当前会话C搜索“旧资料针”。
那么:列表包含C01,不显示“没有匹配的问题”。
并且:没有创建新的问答或调用模型。

这种组织方式与Gherkin中的Given/When/Then思路相通。这里借用它来写清条件、动作和结果,不要求安装Cucumber,也不把中文描述直接宣称为自动化测试。Gherkin参考

六、让Kimi Code帮助澄清,而不是立刻写代码

已按第01篇完成安装和登录的读者,在lesson-02目录运行:

kimi --plan

此处工作目录包含project与练习材料,不要进入别的私人项目。当前官方文档说明--plan用于以规划模式启动,优先读取和探索。它不等于操作系统级只读沙箱;仍需检查具体动作,不批准与本课无关的执行或源码修改。Kimi Code命令文档

先发一个故意保留模糊空间的请求:

我想给这个个人笔记助手增加历史问答搜索。
请先读取project/web_app.py、storage.py、templates/web.html里与历史有关的代码。
只分析,不修改、不运行应用,不读取data、个人配置或密钥,也先不要看SEARCH_SPEC.md参考答案。
请区分:代码已证明的事实、需要我决定的问题、你建议的默认值。
最多提出5个最影响结果的问题,先别写实现方案。

不要只看回答是否流畅。核对它是否发现当前会话隔离、最近50条上限、ready记录也有问题等事实。它若说“用户登录后跨设备搜索”,就要追问这个能力在代码哪里存在,而不是把想象当现状。

然后给出第三、四节的具体取舍,让它起草一份规格。要求目标、范围、输入、匹配、顺序、空与错误状态、副作用、不做项和验收案例齐全。结果先展示在对话里,由你审阅后保存为MY_SPEC.md。这样本篇真正产出的是一份经过取舍的工作文档,不是一段Agent自行猜测的需求。

再给一次审查任务:

请检查这份规格是否仍有可能产生不同实现结果的歧义;若有,最多列出三个,并给出具体输入和预期差异。
特别检查:是否只在最近50条里搜索;是否搜索答案;%与_是否被当成通配符;其他会话记录是否会泄漏。
若规则已明确,不要为了凑数量编造问题。不要修改应用代码。

完整练习流程在TASK.md。开发对话仍可能消耗自己的Kimi额度;没有账户也可以手工完成MY_SPEC并与参考文档比较,但这不算真实Agent执行。本篇作者没有模拟一份“已经与Kimi讨论过”的聊天记录。

七、检查材料完整性,别把它误当成功能验收

search-cases.json保存57条精简记录和12个预期案例,覆盖大小写与空白、问题字段边界、空查询、普通符号、80/81字符、跨会话、旧记录、结果上限和未完成操作。

在lesson-02目录运行,不需要安装第三方库:

macOS/Linux:

python3 audit_cases.py

Windows PowerShell:

py -3 audit_cases.py

预期前两行是:

材料结构通过:57条构造记录,12个预期案例。
未执行搜索功能;没有验证匹配正确性、页面、SQL、权限或副作用。

之后会打印每个案例的说明与预期。脚本检查ID是否重复、预期是否引用存在的记录、是否意外把其他会话ID列为允许结果、是否超过约定展示上限。它没有实现搜索算法,不会替你判断“为什么这个问题应当匹配”。

因此,检查通过后仍需手工核对S01和S08:前者应为A03、A01,后者应为C01。再核对S03的空查询顺序、S09的50条截取,以及S06的长度错误。预期是作者写下的要求,不是程序运行得到的事实。

还缺哪些真正的验收?实现后,要把这些精简记录映射到临时数据库的真实结构,发送页面请求核对列表,再验证伪造身份无效、输入正确转义、重复搜索不新增操作、不调用生成函数,以及原有功能仍通过。SEARCH_SPEC.md把这些明确列为待做。

现在打开project的页面不会出现搜索框,是正确的阶段状态。运行原acceptance.py即使得到10项通过,也只能说明旧功能正常,不能把未实现的新功能勾成“完成”。

八、规格也需要维护,但别把它写成一本书

这份文档应足以让实现者知道结果,却不必提前规定所有函数名、SQL写法、CSS颜色或每一行代码。后续发现一种更简单的实现,只要符合行为约定,就可以采用。

有些变化则必须先改规格。比如你决定“答案里出现RAG也要搜到”,这会改变R4,A02应从S01的排除项变成结果;它还会影响空答案、未完成记录和数据读取。此时保留旧版本,记录新取舍,再更新相关案例,不能只在聊天末尾加一句“顺便也搜答案”,却继续按旧案例验收。

相反,修复排序实现却保持相同顺序约定,不一定需要改规格。区分“行为目标改变”和“实现还没有做到”,能避免一遇到失败就把要求改掉。

本篇不引入需求管理平台、复杂审批表或规格生成框架。一个小Markdown文件、一组能识别错误实现的例子,加上清楚的版本记录,就足够支持眼前这次迭代。

九、交出这三份东西,再开始实现

完成练习后,你应当能交出:一份明确取舍的MY_SPEC;一组与它一致的验收案例;一份仍待实现和验证的事项清单。没有自己的Kimi账户也能手工完成,但不要混淆“文档写好”与“功能交付”。

做两个判断:如果你只需要找问题,Agent建议顺便增加向量数据库,是否必须采用?不必,先说明当前连续子串为何不足,再决定是否改变需求。如果案例检查脚本输出通过,是否可以告诉用户历史搜索上线了?不能,它没有执行任何搜索请求。

本篇对照了实际历史存储与页面代码,检查了案例结构、预期引用和边界,保留了第01篇全部应用源码。材料中的反例用于阻止错误的实现方向,不是模型质量评测结果。

下一篇先建立Git检查点,让这份规格和后续修改都可以比较、保留与撤回。第04篇再根据它拆分任务和安排实现,避免把“需求还没定”造成的反复修改误认为Agent能力不足。