# 把模型能力写成可检验的任务契约

原文: https://yishto.com/learn/ai-foundations/prompts-context-and-capabilities/

作者: [Yishto](https://yishto.com/about/)

把分类与提取任务拆成能力、上下文、输出和拒绝契约，核验结构之外的事实、来源与执行权限。

首次发布: 2026-11-10
内容更新: 2026-11-10

资料分类助手收到一份文本，准备给出类别与摘要。需求如果只写“准确理解资料”，开发者无法判断什么输出算成功。把输入来源、可用材料、输出字段、证据与拒绝条件写成任务契约，才能分别检查模型建议和程序执行。

本课用一句虚构价格资料演示提取子任务，再把同一思路用于资料分类。模型调用并未在示例中运行；代码负责核验已经给出的候选答案，说明提示词以外还需要哪些确定性约束。

## 能力清单要连接到具体任务

文本模型可以用于分类、提取、摘要与候选生成，某些模型还支持视觉、语音或工具调用。能力名称不能直接说明任务质量。分类需要看易混类别，提取需要检查字段与证据，摘要需要判断遗漏，工具调用需要验证选择与参数。

先把需求分成可以观察的动作。资料助手先判断是否属于允许类别，再提取标题与来源，无法判断则标记 needs_review。对只有图片或受损文本的输入，选择支持对应模态的处理路径，或者明确拒绝，而不能把所有输入都硬塞进文本任务。

选择模型时比较相同样本上的结果、延迟、使用成本与可用接口。公开能力声明可以帮助筛选，最终判断仍来自任务评估。复杂推理能力也不意味着简单提取任务值得付出相同资源，应用可以为不同步骤采用不同处理方式。

## 指令与不可信资料分别安排

提示词可以说明角色、任务、允许材料、输出要求和示例，但不同 API 的消息层级与字段有各自定义。不要把某个平台的 system、developer 或 tool 语义直接当成所有模型的统一协议。实施前应核对对应官方文档。

输入文档中出现“改写规则”或“调用删除工具”，应视为待处理内容。来源文本不能取得授权。把资料与任务说明清楚分开有助于理解边界，但执行权限仍要由程序检查，分隔符不会消除 prompt injection。

示例要展示真实需要的边界，使用合成资料即可。加入一条证据不足输入，并明确返回未知，而不是用编造答案维持格式。不要把某一句“请认真思考”写成可保证成功的技巧；提示效果要通过固定样本和失败类别比较。

## 上下文由选择与预算构成

上下文可能包括用户问题、有效规则、检索材料、工具结果和已有状态。窗口预算按 token 计算，并与生成输出共享或受具体接口限制；字符长度不能直接作为保证。长历史中已经过时的决定可能干扰当前任务。

先筛选与当前问题有关的状态，再检索需要的材料，保留文档 ID、版本与片段来源。摘要可以减少长度，但应标明它替代了什么，以及哪些细节可能丢失。对精确价格、编号和引文，保留原文通常比再次概括更容易核验。

截断时应有可观察结果，例如返回“材料不完整”或转入分段处理。若关键限制被删掉，不能继续把输出当作完整任务的答案。工具返回也要按信任边界处理，远程错误页或旧数据不能直接混入权威上下文。

## 输出形状与内容事实分别验证

Structured Outputs 可在受支持的模型和接口中约束指定 schema，官方文档同时说明结构正确的输出仍可能出错。它有产品支持范围与 schema 限制，也需处理 refusal、未完成或截断结果，不能假定每次调用都得到业务对象。

JSON 对象有 item、price、quote 三个字段，只说明形状可解析。item 必须来自输入，price 必须符合提取规则，quote 必须确实出现在来源里。业务校验应在落库或执行前完成，不能让下游把任何合法 JSON 当作正确事实。

下面固定来源为 Item A，要求价格 12，并核验引文。缺失 quote 与 bool 价格也被拒绝，避免类型宽松导致错误接纳。

```python
source = "Item A costs 12 units."
def verify(answer):
    if not isinstance(answer, dict):
        return False
    quote = answer.get("quote")
    return (answer.get("item") == "A" and type(answer.get("price")) is int
            and answer["price"] == 12 and isinstance(quote, str)
            and quote == "costs 12 units" and quote in source)
assert verify({"item": "A", "price": 12, "quote": "costs 12 units"})
assert not verify({"item": "B", "price": 12, "quote": "costs 12 units"})
assert not verify({"item": "A", "price": 12})
assert not verify({"item": "A", "price": True, "quote": "costs 12 units"})
print(verify({"item": "A", "price": 12, "quote": "costs 12 units"}))
```

实际输出是 `True`。合法候选通过，错对象、缺引文与不合法价格类型被拒绝。函数硬编码了这个教学样本的正确事实，只能作为契约测试；它不具备通用提取能力，也不能识别另一篇文章是否真实。

## 拒绝与不确定性属于正常输出

证据不足、冲突、输入损坏和任务不受支持，需要不同的状态与下一步。把这些情况全部当成调用失败，会让重试重复消耗资源；把它们全部补成确定答案，则会掩盖内容问题。为每种状态写明确的界面行为与后台分类。

置信度字段若只是模型自行报出的数字，不能默认当成校准概率。可以评估它与实际错误的关系，但阈值要用独立样本验证。更直接的依据包括必需字段完整性、来源覆盖、候选类别冲突和确定性规则通过情况。

人工复核也要显示需要判断的内容：候选类别、来源片段、缺失原因与修改入口。复核结果可以成为后续评估材料，使用前应确认数据权限与标签质量。用户点了接受不能自动说明答案在事实层面正确。

## 用评估决定怎样修改应用

准备固定评估集，覆盖正常、短文本、类别相似、证据缺失与注入内容。记录每个阶段的输入与版本：上下文选择、提示配置、模型、schema 和验证规则。修改提示后，比较同一组样本，观察是否改善目标错误并引入其他问题。

若来源没取到，应改检索；若字段校验缺失，应改程序；若类别说明模糊，应改定义与提示。模型输出不稳定时，可能需要缩小任务、增加证据或人工环节。不要把所有失败都归为“模型不够强”，也不要用一次成功替代覆盖性评估。

## 练习：交付一份可测试的分类契约

为资料助手写输入、类别表、输出字段、引文规则、拒绝状态与禁止副作用。准备至少一个无法分类和一个来源含恶意指令的样本。将价格提取示例改成多个允许对象，核验对象与价格的对应关系。

验收时，错对象不能借正确引文通过；无证据不生成事实；未完成与拒绝不会被当作成功对象；外部资料无法授予工具权限。保存失败样本与修复前后结果，明确离线验证覆盖的是契约规则，真实模型效果、token 使用与响应时间仍需另外测量。

## 资料来源与延伸阅读

- [Text generation guide](https://developers.openai.com/api/docs/guides/text)
- [Structured outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs)
- [Evaluation best practices](https://developers.openai.com/api/docs/guides/evaluation-best-practices)
- [LLM01: Prompt Injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/)

资料核验：2026-11-10。教学示例不代表线上实测。

## 相关文章

- [大模型 API 入门：Token 与上下文预算](https://yishto.com/learn/models/context-and-tokens/)
- [流式输出与结构化结果：两种不同的可靠性](https://yishto.com/learn/models/streaming-and-structured-output/)
