一、输出控制的重要性
大语言模型的自由生成特性是一把双刃剑——带来创造力但也带来不可预测性。在工程化应用中,输出控制是决定 AI 能否可靠交付的关键能力。
1.1 失控输出的代价
| 场景 | 失控表现 | 影响 |
|---|---|---|
| 客服自动化 | 格式混乱、缺少关键信息 | 客户投诉 |
| 数据批处理 | 输出结构不一致,无法解析 | 下游管道崩溃 |
| 代码生成 | 格式不对、缺少注释 | CI/CD 失败 |
| 内容生成 | 多版本风格不统一 | 品牌一致性受损 |
二、格式约束技术
2.1 Markdown 格式控制
def markdown_format_control():
prompt = """请严格按照以下 Markdown 格式输出:
# 标题
## 摘要
<200 字以内的摘要>
## 核心要点
- 要点 1:<具体内容>
- 要点 2:<具体内容>
- 要点 3:<具体内容>
## 数据表格
| 指标 | 值 | 说明 |
|------|----|------|
| <名称> | <数值> | <说明> |
## 结论
<150 字以内>
---
注意:每个部分都必须输出,缺失则不合格。"""
return prompt
# 格式验证函数
import re
def validate_markdown_structure(text: str) -> dict:
"""验证 Markdown 结构完整性"""
checks = {
"has_title": bool(re.search(r"^# ", text, re.MULTILINE)),
"has_summary": bool(re.search(r"^## 摘要", text, re.MULTILINE)),
"has_keypoints": bool(re.search(r"^## 核心要点", text, re.MULTILINE)),
"has_table": bool(re.search(r"\|.*\|.*\|", text)),
"has_conclusion": bool(re.search(r"^## 结论", text, re.MULTILINE)),
"summary_length_ok": None,
}
# 检查摘要长度
summary_match = re.search(
r"## 摘要\s*\n(.+?)(?=\n##)", text, re.DOTALL
)
if summary_match:
checks["summary_length_ok"] = len(summary_match.group(1)) < 200
return checks
2.2 JSON 输出控制
通用的 JSON 约束 Prompt
输出必须是一个合法的 JSON 对象,格式如下:
{
"status": "success | error",
"data": {
// 按需填充
},
"metadata": {
"timestamp": "<当前时间戳>",
"confidence": 0.0-1.0
}
}
要求:
1. 所有 key 必须使用双引号
2. 字符串值必须使用双引号
3. 不能包含注释或 Markdown 代码块标记
4. 不能有其他文字描述
5. 一定输出纯 JSON
JSON 修复技术
import json
import re
def extract_and_fix_json(text: str) -> dict:
"""从模型输出中提取并修复 JSON"""
# Step 1: 尝试提取 JSON 块
# 移除代码块标记
text = re.sub(r'```json\s*|\s*```', '', text)
try:
return json.loads(text)
except json.JSONDecodeError:
pass
# Step 2: 常见修复
fixes = [
# 修复单引号
lambda s: s.replace("'", '"'),
# 修复末尾逗号
lambda s: re.sub(r',(\s*[}\]])', r'\1', s),
# 修复注释
lambda s: re.sub(r'//.*?\n', '\n', s),
# 修复布尔值
lambda s: s.replace('True', 'true').replace('False', 'false'),
]
for fix in fixes:
try:
return json.loads(fix(text))
except json.JSONDecodeError:
continue
# Step 3: 最后手段 - 重新生成
raise ValueError("无法修复 JSON 输出")
2.3 XML 输出控制
<response>
<metadata>
<model>gpt-4</model>
<timestamp>2026-06-25T12:00:00Z</timestamp>
<type>analysis</type>
</metadata>
<content>
<section id="overview">
<title>概述</title>
<paragraph>{overview_text}</paragraph>
</section>
<section id="details">
<title>详细信息</title>
<item key="point_1">{detail_1}</item>
<item key="point_2">{detail_2}</item>
</section>
</content>
<validation>
<passed>true</passed>
<score>{quality_score}</score>
</validation>
</response>
三、结构化输出技术
3.1 Pydantic + Function Calling
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from openai import OpenAI
class ProductReview(BaseModel):
"""结构化的产品评价输出"""
product_name: str = Field(description="产品名称")
rating: int = Field(ge=1, le=5, description="评分 1-5")
pros: List[str] = Field(min_length=1, max_length=5, description="优点列表")
cons: List[str] = Field(min_length=1, max_length=5, description="缺点列表")
summary: str = Field(max_length=200, description="总结摘要")
recommend: bool = Field(description="是否推荐")
@validator('rating')
def rating_matches_sentiment(cls, v, values):
if 'cons' in values and len(values['cons']) > 3 and v > 4:
raise ValueError('评分与缺点数量不符')
return v
def structured_review_analysis(review_text: str) -> ProductReview:
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4",
messages=[{
"role": "user",
"content": f"分析以下产品评价并提取结构化信息:\n{review_text}"
}],
functions=[{
"name": "extract_review",
"description": "提取产品评价的结构化信息",
"parameters": ProductReview.schema()
}],
function_call={"name": "extract_review"}
)
return ProductReview.parse_raw(
response.choices[0].message.function_call.arguments
)
3.2 枚举约束
from enum import Enum
class Sentiment(Enum):
POSITIVE = "positive"
NEGATIVE = "negative"
NEUTRAL = "neutral"
MIXED = "mixed"
class Category(Enum):
COMPLAINT = "complaint"
INQUIRY = "inquiry"
FEEDBACK = "feedback"
SUGGESTION = "suggestion"
OTHER = "other"
def constrained_classification(text: str) -> tuple:
"""强制输出限定在枚举范围内"""
prompt = f"""从以下选项中选择情感倾向(仅输出一个词):
{', '.join([s.value for s in Sentiment])}
同时从以下选项选择类别(仅输出一个词):
{', '.join([c.value for c in Category])}
文本:{text}
情感:
类别:"""
# ... 调用 LLM 并严格验证输出
# 如果输出不在枚举范围内,重试或降级处理
pass
四、长度与粒度控制
4.1 精确长度控制
| 控制需求 | Prompt 写法 | 效果 |
|---|---|---|
| 字/词精确 | “用 100-150 字回答” | 近似控制,±20% |
| 段落数 | “写 3 段,每段 2-3 句” | 较好控制 |
| 列表项 | “列出恰好 5 个要点” | 较好控制 |
| 代码行数 | “不超过 20 行代码” | 中等控制 |
4.2 渐进式扩展技术
def progressive_expansion(topic: str, max_length: int) -> str:
"""从核心内容开始,逐步扩展文字到目标长度"""
# Phase 1: 生成核心骨架
skeleton_prompt = f"为 '{topic}' 写一个三行的核心概要。"
# ... 获取 skeleton
# Phase 2: 扩展到段落
expand_prompt = f"""基于以下概要,将内容扩展到约 {max_length//2} 字:
{skeleton}
要求:添加具体例子和数据佐证。"""
# ... 获取 expanded
# Phase 3: 精细化调整
final_prompt = f"""将以下内容调整到恰好 {max_length} 字:
{expanded}
如果超出则精简,不足则补充细节。"""
# ... 返回最终结果
pass
五、输出验证与后处理
5.1 验证管道
class OutputValidator:
"""多层次输出验证"""
def __init__(self, rules: dict):
self.rules = rules
def validate(self, output: str) -> dict:
results = {
"passed": True,
"checks": [],
"errors": []
}
# Layer 1: 结构完整性
if self.rules.get("require_structure"):
for section in self.rules["required_sections"]:
if section not in output:
results["passed"] = False
results["errors"].append(f"缺少必要段落: {section}")
# Layer 2: 格式正确性
if self.rules.get("require_json"):
try:
json.loads(output)
except json.JSONDecodeError as e:
results["passed"] = False
results["errors"].append(f"JSON 格式错误: {e}")
# Layer 3: 内容合理性
if self.rules.get("min_length"):
if len(output) < self.rules["min_length"]:
results["passed"] = False
results["errors"].append("内容长度不足")
# Layer 4: 安全过滤
for pattern in self.rules.get("block_patterns", []):
if re.search(pattern, output, re.IGNORECASE):
results["passed"] = False
results["errors"].append(f"检测到违禁内容: {pattern}")
results["checks"] = len(results["errors"])
return results
# 使用
validator = OutputValidator({
"require_structure": True,
"required_sections": ["摘要", "正文", "结论"],
"require_json": False,
"min_length": 100,
"block_patterns": [r"(密码|passwd|secret)"]
})
5.2 后处理管道
class OutputPostProcessor:
"""输出后处理管道"""
def __init__(self):
self.pipeline = []
def add_step(self, name: str, fn):
self.pipeline.append((name, fn))
def process(self, output: str) -> str:
for name, fn in self.pipeline:
try:
output = fn(output)
except Exception as e:
print(f"后处理步骤 '{name}' 失败: {e}")
return output
# 示例管道
processor = OutputPostProcessor()
processor.add_step("trim_whitespace", lambda x: x.strip())
processor.add_step("fix_encoding", lambda x: x.encode('utf-8', errors='replace').decode('utf-8'))
processor.add_step("remove_markdown_code_block",
lambda x: re.sub(r'```\w*\n?|```', '', x))
processor.add_step("ensure_trailing_newline",
lambda x: x if x.endswith('\n') else x + '\n')
六、对比总结
| 技术 | 可靠性 | 灵活性 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| Markdown 格式约束 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ | 文档、报告 |
| JSON 输出 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | API 集成、数据管道 |
| Function Calling | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | 生产级应用 |
| XML 输出 | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | 结构化文档 |
| 枚举约束 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐ | 分类任务 |
| 长度控制 | ⭐⭐ | ⭐⭐⭐ | ⭐ | 内容生成 |
七、总结
精确的输出控制是将大模型从"玩具"变为"工具"的关键一步:
- 优先使用结构化输出 — JSON/Schema 比自由文本可靠百倍
- 验证不可跳过的步骤 — 始终验证输出的完整性和正确性
- 多层级防御 — Prompt 约束 + 代码验证 + 后处理修复
- Function Calling 是终极方案 — 需要语法解析的语言模型保证结构
- 长度控制靠迭代 — 渐进式扩展比一次成型更可控
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
