为什么结构化输出如此重要?
LLM默认输出自由文本,但实际应用中我们通常需要结构化数据——JSON对象、表格、特定格式。不可靠的结构化输出会导致下游解析失败,是LLM应用从原型到生产的主要障碍之一。
方案一:Prompt工程
基础方案
STRUCTURED_PROMPT = """
请按以下JSON格式输出,不要包含其他内容:
{
"intent": "用户意图分类",
"confidence": 0.0-1.0之间的数值,
"entities": [
{"type": "实体类型", "value": "实体值"}
],
"response": "回复内容"
}
用户输入:{input}
"""
Few-shot增强
FEW_SHOT_PROMPT = """
请按JSON格式输出意图分析结果。
示例1:
输入:我想订一张明天去北京的机票
输出:{"intent": "book_flight", "confidence": 0.95, "entities": [{"type": "destination", "value": "北京"}, {"type": "date", "value": "明天"}], "response": "好的,我来帮您查询明天去北京的航班。"}
示例2:
输入:今天天气怎么样
输出:{"intent": "weather_query", "confidence": 0.9, "entities": [{"type": "date", "value": "今天"}], "response": "让我为您查询今天的天气。"}
现在请分析:
输入:{input}
输出:
"""
优缺点
- 优点:简单通用,任何LLM都支持
- 缺点:不可靠,模型可能输出多余文本、格式错误、字段缺失
方案二:JSON Mode
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="qwen3-32b",
messages=[{"role": "user", "content": "分析以下文本的情感,返回JSON"}],
response_format={"type": "json_object"} # 强制JSON输出
)
result = json.loads(response.choices[0].message.content)
JSON Mode保证输出是合法的JSON,但不保证包含特定字段。
方案三:Structured Output(Schema约束)
from pydantic import BaseModel
from typing import List
class SentimentAnalysis(BaseModel):
sentiment: str
confidence: float
key_phrases: List[str]
summary: str
response = client.beta.chat.completions.parse(
model="qwen3-32b",
messages=[{"role": "user", "content": "分析这段文本的情感"}],
response_format=SentimentAnalysis # 直接传入Pydantic模型
)
analysis = response.choices[0].message.parsed # 已解析的Pydantic对象
print(analysis.sentiment)
print(analysis.confidence)
方案四:Constrained Decoding(语法约束解码)
# vLLM支持的Guided Decoding
from vllm import LLM, SamplingParams
llm = LLM(model="qwen3-32b")
# 通过JSON Schema约束输出
json_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0, "maximum": 150},
"skills": {"type": "array", "items": {"type": "string"}}
},
"required": ["name", "age"]
}
sampling_params = SamplingParams(
temperature=0.7,
max_tokens=512,
guided_decoding={"json": json_schema} # 约束解码
)
outputs = llm.generate(["提取人物信息:张三,25岁,会Python和Java"], sampling_params)
result = json.loads(outputs[0].outputs[0].text)
Constrained Decoding在解码时通过约束语法(如JSON Schema、正则表达式、上下文无关文法)确保输出符合指定格式。这是最可靠的方案——100%保证格式正确。
工程实践
输出验证与重试
class StructuredOutputRunner:
def __init__(self, llm, schema, max_retries=3):
self.llm = llm
self.schema = schema
self.max_retries = max_retries
async def generate(self, prompt):
for attempt in range(self.max_retries):
try:
response = await self.llm.generate(prompt)
data = json.loads(response)
# 验证schema
validated = self.schema(**data)
return validated
except json.JSONDecodeError:
# JSON解析失败,添加错误提示重试
prompt = f"{prompt}\n\n上一次输出不是合法JSON,请只输出JSON。"
except Exception as e:
# Schema验证失败
prompt = f"{prompt}\n\n错误:{e}。请修正输出格式。"
raise ValueError(f"Failed to generate valid output after {self.max_retries} attempts")
部分解析
def partial_json_parse(text):
"""处理不完整的JSON输出"""
# 尝试补全不完整的JSON
text = text.strip()
# 统计括号
open_braces = text.count('{') - text.count('}')
open_brackets = text.count('[') - text.count(']')
# 补全
text += ']' * max(open_brackets, 0)
text += '}' * max(open_braces, 0)
try:
return json.loads(text)
except:
return None
结语
结构化输出的可靠性从低到高:Prompt工程 < JSON Mode < Structured Output < Constrained Decoding。生产环境中建议优先使用Structured Output或Constrained Decoding,配合验证重试机制,可以实现接近100%的结构化输出可靠性。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。