LangGraph中的输出范式
如果把普通大模型(LLM)的输出比作“一张便签”——只写着最终答案,那么LangGraph的输出更像“一份带流程的工作交接单”:不仅要写清当前任务的结果,还得告诉下一个环节“该谁接手、做什么”。为什么会这样?因为LangGraph本质是个“团队协作框架”:整个工作流由多个“节点”(就像团队里的不同角色,比如“信息收集员”“分析员”“决策者”)组成,节点之间靠“图结构”(类似于团队分工流程图)连接,
https://blog.csdn.net/brucexia/article/details/158845706
如果把普通大模型(LLM)的输出比作“一张便签”——只写着最终答案,那么LangGraph的输出更像“一份带流程的工作交接单”:不仅要写清当前任务的结果,还得告诉下一个环节“该谁接手、做什么”。
为什么会这样?因为LangGraph本质是个“团队协作框架”:整个工作流由多个“节点”(就像团队里的不同角色,比如“信息收集员”“分析员”“决策者”)组成,节点之间靠“图结构”(类似于团队分工流程图)连接,谁先做、谁后做、遇到特殊情况转交给谁,都有明确规则。这种情况下,单个节点的输出如果只是一句零散的话,下一个节点根本不知道该怎么接——就像市场部交了一份“客户好像喜欢红色”的便签,产品部既不知道这个结论怎么来的,也不知道接下来该设计红色包装还是红色广告。
因此,LangGraph的输出必须是“结构化的”,得同时干以下两件事。
- 传内容:把当前节点的核心成果说清楚(比如“客户偏好调研显示,30~40岁的女性更青睐粉色系,预算集中在200~500元”)。
- 控流程:明确告诉系统下一步该怎么走(比如“请转交给产品设计节点,基于此数据设计3款样品”,或者“信息不足,请返回用户调研节点补充询问预算范围”)。
这就像快递单:既要有“包裹里是什么(内容)”,也要有“下一站寄到哪(流程)”,缺了任何一个,整个链条都会卡住。这种结构化设计正是LangGraph能让多个节点“默契配合”、灵活应对复杂任务的关键——就像团队里的交接单写得越清楚,大家干活越顺畅,不容易出错。
那么,怎么让大模型输出这种“带流程的交接单”呢?主要有以下两种办法。
- 提示工程:提前跟大模型说清楚“交接单该怎么写”。例如给节点发个“工作指南”:你的输出必须包含两部分:①你分析出的客户需求;②下一步建议(选“转报价组”或“转产品组”),用“###”分隔开。
- 输出解析器+提示工程:给个“固定模板”,让大模型必须按模板填。例如强制要求输出格式是“{"结果":"...","下一步":"..."}”,再配合提示确保它不瞎写,就像给交接单印好表格,只让填指定位置。
我们目前可以将提示工程简单地理解为“提示词”,而对于输出解析器,则一般使用LangGraph中的结构化输出对其进行处理。本节将主要讨论其中的两种结构化输出:字符串String与结构化JSON形式。
3.4.1 LangGraph中的格式化输出1:StrOutputParser
在之前的工具绑定示例中,我们发现模型返回的结果包含content、tool_calls、response_metadata等大量冗余元数据,直接使用时需要手动提取核心信息,不仅烦琐,还容易出错。而StrOutputParser作为LangChain中最基础且常用的输出解析器,其核心作用是过滤冗余元数据,直接提取模型返回的纯文本内容,让输出结果更简洁、更易于后续处理,完美适配LangGraph工作流中“节点间数据传递”的需求。
StrOutputParser的设计逻辑非常简单:它会自动解析大模型的返回对象,提取其中的content字段(自然语言文本),忽略tool_calls、response_metadata等额外信息,最终返回一个纯净的字符串。
它的主要适用场景包括:
- 模型直接生成自然语言回答(无须工具调用)时,快速获取纯文本结果。
- 工具调用后,模型整理结果生成回答时,过滤元数据只保留最终回复。
- LangGraph节点间传递文本数据时,简化数据格式,避免冗余信息干扰。
下面是一个使用StrOutputParser的示例:
import bigmodel
from langchain_core.prompts import PromptTemplate
from datetime import datetime
system_prompt=PromptTemplate(
input_variables=["user_query","datatime_now"],
template="你是小华,是一个有用的智能助手,帮助解决用户的问题{user_query},并根据注意{datatime_now}"
).partial(datatime_now=datetime.now())
from langchain_core.output_parsers import StrOutputParser
llm=system_prompt|bigmodel.llm | StrOutputParser()
print(llm.invoke("你是谁?"))
输出结果如下:
你好!我是小华,一个乐于助人的智能助手。我的目标是帮助你解答问题、提供信息或协助完成各种任务。
根据你提供的注意时间:2025年11月10日16:27:42.100315,目前系统时间已设定为该时刻(可能是用于参考或日程安排)。如果你有任何问题、需要提醒、计算、写作帮助,或者其他支持,请随时告诉我!
可以看到,此时根据我们的要求,自动输出了文本内容,并且剔除了其中的干扰项。
3.4.2 LangGraph中的格式化输出2:JSON格式
除了前面我们需要完成的文本输出外,对于输出格式,我们还可以要求其输出特定的JSON格式,如下所示:
import bigmodel
from langchain_core.prompts import PromptTemplate
from datetime import datetime
system_prompt=PromptTemplate(
input_variables=["user_query","datatime_now"],
template="你是小华,是一个有用的智能助手,帮助解决用户的问题{user_query},并根据注意{datatime_now}时间。"
).partial(datatime_now=datetime.now())
from langchain.tools import tool
@tool
def get_weather(loc:str,now_data:datetime):
"""
用于查询指定地点在指定日期的天气情况。
参数:
loc:地点名称(字符串类型,例如"上海")
now_data:查询的日期(datetime类型,包含年/月/日)
返回:包含日期、地点和天气详情的字符串
"""
return f"今天的日期是{now_data},查询到{loc}的天气晴朗,风和日丽,温度为21摄氏度"
tools=[get_weather]
llm_withtool=system_prompt | bigmodel.llm.bind_tools([get_weather])
#解析工具调用指令
tool_calls=llm_withtool.invoke("上海的天气是什么").tool_calls
tools_by_name={tool.name:toolfortoolintools}
tool_results=[]
fortool_callintool_calls:
tools_call_name=tool_call["name"]
tools_call_args=tool_call["args"]
tool_result=tools_by_name[tools_call_name].invoke(tools_call_args)
tool_results.append(tool_result)
#将工具结果回传给模型,生成最终回答
final_prompt=f"用户问的是上海的天气,工具返回结果:{tool_results},请以JSON格式回答。"
final_answer=bigmodel.llm.invoke(final_prompt).content
print(final_answer)
输出结果如下:
{
"date":"2025-11-10",
"time":"16:35:25.701372",
"location":"上海",
"weather":"晴朗",
"description":"风和日丽",
"temperature":21,
"unit":"摄氏度"
}
可以看到,这里对于final_prompt的设计,通过明确指令“请以JSON格式回答”,引导模型将工具返回的文本结果转换为键值对格式的结构化数据。
3.4.3 LangGraph中的格式化输出3:可控的JSON格式
以上示例演示了JSON格式的输出,可以看到我们将结果拆分成一个特定的JSON格式,虽然这样可以解决输出结果要求限定在特定格式下,但是对于输出结果的要求,目前是出于不可控的形式。
以下示例演示对输出结果进行控制,并限制输出的JSON值的内容:
from pydantic import BaseModel,Field
from datetime import datetime
class WeatherReport(BaseModel):
"""
结构化的天气报告模型,用于标准化工具返回的天气信息
包含天气查询的核心数据字段,便于后续解析、存储或跨系统传递
"""
date:datetime=Field(
default_factory=lambda:datetime.now().strftime("%Y年.%m月.%d号"),#格式化当前日期为Y.M.D
description="天气查询的日期(包含年/月/日/时/分/秒),默认使用当前时间"
)
location:str=Field(
default="",
description="查询的地点名称(如城市名、区域名等,字符串类型)"
)
temperature:float=Field(
default=0.0,
description="查询地点的温度,单位为摄氏度(数值类型,支持小数)"
)
这段代码基于Pydantic库定义了一个名为WeatherReport的结构化数据模型,其核心用途是标准化天气查询结果的数据格式,避免信息混乱。代码开头先导入了必要的依赖:BaseModel是Pydantic实现数据建模和验证的基础类,Field用于配置字段的元数据,datetime则用于处理日期时间相关逻辑。WeatherReport类继承自BaseModel后,自动获得了Pydantic的类型校验、字段约束等核心能力,而类内的文档字符串清晰说明了模型的作用——统一天气信息格式,方便后续的解析、存储或跨系统传递,解决了纯文本结果难以进行机器处理的问题。
模型内部定义了三个核心字段,分别对应天气报告的关键信息:date字段标注为datetime类型,用于存储查询日期,通过default_factory指定默认值为当前时间格式化后的字符串(格式为“年.月.号”),但需注意,此处存在类型不匹配的潜在问题(strftime返回字符串,与datetime类型标注冲突);location字段为字符串类型,默认值为空字符串,用于存储查询的地点名称(如城市名);temperature字段为浮点型,默认值为0.0,用于存储以摄氏度为单位的温度,支持小数精度。每个字段通过description参数补充了详细说明,既方便开发者理解,也能辅助后续与大模型交互时明确数据要求,确保输入输出的一致性。
可控JSON格式的使用示例如下:
import bigmodel
from langchain_core.prompts import PromptTemplate
from datetime import datetime
system_prompt=PromptTemplate(
input_variables=["user_query","datatime_now"],
template="你是小华,是一个有用的智能助手,帮助解决用户的问题{user_query},并根据注意{datatime_now}时间。"
).partial(datatime_now=datetime.now())
from langchain.tools import tool
@tool
def get_weather(loc:str,now_data:datetime):
"""
用于查询指定地点在指定日期的天气情况。
参数:
loc:地点名称(字符串类型,例如"上海")
now_data:查询的日期(datetime类型,包含年/月/日)
返回:包含日期、地点和天气详情的字符串
"""
return f"今天的日期是{now_data},查询到{loc}的天气晴朗,风和日丽,温度为21摄氏度"
tools=[get_weather]
llm_withtool=system_prompt|bigmodel.llm.bind_tools([get_weather])
#解析工具调用指令
tool_calls=llm_withtool.invoke("上海的天气是什么").tool_calls
tools_by_name={tool.name:toolfortoolintools}
tool_results=[]
fortool_callintool_calls:
tools_call_name=tool_call["name"]
tools_call_args=tool_call["args"]
tool_result=tools_by_name[tools_call_name].invoke(tools_call_args)
tool_results.append(tool_result)
from pydantic import BaseModel,Field
from datetime import datetime
class WeatherReport(BaseModel):
"""
结构化的天气报告模型,用于标准化工具返回的天气信息
包含天气查询的核心数据字段,便于后续解析、存储或跨系统传递
"""
date:datetime=Field(
default_factory=lambda:datetime.now().strftime("%Y年.%m月.%d号"),#格式化当前日期为Y.M.D
description="天气查询的日期(包含年/月/日/时/分/秒),默认使用当前时间"
)
location:str=Field(
default="",
description="查询的地点名称(如城市名、区域名等,字符串类型)"
)
temperature:float=Field(
default=0.0,
description="查询地点的温度,单位为摄氏度(数值类型,支持小数)"
)
#将工具结果回传给模型,生成最终回答
final_prompt=f"用户问的是上海的天气,工具返回结果:{tool_results},请以JSON格式回答。"
final_answer=bigmodel.llm.with_structured_output(WeatherReport).invoke(final_prompt)
print(final_answer)
输出结果如下:
date=datetime.datetime(2025,11,10,0,0)location='上海'temperature=21.0
从输出结果可以看到,通过with_structured_output(WeatherReport)方法,我们成功实现了“可控且标准化的JSON格式输出”——最终返回的不再是自由格式的JSON字符串,而是WeatherReport类的实例化对象,字段值严格匹配模型定义的类型和含义:date为datetime类型(自动解析为2025年11月10日)、location为字符串类型(准确提取“上海”)、temperature为浮点类型(正确转换为21.0),完全避免了纯提示词要求“JSON格式”时可能出现的字段缺失、类型混乱等问题。
这种“Pydantic模型+with_structured_output”的组合是LangChain中实现可控JSON输出的核心方案,其核心逻辑是通过Pydantic模型定义“数据契约”,再让大模型按照契约生成结构化数据。具体来说,with_structured_output(WeatherReport)会自动将WeatherReport的字段定义、类型约束和描述转换为大模型能理解的提示指令,无须手动编写“包含哪些字段、类型是什么”的烦琐提示;同时,大模型返回结果后,LangChain会自动进行类型校验和转换,比如工具返回的温度是文本“21摄氏度”,会被自动提取数值并转为float类型,日期字符串会被解析为datetime对象,最终封装成WeatherReport实例。这种方式不仅让输出格式完全可控,还省去了手动解析JSON字符串、校验字段类型的步骤,后续可直接通过final_answer.location、final_answer.temperature等属性快速获取关键数据,极大地简化了代码逻辑。
在实际应用中,这种可控JSON格式尤其适合需要严格数据规范的场景,比如将天气报告存入数据库时,可直接通过final_answer.model_dump()将实例转换为标准字典;跨系统传递数据时,通过final_answer.model_dump_json()生成合规JSON字符串,无须担心格式不兼容;在LangGraph工作流中,WeatherReport实例可直接作为节点间传递的状态数据,后续节点能精准提取字段进行二次决策(如根据temperature判断是否推荐户外活动)。相比单纯依赖提示词要求JSON格式,这种方案的稳定性和可维护性大幅提升,是在LangGraph中进行结构化数据处理的首选方式。

更多推荐



所有评论(0)