尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Day3:结构化输出
在LangChain中聊天模型提供了额外的功能:结构化输出。一种使聊天模型以结构化格式(例如JSON)进行响应的技术。例如可能希望将模型输出存储在数据库中并确保输出符合数据库模式。这种需求激发了结构化输出的概念其中可以指示模型使用特定的输出结构进行响应。这样做的核心原因是:从“字符串”到“对象”的范式转换。想象一下在没有这个功能之前我们调用聊天模型得到的是一个AIMessage其内容是一个字符串。model ChatOpenAI() response model.invoke(告诉我关于苹果公司的最新消息。) print(response.content) # 输出: 苹果公司于昨⽇发布了新款iPhone...其股价上涨了2%...这个字符串对人类很友好但对程序不友好。如果我们想从这段文本中提取出“公司名”和“股价变化”并用在后续逻辑中则需要编写复杂且容易出错的解析代码。聊天模型的with_structured_output 方法则允许我们预先定义一个期望的数据结构并要求大模型必须按照这个结构返回信息。一、定义输出结构with_structured_output()要想使用结构化输出能力LangChain提供了一种方法.with_structured_output()该方法需要先定义输出结构然后执行通过 .with_structured_output()得到的Runnable实例。步骤如下(伪代码)代码块#1.定义输出结构 schema {foo:bar} #2.绑定schema其实是生成支持结构化返回的 Runnable 实例 model_with_structure model.with_structured_output(schema) #3.执行 structured_output model_with_structure.invoke(user_input)这是获得结构化输出的最简单、最可靠的方法。此方法将输出结构作为参数输入返回一个类似model的Runnable。不同之处在于执行Runnable后的输出结果输出的不是字符串或消息而是输出与给定输出结构相对应的对象。该 输出结构 可以指定为 TypedDict 类、JSON Schema或 Pydantic类。如果使用 TypedDict 或JSON Schema则Runnable将返回一个字典如果使用 Pydantic类则将返回一个Pydantic对象。参数解析# with_structured_output() 方法定义: def with_structured_output( schema: dict[str, Any] | type[_BM] | type | None None, *, method: Literal[function_calling, json_mode, json_schema] json_schema, include_raw: bool False, strict: bool | None None, **kwargs: Any, ) - Runnable[PromptValue | str | Sequence[BaseMessage | list[str] | tuple[str, str] | str | dict[str, Any]], dict | _BM]:schema支持三种类型dict[str, Any]手写 JSON Schema 字典type[_BM]Pydantic 模型类最常用自动提取 schemaNone不指定极少用methodjson_schema默认原生支持 JSON Schema推荐现代模型OpenAI gpt-4o、DeepSeek 等function_calling用工具调用function call机制返回结构化数据兼容性更广老模型首选json_mode仅强制返回合法 JSON 字符串不会校验字段是否符合 schema弱约束include_raw: bool FalseFalse默认直接返回解析好的 Pydantic 对象 /dictTrue返回字典包含raw模型原始返回、parsed解析后对象、parsing_error解析异常信息方便调试strict: bool | None None开启严格模式严格校验 JSON Schema不允许额外字段。OpenAI 的json_schema模式专用。strictTrue不允许输出 schema 以外的字段strictFalse允许附加额外字段默认为None则不会将strict参数传递给模型。返回值解析该方法返回RunnableLangChain 可运行组件可以放进 LCEL 管道调用.invoke()执行。将来执行时如果include_rawFalse默认且 schema是Pydantic类 则 Runnable 会输出 Pydantic 对象。 否则 Runnable 输出一个字典。如果 include_rawTrue调试模式则 Runnable 输出一个带有键的字典raw BaseMessage对象大模型原始完整返回消息里面包含原始 content /tool_callsparsed 如果出现解析错误则为 None否则类型取决于如上所述的schema。parsing_error Optional[BaseException]1.1 返回 Pydantic 对象我们可以设置执行Runnable后的输出结果指定为Pydantic类这将返回一个Pydantic对象。当收到模型的响应后LangChain会提取出代表Pydantic参数的 JSON对象并用Pydantic模型对其进行解析和验证将这个验证后的JSON转换为一个可用的Pydantic对象实例返回。如下所示:# 定义⼤模型 model ChatOpenAI(modelgpt-4o-mini) # 定义输出结构Pydantic 类 class Joke(BaseModel): 给⽤⼾讲⼀个笑话。 setup: str Field(description这个笑话的开头) punchline: str Field(description这个笑话的妙语) rating: Optional[int] Field(defaultNone, description从1到10分给这个笑话评分) structured_model model.with_structured_output(Joke) result structured_model.invoke(给我讲⼀个关于唱歌的笑话) print(result) #输出 setup今天给你讲一个关于唱歌的笑话 punchline小明在KTV唱歌唱到一半朋友突然冲过来把麦克风抢走了。小明很委屈“我唱得好好的你干嘛抢我麦”朋友说“你唱得是挺好但你再唱下去隔壁就要报警了。”小明不服“为什么”朋友说“因为你唱的是《忐忑》人家以为咱们在拆迁。” rating7还支持嵌套输出class Joke(BaseModel): 给⽤⼾讲⼀个笑话。 setup: str Field(description这个笑话的开头) punchline: str Field(description这个笑话的妙语) rating: Optional[int] Field(defaultNone, description从1到10分给这个笑话评分) class Data(BaseModel): 获取关于笑话的数据。 jokes: List[Joke] structured_model model.with_structured_output(Data) result structured_model.invoke(分别讲⼀个关于唱歌和跳舞的笑话) print(result) #jokes[ # Joke(setup为什么唱歌比赛里麦克风总是一脸委屈, punchline因为它不是被跑调就是被高音“暴击”。, rating8), # Joke(setup为什么跳舞的地板从来不抱怨, punchline因为它早就习惯了被“踩”出节奏感。, rating8) #]1.2 返回 TypedDict什么叫TypedDictTypedDict 是 Python 类型提示工具专门给字典做类型标注用来声明字典里有哪些 key、每个 key 对应的值类型。⚠️重点它只是静态类型提示运行时不会做校验和 Pydantic BaseModel 不一样定义TypedDict最清晰、最常用的定义方式就是类似于定义一个类Python 3.8from typing import TypedDict class User(TypedDict): name: str age: int email: str is_active: bool True # 默认值这个写法代表该字段可选对于它非常重要的一个能力就是捕捉键名拼写错误与类型错误。# 类型检查器会捕获这些错误 bad_user: User { name: Dave, age: forty, # 错误应该是int emial: daveexample.com # 错误拼写错误 }因此我们也可以设置执行Runnable后的输出结果指定为TypedDict类这将返回一个字典且输出后会根据设定进行验证。# 定义输出结构 TypedDict class Joke(TypedDict): 给⽤⼾讲⼀个笑话。 setup: Annotated[str, ..., 这个笑话的开头] punchline: Annotated[str, ..., 这个笑话的妙语] rating: Annotated[Optional[int], None, 从1到10分给这个笑话评分] structured_model model.with_structured_output(Joke) result structured_model.invoke(给我讲⼀个关于唱歌的笑话) print(result) # {setup: 为什么那个歌手唱歌时总是喜欢闭着眼睛, # punchline: 因为他不想看到观众捂耳朵的样子。, # rating: 7}让我们加⼊include_rawTrue再来看看效果structured_model model.with_structured_output(Joke, include_rawTrue) #{raw: AIMessage(content{\n setup: 小明去KTV唱歌为什么全场都给他鼓掌,\n punchline: 因为他终于唱完了。,\n rating: 7\n}, additional_kwargs{parsed: None, refusal: None}, response_metadata{token_usage: {completion_tokens: 158, prompt_tokens: 184, total_tokens: 342, completion_tokens_details: {accepted_prediction_tokens: None, audio_tokens: None, reasoning_tokens: 112, rejected_prediction_tokens: None, text_tokens: 158}, prompt_tokens_details: {audio_tokens: None, cache_write_tokens: None, cached_tokens: 0, text_tokens: 184}}, model_name: qwen3.8-max, system_fingerprint: None, id: chatcmpl-f0a6d0b2-91a6-919c-a1d9-8dd93fbf29f0, service_tier: None, finish_reason: stop, logprobs: None}, idrun--01a13474-b56a-7d71-81e4-b67253dba6d6-0, usage_metadata{input_tokens: 184, output_tokens: 158, total_tokens: 342, input_token_details: {cache_read: 0}, output_token_details: {reasoning: 112}}), parsed: {setup: 小明去KTV唱歌为什么全场都给他鼓掌, punchline: 因为他终于唱完了。, rating: 7}, parsing_error: None}可以看到 include_rawTrue返回字典包含raw模型原始返回、parsed解析后对象、parsing_error解析异常信息方便调试1.3返回 JSON还可以让聊天模型直接返回JSON只不过为了声明JSON我们需要定义JSON schemajson_schema { title: joke, description: 给⽤⼾讲⼀个笑话。, type: object, properties: { setup: { type: string, description: 这个笑话的开头, }, punchline: { type: string, description: 这个笑话的妙语, }, rating: { type: integer, description: 从1到10分给这个笑话评分, default: None, }, }, required: [setup, punchline], } structured_model model.with_structured_output(json_schema) result structured_model.invoke(给我讲⼀个关于唱歌的笑话) print(result) #{setup: 小明问老师“我唱歌怎么样”, punchline: 老师说“你唱得很投入就是调没跟上。”, rating: 7}1.4 有选择的输出创建具有联合类型属性Union的父模式以使⽤ Pydantic 为例其他同理class Joke(BaseModel): 给⽤⼾讲⼀个笑话。 setup: str Field(description这个笑话的开头) punchline: str Field(description这个笑话的妙语) rating: Optional[int] Field(defaultNone, description从1到10分给这个笑话评分) class ConversationalResponse(BaseModel): 以对话的⽅式回应。待⼈友善乐于助⼈。 response: str Field(description对⽤⼾查询的会话响应) class FinalResponse(BaseModel): final_output: Union[Joke, ConversationalResponse] structured_model model.with_structured_output(FinalResponse) result structured_model.invoke(给我讲⼀个关于唱歌的笑话) print(result) #final_outputJoke(setup为什么歌⼿总是带着梯⼦, punchline因为他们想要达到更⾼的⾳调, rating7) result structured_model.invoke(你好) print(result) #final_outputConversationalResponse(response你好很高兴见到你。有什么我可以帮你的吗)二、 结构化输出——实用场景2.1作为信息提取器class Person(BaseModel): ⼀个⼈的信息。 # 注意: # 1. 每个字段都是 Optional “可选的” —— 允许 LLM 在不知道答案时输出 None。 # 2. 每个字段都有⼀个 description “描述” —— LLM使⽤这个描述。 # 有⼀个好的描述可以帮助提⾼提取结果。 name: Optional[str] Field(defaultNone, description这个⼈的名字) hair_color: Optional[str] Field(defaultNone, description如果知道这个⼈头发的颜⾊) skin_color: Optional[str] Field(defaultNone, description如果知道这个⼈的肤⾊) height_in_meters: Optional[str] Field(defaultNone, description以⽶为单位的⾼度) structured_model model.with_structured_output(Person) messages [ SystemMessage(content你是⼀个提取信息的专家只从⽂本中提取相关信息。如果您不知道要提取的属性的值属性值返回null), HumanMessage(content史密斯⾝⾼6英尺⾦发。) ] result structured_model.invoke(messages) print(result) #name史密斯 hair_color⾦发 skin_colorNone height_in_meters1.832.2与工具结合使用注意使用聊天模型原生的工具搭配结构化输出并不好用!!!这里只是了解下其能力。更好的用法见LangGraph Agent 能力。2.2.1 方式1:使用 with_structured_output()的tools参数# 结构输出对象 class SearchResult(BaseModel): 结构化搜索结果。 query: str Field(description搜索查询) findings: str Field(description调查结果摘要) tool def web_search(query: str) - str: 在⽹上搜索信息。 Args: query: 搜索查询 return 西安今天多云转⼩⾬⽓温18-23度东南⻛2级空⽓质量良好 structured_search_model model.with_structured_output( SearchResult, tools[web_search], strictTrue, include_rawTrue, ) result structured_search_model.invoke(搜索当前最新的西安的天⽓) print(result) #{raw: AIMessage(content, additional_kwargs{tool_calls: [{id: call_c1543b34d5b04fdaa5d1e188, function: {arguments: {query: 西安天气 今天 最新预报}, name: web_search, parsed_arguments: {query: 西安天气 今天 最新预报}}, type: function, index: 0}, {id: call_bf6b4e9d546549489c3a0857, function: {arguments: {query: 西安 未来三天天气预报 气温}, name: web_search, parsed_arguments: {query: 西安 未来三天天气预报 气温}}, type: function, index: 1}], parsed: None, refusal: None}, response_metadata{token_usage: {completion_tokens: 88, prompt_tokens: 343, total_tokens: 431, completion_tokens_details: {accepted_prediction_tokens: None, audio_tokens: None, reasoning_tokens: 22, rejected_prediction_tokens: None, text_tokens: 88}, prompt_tokens_details: {audio_tokens: None, cache_write_tokens: None, cached_tokens: 0, text_tokens: 343}}, model_name: qwen3.8-max, system_fingerprint: None, id: chatcmpl-757a1f1b-a6d7-9525-a82d-6803e6e7c850, service_tier: None, finish_reason: tool_calls, logprobs: None}, idrun--01a13489-48fa-7e10-a82c-f1e8a96e2a2f-0, tool_calls[{name: web_search, args: {query: 西安天气 今天 最新预报}, id: call_c1543b34d5b04fdaa5d1e188, type: tool_call}, {name: web_search, args: {query: 西安 未来三天天气预报 气温}, id: call_bf6b4e9d546549489c3a0857, type: tool_call}], usage_metadata{input_tokens: 343, output_tokens: 88, total_tokens: 431, input_token_details: {cache_read: 0}, output_token_details: {reasoning: 22}}), # parsed: None, # parsing_error: None}可以看到它并不能直接帮我们输出想要的SearchResult搜索结果而是只返回了AIMessage(工具调用信息)实际上with_structured_output方法只是让模型知道有哪些工具可以调用但是并不会自动执行工具。要获得工具执行后的结果并整合到最终的结构化输出中我们需要手动处理工具调用然后将工具返回的结果再次传递给模型。见方式2。2.2.2 方法2拆解能力单独依次执行当需要同时使用结构化输出和其他工具时需要注意顺序不要弄反:1.首先绑定工具2.其次添加结构化输出# 结构输出对象 class SearchResult(BaseModel): 结构化搜索结果。 query: str Field(description搜索查询) findings: str Field(description调查结果摘要) tool def web_search(query: str) - str: 在⽹上搜索信息。 Args: query: 搜索查询 # 模拟搜索返回结果 return 西安今天多云转⼩⾬⽓温18-23度东南⻛2级空⽓质量良好 # ⼀次性绑定⼯具和结构化输出 model_with_tools model.bind_tools([web_search]).with_structured_output(SearchResult) # 执⾏查询 messages [HumanMessage(搜索当前最新的西安的天⽓)] result model_with_tools.invoke(messages) print(result) # query西安的天⽓ findings当前西安的天⽓情况预计多云⽓温在18°C到25°C之间有轻微的⻛空⽓湿度适中⽆降⽔。从打印结果可以发现它也没有去调用工具。而是聊天模型直接给出了结果。让我们改造下# 结构输出对象 class SearchResult(BaseModel): 结构化搜索结果。 query: str Field(description搜索查询) findings: str Field(description调查结果摘要) tool def web_search(query: str) - str: 在⽹上搜索信息。 Args: query: 搜索查询 # 模拟搜索返回结果 return 西安今天多云转⼩⾬⽓温18-23度东南⻛2级空⽓质量良好 # ⼿动将⼯具结果加⼊消息列表 model_with_tool model.bind_tools([web_search]) messages [ HumanMessage(搜索当前最新的西安的天⽓) ] ai_msg model_with_tool.invoke(messages) messages.append(ai_msg) # ⼿动将ToolMessahe加⼊消息列表 for tool_call in ai_msg.tool_calls: tool_msg web_search.invoke(tool_call) messages.append(tool_msg) structured_search_model model_with_tool.with_structured_output(SearchResult) result structured_search_model.invoke(messages) print(result) #query西安天⽓ findings西安今天多云转⼩⾬⽓温18-23度东南⻛2级空⽓质量良好。这次的结果符合我们的预期。但依旧很麻烦因为其实我们实际上调用了两次模型:第一次调用模型时模型返回了工具调用然后我们执行工具将工具结果加入消息列表。第二次调用时我们使用结构化输出SearchResult的模型来处理整个消息列表(包括工具调用的结果)然后输出结构化结果。有没有只手动调用一次(实际上允许调用两次模型)就能获取到结构化输出的结果呢?将来在学习LangGraph部分的Agent 就可以做到这件事。
RELATED

相关推荐

API 权限验收:用 Python 检查白名单配置,再验证允许与拒绝

API 权限验收:用 Python 检查白名单配置,再验证允许与拒绝

一个服务从门户目录消失后,持有旧链接或 API 凭证的程序还能不能调用它?只看页面,回答不了这个问题。目录控制的是展示;凭证的调用范围,需要由服务端鉴权并通过受控请求验证。 本文给出一个只依赖 Python 标准库的离线…

📅 2026/10/15 4:02:51
离散元分析(DEM)和机器学习(Machine Learning, ML)

离散元分析(DEM)和机器学习(Machine Learning, ML)

文章目录1. 机器学习辅助2PFV-DEM参数标定2. GNN DEM:最值得作为创新点3. AI预测非饱和土土水特征曲线4. 深度学习识别SEM/MIP细观结构 → DEM胶结模型5. 论文最推荐的AI路线第一阶段第二阶段第三阶段(创新)第四阶段最终可以形成的论文题目方…

📅 2026/10/15 3:57:51
第五章 检索增强与知识库

第五章 检索增强与知识库

前面四章已经让 Agent 具备了工具调用、多步规划、错误恢复和记忆能力。但它还有一个重要限制:模型只能依靠训练数据和当前上下文回答问题。如果用户问的是公司内部文档、产品手册、私有知识库,或者最近刚发生的事件,模型就可能答不上来&…

📅 2026/10/15 3:57:51
MORE NEWS

更多资讯

📰

查询销帮帮产品分类并落入轻易云集成平台:单策略同步实战教程

这个策略解决什么问题在零售与分销一体化的场景里,产品分类是后续商品同步、库存口径、销售统计的「地基」。一次实际项目中,某零售企业同时使用聚水潭管商品、用销帮帮管产品主数据与销售流程。最早他们让两边各自维护一份分类表,结果三个月…

📰

阿里云ChatOps Agent实战:非标环境下Docker容器恢复与overlay2修复

1. 一次内核漏洞引发的容器雪崩现场事情发生在去年冬天的一个凌晨。我负责的一套跑在阿里云ECS上的业务系统突然告警,监控面板上十几个Docker容器的健康检查全部飘红。登录机器一看,docker ps卡住不动,systemctl status docker显示守护进程反…

📰

GanttProject Colloboque 实时协作服务器:从构建、运行到同步协议解析

桌面应用企业应用 【免费下载链接】ganttproject Official GanttProject repository. 项目地址: https://gitcode.com/gh_mirrors/ga/ganttproject 点击查看 免费下载 导读 本文围绕 GanttProject 仓库中的 cloud.ganttproject.colloboque/README.md 展开&#xf…

📰

游戏时长谁说了算?服务器权威时间与客户端对时机制拆解

一局游戏的时长,凭什么信客户端说的?前阵子一个做竞技游戏的朋友跟我吐槽,说他们测试群里有人反馈:"对局结束界面显示时长 32 分 41 秒,可我自己录屏回看明明打了 41 分钟,这游戏是不是在骗人&#xf…

📰

基于YOLOv8的路面裂缝检测系统完整实现指南

春节期间我们单位接到一个紧急任务:对辖区内某条主干道进行路面破损巡检。我分到的活儿是整理近两千张现场照片,全部靠肉眼辨别裂缝、坑槽和修补区域。两天盯下来,眼睛酸得睁不开,表格里还有几十条漏标。那会儿我就下了决心&#…

📰

AI如何通过Mod读懂游戏设计:差分信号与数据管线的价值

前几天看到一条融资消息:一家叫不上名字的新团队拿了数千万人民币规模的投资,核心就一句话——让AI通过Mod去理解游戏设计。这个切入点让我盯了好一会儿。圈内聊AI游戏,聊的无非是AI生成关卡、AI陪玩、AI做NPC对话,这套"拿Mo…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬