第8章 结构化输出与提示工程¶
企业模型输出很少只停在聊天框里。工单分类要写 CRM,合同抽取要进入提醒系统,DataAgent 要生成工具参数,Generative UI 要返回组件树。此时“回答看起来合理”远远不够:字段、枚举、证据、权限、幂等和失败恢复都会变成系统责任。
Prompt 是输入侧契约,结构化输出是输出侧契约,工具调用是执行侧契约;三者只有和版本、校验、权限、评测与回滚一起管理,才能成为生产接口。 “请输出 JSON”只是提示,不是契约。
8.1 从可读文本到四层契约¶
结构化输出的价值不是让文本更整齐,而是把模型结果变成可以验证、可以拒绝、可以恢复的接口对象。语法合法只是最低要求:一个完全合法的 JSON 仍可能包含错误证据、非法业务含义或越权工具参数。
表8-1:常见结构化任务的输出对象与失败后果。来源:本书整理。
| 任务 | 输出对象 | 下游消费方 | 主要风险 |
|---|---|---|---|
| 工单分类 | 类别、置信度、证据、人工复核标记 | CRM、工单系统 | 误分派、自动化越权 |
| 合同抽取 | 日期、金额、义务、风险条款、证据位置 | 合同库、提醒系统 | 脏数据入库、遗漏风险 |
| DataAgent 规划 | 工具名、参数、停止条件、澄清问题 | Runtime、SQL、权限系统 | 调错工具、越权查询 |
| 生成式 UI | 表单 schema、组件树、数据绑定 | 前端渲染层 | 页面不可渲染、交互错位 |
图8-1:结构化任务的四层契约。来源:本书自绘。Alt text:图中从上到下展示 Prompt 输入契约、模型生成、schema 输出契约和下游消费四层,每层都有校验点,输出通过 schema 后才能进入业务系统。
一个结构化任务至少要明确四层契约。
表8-2:结构化任务的四层契约。来源:本书整理。
| 层次 | 关键问题 | 典型资产 |
|---|---|---|
| 语义契约 | 模型要完成什么业务动作 | 任务说明、边界规则、示例 |
| 结构契约 | 输出必须长什么样 | JSON Schema、枚举、字段说明 |
| 执行契约 | 是否允许调用工具、如何执行 | tool schema、权限策略、幂等键 |
| 治理契约 | 怎样发布、评测、灰度和回滚 | Prompt/schema 版本、评测、Trace |
结构化任务发布的最小单位不是 Prompt,而是 Prompt + schema + 工具契约 + 模型/生成参数 + 评测样本 + 回滚策略。 任一部分单独变化,都可能让链路失配。
企业 Prompt 也应接口化,而不是当成一段随手修改的文本。例如投诉分类任务:
你是客服质检助手,只做投诉原因归类,不生成赔付承诺。
任务:
从工单文本中识别主投诉原因,并给出最多三个来自原文的证据句。
业务规则:
- category 只能从 delivery_delay、quality_issue、refund_dispute、service_attitude、unknown 中选择。
- 信息不足时选择 unknown,并填写 missing_info。
- 如果投诉涉及退款和物流,优先选择导致升级的原因。
输出:
返回符合 complaint_classification_v2 的 JSON。不要输出 Markdown。
这段 Prompt 的价值在于规则可测试:枚举能进入 schema,证据可以回指,unknown 提供合法失败出口,“不生成赔付承诺”可以进入安全样本。Few-shot 和推理技巧可以补充能力,但不能替代接口契约。
8.2 解析、schema、业务校验与恢复闭环¶
生产链路必须假设模型会返回“差一点正确”的结果:Markdown 代码块、缺字段、非法枚举、证据不存在、参数不完整、工具越权。invalid 分支不是异常补丁,而是结构化输出的核心设计。
flowchart TD
App["业务应用 / Agent Runtime"] --> Task["任务请求<br/>intent / context / tenant"]
Task --> Prompt["Prompt Template<br/>版本 / 变量 / 示例"]
Prompt --> Contract["输出契约<br/>JSON Schema / grammar / tool schema"]
Contract --> Gateway["LLM Gateway<br/>路由 / 限流 / 审计"]
Gateway --> Engine["模型服务<br/>普通生成 / guided decoding"]
Engine --> Parser["解析与校验<br/>parse / schema validate / business rules"]
Parser -->|valid| Consumer["业务消费方<br/>状态机 / 工具执行 / UI / 数据库"]
Parser -->|invalid| Recovery["恢复策略<br/>repair / retry / fallback / human"]
Consumer --> Tools["工具系统<br/>registry / auth / execution"]
Recovery --> Gateway
图8-2:结构化输出的校验与恢复闭环。来源:本书自绘。Alt text:图中展示模型生成、解析、schema 校验、业务校验和下游消费的循环;校验失败会进入修复、重试、降级或人工复核分支。
请求对象应保存 Prompt、模型、schema 和恢复策略,而不是只传一段文本:
{
"task": "complaint_classification",
"tenant": "demo-retail",
"prompt": {
"template_id": "complaint_classifier",
"version": "2.1.0"
},
"model": {
"name": "qwen3-32b-instruct",
"temperature": 0.1
},
"response_format": {
"type": "json_schema",
"schema_id": "complaint_classification",
"schema_version": "2.0.0"
},
"recovery": {
"max_retries": 2,
"repair": true,
"fallback": "human_review"
}
}
schema 应小而明确。例如:
{
"type": "object",
"required": ["category", "confidence", "evidence", "requires_human_review"],
"additionalProperties": false,
"properties": {
"category": {
"type": "string",
"enum": ["delivery_delay", "quality_issue", "refund_dispute", "service_attitude", "unknown"]
},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"evidence": {"type": "array", "items": {"type": "string"}, "minItems": 1, "maxItems": 3},
"requires_human_review": {"type": "boolean"},
"missing_info": {"type": "string"}
}
}
additionalProperties: false 防止下游误读未定义字段;unknown 则避免模型在信息不足时被迫猜一个错误类别。
结构化链路的状态机应明确哪类错误可以修复、哪类需要人工、哪类必须拒绝:
stateDiagram-v2
[*] --> RenderPrompt
RenderPrompt --> CallModel: prompt valid
RenderPrompt --> Failed: missing variable / context too long
CallModel --> ParseOutput: model returned
CallModel --> Retry: timeout / transient error
ParseOutput --> ValidateSchema: parse ok
ParseOutput --> Repair: parse failed
Repair --> ValidateSchema: repair ok
Repair --> Retry: repair failed
ValidateSchema --> ValidateBusiness: schema ok
ValidateSchema --> Retry: schema error
ValidateBusiness --> Accepted: business rules ok
ValidateBusiness --> HumanReview: low confidence / permission risk
ValidateBusiness --> Retry: recoverable semantic error
Retry --> CallModel: retry budget remains
Retry --> HumanReview: retry budget exhausted
Accepted --> [*]
HumanReview --> [*]
Failed --> [*]
表8-3:结构化请求的失败类型与恢复策略。来源:本书整理。
| 失败类型 | 典型触发条件 | 处理方式 |
|---|---|---|
| Prompt 渲染失败 | 缺变量、过长、未脱敏 | 阻断并补变量/裁剪 |
| 解析失败 | 代码块、尾随文本、半截 JSON | repair 一次,仍失败则有限重试 |
| schema 失败 | 缺字段、类型错、非法枚举 | 返回校验错误重试,超预算转人工 |
| 业务校验失败 | 证据不存在、单位缺失、低置信度 | 补检索、澄清或人工复核 |
| 工具校验失败 | 工具不存在、参数越权、动作需确认 | 拒绝执行并审计 |
| 执行不确定 | 超时、网络中断、非幂等动作状态未知 | 查幂等状态,禁止盲目重试 |
重试不是通用恢复手段。证据不足不能靠重复生成解决,权限拒绝更不能“重试到成功”。
8.3 四组关键工程决策¶
Prompt 约束、约束解码与后置校验¶
Prompt 兼容性最好,但格式可靠性最低;后置校验容易接入,但会消耗一次失败调用;约束解码能减少非法结构,却不能判断业务语义。生产常用三层组合:Prompt 讲清语义 → guided decoding 保证形状 → schema + 业务校验决定能否使用。
约束解码解决“长得对不对”,业务校验解决“能不能用”。
大 schema 一次生成还是小 schema 多步生成¶
简单表单可一次生成。合同、DataAgent 等复杂任务更适合拆成多个小对象:先分类或规划,再按结果抽取字段或生成工具参数。多步会增加调用次数,但错误定位和局部重试更清楚,高风险任务通常值得付出这部分成本。
是否输出完整推理过程¶
企业系统不应默认把完整思维草稿展示或长期记录。更稳妥的是保存结论、证据引用、必要解释、原始输入和人工复核。需要可解释性时,优先要求可验证证据,而不是不可验证的长篇推理文本。
模型选工具还是工作流裁剪工具¶
低风险开放式助手可以让模型在有限工具集中自主选择;退款、审批、数据库查询和外发消息等高风险流程,应先由工作流和 Policy 根据状态、权限裁剪工具,再让模型填参数。工具越多,不仅误调用概率越高,也会增加 Prompt 和缓存压力。
8.4 运行时落点:把模型输出挡在业务动作之前¶
当前 mini-platform 已有 core/gateway/ 和 core/registry/tool_registry.py,可以在此基础上增加三类能力。
表8-4:结构化输出相关能力的建议路径。来源:本书整理。
| 能力 | 建议路径 | 说明 |
|---|---|---|
| Prompt 模板 | mini-platform/core/gateway/prompt_template.py |
管理变量、版本和渲染 |
| 结构化解析 | mini-platform/core/gateway/structured_output.py |
parse、schema validate、repair |
| 工具调用校验 | mini-platform/core/registry/tool_registry.py |
参数校验、权限和执行策略 |
轻量实现可以先支持 JSON 对象解析和必填字段校验,生产再替换为 Pydantic、jsonschema、Instructor、Outlines 或推理引擎 guided decoding。
from dataclasses import dataclass
import json
from typing import Any
@dataclass(frozen=True)
class StructuredResult:
ok: bool
data: dict[str, Any] | None
errors: list[str]
raw: str
def parse_structured_json(raw: str, required: set[str]) -> StructuredResult:
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
return StructuredResult(False, None, [exc.msg], raw)
if not isinstance(data, dict):
return StructuredResult(False, None, ["expected object"], raw)
errors = [f"missing: {f}" for f in sorted(required) if f not in data]
return StructuredResult(not errors, data if not errors else None, errors, raw)
模型最多提出工具名和参数,真正执行必须经过 Registry、Policy 和幂等控制。创建工单、发送邮件、退款、写审批记录等非幂等动作必须携带 idempotency_key。“模型生成了合法参数”不等于“平台允许执行”。
发布前至少检查以下内容:
表8-5:结构化输出发布准入。来源:本书整理。
| 验收项 | 检查问题 | 证据 |
|---|---|---|
| 契约完整性 | Prompt、schema、工具和模型版本是否绑定 | 发布记录、版本、回滚目标 |
| 失败恢复 | 解析/schema/工具失败是否有路径 | 重试、人工队列、降级策略 |
| 安全边界 | 高风险工具是否鉴权和确认 | 权限测试、审计日志 |
| 成本性能 | 重试和多采样是否有预算 | token、P95、失败成本 |
| 回归评测 | 成功、边界、拒答、恶意输入是否覆盖 | 评测报告、失败样本 |
8.5 Schema 演进:把它当 API,而不是 Prompt 附件¶
结构化输出一旦有消费者,schema 就进入 API 生命周期。新增字段通常容易兼容;删除字段、修改枚举、改变字段语义、收紧必填项都需要迁移窗口。模型已经按新 schema 输出,而前端、工具 handler、报告模板、Trace 解析器仍按旧格式消费,是非常常见的线上故障。
每个结构化结果至少应携带 schema_version;高风险链路还应明确 producer、consumer 和迁移说明。
迁移窗口要考虑三种兼容:
- 读兼容:新消费者能解释旧对象和历史 Trace;
- 写兼容:灰度期按租户/任务输出旧版或新版;
- 回放兼容:同一评测样本能比较新旧 schema 的业务结果。
高风险动作迁移时,还应检查幂等键、权限字段、证据字段和失败状态。新字段缺失时不要静默补成“看起来合理”的业务事实。风险等级缺失默认成低风险、审批意见缺失默认通过、日期缺失默认本月,都会把技术默认值变成真实业务判断。若必须补默认,应保留来源标记,区分模型生成、规则补齐、用户确认和人工复核。
Schema 回滚也比 Prompt 回滚复杂。新版本上线后,下游可能已经写入新字段。较稳妥的方式是在短期保留新旧映射和版本化解析,必要时把新请求路由回旧 schema,同时仍能解释已经产生的新版本 Artifact。
固定回放集至少覆盖:正常输入、缺字段、非法枚举、证据不足、权限拒绝和工具超时。每次 Prompt、schema、模型、解析器或工具契约变化都重新运行,并比较格式、业务校验、消费者行为和审计记录。
8.6 观测、发布证据与恢复¶
结构化输出的核心指标不应只有“JSON 合法率”。至少要分开观察:
- parse 失败率;
- schema 失败率与字段缺失率;
- 业务校验失败率;
- 自动 repair / 重试成功率;
- 人工介入率;
- 工具拒绝率与幂等冲突;
- 不同 Prompt/schema/model/tool 版本组合的失败分布。
只有把失败按层分类,团队才知道该修 Prompt、schema、业务规则、权限还是工具,而不是所有问题都归结为“模型不听话”。
Trace 至少应保留 template_id、schema_id、model、generation_config、parse/validation error、retry_count、tool_call、tool_result、latency、token usage 和最终状态。原始输出与“校验后真正被业务消费的对象”应区分保存;敏感文本按数据策略脱敏或缩短留存窗口。
三个典型失败可以说明恢复边界:
- JSON 被 Markdown 包裹:可 repair 一次,高频任务启用 schema/grammar 约束;
- 字段合法但证据不存在:应回到证据校验或人工,而不是继续修 JSON;
- 工具超时后重复创建工单:必须使用业务幂等键查询执行状态,禁止盲目重放。
接口契约的 owner 也应明确。业务定义字段语义,平台维护解析与恢复,安全团队定义高风险动作,前端把“格式失败、证据不足、权限拒绝、人工审核”等状态翻译成用户可理解的交互。字段语义没有共同定义时,合法 JSON 反而更容易把错误悄悄传给下游。
早期平台不需要覆盖所有任务。先跑稳三类链路:信息抽取、工具调用和人工复核。它们分别覆盖读、写和高风险恢复,足以暴露大多数结构化输出问题。
本章小结¶
结构化输出的成熟度不取决于 Prompt 写得多复杂,而取决于输出能否被解析、校验、拒绝、恢复、复现和回滚。 Prompt 负责输入语义,schema 负责输出形状,业务规则判断字段能否使用,Registry 与 Policy 决定动作能否执行。
复杂任务应优先使用小 schema、多步验证和局部重试;高风险工具必须鉴权、幂等和审计;schema 演进要像 API 一样提供版本、迁移窗口和消费者保护。只有这些责任由平台承担,模型输出才真正从“像 JSON 的文本”变成可被企业系统消费的接口。
参考文献¶
JSON Schema. (n.d.). Specification.
OpenAI. (n.d.). Structured Outputs guide.
Guidance. (n.d.). Documentation.
Instructor. (n.d.). Documentation.