LangChain实战:JsonOutputParser与RunnableLambda,解锁多模型链与自定义函数入链

2 阅读9分钟

LangChain实战:JsonOutputParser与RunnableLambda,解锁多模型链与自定义函数入链

在LangChain多模型链路开发中,很多开发者容易陷入一个误区:直接将上一个模型的输出传入下一个模型,忽略了组件间的输入输出类型匹配,导致链路执行报错或结果不符合预期。实际上,标准的多模型协同逻辑,需要对前一个模型的输出进行规范处理,再注入下一个提示词模板,才能实现高效、稳定的组件协同。

本文将聚焦两个核心组件——JsonOutputParser与RunnableLambda,结合原创实战场景,详解标准多模型执行链的构建方法,以及自定义函数如何灵活加入链路,帮你吃透组件间的数据流转逻辑,避开类型匹配坑,提升链路开发的规范性与灵活性。

一、JsonOutputParser:多模型链的标准数据转换利器

1. 多模型链的核心痛点:数据类型不兼容

在构建多模型链路时,最常见的问题的是组件间输入输出类型不匹配:LangChain模型的输出是AIMessage类对象,而提示词模板(PromptTemplate)的输入要求是字典类型。如果直接将模型输出传入下一个提示词模板,会直接触发类型错误,导致链路执行失败。

标准的多模型执行流程应遵循:初始输入 → 提示词模板 → 模型 → 数据转换 → 提示词模板 → 模型 → 解析器 → 最终结果。其中,“数据转换”环节是关键,而JsonOutputParser正是解决这一问题的核心工具——它能将模型输出的AIMessage对象,规范转换为字典类型,完美适配提示词模板的输入要求。

2. JsonOutputParser核心功能与类型约束

JsonOutputParser是LangChain内置的输出解析器,核心作用是将模型输出的AIMessage对象,解析为Python字典(dict),其类型约束十分明确:

  • 输入类型:必须是AIMessage类对象(模型执行后的标准输出);
  • 输出类型:Python字典(dict),可直接作为PromptTemplate的输入,注入模板变量中。

结合LangChain核心组件的类型约束,我们可以明确多模型链的类型流转逻辑:

  • PromptTemplate:输入为dict → 输出为PromptValue对象(模型可直接接收的输入类型);
  • Chat模型(如ChatTongyi):输入为PromptValue、字符串或序列 → 输出为AIMessage对象;
  • JsonOutputParser:输入为AIMessage → 输出为dict;
  • StrOutputParser:输入为AIMessage → 输出为str(用于最终结果的格式化)。

3. 原创实操:标准多模型执行链构建

我们以“生成产品核心参数 → 根据参数撰写产品短评”为实战场景,构建标准多模型链,全程使用原创代码,避开参考资料中的场景与代码逻辑。

from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

# 1. 初始化组件:模型、两个解析器
model = ChatTongyi(model="qwen3-max")
json_parser = JsonOutputParser()  # AIMessage → dict
str_parser = StrOutputParser()    # AIMessage → str

# 2. 构建提示词模板(原创场景:产品参数生成+产品短评)
# 第一个模板:生成指定品类产品的核心参数,要求JSON格式输出
first_prompt = PromptTemplate.from_template(
    "请生成{product_type}的核心参数,要求包含品牌、价格、核心功能3个维度,"
    "严格按照JSON格式返回,key分别为brand、price、core_function,不要添加任何额外说明"
)

# 第二个模板:根据产品参数,撰写简洁有吸引力的产品短评
second_prompt = PromptTemplate.from_template(
    "根据产品参数:品牌{brand},价格{price},核心功能{core_function},"
    "撰写一句产品短评,突出核心优势,语言简洁,不超过30字"
)

# 3. 构建标准多模型链:模板1→模型→JSON解析→模板2→模型→字符串解析
chain = first_prompt | model | json_parser | second_prompt | model | str_parser

# 4. 执行链路,传入初始输入(产品类型:无线蓝牙耳机)
res: str = chain.invoke({"product_type": "无线蓝牙耳机"})

# 输出结果与类型
print("产品短评:", res)
print("结果类型:", type(res))

4. 链路执行流程解析

上述链路的执行流程完全遵循标准逻辑,每一步都实现了类型的无缝匹配:

  1. 初始输入{"product_type": "无线蓝牙耳机"}传入first_prompt,生成PromptValue对象;
  2. 模型接收PromptValue对象,执行后输出AIMessage对象(包含JSON格式的产品参数);
  3. json_parser将AIMessage对象解析为dict(如{"brand": "XXX", "price": "XXX", "core_function": "XXX"});
  4. 该dict作为输入传入second_prompt,生成新的PromptValue对象;
  5. 模型再次执行,输出包含产品短评的AIMessage对象;
  6. str_parser将AIMessage对象解析为字符串,得到最终的产品短评。

这种构建方式,彻底解决了多模型链中类型不兼容的问题,确保链路稳定执行,也是LangChain多模型协同的标准实践。

二、RunnableLambda:自定义函数入链的灵活实现

1. 场景需求:自定义数据转换逻辑

JsonOutputParser适用于“将AIMessage转为dict”的固定场景,但实际开发中,我们常常需要更灵活的自定义数据转换逻辑——比如提取模型输出中的特定字段、对数据进行格式化处理、新增自定义变量等。此时,RunnableLambda就能发挥作用,它能将普通Python函数(或匿名函数)转换为Runnable接口实例,轻松加入链路中。

2. RunnableLambda核心定位与原理

RunnableLambda是LangChain内置的Runnable子类,核心功能是“封装普通函数”,将其转换为符合Runnable接口规范的实例,从而实现自定义函数与LangChain链路的无缝融合。其核心原理的:

LangChain的Runnable接口在实现__or__方法(即|符号)时,支持Callable接口实例(普通函数就是Callable实例),会自动将函数转换为RunnableLambda对象。因此,我们有两种方式将自定义函数加入链路:

  • 显式封装:将函数传入RunnableLambda,生成Runnable实例后入链;
  • 隐式转换:直接将函数入链,底层自动转换为RunnableLambda对象。

3. 原创实操:两种函数入链方式实战

我们延续“产品参数生成+产品短评”的场景,新增自定义数据转换逻辑:提取模型输出中的核心功能字段,添加“适配场景”描述,再传入下一个提示词模板,分别用两种方式实现函数入链。

方式1:显式封装RunnableLambda入链
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

# 1. 初始化组件
model = ChatTongyi(model="qwen3-max")
str_parser = StrOutputParser()

# 2. 自定义数据转换函数:提取核心功能,添加适配场景
def process_core_function(ai_msg):
    # ai_msg是模型输出的AIMessage对象,content是输出文本(假设为纯文本核心功能)
    core_func = ai_msg.content.strip()
    # 自定义处理:添加适配场景描述
    processed_data = {
        "core_function": core_func,
        "scene": "日常通勤、办公学习,适配多场景使用"
    }
    return processed_data

# 3. 显式将函数封装为RunnableLambda实例
process_func = RunnableLambda(process_core_function)

# 4. 构建提示词模板
first_prompt = PromptTemplate.from_template(
    "请生成{product_type}的核心功能,仅输出功能描述,不要添加任何额外内容"
)
second_prompt = PromptTemplate.from_template(
    "产品核心功能:{core_function},适配场景:{scene},请撰写一句产品宣传语"
)

# 5. 构建链路:模板1→模型→RunnableLambda→模板2→模型→解析器
chain = first_prompt | model | process_func | second_prompt | model | str_parser

# 6. 执行链路
res = chain.invoke({"product_type": "便携式充电宝"})
print("产品宣传语:", res)
方式2:函数直接入链(隐式转换)

无需显式封装RunnableLambda,直接将函数加入链路,底层会自动将其转换为RunnableLambda对象,代码更简洁:

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

# 1. 初始化组件(与方式1一致)
model = ChatTongyi(model="qwen3-max")
str_parser = StrOutputParser()

# 2. 自定义数据转换函数(与方式1一致)
def process_core_function(ai_msg):
    core_func = ai_msg.content.strip()
    processed_data = {
        "core_function": core_func,
        "scene": "日常通勤、办公学习,适配多场景使用"
    }
    return processed_data

# 3. 构建提示词模板(与方式1一致)
first_prompt = PromptTemplate.from_template(
    "请生成{product_type}的核心功能,仅输出功能描述,不要添加任何额外内容"
)
second_prompt = PromptTemplate.from_template(
    "产品核心功能:{core_function},适配场景:{scene},请撰写一句产品宣传语"
)

# 4. 函数直接入链(底层自动转换为RunnableLambda)
chain = first_prompt | model | process_core_function | second_prompt | model | str_parser

# 5. 执行链路
res = chain.invoke({"product_type": "便携式充电宝"})
print("产品宣传语:", res)

4. 两种入链方式对比与注意事项

两种方式的核心逻辑一致,仅代码写法不同,具体对比与注意事项如下:

  • 显式封装:代码更规范,可明确看到函数被转换为Runnable实例,便于后续维护和调试,适合复杂函数或大型项目;
  • 隐式转换:代码更简洁,省去封装步骤,适合简单的匿名函数或小型链路,底层实现与显式封装完全一致;
  • 核心注意点:自定义函数的输入输出需匹配前后组件的类型——函数的输入是上一个组件的输出(如AIMessage),函数的输出需符合下一个组件的输入要求(如dict,适配PromptTemplate)。

三、实战总结与核心要点

本文通过实战场景,详解了JsonOutputParser与RunnableLambda的核心用法,以及标准多模型链的构建逻辑,总结两个核心要点,帮你避开开发坑点:

  1. 多模型链构建的核心是“类型匹配”:始终遵循“PromptTemplate→模型→数据转换→PromptTemplate→模型”的标准流程,利用JsonOutputParser解决AIMessage到dict的转换,确保组件间数据流转顺畅;
  2. 自定义函数入链的灵活实现:通过RunnableLambda,可将任意普通函数转换为Runnable实例,支持显式封装和隐式转换两种方式,根据项目复杂度选择即可,核心是保证函数输入输出与前后组件兼容。

无论是JsonOutputParser的固定数据转换,还是RunnableLambda的自定义逻辑,其本质都是基于LangChain的Runnable接口规范,实现组件的协同工作。掌握这两个组件的用法,能大幅提升多模型链路开发的效率与灵活性,为后续构建更复杂的链路(如多步骤数据处理、智能体协同)打下基础。