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

基于load_qa_chain的QA聊天机器人对话历史超token限制求解

解决LangChain QA机器人对话历史触发Token超限的方案(不修改完整记忆)

方案1:动态按Token截断对话历史(保留完整记忆存储)

核心思路:内存中仍保留完整对话历史,但每次构建Prompt时,仅传入最近的、总Token数不超限制的对话片段,确保Prompt整体Token数在模型上限内。

实现步骤:

  • 引入tiktoken库计算Token数
  • 自定义对话历史截断逻辑,在生成Prompt前对chat_history进行处理

代码修改示例:

import tiktoken

# 定义计算Token数的函数
def count_tokens(text, model_name="gpt-3.5-turbo"):
    encoding = tiktoken.encoding_for_model(model_name)
    return len(encoding.encode(text))

# 截断对话历史的函数
def truncate_chat_history(chat_history, max_available_tokens, model_name="gpt-3.5-turbo"):
    # 拆分对话历史为单个轮次
    history_lines = chat_history.split("\n")
    truncated_history = []
    current_token_count = 0

    # 从后往前遍历,保留最近的对话
    for line in reversed(history_lines):
        line_tokens = count_tokens(line, model_name)
        if current_token_count + line_tokens <= max_available_tokens:
            truncated_history.append(line)
            current_token_count += line_tokens
        else:
            break
    # 反转回原顺序
    return "\n".join(reversed(truncated_history))

# 修改generate_response函数
def generate_response(query):
    docs = docsearch.similarity_search(query)
    # 获取完整对话历史
    full_chat_history = memory.load_memory_variables({})["chat_history"]
    # 计算context的Token数
    context_text = "\n".join([doc.page_content for doc in docs])
    context_tokens = count_tokens(context_text)
    query_tokens = count_tokens(query)
    # 模型最大Token数减去context、query和预留的完成Token数
    model_max_tokens = 4097
    completion_reserve = 1000  # 对应chain里的max_tokens=1000
    max_history_tokens = model_max_tokens - context_tokens - query_tokens - completion_reserve

    if max_history_tokens > 0:
        truncated_history = truncate_chat_history(full_chat_history, max_history_tokens)
    else:
        truncated_history = ""  # 若空间不足,暂时清空历史

    # 手动构建输入,传入截断后的历史
    return chain({
        "input_documents": docs, 
        "human_input": query,
        "chat_history": truncated_history
    }, return_only_outputs=True)['output_text']

说明:此方法不会修改memory中存储的完整对话历史,仅在每次调用时动态截取合适长度的历史传入Prompt,既保留了完整记忆,又避免超限。

方案2:优化上下文文档检索(减少Context占用的Token)

核心思路:通过减少Prompt中context部分的Token消耗,为对话历史腾出更多空间,无需修改记忆存储。

具体操作:

  • 调小文档拆分的chunk_size:比如从1000改为500,减少单段文档的Token数
  • 减少检索返回的文档数量:将similarity_search(query)改为similarity_search(query, k=1),只返回最相关的1段文档
  • 改用更精准的检索策略:比如使用MMR检索(最大边际相关性),优先返回与query最相关且冗余度最低的文档

代码修改示例(减少返回文档数量):

def generate_response(query):
    # 只返回最相关的1个文档,减少context的Token数
    docs = docsearch.similarity_search(query, k=1)
    return chain({"input_documents": docs, "human_input": query}, return_only_outputs=True)['output_text']

方案3:更换大上下文窗口模型

直接更换为支持更大上下文的模型(如gpt-3.5-turbo-16k、gpt-4等),从根本上提升Token上限,无需修改记忆或Prompt逻辑。

代码修改示例:

chain = load_qa_chain(
    # 更换为16k上下文的模型
    OpenAI(model_name="gpt-3.5-turbo-16k", temperature=1, max_tokens=1000), 
    chain_type="stuff", 
    memory=memory, 
    prompt=prompt
)

方案4:使用对话摘要Buffer内存(保留完整历史,Prompt用摘要)

使用ConversationSummaryBufferMemory,它会同时存储完整对话历史和历史摘要:当对话历史Token数超过阈值时,自动将早期对话转换为摘要传入Prompt,而完整历史仍保存在内存中。

代码修改示例:

from langchain.memory import ConversationSummaryBufferMemory
from langchain.chat_models import ChatOpenAI

# 初始化摘要Buffer内存,设置Token阈值
memory = ConversationSummaryBufferMemory(
    memory_key="chat_history", 
    input_key="human_input",
    llm=ChatOpenAI(temperature=0),  # 用于生成摘要的模型
    max_token_limit=1500  # 当历史超过1500Token时,自动生成摘要
)

# 后续chain初始化和生成逻辑不变
chain = load_qa_chain(
    OpenAI(temperature=1, max_tokens=1000), 
    chain_type="stuff", 
    memory=memory, 
    prompt=prompt
)

说明:此方法内存中始终保留完整对话历史,仅在Prompt中根据阈值自动替换为摘要,既满足“不修改记忆”的要求,又避免Token超限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:43:13