Skip to content

专题 01 上下文工程与结构化输出

本专题面向“会调模型,但结果不稳定”的前端工程师。它解决的问题不是“怎么把 Prompt 写得更花”,而是怎么把一个概率型系统控制到可复现、可调试、可接入业务流程。

1. 为什么这个专题关键

很多 AI Agent 项目第一次失败,不是失败在模型能力本身,而是失败在上下文组织方式。 同样的模型、同样的用户问题,只要系统指令、知识片段、工具返回、历史消息的排列方式不同,最终输出就可能差很多。

对前端工程师来说,这一层非常像状态管理与数据装配:

  • 页面最终呈现什么,不只取决于接口返回,也取决于状态来源、更新顺序和约束条件。
  • Agent 最终输出什么,也不只取决于模型参数,更取决于上下文输入结构是否清晰。

如果不做上下文工程,常见后果是:

  • 回答忽好忽坏,难以复现。
  • 模型输出结构不稳定,前端难以解析和展示。
  • RAG、工具调用、记忆系统彼此打架,系统越做越乱。

2. 前端工程师最容易踩的误区

2.1 把 Prompt 当成唯一控制手段

很多前端同学一开始会把 Prompt 当成“大总管”,试图把所有约束都塞进一大段文字里。 这会导致两个问题:一是维护困难,二是约束优先级不清晰。

更好的做法是分层管理上下文:

  • 系统层:角色、边界、输出规则。
  • 任务层:本轮目标、用户意图、输入数据。
  • 证据层:检索片段、工具结果、历史摘要。
  • 输出层:结构化 schema、字段含义、失败回退策略。

2.2 只关心“回答像不像”,不关心“能不能消费”

在真实业务里,很多输出不是给人直接看的,而是给前端组件、工作流节点或下游服务消费的。 如果输出格式漂移,链路就会断。

所以结构化输出不是锦上添花,而是工程基础设施。

2.3 盲目塞满上下文窗口

上下文越多不一定越好。无关信息太多会降低注意力密度,增加模型误判和延迟。 很多系统不是“信息不够”,而是“噪音太多”。

3. 上下文工程的核心组成

3.1 指令层级

一个稳定系统需要明确以下层级:

  • 全局系统指令:定义角色、边界、风格和禁止事项。
  • 工作流节点指令:定义某一步该完成什么。
  • 用户输入:表达本轮需求。
  • 外部证据:来自 RAG、工具、数据库或人工输入。
  • 输出契约:规定返回 JSON、字段约束、错误码或拒答格式。

设计原则是:越稳定、越长期不变的信息越靠前;越动态、越一次性的输入越靠后。

3.2 上下文预算

你可以把上下文窗口理解为一块有限屏幕,不同信息源在抢占可见区域。 建议先做预算分配,而不是等超长后再临时裁剪。

一个常见分配方式如下:

  • 10%:系统指令和规则。
  • 20%:任务说明和当前输入。
  • 40%:检索证据或工具结果。
  • 20%:历史对话摘要。
  • 10%:输出 schema 与兜底说明。

这不是固定比例,但能帮助你形成“预算意识”。

3.3 证据优先级

不是所有检索结果都应平铺直叙地喂给模型。 应该先排序、去重、裁剪,再打包。

建议优先级规则:

  1. 与问题最直接相关的证据优先。
  2. 新版本制度优先于旧版本制度。
  3. 原文条款优先于二次解释。
  4. 来源明确的片段优先于来源不明的摘要。

3.4 输出契约

输出契约至少应包含:

  • 返回字段名。
  • 字段类型。
  • 必填与可选字段。
  • 失败时如何返回。
  • 当证据不足时是否拒答。

示例:

json
{
  "answer": "string",
  "citations": [
    {
      "doc_id": "string",
      "title": "string",
      "quote": "string"
    }
  ],
  "confidence": "high|medium|low",
  "need_human_review": true
}

4. 一套可执行的设计方法

4.1 先定义任务,而不是先写 Prompt

先明确以下问题:

  • 这个 Agent 到底要完成什么任务。
  • 输出是给谁用:用户、前端组件还是另一个服务。
  • 成功标准是什么:正确率、格式稳定率、引用准确率还是任务完成率。

如果这些问题不清晰,后面的 Prompt 和 schema 都会漂移。

4.2 拆分静态上下文与动态上下文

静态上下文指长期不变的信息,例如:

  • 系统角色。
  • 输出格式。
  • 安全边界。
  • 行为原则。

动态上下文指每轮请求会变化的信息,例如:

  • 用户问题。
  • 检索证据。
  • 工具返回。
  • 会话摘要。

拆开管理后,系统会更容易版本化和调试。

4.3 用“槽位”而不是“长段落”组织输入

推荐把 Prompt 设计成槽位拼装,而不是一整段长文:

  • system_role
  • task_goal
  • user_input
  • retrieved_context
  • tool_results
  • memory_summary
  • output_schema

这样做的好处是:

  • 更容易追踪是哪一块输入导致问题。
  • 更容易做 A/B 实验。
  • 更容易给工作流系统复用。

4.4 先定义 schema,再约束生成

很多团队先让模型“自由回答”,再写正则或字符串切割去修补。 这在 Demo 阶段可行,但在工程化阶段成本很高。

更好的顺序是:

  1. 先定义字段结构。
  2. 再定义字段解释。
  3. 再定义字段缺失时的默认行为。
  4. 最后让模型按 schema 填充。

4.5 为失败设计恢复路径

结构化输出的关键,不只是“成功时长什么样”,还包括“失败时怎么降级”。 建议至少定义三类失败:

  • 解析失败:返回保底文本与错误码。
  • 证据不足:进入拒答模板。
  • 高风险场景:标记 need_human_review=true

5. 实操步骤

第一步:定义输入输出契约

至少写清楚:

  • 输入字段有哪些。
  • 哪些字段来自用户,哪些来自系统。
  • 输出字段怎么消费。
  • 哪些字段会被 UI 组件直接展示。

第二步:确定上下文槽位

先列出可能输入源,再删除冗余项:

  • 用户问题。
  • 历史摘要。
  • 检索证据。
  • 工具返回。
  • 系统规则。
  • 输出格式。

如果某个输入源不能提升正确率、稳定性或可解释性,就不要放进来。

第三步:控制上下文长度

对每一类输入设置上限:

  • 最多保留多少轮历史摘要。
  • 最多注入多少条证据。
  • 每条证据最大长度多少。
  • 是否需要先做重排或摘要压缩。

第四步:建立结构化输出检查

前端工程里会做类型校验,Agent 输出也要有类似机制。 建议做三层检查:

  • 模型侧约束:Prompt 中声明 schema。
  • 服务侧检查:运行时做 JSON 解析与字段校验。
  • UI 侧兜底:对缺失字段做空态或警告提示。

第五步:沉淀调试样本

至少保留以下失败样本:

  • 证据正确但回答错误。
  • 证据不足但没拒答。
  • 输出结构不完整。
  • 引用错位。
  • 历史消息污染当前回答。

6. 面向前端工程师的落地建议

6.1 把它当成“状态装配系统”来理解

前端常见问题是多个状态源彼此覆盖。 Agent 也一样:系统规则、历史对话、RAG 证据和工具返回都可能互相冲突。

你的任务不是写一段“聪明的 Prompt”,而是设计一套“输入优先级明确、输出格式稳定、失败可恢复”的状态装配系统。

6.2 前端要提前参与输出结构设计

不要等后端把自由文本丢给前端再做展示。 前端应该提前参与字段定义,例如:

  • 回答正文。
  • 引用来源。
  • 风险提示。
  • 后续推荐动作。
  • 置信度等级。

6.3 把 UI 状态和模型状态区分开

例如:

  • isStreaming 是界面状态。
  • need_human_review 是模型决策状态。
  • citations 是业务数据状态。

分层清楚后,产品体验会稳定很多。

7. 常见失败模式与排查方法

7.1 系统指令与业务规则冲突

现象:模型有时遵守格式,有时只顾回答自然语言。 排查:检查系统角色说明是否和任务说明冲突,是否在后文被覆盖。

7.2 检索证据过多导致注意力稀释

现象:明明召回了正确文档,最终还是答错。 排查:减少证据数量,优先做重排和压缩,而不是继续塞更多片段。

7.3 输出格式在边界场景漂移

现象:正常问题能输出 JSON,复杂问题就开始混入解释性文本。 排查:增加边界样本,对空结果、拒答、高风险结果单独定义模板。

7.4 历史消息污染当前任务

现象:上一轮讨论的内容影响了当前问题。 排查:对历史消息做摘要,而不是直接全量透传;必要时按任务切换新会话。

8. 验证方式

你可以用以下指标验证上下文工程是否成型:

  • 结构化输出解析成功率 >= 95%。
  • 相同输入多次运行,关键字段波动可控。
  • 高风险场景下,系统能稳定进入人工复核或拒答。
  • 检索片段减少后,总体正确率不下降或反而提升。
  • 出现故障时,能明确定位是哪类上下文导致问题。

9. 可复用模板

9.1 槽位化 Prompt 模板

text
[System Role]
你是企业知识助手,只能基于给定证据回答。

[Task Goal]
回答用户问题,并输出结构化 JSON。

[User Input]
{{user_input}}

[Retrieved Context]
{{retrieved_context}}

[Memory Summary]
{{memory_summary}}

[Output Schema]
{{output_schema}}

[Fallback Rules]
如果证据不足,明确说明无法确认,并将 need_human_review 设为 true。

9.2 输出校验清单

  • 是否能被 JSON 解析。
  • 必填字段是否齐全。
  • 引用是否存在并与正文一致。
  • 当证据不足时是否拒答。
  • 当存在风险时是否显式标记人工复核。

10. 面试表达

你可以这样讲:

“我后来发现,Agent 质量不稳定的根因不只是模型本身,而是上下文组织方式不合理。于是我把输入拆成系统规则、任务目标、检索证据、历史摘要和输出 schema 五类槽位,再通过结构化输出和运行时校验保证结果可消费。这样做之后,系统不仅正确率更稳定,前端渲染和工作流编排也更可靠。”