python
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from dotenv import load_dotenv
import os
load_dotenv()
llm=ChatOpenAI(
model="qwen-turbo",
base_url=os.getenv("OPENAI_API_BASE"),
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.6
)
# 第一步:生成餐厅名称
prompt_template_name = PromptTemplate(
input_variables =['cuisine'],
template = "我想开一家**{cuisine} 风味餐厅**,请为它取一个雅致好听的名字。"
)
name_chain = prompt_template_name | llm | StrOutputParser()
# 第二步:根据餐厅名生成菜单
prompt_template_items = PromptTemplate(
input_variables = ['restaurant_name'],
template="请为餐厅 {restaurant_name} 设计若干菜品"
)
food_items_chain = prompt_template_items | llm | StrOutputParser()
# ==================改造重点==================
full_chain = (
{"cuisine": RunnableLambda(lambda x: x["input"])}
| RunnablePassthrough.assign(restaurant_name = name_chain)
# 此时数据结构: {"cuisine": "中国", "restaurant_name": "餐厅名字"}
| RunnablePassthrough.assign(menu = RunnableLambda(lambda x: food_items_chain.invoke(x["restaurant_name"])))
)
result = full_chain.invoke({"input": "中国"})
print("完整结果字典:", result)
print("餐厅名称(中间结果):", result["restaurant_name"])
print("菜单(最终输出):", result["menu"])
LangChain RunnableSequence 获取中间输出的几种方案
先梳理你当前链路:
{"cuisine": ...} → name_chain(生成餐厅名) → food_items_chain(生成菜单)
现在链路默认只会返回最后一步结果,拿不到中间生成的餐厅名称。下面给出由简单到工程化的 4 种方案,适配你的代码。
方案 1:使用 RunnablePassthrough.assign()【推荐,原生 LCEL 标准写法】
核心思路:把上一步输出保存到字典里往下传递,不会丢失中间值。
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from dotenv import load_dotenv
import os
load_dotenv()
llm=ChatOpenAI(
model="qwen-turbo",
base_url=os.getenv("OPENAI_API_BASE"),
api_key=os.getenv("OPENAI_API_KEY"),
temperature=0.6
)
# 第一步:生成餐厅名称
prompt_template_name = PromptTemplate(
input_variables =['cuisine'],
template = "我想开一家**{cuisine} 风味餐厅**,请为它取一个雅致好听的名字。"
)
name_chain = prompt_template_name | llm | StrOutputParser()
# 第二步:根据餐厅名生成菜单
prompt_template_items = PromptTemplate(
input_variables = ['restaurant_name'],
template="请为餐厅 {restaurant_name} 设计若干菜品"
)
food_items_chain = prompt_template_items | llm | StrOutputParser()
# ==================改造重点==================
full_chain = (
{"cuisine": RunnableLambda(lambda x: x["input"])}
| RunnablePassthrough.assign(restaurant_name = name_chain)
# 此时数据结构: {"cuisine": "中国", "restaurant_name": "餐厅名字"}
| RunnablePassthrough.assign(menu = RunnableLambda(lambda x: food_items_chain.invoke(x["restaurant_name"])))
)
result = full_chain.invoke({"input": "中国"})
print("完整结果字典:", result)
print("餐厅名称(中间结果):", result["restaurant_name"])
print("菜单(最终输出):", result["menu"])
✅ 优点:纯 LCEL 语法、官方标准,链路所有中间数据保存在字典,invoke 返回完整结构体。
方案 2:手动封装,在 RunnableLambda 捕获中间变量(简单调试用)
适合临时打印中间值,不适合生产。
full_chain = (
{"cuisine": RunnableLambda(lambda x: x["input"])}
| name_chain
| RunnableLambda(lambda rest_name: (
print(f"【中间餐厅名】{rest_name}"), # 直接打印
{"restaurant_name": rest_name}
)[1])
| food_items_chain
)
方案 3:使用回调 CallbackHandler 捕获所有 LLM 输出
适合全局监控整条链路所有 LLM 调用,跨 chain 统一捕获,缺点无法区分属于哪一步,适合日志系统。
from langchain_core.callbacks import BaseCallbackHandler
class CaptureHandler(BaseCallbackHandler):
def on_llm_end(self, response, **kwargs):
print("LLM输出:", response.generations[0][0].text)
# invoke时传入
result = full_chain.invoke({"input": "中国"}, config={"callbacks":[CaptureHandler()]})
方案 4:拆分链路分步调用(最简单粗暴)
不串联执行,分步执行直接保存变量,适合开发调试:
input_data = {"input": "中国"}
# 第一步
cuisine = input_data["input"]
rest_name = name_chain.invoke({"cuisine": cuisine})
print("餐厅名称:", rest_name)
# 第二步
menu = food_items_chain.invoke({"restaurant_name": rest_name})
print("菜单:", menu)
关键原理说明
RunnableSequence(A | B | C)的机制:- A 的输出 直接作为 B 的输入;
- 默认只会向下单向流转,不会自动保留历史中间值;
RunnablePassthrough.assign()的作用: 接收上游字典,执行里面定义的 runnable,把结果追加到原有字典继续向下传递,是 LCEL 保存中间状态的标准工具。
进阶:如果你希望最终返回结构化对象(餐厅名 + 菜单)
直接使用方案 1 的代码,返回值是字典:
{
"cuisine": "中国",
"restaurant_name": "xxx酒楼",
"menu": "菜品清单..."
}