第 5 / 15 章 约 8 分钟
Prompt Engineering
明确目标和输出约束,维护提示词版本。
内容同步:2026年10月25日 · 文档版本 12
这个模块在 Agent 里的位置
Prompt 是 Agent 的任务入口契约。它决定:
- 模型理解的目标是什么。
- 允许使用哪些工具。
- 输出必须满足什么结构。
- 数据缺失时应该怎么表达。
- 哪些动作必须交给权限系统或人审。
但 prompt 不是安全边界,也不是唯一的约束层。生产系统里,真正稳定的契约通常落在四层:
| 层 | 作用 | 失效后果 |
|---|---|---|
| Prompt | 引导目标、优先级和边界 | 行为漂移 |
| Tool schema | 限制参数、时间窗、topK | 查询失控 |
| Output schema | 限制结果结构 | 输出不可解析 |
| Runtime validator | 校验引用、权限、业务规则 | 错误结果进入下游 |
如果一个规则只写在 prompt 里,它迟早会被模型绕过或误解。
先记住几个概念
| 概念 | 含义 | 为什么重要 |
|---|---|---|
| System prompt | 长期角色和安全规则 | 决定默认行为 |
| Task prompt | 本次任务目标 | 决定模型要做什么 |
| Tool prompt | 工具适用条件和失败处理 | 减少误用和重复调用 |
| Prompt version | 不可变的版本号 | 支持回放、评估和回滚 |
| Output contract | 输出 schema 和校验规则 | 保证结果可被系统消费 |
| Regression set | 回归样例集 | 判断 prompt 变更是否可上线 |
从一个真实问题开始:同一句话,两次执行结果完全不同
一个内部资料分析 Agent 收到任务:
分析过去 30 天的风险事件,输出 Top 10 实体和建议。
第一次执行:
- 模型先查聚合表。
- 返回 10 个实体。
- 每个实体带 evidenceRefs。
- 总耗时 8 秒。
第二次执行:
- 模型认为“要更全面”。
- 逐个实体查证据。
- 上下文迅速膨胀。
- 触发压缩。
- 压缩后丢失引用。
- 模型重新查询。
- 总耗时 3 分钟,成本变成原来的 12 倍。
问题往往不在“模型不稳定”,而在契约没有收口:
- prompt 只说“要全面”,没有限制查询次数。
- tool 没有 maxCalls。
- tool 没有 topK。
- output schema 没限制 entities 数量。
- validator 没检查 evidenceRefs 是否存在。
这四层只要有一层缺失,模型就会把模糊目标解释成不同执行路径。
Prompt 应该只写“决策信息”,不写“全量数据”
一个常见误区是把所有资料塞进 prompt:
system: 你是分析助手
user: 这些是全部事件:...
这会带来三个问题:
- token 成本随任务规模线性上升。
- 关键约束被大量数据稀释。
- 模型容易把旧信息当成当前事实。
更合理的方式是:
- prompt 写目标、身份、时间窗、输出契约。
- 数据通过受限工具按需查询。
- 工具返回 topK、摘要和引用。
- 引用进入 output schema,由 validator 校验。
示例:
目标:
输出 Top 10 风险实体。
限制:
- 只看近 30 天。
- 每个结论必须引用证据窗口 ID。
- 最多调用 5 次查询工具。
- 缺数据时写入 unresolvedQuestions。
输出:
entities: 最多 10 个
evidenceRefs: 必填
unresolvedQuestions: 可为空数组
这段 prompt 的价值不在于“写得漂亮”,而在于它明确告诉模型:什么算完成,什么算缺数据,什么不允许做。
契约分层
流程图加载中…
这四层的关系是:
- prompt 引导模型。
- tool schema 限制模型能传什么参数。
- output schema 限制结果结构。
- runtime validator 检查引用、权限和业务规则。
例如要求“每个结论必须引用来源”:
- prompt 告诉模型要引用。
- tool 返回 sourceId。
- output schema 要求 sourceRefs。
- validator 检查 sourceId 是否存在。
只要 validator 拒绝伪造引用,模型就不能靠编一个 ID 通过校验。
Prompt 版本和变更评估
Prompt 一旦进入生产,就必须像代码一样管理版本。
create table agent_prompt_versions (
id bigint primary key,
tenant_id bigint not null,
scenario text not null,
version int not null,
prompt_hash text not null,
body text not null,
status text not null,
created_at timestamptz not null,
unique (tenant_id, scenario, version)
);
create table agent_prompt_assignments (
id bigint primary key,
run_id bigint not null,
prompt_version_id bigint not null,
assigned_at timestamptz not null
);
create index agent_prompt_assignment_run_idx
on agent_prompt_assignments (run_id);
设计要点:
- prompt body 不可原地修改。
- run 必须记录实际使用的版本。
- 变更走新版本,不覆盖旧版本。
- 每个版本绑定回归样例。
- 高频读取可以缓存 immutable 版本。
一个 prompt 版本的回归集至少要记录:
| 指标 | 说明 |
|---|---|
| 成功率 | 输出是否通过 schema 和业务校验 |
| 平均 turn 数 | 模型推理轮数 |
| tool call 数 | 是否出现重复查询 |
| token 成本 | 输入、输出、缓存 token |
| 引用命中率 | 引用是否真实存在 |
| 人工抽检结果 | 业务语义是否正确 |
一个工程化的 prompt 契约示例
{
"promptVersionId": 18,
"scenario": "timeline-risk-review",
"layers": {
"system": "你是严谨的数据分析 Agent。",
"task": "分析近 30 天风险事件,输出 Top 10 实体。",
"constraints": ["只使用工具返回的数据", "每个结论必须有 evidenceRefs", "缺数据时不要编造"],
"outputContract": {
"type": "object",
"required": ["entities", "evidenceRefs", "unresolvedQuestions"],
"properties": {
"entities": {
"type": "array",
"maxItems": 10
},
"evidenceRefs": {
"type": "array",
"minItems": 1
},
"unresolvedQuestions": {
"type": "array"
}
}
}
}
}
对应的 tool 也要支持这些约束:
| Prompt 约束 | Tool 支持 |
|---|---|
| 只看近 30 天 | startTs / endTs 必填 |
| 最多 10 个实体 | topK <= 10 |
| 必须引用证据 | 返回 evidenceWindowIds |
| 缺数据要说明 | 返回 coverage / missing |
常见工程错误
| 错误 | 实际后果 | 正确做法 |
|---|---|---|
| Prompt 只说“要全面” | 工具调用失控 | 明确 topK、时间窗、最大调用次数 |
| 输出格式只在 prompt 里 | 模型偶尔偏离 | output schema + validator |
| 安全规则只在 prompt 里 | 可被注入绕过 | permission policy |
| Prompt 内嵌全量数据 | 成本和注意力失控 | 工具按需检索 |
| Prompt 没有版本 | 无法归因和回滚 | prompt version + assignment |
| 没有回归集 | 变更后质量下降 | 回归样例和指标 |
| 用户补充约束直接拼接 | 新旧约束冲突 | 显式合并契约 |
上线前检查
- Prompt 有版本。
- run 记录 prompt version。
- 输出有 schema。
- 引用可校验。
- 数据缺失有显式字段。
- Prompt 变更有回归集。
- 高风险动作不受 prompt 控制,受权限控制。
- 用户补充约束会合并进契约,而不是简单追加。