ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

【三个月 AI Agent 实战学习】Day 6:结构化输出(JSON)—— 让模型与代码无缝协作

【三个月 AI Agent 实战学习】Day 6:结构化输出(JSON)—— 让模型与代码无缝协作 Day 6 详细展开结构化输出JSON—— 让模型与代码无缝协作欢迎来到第六天在前几天我们学习了如何让模型回答问题、如何让它一步步思考。但在构建 AI Agent 时模型往往不是最终的执行者——它需要把“意图”转化为结构化的数据如 JSON交给程序去执行如调用 API、查询数据库。因此强制模型输出合法 JSON 并稳定解析是 Agent 开发的一项基本功。一、今日学习目标学会通过系统提示System Prompt和用户提示User Prompt强制模型输出 JSON 格式。掌握如何处理模型输出中的常见干扰如 Markdown 代码块标记、额外文字、单引号等。编写一个健壮的 JSON 提取与解析函数能够从模型的原始输出中提取出合法 JSON。实现一个带重试机制的调用流程如果 JSON 解析失败自动重新请求并附带错误反馈指导模型修正。理解结构化输出在 Agent 工具调用中的重要性——模型输出的 JSON 参数将直接用于函数调用。二、详细实现步骤步骤 1设计一个需要结构化输出的任务我们选择“情感分析”作为示例任务输入一段用户评论让模型分析情感倾向正面/负面/中性并提取关键词最终以 JSON 对象返回。这个 JSON 对象包含两个字段sentiment字符串和keywords字符串数组。新建json_output_demo.pyimportosimportjsonimportreimporttimefromdotenvimportload_dotenvfromopenaiimportOpenAI load_dotenv()clientOpenAI(api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com)defanalyze_sentiment(text:str)-str:调用模型进行情感分析返回原始字符串system_prompt你是一个情感分析助手。请分析用户评论的情感倾向并提取其中的关键词。user_promptf 请分析以下评论的情感倾向正面/负面/中性并提取关键词。 要求必须只输出一个 JSON 对象格式如下 {{sentiment: 正面/负面/中性, keywords: [关键词1, 关键词2, ...]}} 不要输出任何其他文字。 评论{text}responseclient.chat.completions.create(modeldeepseek-chat,messages[{role:system,content:system_prompt},{role:user,content:user_prompt}],temperature0.1,# 低温度保证格式稳定max_tokens200)returnresponse.choices[0].message.content# 测试test_comment这家餐厅的菜味道很棒服务也很周到就是价格稍微有点贵。raw_outputanalyze_sentiment(test_comment)print( 原始输出 )print(raw_output)运行脚本python json_output_demo.py你会看到模型大概率会输出一个干净的 JSON但有时也会出现以下情况被包裹在 Markdown 代码块中 json\n{…}\n前面多了一句“好的分析结果如下”使用了单引号{sentiment: 正面, ...}Python 风格不是合法 JSON这些都是我们需要在代码中处理的。步骤 2编写健壮的 JSON 提取函数我们不能假设模型输出的就是纯 JSON必须编写一个函数来“清洗”并解析它。这个函数需要去除首尾空白。如果以 开头则剥离代码块标记包括json语言标识。尝试用json.loads解析。如果失败尝试用正则从文本中提取{...}部分贪婪匹配再解析。如果仍失败返回None或抛出异常。在json_output_demo.py中追加defextract_json(text:str):从模型输出中提取并解析 JSON 对象texttext.strip()# 去除 Markdown 代码块标记iftext.startswith():texttext.strip()# 移除可能的语言标识如 jsoniftext.startswith(json):texttext[4:]texttext.strip()# 尝试直接解析try:returnjson.loads(text)exceptjson.JSONDecodeError:pass# 如果直接解析失败尝试用正则提取第一个 {...} 或 [...] 块# 这里主要针对对象matchre.search(r\{.*\},text,re.DOTALL)ifmatch:try:returnjson.loads(match.group(0))exceptjson.JSONDecodeError:pass# 还可以尝试替换单引号为双引号但要小心字符串中的单引号这里简单处理try:cleanedtext.replace(,)returnjson.loads(cleaned)exceptjson.JSONDecodeError:returnNone# 测试提取函数parsedextract_json(raw_output)print(\n解析结果:,parsed)print(类型:,type(parsed))ifparsed:print(情感:,parsed.get(sentiment))print(关键词:,parsed.get(keywords))步骤 3实现带重试机制的调用即使有了提取函数有时模型仍然输出无法解析的内容例如完全跑题、输出成数组等。这时我们可以在代码里自动重试重新调用模型并在提示中加入上一次的错误信息要求模型修正。这类似于“自我纠正”的简单实现。在json_output_demo.py中继续添加defanalyze_sentiment_with_retry(text:str,max_retries:int2)-dict:带重试的情感分析返回解析后的字典或 Noneretries0whileretriesmax_retries:rawanalyze_sentiment(text)# 每次重新调用parsedextract_json(raw)ifparsedisnotNone:returnparsed retries1# 简单等待避免请求过快time.sleep(1)# 可以在下一次调用时修改 Prompt但这里我们直接再次调用因为模型可能随机改变输出# 更好的做法是传入错误信息但为了简单我们直接重试returnNone# 测试重试机制可以故意用一个复杂评论观察是否触发重试resultanalyze_sentiment_with_retry(test_comment)print(\n最终结果:,result)在实际应用中你可以把上一次解析失败的信息附加到新的 Prompt 中例如“你上一次输出的格式不合法无法用 Python 的 json.loads 解析。请务必只输出一个合法 JSON 对象不要包含其他内容。”这样模型会更容易修正。步骤 4进一步稳定输出 —— 使用 JSON Mode如果支持很多模型 API包括 DeepSeek支持JSON Mode在请求中设置response_format{type: json_object}API 会强制模型输出 JSON并拒绝非 JSON 的返回或自动截断。DeepSeek 的deepseek-chat模型是否支持查阅文档发现DeepSeek 目前不支持response_format参数所以不能依赖 JSON Mode。但其他一些模型如 OpenAI 的 gpt-4-turbo 等支持。我们可以了解一下这个方法未来可能用到。由于 DeepSeek 不支持我们仍需依赖 Prompt 和解析。不过我们可以模拟使用 JSON Mode 的思路在 System Prompt 中极度强调 JSON 格式并降低 temperature 到 0。这通常能获得很高的成功率。步骤 5测试多个输入为了验证稳定性可以定义几个不同的评论循环调用并输出解析结果comments[这个产品质量太差了用了两次就坏了差评,物流很快包装完好但是产品本身一般。,客服态度真好耐心帮我解决了问题赞,总体来说还行吧没有惊喜也没有失望。]forcincomments:resanalyze_sentiment_with_retry(c)print(f\n评论:{c})print(f结果:{res})运行后观察解析成功率应该很高如果有失败注意看原始输出是什么思考如何改进提取函数或重试策略。三、常见问题与调试Q1模型输出的 JSON 字段顺序不稳定或者有时多出字段怎么办→ 字段顺序不影响 JSON 解析只要字段名和类型正确即可。多出字段一般不会导致json.loads失败我们可以在解析后只取需要的字段。如果希望严格控制可以在 Prompt 中说明“只包含这两个字段不要增加其他字段”。Q2模型输出的字符串中包含换行符或特殊字符导致提取失败。→ 使用re.DOTALL让.匹配换行符同时正则中\{.*\}是贪婪匹配应该能匹配完整的对象。如果字符串值中包含}字符贪婪匹配可能出错这种情况相对罕见可以忽略或改进正则。Q3重试几次后仍然失败怎么办→ 可以增加重试次数或者回退到更简单的输出格式如让模型只输出情感标签用简单字符串解析。在工程中通常设置最多 2-3 次重试然后返回默认值或抛异常记录日志。Q4是否有必要使用结构化输出的库如 Pydantic→ 后续在 LangChain 中我们会使用PydanticOutputParser它封装了类似的重试和格式引导逻辑可以简化开发。但理解底层原理仍然很重要因为当框架不满足需求时你需要自己写。Q5DeepSeek 的 JSON 输出稳定性如何→ 总体来说还不错配合低温度和明确的 Prompt解析成功率在 90% 以上。但仍然存在失败的可能所以需要重试机制。四、今日总结与作业今天你完成了✅ 学会了通过 Prompt 设计强制模型输出 JSON。✅ 编写了健壮的 JSON 提取函数能够处理常见的输出干扰。✅ 实现了带重试机制的调用流程提高了整体稳定性。✅ 认识到结构化输出是 Agent 与代码协作的桥梁。今日作业必做将今天的情感分析任务改成一个“信息抽取”任务输入一段新闻提取其中的人名、组织名、地点、日期输出为 JSON 对象包含这几个字段都是数组。编写完整的调用、解析、重试代码并测试 3 条新闻。尝试故意构造一个让模型输出格式错误的场景例如在 Prompt 中允许模型添加解释然后观察你的提取和重试机制是否能够挽救。思考题在 Agent 系统中模型需要输出工具调用的参数 JSON。如果参数结构很复杂如嵌套对象你会如何设计 Prompt 和解析机制请用文字描述你的方案。明日预告我们将进行第一周的阶段总结整合前几天的知识完成一个综合小项目——“全自动周报生成器”。请准备好前几天的代码和笔记明天我们会用到。有任何问题欢迎随时提问
返回列表