大模型稳定输出JSON格式的实战指南:从Prompt到函数调用的完整方案

发布时间:2026/8/4 13:22:57
大模型稳定输出JSON格式的实战指南:从Prompt到函数调用的完整方案 在构建基于大模型的智能应用时你是否遇到过这样的困扰你向模型提问“列出三个用户信息包括姓名、年龄和邮箱”期望得到一个结构化的JSON数组但模型却返回了一段自由文本甚至夹杂着Markdown代码块标记这种输出格式的不稳定性是AI应用开发特别是Agent智能体开发中一个高频痛点。它不仅增加了后端数据解析的复杂度更可能导致整个业务流程中断。本文将深入探讨大模型稳定输出JSON格式的实战方案。无论你是正在开发一个需要精准数据提取的AI Agent还是在准备相关技术面试本文都将为你提供从核心原理到工程落地的完整指南。我们将从问题根源出发逐步拆解Prompt工程、函数调用、后处理校验以及使用专业框架等多种解决方案并附上可运行的代码示例和避坑清单帮助你彻底解决JSON输出“抽风”的问题。1. 为什么大模型输出JSON不稳定在深入解决方案之前我们首先要理解问题的根源。大语言模型LLM本质上是基于概率生成文本的序列预测模型其训练目标是生成“合理”的下一个词元Token而非严格遵守特定的数据格式规范。1.1 不稳定性表现与根源分析常见的不稳定输出形式自由文本混杂模型在JSON前后添加解释性文字如“好的这是你要的数据”或“结果如下”。格式错误缺少引号、括号不匹配、键名未加双引号JSON标准要求必须为双引号、尾部多余逗号。Markdown代码块返回json {...}将JSON包裹在Markdown标记中。结构漂移要求的字段缺失、多出未要求的字段或数组元素结构不一致。类型错误数字被输出为字符串如age: “25”布尔值被写为单词。根本原因可以归结为三点训练数据偏差模型在训练时接触了大量非纯JSON的文本如技术博客、问答对其中JSON常被嵌入解释性上下文中。Prompt歧义用户的指令Prompt不够精确模型无法区分你是要“生成JSON”还是“描述JSON”。采样随机性即使使用低温度Temperature设置模型的生成过程仍有一定随机性可能导致格式上的微小差异。1.2 稳定JSON输出的核心需求场景在以下场景中格式稳定的JSON输出至关重要AI Agent / 智能体Agent根据观察和思考需要调用工具Tool/Function。工具调用的参数必须以结构化数据通常是JSON的形式精确传递。数据提取与结构化从非结构化文本如新闻、报告中提取实体、关系并转化为数据库可接收的格式。API接口集成大模型作为后端服务需要向前端或其他服务返回可直接解析的数据对象。自动化工作流将大模型的输出作为下一个自动化节点的输入格式错误会导致流程失败。2. 环境与工具准备在开始实战前我们需要搭建一个统一的实验环境。本文将以 OpenAI GPT 系列模型兼容 OpenAI API 的模型为例使用 Python 语言进行演示。其他模型如 Claude、国产大模型和语言如 JavaScript的思路相通。2.1 基础环境配置确保你的开发环境已安装 Python 3.8。我们主要使用openai和pydantic这两个核心库。# 创建并进入项目目录 mkdir stable-json-output cd stable-json-output # 创建虚拟环境推荐 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 安装核心依赖 pip install openai pydantic # 可选安装用于更丰富功能如框架的库 # pip install instructor # 用于结构化输出的强大框架 # pip install litellm # 统一多模型调用2.2 获取并配置API密钥你需要一个 OpenAI API 密钥或兼容 OpenAI API 的服务如 Azure OpenAI, 国内大模型平台的密钥。# config.py 或直接在代码中设置环境变量 import os # 方法一直接设置不推荐用于生产环境 # openai.api_key “your-api-key-here” # 方法二使用环境变量推荐 # 在终端中执行export OPENAI_API_KEY‘your-api-key-here’ # 或在代码中通过os.environ设置 os.environ[“OPENAI_API_KEY”] “your-api-key-here” # 如果你使用其他兼容服务可能还需要设置 base_url # os.environ[“OPENAI_API_BASE”] “https://api.xxx.com/v1”3. 方案一精炼Prompt工程法这是最基础、最直接的方法通过精心设计提示词来引导模型。关键在于明确性、强制性并提供高质量示例。3.1 基础指令强化一个糟糕的Prompt“给我一些用户数据。” 一个较好的Prompt“生成一个包含三个用户对象的JSON数组每个对象有name字符串、age整数、email字符串字段。只输出JSON不要有任何其他文字。”# basic_prompt.py import openai import json def get_completion_basic(prompt): client openai.OpenAI() response client.chat.completions.create( model“gpt-3.5-turbo”, # 或 “gpt-4”, “gpt-4-turbo-preview” messages[{“role”: “user”, “content”: prompt}], temperature0.1, # 降低随机性对格式化输出有益 max_tokens500 ) return response.choices[0].message.content # 测试基础Prompt prompt “”” 请生成一个包含三个用户信息的JSON数组。 每个用户是一个对象包含以下字段 - name: 字符串表示用户名 - age: 整数表示年龄 - email: 字符串表示电子邮箱 请确保输出是**纯粹的、有效的JSON字符串**不要包含任何Markdown代码块标记如json也不要输出任何解释性文字。 “”” result get_completion_basic(prompt) print(“原始输出”) print(result) print(“\n尝试解析”) try: parsed json.loads(result) print(“解析成功”, json.dumps(parsed, indent2, ensure_asciiFalse)) except json.JSONDecodeError as e: print(f“解析失败错误{e}”) # 尝试清理常见的非JSON前缀/后缀 cleaned result.strip() if cleaned.startswith(‘json’): cleaned cleaned[7:] elif cleaned.startswith(‘’): cleaned cleaned[3:] if cleaned.endswith(‘’): cleaned cleaned[:-3] cleaned cleaned.strip() print(“清理后内容”, cleaned) try: parsed json.loads(cleaned) print(“清理后解析成功”, json.dumps(parsed, indent2, ensure_asciiFalse)) except: print(“清理后仍然无法解析。”)关键点分析明确结构清晰定义JSON的根数组、元素对象和每个字段的名称、类型。强制指令使用“纯粹的、有效的JSON字符串”、“不要包含任何...”、“只输出JSON”等强约束性语句。降低Temperaturetemperature0.1使输出更确定更倾向于遵循指令。3.2 少样本学习Few-Shot Learning在Prompt中提供输入输出的示例是引导模型格式最有效的方法之一。# few_shot_prompt.py import openai import json def get_completion_few_shot(prompt): client openai.OpenAI() response client.chat.completions.create( model“gpt-3.5-turbo”, messages[{“role”: “user”, “content”: prompt}], temperature0.1, response_format{ “type”: “json_object” } # 注意此参数要求模型必须输出JSON对象对数组有限制 ) return response.choices[0].message.content # 少样本Prompt包含一个清晰的示例 few_shot_prompt “”” 你的任务是将用户的自然语言请求转换为一个特定的JSON格式。 例如 用户请求“列出两个产品需要产品名和价格。” 输出必须是以下格式的纯JSON无任何额外文本 { “products”: [ {“name”: “产品A”, “price”: 100}, {“name”: “产品B”, “price”: 200} ] } 现在请处理新的请求 用户请求“创建三个图书条目包含标题、作者和出版年份。” 请根据上述示例的格式输出对应的JSON。 “”” result get_completion_few_shot(few_shot_prompt) print(“少样本学习输出”) print(result) try: parsed json.loads(result) print(“解析成功”, json.dumps(parsed, indent2, ensure_asciiFalse)) except json.JSONDecodeError as e: print(f“解析错误{e}”)注意OpenAI API 提供了response_format{ “type”: “json_object” }参数它能强制模型输出一个有效的JSON对象。这是一个非常强大的特性但它有两个重要限制你必须同时在系统或用户消息中明确要求模型输出JSON。它保证输出是JSON对象以{开头但不保证是JSON数组以[开头。如果你需要数组可能仍需结合Prompt。4. 方案二函数调用Function Calling与工具使用这是目前生产环境中实现结构化输出最稳定、最受推荐的方法。模型不直接输出JSON而是输出一个“意图调用某个函数”的请求其中参数是结构化的JSON。开发者预先定义好函数工具的Schema模型会严格遵循这个Schema来生成参数。4.1 使用OpenAI原生函数调用# function_calling.py import openai import json # 1. 定义我们期望模型能够调用的“函数”工具的Schema tools [ { “type”: “function”, “function”: { “name”: “extract_user_info”, “description”: “从描述中提取用户信息并生成结构化的列表”, “parameters”: { “type”: “object”, “properties”: { “users”: { “type”: “array”, “description”: “用户对象列表”, “items”: { “type”: “object”, “properties”: { “name”: {“type”: “string”, “description”: “用户姓名”}, “age”: {“type”: “integer”, “description”: “用户年龄”}, “email”: {“type”: “string”, “description”: “用户邮箱”} }, “required”: [“name”, “age”, “email”] } } }, “required”: [“users”] } } } ] # 2. 准备用户请求 messages [{“role”: “user”, “content”: “帮我提取这三个人信息张三25岁zhangsanexample.com李四30岁lisiexample.com王五28岁wangwuexample.com。”}] client openai.OpenAI() # 3. 发起聊天补全请求并告知模型可用的工具 response client.chat.completions.create( model“gpt-3.5-turbo”, messagesmessages, toolstools, tool_choice“auto”, # 让模型决定是否调用函数。设为 {“type”: “function”, “function”: {“name”: “extract_user_info”}} 可强制调用 temperature0 ) # 4. 解析模型的响应 response_message response.choices[0].message print(“模型原始响应消息”, response_message) # 5. 检查模型是否决定调用函数 if response_message.tool_calls: # 通常只有一个工具调用 tool_call response_message.tool_calls[0] if tool_call.function.name “extract_user_info”: # 提取函数调用参数这已经是标准的JSON字符串 function_args_str tool_call.function.arguments print(“\n模型生成的函数参数JSON字符串”) print(function_args_str) # 解析JSON try: function_args json.loads(function_args_str) print(“\n解析后的结构化数据”) print(json.dumps(function_args, indent2, ensure_asciiFalse)) # 在实际应用中这里你会调用真实的 extract_user_info 函数 # result extract_user_info(**function_args) except json.JSONDecodeError as e: print(f“解析函数参数失败{e}”) else: print(“模型没有选择调用函数返回了普通文本”, response_message.content)方案优势极高稳定性模型输出的arguments严格遵循你定义的 JSON Schema格式错误率极低。类型安全Schema中定义了类型string, integer等模型会尽力遵守。意图明确将“生成数据”的任务转化为“调用函数”更符合模型在工具使用场景下的训练目标。这是构建可靠AI Agent的基石。5. 方案三使用专业框架Instructor对于复杂的数据结构手动编写Prompt和解析逻辑依然繁琐。Instructor库应运而生它利用Pydantic模型来定义数据结构并通过修补patchOpenAI客户端将结构化输出的过程极大简化。5.1 安装与基础使用首先安装库pip install instructor# instructor_basic.py import instructor from openai import OpenAI from pydantic import BaseModel, Field from typing import List # 1. 使用instructor修补OpenAI客户端 client instructor.patch(OpenAI()) # 2. 使用Pydantic定义你期望的数据结构 class User(BaseModel): name: str Field(…, description“用户姓名”) age: int Field(…, description“用户年龄”) email: str Field(…, description“用户邮箱”) class UserList(BaseModel): “”“一个包含多个用户的列表”“” users: List[User] # 3. 发起请求直接指定response_model completion client.chat.completions.create( model“gpt-3.5-turbo”, messages[ {“role”: “user”, “content”: “提供三个虚构的用户信息包括姓名、年龄和邮箱。”} ], response_modelUserList, # 核心指定返回的模型类型 max_retries2, # 自动重试提高成功率 ) # 4. 直接得到Pydantic模型实例 extracted_data completion print(“提取的数据类型”, type(extracted_data)) print(“\n结构化数据”) print(extracted_data.model_dump_json(indent2, ensure_asciiFalse)) # 5. 像操作普通对象一样使用数据 for user in extracted_data.users: print(f“用户{user.name}, 年龄{user.age}”)5.2 处理复杂嵌套与可选字段Instructor配合Pydantic能轻松处理复杂场景。# instructor_advanced.py import instructor from openai import OpenAI from pydantic import BaseModel, Field from typing import List, Optional from enum import Enum class Department(str, Enum): ENGINEERING “Engineering” SALES “Sales” HR “HR” class Address(BaseModel): street: str city: str postal_code: Optional[str] None # 可选字段 class Employee(BaseModel): id: int full_name: str Field(…, description“员工全名”) department: Department email: str address: Optional[Address] None # 嵌套对象可选 skills: List[str] Field(default_factorylist, description“技能列表”) class CompanyRoster(BaseModel): company_name: str employees: List[Employee] client instructor.patch(OpenAI()) # 模拟一个复杂的用户请求 user_query “”” 请为‘创新科技有限公司’生成一个包含5名员工的名单。 需要包含以下信息员工ID、全名、部门只能是Engineering, Sales, HR之一、邮箱。 其中为至少2名员工添加住址信息街道、城市、邮编可选。 为所有员工添加一些相关的技能标签。 “”” roster client.chat.completions.create( model“gpt-4-turbo-preview”, # 复杂结构建议使用更强模型 messages[{“role”: “user”, “content”: user_query}], response_modelCompanyRoster, max_retries3, ) print(“公司花名册”) print(roster.model_dump_json(indent2, ensure_asciiFalse)) # 访问嵌套数据 print(f“\n公司名称{roster.company_name}”) for emp in roster.employees: addr_info f“, 住址{emp.address.street}, {emp.address.city}” if emp.address else “” print(f“- {emp.full_name} ({emp.department}){addr_info}”)框架优势总结声明式编程用Python类定义结构无需手动编写JSON Schema。自动重试与校验框架会自动处理模型的格式错误并尝试重新生成。类型丰富完美支持枚举、嵌套模型、可选字段、列表等复杂类型。无缝集成返回的就是Pydantic模型便于后续的验证、序列化和使用。6. 方案四输出后处理与校验无论使用哪种方法在生产环境中添加一道后处理校验都是最佳实践。这可以作为格式错误的最后防线。6.1 健壮的解析与修复函数# post_processing.py import json import re from typing import Any, Optional def robust_json_parse(text: str, expected_type: Optional[type] None) - Any: “”” 尝试从可能被污染的文本中解析JSON。 步骤1. 直接解析 2. 清理常见包装 3. 查找JSON子串 4. 尝试修复常见语法错误 “”” original_text text.strip() # 尝试1直接解析 try: result json.loads(original_text) if expected_type and not isinstance(result, expected_type): raise TypeError(f“解析出的类型是 {type(result)} 但期望的是 {expected_type}”) return result except json.JSONDecodeError as e1: pass # 继续尝试清理 cleaned original_text # 尝试2移除Markdown代码块标记 markdown_json_pattern r‘^(?:json)?\s*\n?(.*?)\n?$’ match re.search(markdown_json_pattern, cleaned, re.DOTALL | re.IGNORECASE) if match: cleaned match.group(1).strip() # 尝试3查找第一个‘{‘或’[‘到最后一个’}‘或’]‘之间的内容 # 这可以处理前面有解释性文字的情况 start_chars {‘{‘: ‘}’, ‘[‘: ‘]’} for start_char, end_char in start_chars.items(): start_idx cleaned.find(start_char) if start_idx ! -1: # 从找到的开始字符开始找到最后一个匹配的结束字符 stack [] end_idx -1 for i in range(start_idx, len(cleaned)): ch cleaned[i] if ch start_char: stack.append(ch) elif ch end_char: if stack: stack.pop() if not stack: # 栈空找到匹配的结束 end_idx i break if end_idx ! -1: candidate cleaned[start_idx:end_idx1] try: result json.loads(candidate) if expected_type and not isinstance(result, expected_type): raise TypeError(f“子串解析类型不匹配”) return result except: pass # 尝试4尝试修复一些常见的简单语法错误谨慎使用 # 例如单引号替换为双引号不完全可靠因为文本中可能包含合法单引号 # 更推荐使用专门的库如 json5 或 demjson3这里仅作演示 try: # 这是一个非常简单的修复可能引入新错误仅用于最后尝试 repaired re.sub(r“(?!\\)‘“, ‘“’, cleaned) # 替换未转义的单引号 repaired re.sub(r’(?!\\)’‘, ‘“’, repaired) result json.loads(repaired) return result except: pass # 所有尝试都失败 raise json.JSONDecodeError(f“无法从文本中解析出有效的JSON。原始文本开头{original_text[:100]}…”, original_text, 0) # 测试后处理函数 test_cases [ # 纯JSON ‘[{“name”: “Test”, “age”: 30}]’, # 带Markdown ‘json\n[{“name”: “Test”, “age”: 30}]\n’, # 前面有文字 ‘这是你要的数据 [{“name”: “Test”, “age”: 30}]’, # 前后都有文字 ‘结果如下\n\n[{“name”: “Test”, “age”: 30}]\n\n以上是全部信息。’, # 格式略有瑕疵实际中模型可能产生 “[{‘name’: ‘Test’, ‘age’: 30}]”, # 单引号非标准JSON ] for i, test in enumerate(test_cases): print(f“\n测试用例 {i1}: {test[:50]}…”) try: parsed robust_json_parse(test, expected_typelist) print(f“ 解析成功: {parsed}”) except Exception as e: print(f“ 解析失败: {e}”)6.2 集成到完整流程中在实际调用中你应该将后处理作为安全网。# integrated_pipeline.py import openai import json from post_processing import robust_json_parse # 导入上面的函数 def get_structured_user_data(prompt: str) - list: “””一个集成了Prompt、调用和后处理的完整流程”“” client openai.OpenAI() # 使用强化的Prompt enhanced_prompt f“”” {prompt} 请将输出严格限制为一个纯粹的JSON数组数组中的每个元素是一个用户对象包含“name”字符串、“age”整数、“email”字符串字段。 不要输出任何其他文字、标记或解释。 “”” try: response client.chat.completions.create( model“gpt-3.5-turbo”, messages[{“role”: “user”, “content”: enhanced_prompt}], temperature0.1, # 也可以结合response_format # response_format{“type”: “json_object”}, # 注意这要求输出是对象不是数组 ) raw_output response.choices[0].message.content # 关键步骤使用健壮的后处理 user_list robust_json_parse(raw_output, expected_typelist) # 额外的数据校验可选 for user in user_list: if not isinstance(user.get(‘name’), str): raise ValueError(f“Invalid name type: {user.get(‘name’)}”) if not isinstance(user.get(‘age’), int): # 尝试转换如果可能 try: user[‘age’] int(user[‘age’]) except: raise ValueError(f“Invalid age value: {user.get(‘age’)}”) return user_list except (json.JSONDecodeError, ValueError, TypeError) as e: # 记录日志并可能触发降级策略如返回空列表、使用默认值、请求人工干预 print(f“结构化数据提取失败: {e}”) # 降级尝试一个更简单、更直接的请求 return [] # 或 raise # 使用 users get_structured_user_data(“生成两个用户信息张三和李四。”) print(“最终获取到的用户列表”, json.dumps(users, indent2, ensure_asciiFalse))7. 方案对比与选型指南面对多种方案如何选择下表从多个维度进行了对比特性/方案精炼Prompt工程函数调用 (Function Calling)Instructor (Pydantic)后处理校验稳定性中低依赖模型理解和遵循指令的能力高模型严格遵循预定义Schema非常高框架提供自动重试和校验作为补充极高最后防线开发复杂度低只需编写Prompt中需要定义JSON Schema中低用Python类定义更直观中需要编写解析逻辑灵活性高可随时修改Prompt中修改Schema需更新函数定义中修改Pydantic模型高可适配各种不规则输出类型安全无有通过Schema定义强集成Pydantic类型系统无需自行校验输出结构任意JSON必须符合函数参数Schema必须符合Pydantic模型任意但目标是规整为预期结构适用场景简单、临时的数据提取对稳定性要求不高的场景AI Agent工具调用需要强格式保证的API交互复杂嵌套数据提取需要类型安全和自动重试的项目必备的安全生产环节处理第三方或不可控模型的输出推荐指数⭐⭐⭐⭐⭐⭐⭐ (Agent场景)⭐⭐⭐⭐⭐ (数据提取场景)⭐⭐⭐⭐⭐ (必须搭配使用)选型建议对于AI Agent开发优先使用函数调用Function Calling。这是OpenAI为工具使用设计的原生方式稳定性和生态兼容性最好。对于复杂数据提取任务强烈推荐使用Instructor库。它用Pythonic的方式将定义、调用、校验融为一体大幅提升开发效率和可靠性。Prompt工程作为辅助手段在函数调用或Instructor的提示部分使用进一步明确任务要求。后处理校验无论采用哪种方案都必须实施。这是保证程序鲁棒性的关键。8. 高级技巧与面试常见问题8.1 处理模型“幻觉”与字段缺失即使格式正确模型生成的内容也可能不符合要求幻觉或缺失字段。解决方案在Schema/模型定义中使用Field(…, description“”)提供清晰、无歧义的字段描述。设置required字段在JSON Schema或Pydantic中明确必填字段。使用max_retriesInstructor支持让框架自动重试。后验证与默认值from pydantic import BaseModel, Field, validator class User(BaseModel): name: str age: int Field(default0, ge0, le120) # 设置默认值和范围 email: Optional[str] None validator(‘email’) def validate_email_format(cls, v): if v is not None and “” not in v: # 可以尝试修复或引发错误 # 或者 return a default like “unknownexample.com” raise ValueError(‘invalid email format’) return v8.2 流式输出中的JSON处理当使用流式响应Streaming时不能等完整响应回来再解析。策略对于函数调用OpenAI的流式响应中tool_calls的arguments是一个增量生成的字符串。你需要自己拼接这些Delta并在收到结束信号后尝试解析。使用专门模式有些服务或框架提供了“流式JSON”模式如response_format{“type”: “json_object”}在某些模型上支持流式输出JSON的每个键值对。8.3 大模型面试高频考点如何保证大模型输出JSON的稳定性标准答案采用多层保障策略。首选使用模型的函数调用Function Calling功能通过预定义严格的JSON Schema来约束输出其次可以使用像Instructor这样的库通过Pydantic模型声明数据结构并利用其自动重试机制。同时必须编写健壮的后处理解析函数以处理模型可能输出的非纯JSON文本如Markdown包装。在Prompt设计上要使用清晰、强制的指令并结合少样本示例。函数调用Function Calling的原理是什么模型并不直接执行函数。开发者预先定义好工具函数的列表及其参数Schema。当用户输入与某个工具的描述匹配时模型会输出一个特殊的结构化消息表明它“想要调用”某个函数并生成一个符合该函数Schema的JSON参数。开发者收到这个请求后在自己的代码中真正执行对应的函数。如果模型就是不生成有效JSON怎么办降级策略首先记录错误并重试2-3次。如果仍然失败可以回退到一个更简单、约束更强的Prompt。最终手段是向用户返回一个友好的错误信息并提示其重新表述请求或转为人工处理。监控与迭代收集所有失败的案例分析原因。是Prompt不清晰Schema太复杂还是模型能力不足根据分析结果优化你的Schema和Prompt。如何设计一个用于提取信息的Pydantic模型从核心实体开始使用明确的字段名和类型str,int,List,Optional。为每个字段添加Field(…, description“”)描述要具体、无歧义。使用嵌套模型BaseModel组织复杂关系。利用枚举Enum约束字段的取值范围。为可选字段设置合理的默认值如None。添加验证器validator进行业务逻辑校验。9. 完整实战案例构建一个稳定的用户信息提取Agent让我们综合运用以上知识构建一个从一段自由文本中提取用户信息并存入模拟数据库的简单Agent。# user_info_agent.py import instructor from openai import OpenAI from pydantic import BaseModel, Field, validator from typing import List, Optional import json import sqlite3 from datetime import datetime # --- 1. 定义数据结构 --- class Address(BaseModel): street: Optional[str] None city: Optional[str] None country: str “中国” # 默认值 class UserInfo(BaseModel): “”“从文本中提取出的单个用户信息”“” name: str Field(…, description“用户的全名”) age: Optional[int] Field(None, ge0, le120, description“用户年龄如果没有明确提及则为None”) email: Optional[str] Field(None, description“邮箱地址”) phone: Optional[str] Field(None, description“手机号码”) address: Optional[Address] None source_text_snippet: str Field(…, description“原文中提及该用户的那部分文本”) validator(‘email’) def email_contains_at(cls, v): if v is not None and “” not in v: # 简单校验生产环境应用更复杂的正则 raise ValueError(‘Email must contain ’) return v class ExtractedData(BaseModel): “”“提取任务的总结果”“” users: List[UserInfo] raw_text_summary: str Field(…, description“对原始文本的简要总结”) # --- 2. 修补客户端并定义提取函数 --- client instructor.patch(OpenAI()) def extract_users_from_text(text: str) - ExtractedData: “””核心提取函数使用Instructor”“” prompt f“”” 请从以下文本中提取所有提到的用户信息。 文本内容 “”{text}”” 请仔细识别文中提到的每一个人并尽可能提取他们的姓名、年龄、联系方式邮箱或电话和住址信息。 如果某些信息如年龄、邮箱没有明确提及请将其设为null。 请确保“source_text_snippet”字段准确记录原文中描述该用户的句子或片段。 最后请用一句话总结原始文本的主要内容填入“raw_text_summary”。 “”” try: extracted client.chat.completions.create( model“gpt-4-turbo-preview”, # 复杂信息提取建议用更强模型 messages[{“role”: “user”, “content”: prompt}], response_modelExtractedData, max_retries3, temperature0, ) return extracted except Exception as e: print(f“信息提取失败: {e}”) # 返回一个空的提取结果作为降级 return ExtractedData(users[], raw_text_summary“提取失败”) # --- 3. 模拟数据库存储 --- def init_database(): conn sqlite3.connect(‘:memory:’) # 内存数据库方便演示 cursor conn.cursor() cursor.execute(“”” CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, email TEXT, phone TEXT, address_street TEXT, address_city TEXT, address_country TEXT, source_snippet TEXT, extracted_at TIMESTAMP, raw_summary TEXT ) “””) conn.commit() return conn def save_to_database(conn, data: ExtractedData): cursor conn.cursor() for user in data.users: cursor.execute(“”” INSERT INTO users (name, age, email, phone, address_street, address_city, address_country, source_snippet, extracted_at, raw_summary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) “””, ( user.name, user.age, user.email, user.phone, user.address.street if user.address else None, user.address.city if user.address else None, user.address.country if user.address else “中国”, user.source_text_snippet, datetime.now().isoformat(), data.raw_text_summary )) conn.commit() print(f“成功保存 {len(data.users)} 条用户记录到数据库。”) # --- 4. 主程序 --- if __name__ “__main__”: # 模拟输入文本 input_text “”” 在我们的项目团队中张三25岁主要负责后端开发他的联系邮箱是zhangsancompany.com。 李四来自北京住在朝阳区建国路123号她今年30岁了电话是13800138000。 还有一位同事王五他的邮箱是wangwuexample.org常驻上海。 最近我们招募了实习生赵六他今年22岁。 “”” print(“原始文本”) print(input_text) print(“\n” “”*50 “\n”) # 初始化数据库 db_conn init_database() # 执行提取 print(“正在使用大模型提取用户信息…”) result extract_users_from_text(input_text) print(“\n提取结果”) print(result.model_dump_json(indent2, ensure_asciiFalse)) # 保存到数据库 if result.users: save_to_database(db_conn, result) # 查询验证 cursor db_conn.cursor() cursor.execute(“SELECT name, age, email, phone FROM users”) saved_users cursor.fetchall() print(“\n数据库中保存的用户”) for user in saved_users: print(user) else: print(“未提取到用户信息。”) db_conn.close()这个案例展示了从定义数据结构、使用Instructor稳定提取、数据后验证到持久化存储的完整流程是一个生产可用Agent的简化原型。10. 总结与最佳实践清单要确保大模型稳定输出JSON没有银弹而是一套组合拳。以下是贯穿整个开发周期的核心实践设计阶段明确需求首先彻底厘清你需要的数据结构。使用工具如pydantic或JSON Schema进行正式定义。选择正确工具对于Agent工具调用用原生函数调用对于复杂数据提取用InstructorPydantic。开发阶段强化Prompt在系统或用户消息中明确要求“只输出JSON”并提供清晰示例。将temperature设为较低值如0-0.3。利用API特性如果适用务必使用response_format{ “type”: “json_object” }参数。实现健壮解析编写一个robust_json_parse函数作为处理模型原始输出的安全网。测试与监控阶段全面测试使用包含边缘案例缺失字段、格式污染、怪异字符的文本测试你的流程。实施重试机制对于非关键任务配置2-3次自动重试如Instructor的max_retries。记录失败案例所有解析失败的请求都应记录其原始Prompt和模型输出用于后续分析和Prompt/Schema优化。生产部署阶段设置超时与降级为LLM调用设置合理超时并规划好降级策略如返回空值、使用缓存、触发人工流程。监控关键指标跟踪JSON解析成功率、字段填充率、模型调用延迟和成本。持续迭代大模型能力和最佳实践在快速演进定期回顾和更新你的Prompt、Schema及后处理逻辑。稳定、可靠的结构化输出是将大模型从“玩具”变为“生产工具”的关键一步。通过本文介绍的多层策略你可以显著提升AI应用的数据交互可靠性为构建复杂的智能体系统和自动化流程打下坚实基础。

相关新闻