跳转至

结构化输出与角色提示

把 LLM 接入实际应用时,结构化输出是核心需求。本篇讲如何让模型稳定输出 JSON、XML 等结构化格式,以及"角色提示"如何配合使用。

1. 为什么需要结构化输出

LLM 默认输出自然语言,但应用程序需要结构化数据

场景 需要的格式
写入数据库 JSON / 字典
调用 API JSON / XML
渲染 UI JSON
数据分析 CSV / 表格
配置生成 YAML / TOML

如果模型输出"有时是 JSON,有时是带前言的 JSON,有时还多了 markdown 围栏",应用就不稳定。让输出 100% 可解析,是工程化的基础

2. JSON 输出的基本写法

请提取下面文本中的人名和年龄,按 JSON 输出。

格式:
{
  "people": [
    {"name": "string", "age": number}
  ]
}

要求:
- 仅输出 JSON,不要任何额外文字
- 没找到时输出 {"people": []}
- 年龄缺失用 null

文本:
"小明今年 25 岁,他妈妈年纪比较大但没说几岁,弟弟 12 岁。"

输出:

{
  "people": [
    {"name": "小明", "age": 25},
    {"name": "小明的妈妈", "age": null},
    {"name": "小明的弟弟", "age": 12}
  ]
}

3. JSON 输出的关键技巧

技巧 1:给 schema 不给例子

直接写字段定义比给完整例子更不容易让模型"复制例子":

输出 schema:
- title: string,文章标题
- tags: string[],至少 3 个,全小写
- summary: string,不超过 100 字
- score: number,1-10

技巧 2:明确"只输出 JSON"

模型常常会加前言("以下是结果:")或 Markdown 围栏(```json)。

仅输出 JSON 对象,不要:
- 前言(如"以下是 JSON:")
- Markdown 围栏(如 ```json)
- 后续解释

技巧 3:处理缺失字段

要求:
- 字段缺失时用 null,不要省略字段
- 不要返回部分字段

避免模型"觉得没必要就不返回"。

技巧 4:给一个完整示例

少量 Few-shot 能稳住格式:

示例:

输入:"苹果公司由乔布斯创立"
输出:
{
  "company": "苹果",
  "founder": "乔布斯",
  "year": null
}

4. JSON Schema 严格模式

OpenAI、Anthropic 都支持强制 JSON 输出模式

OpenAI Function Calling

response = openai.chat.completions.create(
    model="gpt-4",
    messages=[...],
    response_format={"type": "json_schema", "json_schema": {
        "name": "extract_people",
        "strict": True,
        "schema": {
            "type": "object",
            "properties": {
                "people": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "age": {"type": ["number", "null"]}
                        },
                        "required": ["name", "age"]
                    }
                }
            }
        }
    }}
)

Anthropic Tool Use

response = anthropic.messages.create(
    model="claude-opus-4-7",
    tools=[{
        "name": "extract_people",
        "input_schema": {...}
    }],
    tool_choice={"type": "tool", "name": "extract_people"},
    messages=[...]
)

强制模式 vs 提示词约束

维度 Schema 模式 提示词约束
稳定性 100% 合法 JSON 95-99%
灵活性 字段固定 可临时调整
复杂度 需要写 schema 写文字即可
适用场景 生产代码 原型、Notebook

生产环境务必用 Schema 模式

5. XML 输出(Claude 推荐)

Claude 对 XML 标签的处理特别稳定,尤其在嵌套场景:

请按以下 XML 输出:

<analysis>
  <summary>简要总结</summary>
  <issues>
    <issue severity="high|medium|low">
      <line>行号</line>
      <description>问题描述</description>
      <fix>修复建议</fix>
    </issue>
    ...
  </issues>
</analysis>

代码:
{code}

XML 在以下场景比 JSON 更好: - 内容包含大段自由文字(JSON 转义麻烦) - 嵌套层级深 - 给 Claude 用

6. 表格输出

适合给人看的、列数固定的数据:

按 markdown 表格输出,列固定:
| 序号 | 问题 | 严重 | 行号 | 建议 |
|---|---|---|---|---|

要求:
- 严重 用 高/中/低
- 行号 写具体数字
- 建议 不超过 30 字

表格输出便于人审,但不便于程序解析。需要程序处理时还是用 JSON。

7. CSV 输出

数据导出场景:

请输出 CSV 格式,第一行是表头:

要求:
- 表头:name,age,city
- 字段间用逗号分隔
- 含逗号的字段用双引号包围
- 仅输出 CSV,不要其他文字

注意:CSV 解析在含特殊字符时容易出问题,复杂数据优先 JSON

8. YAML / TOML 输出

适合输出配置文件

请生成一份 nginx 配置(YAML 格式),要求:
- 监听 80 和 443
- HTTPS 重定向
- 静态文件路径 /var/www/html

模型对 YAML 的稳定性不如 JSON,复杂配置先生成 JSON 再转 YAML 更稳

9. 角色提示(Role Prompting)

什么是角色提示

给模型设定一个身份,影响它的: - 知识激活范围 - 语言风格 - 输出深度 - 价值取向

角色提示对结构化输出的作用

听起来矛盾,但好的角色能让结构化输出更稳定

❌ 没有角色:

请提取 JSON

✅ 有角色:

你是一个专业的数据提取系统,输出严格符合 JSON Schema。
你不会附加任何解释,不会输出 Markdown。

请提取 JSON

模型更"在角色里",会刻意保持纪律。

10. 角色提示的层次

层次 1:通用身份

你是一名 Python 工程师

激活 Python 相关知识。

层次 2:具体专长

你是一名熟悉 asyncio 的 Python 工程师

更精准。

层次 3:经验 + 偏好

你是一名有 10 年经验的 Python 工程师,
偏好显式优于隐式,喜欢用类型注解,
代码风格遵循 PEP 8。

输出风格会接近你的偏好。

层次 4:完整人设

你是 Bob,一名在 Anthropic 工作 5 年的 ML 工程师。
你说话直接,不喜欢绕弯子。
你看代码先看正确性,再看性能,最后看可读性。
你不容忍未经验证的优化。

模型会"演"得更具体。

11. 角色 + 结构化输出 模板

把两者组合,是生产环境的常见模式:

你是一个生产级 JSON API。

行为约束:
- 始终返回符合 schema 的 JSON
- 永远不输出自然语言解释
- 遇到无法处理的输入,返回 {"error": "...", "code": "..."}
- 永远不偏离任务范围

Schema:
{
  "result": "string",
  "confidence": "number 0-1",
  "metadata": {...}
}

任务:
{...}

效果好很多。

12. 处理"模型不听话"

哪怕用了所有技巧,偶尔还是会出问题。准备好兜底:

兜底 1:解析失败时重试

def call_with_retry(prompt, max_retry=3):
    for i in range(max_retry):
        response = call_model(prompt)
        try:
            return json.loads(response)
        except json.JSONDecodeError:
            prompt += f"\n\n上次输出无法解析为 JSON,请重新输出,仅 JSON。"
    raise ValueError("model failed")

兜底 2:剥离 Markdown 围栏

def extract_json(text):
    text = text.strip()
    if text.startswith("```"):
        text = text.split("```")[1]
        if text.startswith("json"):
            text = text[4:]
    return json.loads(text.strip())

兜底 3:用 schema 校验

from pydantic import BaseModel

class Result(BaseModel):
    title: str
    score: int

result = Result.model_validate_json(response)  # 严格校验

13. 结构化输出的反模式

反模式 1:字段太多

一次让模型输出 30 个字段,模型会偷懒漏掉

解法:分多次提取,每次 5-10 个字段。

反模式 2:字段类型混乱

"age": "可能是数字,也可能是 'unknown'"

类型不固定,下游难处理。统一为可选 number + 用 null 表示未知

反模式 3:嵌套过深

5 层嵌套的 JSON,模型容易在第 4 层出错。控制在 3 层以内

反模式 4:自由文字混结构化

{
  "summary": "包含 markdown 的长篇内容..."
}

JSON 字符串里嵌 Markdown,转义复杂。长文用 XML,短文用 JSON

14. 实战案例:客服意图识别

需求:把用户消息分类为预设意图,提取关键参数。

你是一个生产级意图识别系统。

行为约束:
- 始终返回 JSON
- 不要解释、不要前言
- 无法识别时 intent 设为 "unknown"

意图列表:
- query_order: 查订单状态
- request_refund: 退款申请
- product_question: 商品咨询
- complaint: 投诉
- unknown: 无法识别

Schema:
{
  "intent": "上述意图之一",
  "confidence": "0-1 的数字",
  "params": {
    "order_id": "string 或 null",
    "product_name": "string 或 null"
  },
  "raw_message": "原始消息"
}

示例:

输入:"我那个订单 12345 怎么还没到?"
输出:
{
  "intent": "query_order",
  "confidence": 0.95,
  "params": {"order_id": "12345", "product_name": null},
  "raw_message": "我那个订单 12345 怎么还没到?"
}

输入:"你们家狗粮有几种?"
输出:
{
  "intent": "product_question",
  "confidence": 0.9,
  "params": {"order_id": null, "product_name": "狗粮"},
  "raw_message": "你们家狗粮有几种?"
}

现在:
输入:"{user_message}"
输出:

效果: - 输出 100% 可解析 - 字段稳定 - 拿 confidence 决定走人工兜底

总结

  • 结构化输出是 LLM 工程化的基础,需要 100% 可解析
  • JSON 最常用、最稳定,XML 适合 Claude + 长文混合,表格适合给人看
  • 提示词级约束:明确 schema、明确"只输出 JSON"、给 1 个完整示例
  • 生产环境强烈推荐用 Schema 模式(OpenAI Function Calling / Claude Tool Use)
  • 角色提示配合结构化输出能显著提升稳定性
  • 角色 4 个层次:身份 → 专长 → 经验+偏好 → 完整人设
  • 兜底策略:重试、剥离 Markdown 围栏、Pydantic 校验
  • 反模式:字段太多、类型混乱、嵌套过深、长文混 JSON
  • 工程化模板:身份 + 行为约束 + Schema + 示例 + 错误处理

评论