Langchain+Redis持久层异常:回复总是对应上一次请求
问题
使用Langchain搭配Redis作为会话持久层时,出现异常:每次请求的回复总是对应上一次的提问内容。具体表现:
第一次请求:
$ curl -XPOST -H "session-id: 123" -d '{"message": "what is the capital of united states?"}' http://localhost:8000 {"message":"Of course! How can I assist you today?"}
第二次请求不同内容,却得到上一次的正确回复:
$ curl -XPOST -H "session-id: 123" -d '{"message": "hello?"}' http://localhost:8000 {"message":"The capital of the United States is Washington, D.C."}%
问题原因
核心问题在于Prompt模板未包含当前用户的提问。原代码中注释掉了HumanMessage(content="{question}"),导致每次构建Prompt时,仅传入系统消息和历史会话,未将本次用户提问传递给LLM:
- 第一次请求时,历史会话为空,LLM仅收到系统消息,因此返回默认问候语;
- 请求结束后,
RunnableWithMessageHistory自动将本次用户提问和LLM回复存入Redis; - 第二次请求时,Prompt的
history字段包含第一次的用户提问,LLM针对历史内容做出回复,而本次的"hello?"未被传入Prompt,最终出现回复延迟的现象。
修复方案
恢复Prompt模板中注释掉的用户提问部分,确保每次请求都将当前用户的提问加入到Prompt中:
修改后的Prompt代码片段:
prompt = ChatPromptTemplate.from_messages( [ SystemMessage(content="You are a helpful assistant."), MessagesPlaceholder(variable_name="history"), HumanMessage(content="{question}"), # 恢复该行代码 ] )
修复后,每次请求的Prompt会包含「系统消息 + 历史会话 + 当前用户提问」,LLM能正确针对本次提问做出回复,同时会话历史也会正常存入Redis,后续请求可正确关联上下文。
完整修复后的代码
import os from typing import Any import orjson from langchain.globals import set_debug from langchain_community.chat_message_histories import RedisChatMessageHistory from langchain_core.messages import SystemMessage, HumanMessage from langchain_core.prompts import ChatPromptTemplate from langchain_core.prompts import MessagesPlaceholder from langchain_core.runnables.history import RunnableWithMessageHistory from langchain_openai import ChatOpenAI from starlette.applications import Starlette from starlette.middleware import Middleware from starlette.middleware.cors import CORSMiddleware from starlette.requests import Request from starlette.responses import JSONResponse from starlette.routing import Route from tenacity import retry from tenacity import stop_after_attempt set_debug(True) llm = ChatOpenAI( model="gpt-4o", temperature=0, openai_api_key=os.environ["OPENAI_APIKEY"], ) prompt = ChatPromptTemplate.from_messages( [ SystemMessage(content="You are a helpful assistant."), MessagesPlaceholder(variable_name="history"), HumanMessage(content="{question}"), ] ) chain = prompt | llm chain_with_history = RunnableWithMessageHistory( chain, lambda session_id: RedisChatMessageHistory(session_id, url=os.environ["REDIS_DSN"]), input_messages_key="question", history_messages_key="history", ) class OrjsonResponse(JSONResponse): def render(self, content: Any) -> bytes: return orjson.dumps(content) @retry(stop=stop_after_attempt(3)) async def echo(request: Request): data = await request.json() output = chain_with_history.invoke( {"question": data["message"]}, config={"configurable": {"session_id": request.headers["session-id"]}}, ) return OrjsonResponse({"message": output.content}) app = Starlette( routes=[ Route("/", echo, methods=["POST"]), ], middleware=[ Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["POST"]) ], )
内容的提问来源于stack exchange,提问作者Rodrigo
相关产品推荐
相关产品推荐

