Prompt工程中的结构化输出有什么用
Prompt工程不只是写几句自然语言让大模型回答问题。在真实生产环境中,大模型API返回的往往是JSON Schema约束下的结构化数据,用于下游系统自动解析和调度。结构化输出解决的核心问题是:让大模型的回复格式可预测、可编程、可校验,而不是让后端工程师写一堆正则表达式去提取文本中的字段。
当前主流的大模型服务(OpenAI GPT系列、Anthropic Claude、通义千问等)均支持JSON Mode和Function Calling两种结构化输出机制。JSON Mode强制模型输出合法的JSON字符串;Function Calling则更进一步,让模型在对话过程中主动”决定”调用哪个预定义函数,并以结构化参数的形式返回调用意图。
JSON Mode配置与输出约束实战
JSON Mode的启用方式因平台而异,但核心逻辑一致:告诉模型”请以JSON格式输出”,并提供一个JSON Schema作为约束。以OpenAI API为例:
from openai import OpenAI
client = OpenAI(api_key="sk-xxx")
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "你是一个订单信息提取器,将用户输入转为结构化订单JSON。"},
{"role": "user", "content": "我要两杯美式咖啡,大杯,一杯加燕麦奶一杯不加糖,送到3楼工位A12。"}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "order",
"strict": True,
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"size": {"type": "string", "enum": ["中杯", "大杯"]},
"modifiers": {"type": "array", "items": {"type": "string"}}
},
"required": ["name", "size", "modifiers"],
"additionalProperties": False
}
},
"delivery": {"type": "string"}
},
"required": ["items", "delivery"],
"additionalProperties": False
}
}
}
)
import json
order = json.loads(response.choices[0].message.content)
print(order)
关键参数是strict: True,它要求模型严格遵循Schema定义,不允许额外字段,也不允许省略required字段。启用strict模式后,模型会在内部先做Schema校验,如果输出不满足约束会自动重试,对调用方透明。
实际踩坑点:additionalProperties: False必须加在每个object层级,否则strict模式会报错。嵌套对象内部的Schema也需要逐层声明,漏掉任何一层都会导致400错误。
Function Calling机制与多轮工具调度
Function Calling和JSON Mode的区别在于语义层面。JSON Mode只是格式约束,模型仍然在”回答问题”;Function Calling让模型进入”决策”模式——模型判断当前对话是否需要调用外部工具,如果需要,返回一个函数调用意图(包含函数名和参数),由调用方执行后将结果喂回模型继续对话。
典型配置代码:
tools = [
{
"type": "function",
"function": {
"name": "query_inventory",
"description": "查询指定SKU的库存数量和仓库位置",
"parameters": {
"type": "object",
"properties": {
"sku_id": {"type": "string", "description": "商品SKU编号"},
"warehouse": {"type": "string", "enum": ["北京仓", "上海仓", "广州仓"], "description": "仓库位置"}
},
"required": ["sku_id", "warehouse"],
"additionalProperties": False
},
"strict": True
}
},
{
"type": "function",
"function": {
"name": "create_order",
"description": "根据商品和数量创建采购订单",
"parameters": {
"type": "object",
"properties": {
"sku_id": {"type": "string"},
"quantity": {"type": "integer", "minimum": 1},
"warehouse": {"type": "string"}
},
"required": ["sku_id", "quantity", "warehouse"],
"additionalProperties": False
},
"strict": True
}
}
]
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "你是供应链助手,帮用户查询库存和下单。"},
{"role": "user", "content": "北京仓SKU-8821还有多少?不够的话帮我下单补100件。"}
],
tools=tools
)
# 检查模型是否决定调用工具
if response.choices[0].message.tool_calls:
for tool_call in response.choices[0].message.tool_calls:
print(f"调用函数: {tool_call.function.name}")
print(f"参数: {tool_call.function.arguments}")
模型在这一轮对话中会先返回query_inventory的调用意图,调用方执行真实查询后将结果追加到messages继续对话,模型再决定是否调用create_order。这种多轮工具调度是Agent工作流的基础。
结构化输出的常见故障与排查方法
生产环境中结构化输出出问题的频率远超预期,以下是高频故障清单:
1. Schema校验失败导致API 400
原因通常是Schema本身不符合JSON Schema规范。常见错误:在object类型中忘记写properties;数组元素缺少items定义;enum值类型与property类型不匹配(比如enum写数字但type声明string)。排查方法:用jsonschema库先校验Schema本身:
from jsonschema import Draft7Validator
Draft7Validator.check_schema(your_schema) # 如果Schema本身有问题会抛异常
2. 模型输出截断导致JSON解析失败
复杂Schema或长上下文对话中,模型可能因为max_tokens限制截断输出,JSON不完整导致解析报错。解决方案:合理设置max_tokens(通常4096对大多数结构化输出足够),并在代码层做截断检测:
content = response.choices[0].message.content
if response.choices[0].finish_reason == "length":
# 输出被截断,需要拆分请求或增大max_tokens
raise OutputTruncatedError("模型输出被截断,当前max_tokens不足")
3. 枚举值漂移
即使声明了enum,模型偶尔仍可能输出不在enum列表中的值。这在非strict模式下更常见。启用strict: True可以基本消除此问题,但会增加延迟(模型内部重试)。对延迟敏感的场景可以在调用方做二次校验和兜底映射:
ALLOWED_SIZES = {"中杯", "大杯"}
def safe_get_size(raw):
if raw in ALLOWED_SIZES:
return raw
# 模糊匹配兜底
for s in ALLOWED_SIZES:
if s in raw:
return s
return "中杯" # 默认值
Prompt工程与结构化输出的性能优化策略
结构化输出会带来额外的token消耗和延迟。几个实际有效的优化手段:
精简Schema定义:去掉不必要的嵌套层级。一个三层的object嵌套可以用一层flat object加前缀字段替代,减少模型输出token数。字段名尽量短,description尽量精炼(description也是token)。
预填充引导:在messages最后加一条assistant消息,预填充JSON的开头部分,引导模型直接续写:
messages = [
{"role": "system", "content": "提取订单信息为JSON"},
{"role": "user", "content": "三杯拿铁大杯送到前台"},
{"role": "assistant", "content": '{"items":'} # 预填充JSON开头
]
这种方式对不支持JSON Mode的旧版模型API尤其有效,可以显著提高输出格式的一致性。
批量处理:如果需要从大量文本中提取结构化数据,把多条输入拼到一次请求中(用分隔符隔开),让模型一次返回多个JSON对象组成的数组。相比逐条调用,批量方式减少API调用次数,也降低总延迟。需要注意单次请求的token上限,建议每批不超过20条。
不同大模型平台的结构化输出差异
各平台对结构化输出的支持程度和实现方式有差异:
OpenAI的response_format支持json_schema类型,配合strict模式是目前最严格的结构化输出方案,基本不会出现格式违规。Anthropic Claude支持tool_use,本质是Function Calling的变体,但没有等价于strict: True的Schema强制模式,需要调用方做额外校验。通义千问的result_format参数支持message模式下的JSON输出,但稳定性不如OpenAI,建议配合示例few-shot使用。百度文心一言通过functions参数支持Function Calling,参数结构与OpenAI兼容,但嵌套对象的校验较松,需要后端做二次Schema校验。
跨平台适配的实践做法:在业务层抽象一个统一的ToolDefinition接口,根据目标平台做格式转换,核心Schema逻辑只维护一份:
def to_openai_tool(schema: dict) -> dict:
return {"type": "function", "function": schema, "strict": True}
def to_anthropic_tool(schema: dict) -> dict:
return {"name": schema["name"], "description": schema["description"],
"input_schema": schema["parameters"]}
这样在切换大模型供应商时,只需调整适配层,业务逻辑和Schema定义不动。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/prompt-gong-cheng-shi-zhan-da-mo-xing-jie-gou-hua-shu-chu/