第 13 章 · 工程
综合项目:把知识变成一个可验证的系统
不再按名词分章,而是从需求、数据、模型、服务、评测和复盘出发,完成一次端到端的大模型项目推演。
学习准备与本章目标
- 开始前
- 完成前十二章主教材 · 能读懂基础 Python · 理解训练与推理的区别
- 学完后
- 把模糊需求改写成可验收指标 · 设计可复现的实验与系统架构 · 用证据定位问题并完成技术复盘
本章路线图
前十二章按知识主题展开,本章改用真实项目的顺序:定义问题 → 建立基线 → 准备数据 → 选择方案 → 实现最小闭环 → 评测 → 优化 → 上线观测 → 复盘。这条顺序很重要,因为工程失败往往不是“模型知识少”,而是还没说清目标就开始换模型,或者没有稳定基线就宣称优化有效。
贯穿本章的任务是做一个面向个人知识库的问答助手。它需要回答资料中的问题、给出可追溯引用、在证据不足时明确拒答,并满足可交互的延迟要求。你也可以把同一套模板迁移到客服、代码助手、报告抽取或多模态检索。
第一件事不是选模型,而是写清任务契约
“做一个效果好的知识库助手”无法直接验收。把它拆成四类可观察约束:输入是什么,输出必须包含什么,什么情况算失败,成本与延迟上限是多少。例如:输入是一条中文问题和用户身份;输出包含答案、引用片段与文档位置;无证据时不得补写事实;测试集事实正确率达到约定门槛;95% 请求在目标时延内完成。
任务契约还要写出非目标。第一版可以明确不支持复杂表格、不执行外部写操作、不处理超过规定大小的文件。非目标不是偷懒,而是防止评测集合、权限模型和容量估算不断漂移。
用用户故事和失败故事限定边界
用户故事描述正常路径:“作为读者,我想询问某份资料的结论,并跳到原文核对。”失败故事描述系统必须如何收缩:“资料没有答案时,系统应说明未找到证据,而不是根据常识猜测。”每个故事都应转成至少一条测试样例。
建议先建立失败分类:问题不可回答、权限不足、检索为空、检索命中但证据冲突、上下文过长、模型没有遵循引用格式、工具超时、输出包含敏感信息。分类越具体,后面越容易判断该改语料、检索、提示词、模型还是服务层。
先做三个逐级增强的基线
基线不是随便做个弱版本,而是让新增复杂度有比较对象。可以建立三层:关键词检索直接返回片段;向量检索加固定模板生成;加入混合检索、重排与引用校验。每升级一层只引入少数变量,并在同一测试集上记录变化。
如果第三层只提升了少量正确率,却让延迟和成本翻倍,就不能只凭“架构更先进”接受它。把结果写成一行实验记录:代码版本、数据版本、配置、随机种子、指标、硬件、耗时与结论。缺任一项都可能让结果无法复现。
画出数据流,再画组件图
数据流关心信息如何变化:文档经过解析、清洗、切块和索引,问题经过规范化、检索和重排,证据与指令组成上下文,模型生成答案,校验器检查引用,日志记录结果。组件图关心由谁执行:对象存储、元数据数据库、向量索引、推理服务、权限服务和观测系统。
不要把两张图混成一张。数据流能发现“文档版本有没有跟着片段走”,组件图能发现“索引服务不可用时谁来降级”。每条箭头至少标出数据格式、主键、大小上限和失败返回。
数据版本决定实验是否可信
训练数据、检索语料和评测集都需要不可变版本。最简单的做法是为原始文件计算内容哈希,为解析器和切块配置记录版本,并生成一份清单。索引记录应能反查原文版本、页码或段落位置。文档更新时创建新版本,确认索引完成后再切换读流量。
评测集不能在每次看到坏结果后悄悄修改。新增样例应记录原因,并同时保留历史版本的分数。否则“指标提高”可能只是删除了难题。还要区分开发集和最终验收集,减少围绕固定答案过拟合。
容量估算从请求路径逐项计算
先估请求量:峰值每秒请求数、平均输入长度、检索候选数、重排文档数、最终上下文 Token、平均输出 Token。再估资源:索引大小、Embedding 吞吐、重排吞吐、模型预填充和解码负载、KV Cache 占用、日志增长。
一个粗略但有用的推理账本是:总耗时等于排队、检索、重排、预填充、逐 Token 解码和后处理之和。总成本等于存储、离线建索引、在线检索、推理和观测成本之和。先测每一项,再决定优化哪里;不要把所有慢都归因于模型。
接口设计要让错误可见
请求应包含稳定的会话或追踪标识、用户权限上下文、问题、可选过滤条件和超时预算。响应除答案外,还应包含引用、使用的语料版本、完成状态和可机器读取的错误类型。流式输出需要单独定义开始、增量、引用和结束事件。
内部组件也应使用明确契约。检索返回的不只是字符串,而是片段 id、文档 id、位置、分数和版本;生成器接收结构化证据,而不是靠拼接约定猜字段。这样才能对某一层做离线回放和替换实验。
最小闭环必须端到端可运行
第一版只选择少量文档、一个固定 Embedding 模型、一个简单向量索引和一个生成模型。目标不是立即追求最高分,而是让一条请求完整经过摄取、检索、生成、引用和日志,并能在新环境按说明复现。
闭环完成的证据包括:一条安装命令或锁定依赖、一个构建索引命令、一个启动服务命令、一组自动化测试和一份小型评测报告。如果只能在作者电脑的交互式笔记本中运行,还不算完成工程闭环。
评测要把系统拆成可诊断的层
端到端正确率告诉你结果好不好,但不告诉你为什么。至少补充检索召回率、重排命中率、引用支持率、拒答准确率、格式通过率、延迟分位数和单请求成本。对生成答案进行错误归因时,先检查正确证据是否进入上下文,再检查模型是否使用证据。
测试集应覆盖直接事实、多跳组合、时间敏感、歧义问题、无答案、冲突证据、权限隔离和恶意指令。报告不仅给平均数,还要按问题类型切片。总体分数稳定时,某一高风险切片可能已经明显退化。
优化遵循“先定位,再改变一个变量”
如果召回不足,检查解析质量、切块边界、查询改写、关键词与向量混合;如果召回正确但排序差,调整重排器和候选规模;如果证据正确而答案错误,检查上下文结构、指令冲突和模型能力;如果答案正确但太慢,再看缓存、批处理、量化和模型大小。
一次实验只改变一个主要因素,并提前写出假设:“把切块从固定长度改为标题感知切块,会提高跨段事实题的召回率,同时可能增加索引条目。”结果与假设不符时也要保留,它能排除一条错误路径。
上线前必须设计权限、降级和回滚
权限过滤应发生在检索阶段,不能先取出无权内容再要求模型不要泄露。工具调用默认最小权限,写操作需要幂等键、审计记录和必要的人工确认。日志中避免保存完整敏感上下文,调试样本应脱敏并设置保留期限。
降级策略可以是:重排服务不可用时退回基础检索;大模型超时时返回证据片段;索引更新失败时继续读取上一稳定版本。发布时保留旧配置和旧索引的快速切换能力,并用小流量或影子请求比较新旧系统。
观测面板回答四个问题
线上观测应能回答:现在是否可用,质量是否变化,资源是否足够,问题影响了谁。对应指标包括错误率与超时率、抽样质量与用户反馈、GPU/CPU/内存/队列、按版本和用户组切片的请求结果。
每条请求用同一个 trace id 串起检索、重排和生成,但不要把高基数文本直接作为指标标签。指标用于发现异常,结构化日志用于定位单次请求,分布式追踪用于解释跨组件延迟,三者用途不同。
复盘不是总结成绩,而是更新认知
项目复盘至少写五部分:原始目标与实际范围,最终架构与关键取舍,实验表与失败尝试,线上或离线结果,下一轮优先级。每个结论都指向证据,例如某版评测、某条性能曲线或某组错误样例。
最后做一次反事实检查:如果数据量增加十倍、请求量增加十倍、模型服务不可用、某个文档被撤回,当前设计会怎样变化?这能把只对演示有效的方案,推进为可维护的系统。
章末检查
- 能否把一句模糊需求改写为输入、输出、失败条件、质量指标和资源约束?
- 是否有一个无需大模型也能运行的基线,以及逐级增加复杂度的实验顺序?
- 每条答案能否追溯到特定版本的原文片段?
- 端到端分数下降时,能否定位到检索、重排、生成、校验或服务层?
- 是否记录了代码、数据、配置、随机种子、硬件和指标版本?
- 权限、超时、重试、降级、回滚和审计是否在上线前定义?
- 能否用一页设计文档和一张实验表解释最终取舍?
如果有两项答不上来,先补系统闭环和实验记录,不要急着继续堆模型或框架。
延伸资料
- 进入本章的“原理深挖”,完成编程与系统设计专题,重点练容量估算、接口、并发和故障场景。
- 进入“代码实践”,训练并优化迷你语言模型,把数据管线、训练循环、评测和性能分析串成一次完整实验。
- 回到前十二章时,使用本章的任务契约、分层评测和单变量实验方法重新检查每个专题。
本章进阶内容
原理专题与代码实践
完成主教材后,按顺序阅读原理专题、完成代码实践,并用掌握标准复查本章内容。
- 01
把需求改写成可测量的系统目标
- 02
完成容量、接口、数据流与故障设计
- 03
训练并优化一个迷你语言模型
- 04
提交设计文档、实验记录与复盘报告
本章术语
六个关键词
- 任务契约
- 对输入、输出、失败条件、指标与资源约束的可验收定义。
- 基线
- 用于判断新增复杂度是否真正有效的稳定参照方案。
- 数据版本
- 能唯一确定语料、处理配置与评测集合状态的不可变标识。
- 可观测性
- 借助指标、日志和追踪理解系统内部状态与故障原因的能力。
- 降级
- 依赖故障或预算不足时主动切换到能力较弱但可用的路径。
- 单变量实验
- 一次只改变一个主要因素,以便把结果变化归因到具体假设。
章节记录
