使用ConversationalRetrievalChain时,LLM无答案如何取消返回源文档?
解决ConversationalRetrievalChain无答案时不返回源文档的问题
核心思路
默认的ConversationalRetrievalChain只要设置return_source_documents=True,就会始终返回检索到的源文档,不管LLM最终是否能回答问题。要实现无答案时不返回源文档,需要在获取Chain输出后添加判断逻辑,识别LLM的无法回答回复,再动态移除源文档字段。
具体实现方法
方法1:结果后处理(简单直接)
调用Chain得到结果后,检查LLM的回答是否包含预设的无法回答关键词,若是则删除source_documents字段。
示例代码:
from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferMemory # 初始化配置好的Chain(带ConversationBufferMemory,return_source_documents=True) chain = ConversationalRetrievalChain.from_llm( llm=your_llm_instance, retriever=your_retriever_instance, memory=ConversationBufferMemory(memory_key="chat_history", return_messages=True), return_source_documents=True ) def get_answer_with_filtered_sources(query): result = chain({"question": query}) # 根据LLM实际返回的无法回答话术调整关键词列表 no_answer_triggers = ["I'm sorry, but I don't have access to", "无法回答该问题"] # 判断是否属于无法回答场景 if any(trigger in result["answer"] for trigger in no_answer_triggers): result.pop("source_documents", None) return result
方法2:自定义Chain(优雅复用)
继承ConversationalRetrievalChain,重写_call方法,在内部完成判断和源文档过滤:
from langchain.chains.conversational_retrieval.base import ConversationalRetrievalChain class FilteredConversationalRetrievalChain(ConversationalRetrievalChain): def _call(self, inputs): # 调用父类逻辑获取原始结果 raw_result = super()._call(inputs) # 识别无法回答场景 no_answer_triggers = ["I'm sorry, but I don't have access to", "无法回答该问题"] if any(trigger in raw_result["answer"] for trigger in no_answer_triggers): raw_result.pop("source_documents", None) return raw_result # 用自定义Chain初始化 chain = FilteredConversationalRetrievalChain.from_llm( llm=your_llm_instance, retriever=your_retriever_instance, memory=ConversationBufferMemory(memory_key="chat_history", return_messages=True), return_source_documents=True )
方法3:标准化LLM输出(精准识别)
通过提示词要求LLM在无法回答时返回标准化标记,再用输出解析器识别场景:
- 定义输出格式规范,要求LLM返回结构化结果
- 解析结果后判断是否需要保留源文档
示例代码:
from langchain.output_parsers import JsonOutputParser from langchain.prompts import PromptTemplate from pydantic import BaseModel, Field # 定义结构化输出格式 class AnswerSchema(BaseModel): answer: str = Field(description="回答内容") can_answer: bool = Field(description="是否可回答该问题,无法回答时为false") parser = JsonOutputParser(pydantic_object=AnswerSchema) # 带格式要求的提示词 custom_prompt = PromptTemplate( template="根据提供的文档回答问题,无法回答时请将can_answer设为false。\n{format_instructions}\n问题:{question}\n文档:{context}", input_variables=["question", "context"], partial_variables={"format_instructions": parser.get_format_instructions()} ) # 初始化Chain时使用自定义提示 chain = ConversationalRetrievalChain.from_llm( llm=your_llm_instance, retriever=your_retriever_instance, memory=ConversationBufferMemory(memory_key="chat_history", return_messages=True), return_source_documents=True, combine_docs_chain_kwargs={"prompt": custom_prompt} ) def get_filtered_result(query): result = chain({"question": query}) try: parsed_data = parser.parse(result["answer"]) if not parsed_data.can_answer: result.pop("source_documents", None) except: # 解析失败时默认保留源文档,可根据需求调整逻辑 pass return result
注意事项
- 无法回答的触发关键词需要根据你的LLM实际返回话术调整,确保识别准确;
- 标准化输出的方式可靠性更高,适合LLM回复风格不稳定的场景;
- 自定义Chain的方式对现有代码侵入性小,适合需要多处复用的场景。
内容的提问来源于stack exchange,提问作者suramya verma
相关产品推荐
相关产品推荐

