You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LangChain中RunnableLambda分支对话历史重复记录问题求助

解决方案:避免LangChain对话历史重复记录并传递会话配置

问题根源分析

外层full_chain和分支内的RunnableWithMessageHistory重复实例化,导致LangSmith中对话历史被两次加载/记录;移除外层后,分支组件无法获取conversation_id、user_id等必要会话参数,因此报错。核心解决思路是只保留一个层级的RunnableWithMessageHistory,并确保会话参数能完整传递到该组件。


方案1:仅在分支层级使用RunnableWithMessageHistory,手动传递会话参数

直接移除外层的RunnableWithMessageHistory,通过输入参数将user_id、conversation_id传递给分支内的对话历史组件,确保仅一次加载/记录对话历史。

示例代码:

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_openai import ChatOpenAI

# 1. 定义对话历史获取函数(替换为你的实际存储逻辑,比如Redis/数据库)
def get_session_history(user_id: str, conversation_id: str) -> BaseChatMessageHistory:
    return InMemoryChatMessageHistory(session_id=f"{user_id}_{conversation_id}")

# 2. 定义分支内的生成链(包含对话历史)
llm = ChatOpenAI(model="gpt-3.5-turbo")
branch_llm_chain = llm | (lambda x: x.content)

branch_chain = RunnableWithMessageHistory(
    branch_llm_chain,
    get_session_history=get_session_history,
    input_messages_key="user_input",
    history_messages_key="chat_history",
    # 指定从输入中提取会话参数的key
    user_id_key="user_id",
    conversation_id_key="conversation_id"
)

# 3. 定义分支路由逻辑
def route_to_branch(inputs):
    # 这里替换为你的实际分支判断逻辑(比如根据输入类型选择不同分支)
    return branch_chain.invoke(inputs)

# 4. 外层链:仅负责传递参数,不嵌套对话历史组件
full_chain = RunnablePassthrough() | RunnableLambda(route_to_branch)

# 调用示例
full_chain.invoke({
    "user_id": "user_123",
    "conversation_id": "conv_456",
    "user_input": "今天天气怎么样?"
})

方案2:外层保留逻辑但剥离对话历史,通过参数透传传递会话配置

如果外层需要处理输入预处理、权限校验等逻辑,可将外层的RunnableWithMessageHistory替换为普通逻辑链,仅在分支内处理对话历史,通过RunnablePassthrough确保会话参数完整传递。

示例代码:

# 外层预处理链(比如输入清洗、权限校验)
preprocess_chain = RunnableLambda(lambda inputs: {
    **inputs,
    "user_input": inputs["raw_input"].strip()  # 示例预处理逻辑
})

# 组合外层与分支链
full_chain = preprocess_chain | RunnableLambda(route_to_branch)

# 调用方式与方案1一致
full_chain.invoke({
    "user_id": "user_123",
    "conversation_id": "conv_456",
    "raw_input": "   今天天气怎么样?   "
})

方案3:禁用其中一个实例的LangSmith跟踪(临时 workaround)

如果因业务需求必须保留双层RunnableWithMessageHistory,可通过禁用其中一个实例的跟踪,避免重复记录。但此方法会丢失对应层级的跟踪数据,仅作为临时方案。

示例代码:

# 禁用分支内对话历史组件的LangSmith跟踪
branch_chain = RunnableWithMessageHistory(
    branch_llm_chain,
    get_session_history=get_session_history,
    input_messages_key="user_input",
    history_messages_key="chat_history",
    user_id_key="user_id",
    conversation_id_key="conversation_id",
    config={"callbacks": []}  # 清空回调,禁用跟踪
)

关键注意事项

  • 确保所有调用full_chain的场景,都传入user_id和conversation_id参数;
  • 对话历史获取函数get_session_history需根据实际存储实现,避免内存存储(InMemoryChatMessageHistory)仅用于测试;
  • 优先选择方案1/2,从架构层面避免重复实例化,而非依赖跟踪禁用的临时方案。

内容的提问来源于stack exchange,提问作者Keita Kiuchi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:35:08