1.2 使用LangChain中的RunnableLambda

📄 文件代码内容

python 复制代码
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

model = ChatTongyi(model="qwen3-max")
str_parser = StrOutputParser()

first_prompt = PromptTemplate.from_template(
    "我邻居姓:{lastname},刚生了{gender},请帮忙起名字,仅生成一个名字,并告知我名字,不要额外信息。"
)

second_prompt = PromptTemplate.from_template(
    "姓名{name},请帮我解析含义。"
)

# 函数的入参:AIMessage -> dict  ({"name": "xxx"})
# my_func = RunnableLambda(lambda ai_msg: {"name": ai_msg.content})
#可以封装,也可以直接把函数加入到链中
chain = first_prompt | model | (lambda ai_msg: {"name": ai_msg.content}) | second_prompt | model | str_parser

for chunk in chain.stream({"lastname": "曹", "gender": "女孩"}):
    print(chunk, end="", flush=True)

🔄 代码流程梳理

  1. 导入依赖

    • StrOutputParser:将模型输出的 AIMessage 转为纯字符串。
    • PromptTemplate:定义提示模板。
    • ChatTongyi:通义千问聊天模型。
  2. 初始化组件

    • 创建 ChatTongyi 实例(模型)。
    • 创建 StrOutputParser 实例(解析器)。
  3. 定义两个提示模板

    • first_prompt:要求模型根据姓氏和性别生成一个名字(限定只输出名字,无额外信息)。
    • second_prompt:要求模型解析该名字的含义(输入变量为 name)。
  4. 构建 LCEL 链(核心)

    python 复制代码
    chain = (
        first_prompt
        | model
        | (lambda ai_msg: {"name": ai_msg.content})   # ① 转换
        | second_prompt
        | model
        | str_parser
    )
    • 步骤 1first_prompt 根据输入 {"lastname": "曹", "gender": "女孩"} 生成提示字符串。
    • 步骤 2model 生成名字(返回 AIMessage,例如 content="曹梦瑶")。
    • 步骤 3lambda 函数 接收上一步的 AIMessage,提取 content,并将其包装成字典 {"name": "曹梦瑶"},以便注入 second_prompt
    • 步骤 4second_prompt 使用该字典生成新的提示字符串(如 "姓名曹梦瑶,请帮我解析含义。")。
    • 步骤 5model 再次调用,生成名字含义的回复(AIMessage)。
    • 步骤 6str_parser 将最终 AIMessage 转为纯字符串。
  5. 流式输出

    • 调用 chain.stream() 并传入参数,逐块打印最终结果(stream 支持渐进式生成,适合长文本)。

💡 高频面试知识点与回答思路

1. RunnableLambda 的作用与使用场景
  • 作用 :将任意 Python 函数包装成 Runnable,使其能够参与 LCEL 链式编排。
  • 使用场景
    • 数据格式转换(如本例中 AIMessagedict)。
    • 插入日志、调试、缓存等副作用。
    • 调用外部 API 或执行自定义业务逻辑。
  • 实现方式 :可直接使用 lambda 表达式,也可用 RunnableLambda(func) 显式包装。
2. LCEL 链的数据流转规则
  • 链中每个节点必须是 Runnable,前一个节点的输出类型需与后一个节点的输入类型兼容。
  • 若类型不匹配,必须通过 RunnableLambdaRunnablePassthrough 进行适配。
  • 本例中,model 输出 AIMessage,但 second_prompt 期望接收包含 name 键的字典,因此 lambda 担任转换角色。
3. streaminvoke 的区别
  • invoke:一次性执行整条链,返回完整结果。
  • stream:返回生成器,逐步产出结果(适合长文本、流式输出)。
  • 两者都支持 input 参数,但 stream 对内存更友好,用户体验更好。
4. StrOutputParser 的作用
  • AIMessage 对象中的 content 字段提取出来,返回纯字符串。
  • RunnableLambda 不同,它是 LangChain 内置的解析器,专门用于输出提取。
5. 链式调用中如何处理中间变量的传递?
  • 可通过 RunnableParallel 并行传递,或通过 RunnablePassthrough 保留中间值。
  • 本例中,使用 lambda 将中间结果转为所需格式,满足下一环节的输入要求。
6. 如果 lambda 函数逻辑复杂,如何保持代码可读性?
  • 可以单独定义一个普通函数,然后用 RunnableLambda(func) 包装,避免 lambda 过于臃肿。
  • 也可定义类实现 __call__ 或继承 Runnable

📌 面试回答话术(示例)

"该文件演示了 LangChain 中 RunnableLambda 的典型用法。在 LCEL 链中,模型第一次输出的是 AIMessage,而后续的 PromptTemplate 需要字典格式的输入,因此通过一个 lambda 函数提取 content 并构造字典。这种手法在需要将模型输出'喂'给下一个 Prompt 时非常常见。流式输出(stream)能提升长文本生成的用户体验。若需更复杂的转换,我会使用显式的 RunnableLambda 包装函数,保证代码可维护性和可测试性。"