结构化输出:JSON Mode与Pydantic解析
核心知识点讲解
什么是结构化输出?
结构化输出是指让大语言模型生成符合特定格式的输出,如JSON、XML、CSV等。与自由文本输出不同,结构化输出具有明确的格式要求和数据结构,使得输出结果更加规范、一致和易于处理。
结构化输出的优势:
- 便于程序自动处理和解析
- 减少格式错误和歧义
- 提高数据的一致性和可靠性
- 简化下游应用的开发
- 便于验证输出的正确性
为什么需要结构化输出?
在构建AI智能体时,结构化输出尤为重要,因为:
- 数据交换需求:智能体需要与其他系统或服务交换数据
- 自动化处理:输出结果需要被程序自动处理,而不是人工阅读
- 多步骤流程:智能体的输出可能作为后续步骤的输入
- 数据验证:需要确保输出数据的完整性和正确性
- 用户体验:结构化输出可以提供更一致、更可预测的用户体验
JSON Mode 简介
JSON Mode是OpenAI API提供的一种功能,它允许模型生成纯JSON格式的输出。当启用JSON Mode时,模型会确保其输出是有效的JSON格式,避免生成任何额外的文本或解释。
JSON Mode的特点:
- 强制模型生成有效的JSON
- 不允许生成任何非JSON内容
- 需要在提示词中明确要求JSON输出
- 适用于需要严格格式的场景
Pydantic 简介
Pydantic是一个Python库,用于数据验证和设置管理。它使用Python类型提示来定义数据模型,并自动验证输入数据是否符合模型定义。在大语言模型的语境中,Pydantic常用于解析和验证模型的输出。
Pydantic的特点:
- 使用类型提示定义数据模型
- 自动验证数据类型和约束
- 提供详细的错误信息
- 支持嵌套模型和复杂数据结构
- 与Python生态系统无缝集成
结构化输出的实现方法
实现结构化输出主要有以下几种方法:
- 提示词约束:在提示词中明确要求特定格式的输出
- JSON Mode:使用OpenAI API的JSON Mode功能
- Pydantic解析器:使用Pydantic库解析和验证输出
- 模板填充:提供输出模板,让模型填充具体内容
- 正则表达式:使用正则表达式提取结构化信息
实用案例分析
案例一:生成产品信息
场景描述:
你需要构建一个智能体,用于从产品描述中提取关键信息,并以JSON格式输出。
传统方法:
让模型自由生成文本,然后手动解析或使用正则表达式提取信息。
结构化输出方法:
# 使用JSON Mode
prompt = """请从以下产品描述中提取关键信息,并以JSON格式输出:
产品描述:
Apple iPhone 15 Pro Max,6.7英寸Super Retina XDR显示屏,A17 Pro芯片,钛金属机身,256GB存储容量,深空黑色。
JSON格式应包含:品牌、型号、屏幕尺寸、芯片、机身材质、存储容量、颜色。"""
# 调用API时启用JSON Mode
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"} # 启用JSON Mode
)应用效果:
- 模型生成的输出是有效的JSON格式
- 信息提取更加准确和完整
- 下游应用可以直接解析和使用输出结果
案例二:情感分析与实体识别
场景描述:
你需要构建一个智能体,用于分析用户评论的情感倾向,并识别评论中提到的产品实体。
传统方法:
分别进行情感分析和实体识别,然后手动整合结果。
结构化输出方法:
# 使用Pydantic定义数据模型
from pydantic import BaseModel
from typing import List
class Entity(BaseModel):
text: str
type: str
class SentimentAnalysisResult(BaseModel):
sentiment: str # positive, negative, neutral
score: float # 0-1
entities: List[Entity]
summary: str
# 定义提示词
prompt = """请分析以下用户评论的情感倾向,并识别评论中提到的产品实体:
评论:这款iPhone 15 Pro Max的相机表现非常出色,尤其是在低光环境下,但是价格有点贵。
请以JSON格式输出分析结果,包含:情感倾向(positive/negative/neutral)、情感得分(0-1)、识别的实体(文本和类型)、以及简短总结。"""
# 调用API
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
# 解析和验证结果
result = SentimentAnalysisResult.parse_raw(response.choices[0].message.content)应用效果:
- 情感分析和实体识别结果整合在一个结构化输出中
- 输出结果经过自动验证,确保格式正确
- 可以直接访问和使用结构化数据的各个字段
案例三:旅行计划生成
场景描述:
你需要构建一个智能体,用于根据用户的需求生成旅行计划。
传统方法:
让模型生成自由文本形式的旅行计划。
结构化输出方法:
# 使用Pydantic定义数据模型
from pydantic import BaseModel
from typing import List, Optional
from datetime import date
class Activity(BaseModel):
day: int
title: str
description: str
duration: str
location: str
cost: Optional[str] = None
class TravelPlan(BaseModel):
destination: str
start_date: str
end_date: str
duration: int
activities: List[Activity]
budget: str
packing_list: List[str]
tips: List[str]
# 定义提示词
prompt = """请为以下旅行需求生成详细的旅行计划:
目的地:巴黎
出发日期:2024年6月1日
返回日期:2024年6月5日
预算:中等
兴趣:艺术、美食、历史
请以JSON格式输出旅行计划,包含:目的地、出发日期、返回日期、行程天数、每天的活动安排(包括标题、描述、时长、地点、费用)、总预算、 packing清单、以及旅行建议。"""
# 调用API
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
# 解析和验证结果
plan = TravelPlan.parse_raw(response.choices[0].message.content)应用效果:
- 旅行计划以结构化形式呈现,包含所有必要信息
- 可以轻松访问和处理计划的各个部分
- 输出结果格式一致,便于与其他系统集成
代码示例
示例1:使用JSON Mode生成结构化输出
import openai
import json
# 设置API密钥
openai.api_key = "YOUR_API_KEY"
# 定义提示词
prompt = """请从以下产品描述中提取关键信息,并以JSON格式输出:
产品描述:
Apple iPhone 15 Pro Max,6.7英寸Super Retina XDR显示屏,A17 Pro芯片,钛金属机身,256GB存储容量,深空黑色。
JSON格式应包含:品牌、型号、屏幕尺寸、芯片、机身材质、存储容量、颜色。"""
# 调用API,启用JSON Mode
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"} # 启用JSON Mode
)
# 提取和打印结果
product_info = response.choices[0].message.content
print("原始输出:")
print(product_info)
# 解析JSON
product_data = json.loads(product_info)
print("\n解析后的数据:")
print(f"品牌: {product_data.get('品牌')}")
print(f"型号: {product_data.get('型号')}")
print(f"屏幕尺寸: {product_data.get('屏幕尺寸')}")
print(f"芯片: {product_data.get('芯片')}")
print(f"机身材质: {product_data.get('机身材质')}")
print(f"存储容量: {product_data.get('存储容量')}")
print(f"颜色: {product_data.get('颜色')}")
except Exception as e:
print(f"错误: {e}")示例2:使用Pydantic解析和验证输出
import openai
from pydantic import BaseModel, Field
from typing import List, Optional
# 设置API密钥
openai.api_key = "YOUR_API_KEY"
# 使用Pydantic定义数据模型
class Item(BaseModel):
name: str
quantity: int
price: float
category: str
class ShoppingList(BaseModel):
title: str
items: List[Item]
total_items: int
estimated_total: float
store_recommendations: List[str]
# 定义提示词
prompt = """请根据以下需求生成购物清单:
我需要为周末的烧烤聚会准备食材,大约有10人参加。我需要购买肉类、蔬菜、饮料和调味料。
请以JSON格式输出购物清单,包含:标题、物品列表(每个物品包含名称、数量、价格、类别)、物品总数、估计总费用、以及推荐的购买地点。"""
# 调用API
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
# 提取结果
json_output = response.choices[0].message.content
print("原始JSON输出:")
print(json_output)
# 使用Pydantic解析和验证
shopping_list = ShoppingList.parse_raw(json_output)
print("\n解析和验证后的购物清单:")
print(f"标题: {shopping_list.title}")
print(f"物品总数: {shopping_list.total_items}")
print(f"估计总费用: ${shopping_list.estimated_total:.2f}")
print("\n物品列表:")
for item in shopping_list.items:
print(f"- {item.name} (数量: {item.quantity}, 价格: ${item.price:.2f}, 类别: {item.category})")
print("\n推荐购买地点:")
for store in shopping_list.store_recommendations:
print(f"- {store}")
except Exception as e:
print(f"错误: {e}")示例3:使用LangChain的输出解析器
from langchain import LLMChain, PromptTemplate
from langchain_openai import ChatOpenAI
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
# 设置API密钥
import os
os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"
# 使用Pydantic定义数据模型
class MovieRecommendation(BaseModel):
title: str = Field(description="电影标题")
director: str = Field(description="导演")
year: int = Field(description="上映年份")
genre: str = Field(description="电影类型")
rating: float = Field(description="评分,1-10分")
summary: str = Field(description="电影简介")
reasons: List[str] = Field(description="推荐理由")
# 创建输出解析器
parser = PydanticOutputParser(pydantic_object=MovieRecommendation)
# 定义提示词模板
prompt_template = PromptTemplate(
input_variables=["genre", "actor"],
template="""请推荐一部{genre}类型的电影,由{actor}主演。
{format_instructions}
请提供电影的标题、导演、上映年份、类型、评分、简介以及推荐理由。""",
partial_variables={"format_instructions": parser.get_format_instructions()}
)
# 初始化LLM
llm = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# 创建LLMChain
chain = LLMChain(llm=llm, prompt=prompt_template)
# 运行链
try:
result = chain.run(genre="科幻", actor="汤姆·克鲁斯")
print("原始输出:")
print(result)
# 解析输出
movie = parser.parse(result)
print("\n解析后的电影推荐:")
print(f"标题: {movie.title}")
print(f"导演: {movie.director}")
print(f"年份: {movie.year}")
print(f"类型: {movie.genre}")
print(f"评分: {movie.rating}")
print(f"简介: {movie.summary}")
print("推荐理由:")
for reason in movie.reasons:
print(f"- {reason}")
except Exception as e:
print(f"错误: {e}")示例4:处理复杂的嵌套结构
import openai
from pydantic import BaseModel
from typing import List, Optional
# 设置API密钥
openai.api_key = "YOUR_API_KEY"
# 使用Pydantic定义嵌套数据模型
class Question(BaseModel):
text: str
options: List[str]
correct_answer: str
explanation: Optional[str] = None
class Quiz(BaseModel):
title: str
topic: str
difficulty: str # easy, medium, hard
questions: List[Question]
total_questions: int
# 定义提示词
prompt = """请生成一个关于人工智能基础知识的测验,包含5道多选题。
请以JSON格式输出测验,包含:标题、主题、难度、问题列表(每个问题包含题目、选项、正确答案、解释)、以及总问题数。"""
# 调用API
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
# 解析和验证结果
quiz_data = response.choices[0].message.content
quiz = Quiz.parse_raw(quiz_data)
print(f"测验标题: {quiz.title}")
print(f"主题: {quiz.topic}")
print(f"难度: {quiz.difficulty}")
print(f"总问题数: {quiz.total_questions}\n")
# 打印问题
for i, question in enumerate(quiz.questions, 1):
print(f"问题 {i}: {question.text}")
print("选项:")
for j, option in enumerate(question.options, 1):
print(f"{j}. {option}")
print(f"正确答案: {question.correct_answer}")
if question.explanation:
print(f"解释: {question.explanation}")
print()
except Exception as e:
print(f"错误: {e}")总结与思考
关键要点回顾
结构化输出的概念:让大语言模型生成符合特定格式的输出,如JSON、XML等。
结构化输出的优势:便于程序自动处理,减少格式错误,提高数据一致性,简化下游应用开发。
JSON Mode:OpenAI API提供的功能,强制模型生成有效的JSON格式输出。
Pydantic:Python库,用于数据验证和设置管理,可用于解析和验证模型的输出。
实现方法:提示词约束、JSON Mode、Pydantic解析器、模板填充、正则表达式。
实践建议
明确格式要求:在提示词中明确指定所需的输出格式和结构。
使用JSON Mode:对于需要JSON输出的场景,启用OpenAI API的JSON Mode功能。
定义数据模型:使用Pydantic等库定义清晰的数据模型,确保输出数据的结构正确。
添加格式指令:在提示词中添加格式指令,帮助模型理解输出要求。
处理错误情况:实现错误处理机制,应对模型输出不符合预期格式的情况。
验证输出结果:使用Pydantic等库验证输出结果的正确性和完整性。
逐步复杂:从简单的结构开始,逐步过渡到更复杂的嵌套结构。
未来学习方向
多模态结构化输出:探索如何在多模态场景中实现结构化输出。
动态结构生成:研究如何根据不同场景动态生成输出结构。
输出质量评估:开发评估结构化输出质量的方法和指标。
自动修复机制:实现自动检测和修复输出格式错误的机制。
与工具调用集成:探索如何将结构化输出与工具调用相结合,实现更复杂的智能体功能。
性能优化:研究如何优化结构化输出的生成速度和准确性。
结构化输出是构建可靠、高效AI智能体的重要技术。通过掌握JSON Mode和Pydantic解析等技术,你将能够让智能体生成更加规范、一致和易于处理的输出,从而构建更加健壮的AI应用。在接下来的课程中,我们将继续探索更多高级技术,帮助你进一步提升智能体的能力。