下篇:从 Prompt 到 Agent 的工程全景 — 提示语、上下文与驾驭三大工程、Skill、FC+MCP、Agent 设计、ReAct、SDD、模型路由与评测
上篇讲了 AI 怎么"看懂"文字,中篇讲了模型怎么"变聪明"。这篇是三部曲最关键的一篇:模型有了,怎么把它变成能在真实环境里稳定干活的 AI 系统。
大模型从"会聊天"到"能交付",中间隔的不是一个更好的提示词,而是一整套工程体系。
这套体系经历了三次重心迁移——从"怎么说"(Prompt Engineering),到"��什么"(Context Engineering),再到"如何让模型在真实执行中持续做对"(Harness Engineering)。
这一篇,我们把这三次迁移拆开来看,然后再深入到 Agent 的完整工程实践。
一、Prompt Engineering — "我该怎么对模型说"
一句提示词,效果天差地别
同一个模型,换一种说法,输出质量可能差三倍。
说"帮我总结这篇文章" → 模型给一段无关痛痒的概述。
说"请以资深技术编辑的身份,用三段结构总结这篇文章:先讲核心观点,再讲论证方式,最后讲局限性,每段不超过 150 字" → 输出立竿见影好很多。
这就是 Prompt Engineering 最早的价值:模型不是不会,而是你没有把约束讲清楚。
Prompt Engineering 为什么有效
因为大模型是一个对上下文极度敏感的"概率生成系统"。它不是命令行——不会严格按指令执行。它的行为取决于你喂给它的所有文本构成的"概率空间":
- 你给它什么身份,它沿着那个身份分布去采样
- 你给它什么例子,它沿着那个模式继续补全
- 你强调什么约束,它把那部分当成高权重信号
Prompt Engineering 的本质,不是"下命令",是塑造局部概率空间。
Prompt 的四层体系
在实际工程中,Prompt 不是一句话,而是一个分层的体系:
| 层级 | 内容 | 示例 |
|---|---|---|
| 系统指令 | 定义角色、规则、边界 | "你是一个有 10 年经验的 Python 后端工程师..." |
| Few-shot 示例 | 示范输入输出格式 | 给 2-3 个"问题→回答"对,固定输出范式 |
| 用户输入 | 当前任务描述 | "帮我写一个处理 CSV 文件的函数" |
| 输出约束 | 格式、长度、风格 | "用 JSON 格式输出,包含 risks、complexity 字段" |
每一层都在缩小模型的"搜索空间",让它更准确。
高质量 Prompt 的五个要素
① 角色设定。 格式:"你是一个 X 领域的专家,擅长 A/B/C"。差的:不说角色。好的:限定专业视角。
② 上下文完整。 差:"这个有 Bug,帮我修"。好:"以下是一个 Python 函数,输入用户 ID,输出用户名。当用户不存在时应抛出 UserNotFoundError。现在输入 -1 时会崩溃——帮我修复:[代码]"
③ 输出格式明确。 差:"分析一下这个需求"。好:"用 JSON 输出,包含字段:risks(风险列表)、complexity(low/medium/high)、estimatedDays(整数)"
④ 思维链引导。 在末尾加"请一步步思考,展示你的推理过程"。复杂推理任务准确率可提升 20-40%。
⑤ 拒答边界。 先划红线,再让回答。比如"如果资料里没有相关信息,直接说'不知道',不要编造。"
Prompt 的六个常见陷阱
| 陷阱 | 现象 | 解法 |
|---|---|---|
| 指令冲突 | System Prompt 和用户输入矛盾 | 明确优先级 |
| 上下文污染 | 历史对话干扰当前回答 | 关键任务开新会话 |
| 负面指令无效 | "不要做 X"往往不如"做 Y" | 用正面指令替代 |
| 过长 Prompt 稀释重点 | 指令太多,模型"忘了"核心约束 | 精简指令 + Few-shot |
| 格式不稳定 | 同样 Prompt 输出格式不一致 | 加 Few-shot 固定格式 |
| Prompt Injection | 用户在输入里嵌入特殊指令 | 输入清洗 + 职责分离 |
Prompt 的天花板
Prompt 非常擅长:澄清任务、约束输出、激发已有能力。但它不擅长:凭空补齐缺失知识、管理大量动态信息、处理长链条任务中的状态变化。
说得直接一点:Prompt 解决的是"表达问题",不是"信息问题"。
当任务从单轮对话进入真实业务场景,你会发现模型答得不好,未必是因为你不会问——可能是因为你根本没把需要的材料给它看。
问题的焦点从"怎么说"变成了"给什么"——第二次迁移来了。
二、Context Engineering — "模型应该看到什么"
从"一句话"到"一整套输入环境"
在 Context Engineering 的视角下,给模型的不是一段"系统提示词+用户输入",而是一整套经过设计的信息环境。
它包括:
- 用户当前输入
- 历史对话(全文 or 摘要 or 压缩版)
- 外部知识检索结果(RAG 返回的文档片段)
- 工具调用返回
- 当前任务状态
- 系统规则与安全约束
- 其他 Agent 传过来的结构化结果
Prompt 只是 Context 的一部分,不是全部。
为什么 Context Engineering 会兴起
核心原因:模型的使用场景变了。
最早的主流交互是一问一答。这个模式下,Prompt 的权重很高——任务短、链路短、状态少。
但 Agent 开始火以后,模型被放进真实的执行环境:要持续多轮对话、要调搜索/浏览器/代码/数据库等工具、要在多步骤间传递中间结果、要根据外部反馈修正计划。
这时候,你已经不是在优化"单次回答对不对"——而是在优化"一整条任务链路能不能跑通"。
Context Engineering 的五个关键问题
问题一:窗口里塞什么?
128K 的上下文窗口,不意味着你应该把 128K 信息全塞进去。Lost in the Middle 效应告诉我们:中间的信息最容易被模型忽略。所以重要的信息要放在开头或结尾。
问题二:哪些该完整保留,哪些该摘要压缩?
历史对话太长会占满窗口。做法:
- 最近 3 轮 → 原文保留
- 更早的对话 → 摘要压缩
- 工具返回结果 → 裁剪掉冗余字段
问题三:信息什么时候塞进去?
不是"一开始全部给"。Skill 的渐进式披露机制把事情分三层:
- 元数据层(~50 Token):技能名称、触发条件——全局加载
- 指令层(~500 Token):标准作业流程——触发时才加载
- 资源层:脚本、模板、API 文档——执行时才加载
不让模型从第一秒就背着所有信息。
问题四:多 Agent 之间怎么传信息?
Agent A 的输出给 Agent B——是给原文、摘要、还是结构化字段?原文信息完整但占 Token;结构化字段省空间但丢失细节;摘要介于两者之间。
问题五:上下文缓存(Context Caching)
如果多轮对话使用相同的系统提示词,不用每次都重新计算——缓存这部分计算结果,显著降低首 Token 延迟。Claude 和 Gemini 都支持 Prompt Cache。
Context Engineering 的局限
即使信息是对的,模型也未必会稳定执行对。它可能:计划很好但执行偏了、调了工具但误解了结果、中间某步出错后继续一路错下去、在长流程里逐渐偏航却没人发现。
Prompt 和 Context 都主要作用在"输入侧"——一个优化意图表达,一个优化信息供给。但当模型开始连续行动时,还有一个更难的问题:谁来持续监督它、约束它、纠正它?
这就是第三次迁移——Harness Engineering。
三、Harness Engineering — "如何让模型稳定执行"
不只是"驾驭",是对整个执行过程的工程化
Harness 这个词本身就有"缰绳、马具、约束装置"的意思。放在 AI 系统里,它的含义很直白:当模型从"回答问题"走向"执行任务",系统不能只负责给信息,还必须负责驾驭过程。
LangChain 工程师给了一个非常精准的定义:
Agent = Model + Harness
Harness = Agent - Model
Harness 是在 Agent 环境中除了模型以外的所有东西——它决定了模型看到什么、能做什么、按什么规则做、做错了怎么纠偏、最后如何把能力稳定地交付出来。
三层工程体系的关系
用一个例子串起来——假设你要让一个新人完成一次重要客户拜访:
Prompt Engineering → 你把任务讲清楚:
"见面先寒暄,再介绍方案,再问需求,最后确认下一步。"
Context Engineering → 你给他准备资料:
客户背景、过往沟通记录、产品报价、竞品情况、会议目标。
Harness Engineering → 你不只说完话给资料就完了,你还会:
- 让他带着 checklist 去(约束)
- 关键节点实时汇报(观测)
- 核对纪要与录音(校验)
- 出现偏差马上纠正(纠偏)
- 按明确标准验收结果(验收)
三层不是互相取代的关系,是层层包含:
Prompt 是对"提示词"的工程化
Context 是对"输入环境"的工程化
Harness 是对"整个执行控制系统"的工程化
边界一层比一层大,后者天然包含前者。越往后,你面对的不是"怎么问得更好",而是"怎么让系统持续稳定地跑"。
Harness 的六层架构
一个成熟的 Harness 至少包含六层:
第一层:上下文管理。 角色与目标定义、信息选择与裁剪(去掉不相干的、留下关键的)、上下文结构化组织(分层级、不堆砌)。做的是"让模型在正确的信息边界内思考"。
第二层:工具系统。 给什么工具(不是越全越好,围绕任务场景配置)、什么时候调用(判断当前上下文是否足够)、怎么把工具结果喂回模型(提炼有效证据,不是全量塞回)。
第三层:执行编排。 步骤划分、决策节点、中间产物、终止条件、异常处理逻辑。很多 Agent 不是不会某一步,而是不会"串起一整条链"。
第四层:状态与记忆。 当前任务进行到哪一步、哪些中间结果要保留、哪些内容形成长期记忆。临时状态、会话记忆、长期偏好——这三者必须分离,混在一起系统越来越乱。
第五层:评估与观测。 输出验收(是否满足任务要求)、环境验证(代码是否真的可运行)、自动测试、日志指标观测、质量归因(问题出在模型/上下文/工具/流程哪一环)。
第六层:约束、校验与失败恢复。 限制模型可做和不可做的事、输出前检查、失败时分析原因重试或切换备用路径。真实环境里失败是常态——搜索不准、API 超时、文档格式混乱——没有恢复机制,Agent 每次出错都只能从头来。
OpenAI 和 Anthropic 的 Harness 实践
Anthropic 的 Context Reset。 长任务中上下文满了,通常做法是压缩历史再跑。Anthropic 发现对于某些模型,仅压缩不够——模型接近窗口极限时会"焦虑",急于收尾。他们的做法是 Context Reset——直接换一个干净上下文的 Agent,两模型交班时把工作交接清楚。这很像工程里的"进程重启与状态恢复"——不是所有内存泄漏都能靠清理缓存解决。
Anthropic 的 Generator + Evaluator 分离。 让模型自己评估自己的产出时,它往往过度乐观。所以他们把"干活的人"和"打分的人"拆开——Planner 扩展需求、Generator 逐步实现、Evaluator 像 QA 一样实际操作应用检查质量。生产与验收必须分离——这是个朴素的工程原则,放在 AI 系统里同样成立。
OpenAI 的渐进式披露。 早期他们写了一个巨大的 AGENTS.md,把所有规范塞进去——Agent 反而更迷糊。最终方案是 AGENTS.md 只有约 100 行,充当"目录页",指向仓库里的详细文档(架构/设计/执行计划/质量评分)。Agent 先看目录,需要时再去查。文档新鲜度用 CI 自动校验,还有一个专门"文档园丁"Agent 定期扫描过期文档。
OpenAI 的"人类不写代码,只设计环境"。 他们的工程师核心工作是三件事:拆解意图(把产品目标拆成 Agent 能理解的小块)、补全能力(Agent 失败时问"环境里缺了什么"而不是"再试一次")、建立反馈回路(让 Agent 看到自己工作的结果)。当出了问题,修复方向几乎从来不是"更努力地调 Prompt",而是"缺了什么结构性能力"。
四、Skill — 大模型的"能力封装"
Skill 是什么
Skill 是一个结构化的本地文件夹,用来补充某个领域的流程、知识和工具。它的设计思想是——把"怎么让模型做好一个特定类型任务"这件事,从 Prompt 里抽出来,封装成可复用的模块。
这也是 Context Engineering 的典型实践:不让模型从第一秒就看到全部能力和全部信息,而是只在需要时暴露最相关的那部分。
Skill 的文件结构
一个典型的 Skill 包含:
my-skill/
├── SKILL.md # 主说明:名称、触发条件、功能概述
├── rules/ # 规则与流程文档
│ └── sops.md # 标准作业流程
├── templates/ # 模板与示例
│ └── report.md # 输出模板
├── scripts/ # 脚本与工具
│ └── analyze.py # 自定义分析脚本
└── references/ # 参考资料
└── docs.md # 领域知识
SKILL.md 最关键——它决定了模型在什么场景下自动加载这个 Skill,以及加载后应该遵循什么规则。
Skill vs MCP Tool
| Skill | MCP Tool | |
|---|---|---|
| 粒度 | 完整领域能力包 | 单个函数/工具 |
| 内容 | 流程+知识+工具+模板 | 一个 API 调用 |
| 定义者 | 用户自己设计 | 第三方服务提供 |
| 典型大小 | 数百行文档+脚本 | 几十行 JSON Schema |
Skill 是"生产线",MCP Tool 是"螺丝刀"。Agent 需要两者都用。
五、Function Calling & MCP — 让 LLM 突破文字边界
LLM 的根本限制
LLM 只能输出文本。不能执行代码、不能读写文件、不能调用 API 获取实时数据、不能控制任何外部系统。如果没有任何扩展,它就是个"纯文字生成机"。
Function Calling(函数调用协议) 就是为此诞生的:让 LLM 在需要时输出一段结构化的 JSON "指令",由外部代码真正执行,再把结果反馈给模型。
Function Calling 的完整流程
以"查北京天气"为例:
Step 1:定义工具。 开发者把工具的"说明书"传给模型——函数名、功能描述、参数类型和说明。description 是关键——模型根据这段文字判断"什么时候该调哪个工具"。
Step 2:LLM 输出调用指令。 用户问"北京今天天气怎么样?"模型的回应不是自然语言,而是一段 JSON:
{"name":"get_weather","parameters":{"city":"北京","unit":"celsius"}}
Step 3:外部代码执行工具。 代码解析 JSON,真正调天气 API。
Step 4:结果注入 Context,再次调用 LLM。 把工具结果写入对话历史,重新发给 LLM。
Step 5:LLM 基于真实数据生成回答。 "北京今天晴,22°C,湿度 45%。"
关键认知:LLM 自己什么都没执行——它只是输出了一段 JSON,外部系统帮它完成了真正的动作。
LLM "决定"调哪个工具的机制
LLM 没有专门的"决策模块"。它的决策和生成文字完全一样——预测下一个 Token。训练时,OpenAI/Anthropic 给了模型大量"什么时候该调工具"的示例数据,模型学会了:Context 里有工具列表+用户问实时信息 → 下一个 Token 大概率是 {"name":...} 而不是自然语言。
本质上,LLM 只是在"续写"一段包含工具调用格式的对话,和写普通文字没有区别。
多工具串联:Agent 的雏形
用户:"帮我查北京和上海的天气,然后推荐周末去哪个城市。"
- Round 1:LLM 调用 get_weather(city="北京")
- Round 2:结果注入 Context,LLM 调用 get_weather(city="上海")
- Round 3:两个结果都在 Context 里,LLM 综合生成推荐
这种模式就是 Agent 的雏形——LLM 自主规划多个步骤,逐步完成目标。
MCP:不只调用,还要"连接"
Function Calling 解决了"怎么调"的问题。MCP(Model Context Protocol,模型上下文协议)解决的是"怎么连"——它是 AI 应用的"USB-C 接口",给 AI 提供标准化的方式去连接外部数据源和工具。
FC 和 MCP 的分层关系:
应用层:Agent 编排(做什么任务、调什么工具、什么时候停)
↓
协议层:Function Calling(模型怎么决定调工具)
↓
连接层:MCP(怎么连上工具 — 工具发现、连接管理、权限控制)
↓
执行层:实际工具(数据库、搜索引擎、文件系统...)
核心结论:FC 和 MCP 不是竞争关系。FC 是"调用方式",MCP 是"连接协议"——两者分层协作。
MCP 的核心价值:
- 标准化:所有工具通过同一协议接入,不用为每个工具写定制代码
- 工具发现:新工具上线后自动出现在模型可用的工具列表中
- 安全性:统一管理认证、授权和数据流,避免每个工具各自实现安全逻辑
六、Agent 设计 — 让 LLM 从"问答机器"变成"执行者"
什么是 Agent
纯 LLM 是一个"问答机器":一问一答,不能主动做事。
Agent 在 LLM 之上加了一个执行循环(Agent Loop):
用户目标
→ 思考(LLM:我现在要做什么?)
→ 行动(调用工具,外部执行)
→ 观察(工具结果注入 Context)
→ 判断(LLM:目标完成了吗?)
→ 没完成 → 继续循环
→ 完成 → 输出结果
核心差异:
| 纯 LLM | Agent | |
|---|---|---|
| 交互方式 | 一问一答 | 自主多步执行 |
| 工具使用 | 无 | 可调用任意工具 |
| 状态保持 | 单次对话 | 跨步骤追踪状态 |
| 主动性 | 被动响应 | 可自主规划和执行 |
| 错误处理 | 无 | 重试/切换方案 |
LLM 在 Agent 中扮演什么角色
LLM 是 Agent 的"大脑",工具是"四肢"。
LLM 具体负责:意图理解(把自然语言拆成可执行计划)、工具选择(决定调哪个工具传什么参数)、结果解读(理解工具返回内容并判断下一步)、错误处理(工具失败时决定重试还是换方案)、任务完成判断。
关键认知:LLM 每次只处理当前 Context 内的信息。 它看不到"过去做了啥"——只能看到被注入 Context 的工具结果和对话历史。Agent 框架的核心工作之一就是把每步的工具结果正确地写入 Context,让 LLM 能"记住"已做过什么。
Claude Code:一个实战 Agent 解剖
Claude Code 是 Anthropic 官方的命令行 AI 编程助手,采用工具增强的 Agent 架构。LLM 被放进一个循环里持续决策和执行。
一个完整任务链路("帮我把这个函数改成异步的"):
1. LLM 决策:先读文件 → 调用 Read 工具
2. 文件内容注入 Context → LLM 理解代码结构,规划方案
3. LLM 决策:修改函数 → 调用 Edit 工具
4. 修改结果注入 Context → LLM 检查是否需要同步改调用方
5. LLM 决策:搜索调用处 → 调用 Grep 工具
6. 搜索结果注入 Context → LLM 逐一修改调用方
7. LLM 决策:验证 → 调用 Bash 运行测试
8. 测试结果注入 Context → 通过,任务完成
每一步 LLM 做的事都是:理解当前状态 → 决定下一步 → 解读结果 → 继续或结束。
权限分级机制:读操作自动执行、写操作需确认、危险命令(rm -rf/force push)永远需确认。Agent 越自主,确认机制越要严格——这是 Human-in-the-loop 的核心实践。
OpenClaw:多平台 Agent 网关
OpenClaw 是开源的个人 AI 助手网关,核心理念是"你在哪里聊天,AI 就在哪里"。它把 Claude/GPT/Gemini 接入 WhatsApp、Telegram、Slack 等日常 IM 工具。
LLM 在 OpenClaw 中有三个角色:
- 对话者:理解用户语言,生成回复
- 工具调度员:决定何时调搜索/代码执行等工具
- 内容生成器:基于工具返回的真实数据组织最终回答
LLM 可热切换:主模型失败时自动换备用模型——LLM 对 Agent 层透明。不同 Agent 可配不同 LLM(个人助手用 Opus、编程助手用 Sonnet)。
Agent 设计的四个关键原则
原则一:Human-in-the-loop。 高风险操作必须人工确认。Claude Code 的权限分级是最佳实践——Agent 越自主,确认机制越要严格。
原则二:工具边界即能力边界。 Agent 能做什么,完全由工具决定。设计 Agent = 设计工具集 + 设计权限边界。
原则三:失败是常态,容错是必须。 工具调用在生产环境中有不可忽视的失败率。需要重试机制、降级策略、人工介入触发点。
原则四:Context 是稀缺资源。 长任务中 Context Window 会耗尽——要在耗尽前主动压缩历史、提炼关键信息,而不是等溢出再处理。
七、ReAct — AI 的"思考-行动"循环
ReAct = Reasoning + Acting
ReAct 是一个推理框架,核心很简单:不只是在脑子里想,而是想一步、做一步、看结果、再继续想。
Thought: 我需要查北京天气
Action: call get_weather("北京")
Observation: 北京今天 22°C,晴
Thought: 还需要查上海天气
Action: call get_weather("上海")
Observation: 上海今天 26°C,多云
Thought: 北京 22 度晴天更适合出游
Final Answer: 推荐去北京
ReAct 的每次"思考"和"行动"都写入 Context,形成了驱动的执行力。它是几乎所有现代 Agent 框架(LangChain、AutoGPT、Claude Code)的基础循环。
为什么 ReAct 比纯 CoT 更好
纯 CoT:只在"脑子里"想。对于需要真实世界信息(天气、搜索结果、文件内容)的任务,空想解决不了问题。
ReAct:想一步 → 拿到真实数据 → 基于真实数据再想下一步。幻觉大幅降低,因为每一步的推理都建立在工具返回的真实数据上。
八、SDD — 规格驱动开发
为什么必须 SDD
直接让 AI "帮我做一个登录功能",就像找了个外包但只给一句话。纯 Prompt 驱动的 AI 开发有三个致命问题:
- 需求漂移。AI 不知道你的约束——"这个接口不能改已有签名""这个错误码要跟前端约定好"。它按自己理解写,你改一遍它重写一遍,3 轮以后代码越来越乱。
- 历史逻辑丢失。上一轮说好"用 JWT + refresh token",下一轮改了路由逻辑,AI 可能悄悄切回 session。没有规格锁住决策历史,每次对话都是一次"重来"。
- 验收无依据。代码写出来了,你怎么判断它对不对?靠肉眼扫一遍吗?没有规格就没有自动化验收,没有自动化验收就等于每次上线都是凭运气。
SDD 就是解决这三个问题的:先写规格文档 → 确认规格 → AI 按规格实现 → 按规格验收。
SDD 的完整生命周期
需求输入
↓
┌─────────────────────────────┐
│ 第一步:规格编写(Human) │
│ - 明确需求目标和范围 │
│ - 定义系统行为(正常+异常) │
│ - 声明设计约束和边界 │
│ - 拆分任务和验收标准 │
└─────────────┬───────────────┘
↓
┌─────────────────────────────┐
│ 第二步:规格确认(Human+AI) │
│ - AI 审核规格完整性 │
│ - 发现边界情况和遗漏 │
│ - Human 确认最终规格 ← 关键 │
└─────────────┬───────────────┘
↓
┌─────────────────────────────┐
│ 第三步:AI 按规格实现 │
│ - 严格按步骤执行 │
│ - 不能修改规格里没说的东西 │
│ - 每一阶段代码对照验收标准 │
└─────────────┬───────────────┘
↓
┌─────────────────────────────┐
│ 第四步:自动化验收 │
│ - 按规格中的验收标准逐条检查 │
│ - 通过 → 合并 / 不通过 → 退回 │
│ - 形成版本级的可追溯记录 │
└─────────────────────────────┘
每一步的决策权在哪? 第一步和第二步的确认是 Human 的——你定规格方向和最终拍板。第三步和第四步是 AI+自动化系统执行的——AI 写代码,自动化验收,Human 只需要看最终结果。这才是人机协作的正确分工。
怎么写一份好规格
好规格给 What(要什么),不给 How(怎么实现)。举一个具体例子——做一个"用户提现"功能:
差的规格(一句需求):
做一个提现功能。
AI 不知道:限不限额?手续费?支持什么方式?要什么校验?——写出来基本等于随机。
好的规格(结构化需求):
目标:做一个提现功能
范围:
- 只支持微信零钱提现(暂不支持银行卡)
- 单笔最低 1 元,最高 50000 元
- 每人每天最多提现 3 次
- 手续费:1000 元及以下收 2 元,以上收 0.1%
系统行为:
正常路径:
1. 用户发起提现请求(金额+微信 openid+备注)
2. 校验余额足够、额度不超、次数不超
3. 冻结资金
4. 调用微信支付企业付款接口
5. 微信返回成功 → 扣减余额 + 解冻 + 写入账务流水
6. 推送提现成功通知
异常路径:
- 余额不足 → 返回错误码 BALANCE_NOT_ENOUGH + 当前余额
- 微信接口超时 → 标记为"处理中",走异步对账
- 微信回调失败 → 进入人工审核队列,冻结资金不解冻
- 今日提现次数已满 → 返回错误码 DAILY_LIMIT_REACHED + "明日00:00重置"
设计约束:
- 不修改现有 account 表的提现相关索引
- 账务流水使用现有的 ledger 模块写入,不新建表
- 敏感操作日志写入 audit_log,必须包含:操作人ID、时间戳、金额、目标 openid 前 4 后 2 位
任务拆分:
1. 新建 withdraw 模块(controller + service + model)
2. 实现提现校验逻辑(余额/额度/次数)
3. 对接微信支付企业付款
4. 实现账务流水记录
5. 实现异步对账逻辑
6. 单元测试覆盖正常+异常路径
验收标准:
- 所有正常路径单元测试通过
- 所有异常路径单元测试通过
- 微信支付 mock 下端到端验证通过
- 并发场景下不会出现重复扣款(幂等校验)
- API 响应时间 < 500ms(不含第三方接口超时)
这份规格写下来大概花了 10 分钟,但它做到了三件事:
- AI 不再猜。范围明确了"只支持微信零钱、不支持银行卡",AI 就不会自作主张加银行卡提现。
- 异常路径被穷举。最有价值的部分——余额不足、超时、回调失败——不是让你自己想,是你让 AI 知道的。漏了异常路径,上线就是事故。
- 验收可自动化。验收标准是具体的、可执行的——跑测试就能验证,不用人肉点一遍界面。
SDD 的四个常见陷阱
陷阱一:规格写太细。
把实现细节也写进规格——"先用 Python 的 decimal 库做金额计算,再调 WxPayService.transfer() 方法"——等于人先把代码在脑子里写了一遍,失去用 AI 的意义。
怎么判断太细了?记住一句话:如果你的规格里出现了具体的函数名、类名、变量名、数据库表的字段名,你大概率在替 AI 做它的工作。 除非这些是"不能改的约束"(比如不能改已有的 account 表索引),否则不应该出现在规格里。
陷阱二:规格写太粗。
只写一句"做个提现功能",给 AI 留一个 90% 的模糊空间——它只能用训练数据里最常见的"提现实现"来填这个空,而那个最常见的实现大概率跟你的系统长得不一样。
好规格给出边界。边界不是限制 AI,是保护你自己——你给 AI 画了一个圈,它可以在圈里尽情发挥,但不会跑出圈外把你已有的系统搞崩。
陷阱三:规格写了不验。
SDD 的真正威力在于"按规格验收"。规格里写的异常路径有 5 条、验收标准有 5 项——如果没人验证 AI 的输出是否符合这 10 条,规格就只是一份被忽略的文档。AI 生成代码后需要自动化规格符合性检查。
陷阱四:把 SDD 当成"写一次就完了"。
需求是会变的。SDD 不是瀑布模型里的"需求文档"——签完字就放冰箱里不动。SDD 规格是活的:需求变了 → 先改规格 → 确认 → 再让 AI 改代码。规格和代码始终一致,这是 SDD 对比"直接改代码"的最大优势——代码改了不看规格是什么,但规格为什么要改、怎么改的,你都留下了完整版本记录。
SDD 在 Agent 时代的真正角色
很多人把 SDD 理解成"写需求文档让 AI 写代码"——这只是最表层的用法。在 Agent 时代,SDD 的真正角色是稳定 Agent 行为的工程锚点。
Agent 不是简单的代码生成器。它是一个自主的、多步执行的角色——它会调用工具、读写文件、发送网络请求、创建/删除数据库记录。一个每分钟可以执行几十个真实操作的 Agent,如果它的行为边界没有被规格锁死——它可能改错文件、删错数据、发错 API 请求。
SDD 在 Agent 场景下多了一层含义:给 Agent 的操作行为设定约束边界。
你把规格发给 Agent:
约束:
- 只允许修改 src/modules/withdraw/ 下的文件
- 数据库操作只允许 INSERT 到 withdraw_record 表,不允许 DELETE 任何记录
- 调用外部 API 前必须经过 Human 确认
Agent 在 ReAct 循环里每做一个操作之前,先检查是否违反规格约束。这不是"让人多写点文档"的官僚主义,这是在用工程手段控制 Agent 这种"有能力但不够可靠"的系统的风险。
当你把 SDD 从"给代码生成器看的需求文档"升级为"给 Agent 看的操作约束"时,规格就从一个静态文档变成了一个实时生效的运行时规则引擎。
总结
SDD 的价值不是写一份漂亮的文档,而是完成了两件事:
- 降低 AI 的不确定性。 规格缩小了 AI 发挥的自由度,让它在你的系统边界内工作。
- 降低 Humans 的认知负荷。 你不需要在跟 AI 的每一轮对话里重复所有约束——规格一次写好,AI 每次执行前都读一次,相当于你请了一个永远记得所有边界条件的程序员。
它的额外收益是追溯和问责——别人问你"为什么这个系统要做一个提现功能",你不需要用脑子回忆,规格本身就是一个完整的、被批准过的技术决策记录。出问题的时候,查规格就知道什么时候做的决定、谁批准的、为什么这么设计。这是软件开发里最稀缺的东西——不是代码,是决策记录。
九、模型路由 — 用便宜的模型处理简单任务
不是所有问题都需要最强模型
一个用户问"你好"也要调 GPT-4 回答——这是浪费。模型路由的核心思想:根据问题复杂度,把请求发给不同级别的模型。
三种路由策略
静态路由(规则路由)。 关键词匹配——看到"解释一下""定义一下" → 小模型。看到"分析""推理""编程" → 大模型。简单但灵活度不够。
LLM-as-Judge 路由。 用一个便宜模型先"看"问题,判断复杂度,再决定发给谁。比静态路由灵活,但判断本身偶尔会出错。
级联路由。 先调小模型 → 生成结果 → 用一个验证器评估质量 → 质量不达标 → 升级到大模型重跑。质量最好,但延迟最高(可能跑两遍)。
好的路由策略:在效果打九五折的同时,成本降到三分之一。没有 AI 系统需要所有请求全量发给最贵模型。
十、评测 — 怎么知道 AI 系统真的"好"
基准测试和真实场景是两个世界
模型在 MMLU 上拿 90% 不代表在生产环境能稳定工作。你的真实用户可能在凌晨三点用方言问会计问题——跟 MMLU 上的标准化选择题完全没有关系。
四层评测体系
第一层:能力评测(Benchmark)。 MMLU(通用知识)、HumanEval(代码)、GSM8K(数学)——快速知道模型的"基础素质"。成本低,跟你的业务场景可能毫不相关。
第二层:场景评测(Domain Eval)。 用你的业务数据构造评测集。比如你的客服系统场景:用户问"怎么开发票"、用户投诉"商品质量问题"——评测模型在这些真实场景下的表现。
第三层:LLM-as-Judge。 用一个大模型评估另一个模型的输出质量。不是单模型打分(自评失真),而是"交叉评委"——用 Claude 评 GPT 的输出,用 GPT 评 Claude 的输出。关键:给 Judge 明确的评分维度和 rubric,而不是模糊地问"这个回答好不好"。
第四层:线上评测(Online Eval)。 最终指标:用户满意度、任务完成率、转人工率。基准测试再高、离线评测再准,线上不行的系统就是不行。
Harness 的评测工具
lm-evaluation-harness(EleutherAI 开源)是当前最主流的评测框架,支持 200+ 基准测试统一跑分。但它的局限性也很明显:测的不是你的业务场景、模型可能在预训练时见过评测数据(数据污染导致虚高)。
自建评测体系六步法:收集真实用户问题 → 标注标准答案 → 构造不同难度级别 → 自动化跑分 → 人工抽查边缘 Case → 持续更新评测集(每周加新问题)。
写在结尾
下篇讲完了从 Prompt 到 Agent 的完整工程全景。三部曲到此结束。
回顾这三篇:
上篇讲了 AI 的"基础心脏"——Token 怎么分词、Embedding 怎么把文字变数字、Attention 怎么理解上下文、预训练怎么压缩知识。
中篇讲了"怎么训练"——SFT/RLHF/DPO 把模型从续写机器变成有用助手,Reasoning/CoT 激活思考能力,后训练收尾,MoE+蒸馏+Infra 让它便宜,幻觉是贯穿始终的工程挑战。
下篇讲了"怎么落地"——Prompt/Context/Harness 三层工程体系层层递进,Skill 封装能力,FC+MCP 突破文字边界,Agent+ReAct 实现自主执行,SDD 规格化开发,模型路由降本增效,评测体系守住质量底线。
一条完整的知识链:
看懂文字(上)
→ 学聪明(中)
→ 能干活(下)
AI 工程的核心竞争,不是"谁接入的模型更强",而是谁更早建立起一套成熟的工程体系——它知道该给模型看什么,允许模型做什么,要求模型如何验收结果,又在失败时如何把它拉回正轨。
提示语工程解决"说清楚",上下文工程解决"给对信息",Harness 工程解决"稳定执行"——三层缺一不可。
三部曲完。共 24 个核心概念,覆盖从 Token 分词到 Agent 落地的完整 AI 工程知识体系。
夜雨聆风