Langchain 源码剖析 Chains系列(三)

释放双眼,带上耳机,听听看~!

ConversationalRetrievalChain

ConversationalRetrievalChain类旨在处理包含聊天历史记录的对话链路。这个类与RetrievalQAChain类的主要区别在于,前者可以接收并使用聊天历史作为输入参数。这意味着,ConversationalRetrievalChain可以基于整个聊天对话的上下文来生成新的问题和答案,而不仅仅是当前的问题。

具体来说,当我们使用ConversationalRetrievalChain进行问答系统构建时,我们可以传入一个包含历史聊天记录的字典。这种方式使得系统能够理解前文的语境,从而生成更精准、更相关的后续问题和答案。反观RetrievalQAChain类,它仅关注单次的问题和答案交互,而无法利用到之前的对话历史。

因此,如果我们需要构建一个更复杂的对话系统,考虑到全局的聊天历史和上下文,ConversationalRetrievalChain会是一个更合适的选择。

快速开始

from langchain.memory import ConversationBufferMemory
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.document_loaders import PyPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.chat_models import ChatOpenAI

loader = PyPDFLoader("试用期评估及转正管理办法.pdf")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents=documents)

embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(texts, embeddings)

# 创建一个内存对象,用于跟踪输入/输出并进行对话
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

qa = ConversationalRetrievalChain.from_llm(ChatOpenAI(temperature=0), docsearch.as_retriever(), memory=memory)
query = "如何转正?"
result = qa({"question": query})
# {'question': '如何转正?', 'chat_history': [HumanMessage(content='如何转正?', additional_kwargs={}, example=False), AIMessage(content='根据提供的信息,员工可以通过以下方式进行转正:nn1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。转正申请将按照试用期满评估程序进行评估。nn2. 按期转正:根据试用期满评估程序进行转正。在试用期结束时,人事行政部将发放《试用期转正述职报告模板》给新员工填写,并将《试用期转正评估表》发给部门负责人。新员工需要填写述职报告并提交给部门负责人审核,然后由人事行政部安排转正述职评审。nn3. 终止试用:如果试用期员工在试用期间不能达到公司岗位任职要求,公司可以随时停止试用并予以辞退。此外,如果试用期员工在试用期间严重违反公司规章制度并造成不良影响,公司也可以随时停止试用并予以辞退。nn请注意,具体的转正流程和要求可能会根据公司的规定而有所不同。建议您与人事部门或直接上级咨询,以了解公司的具体转正政策和程序。', additional_kwargs={}, example=False)], 'answer': '根据提供的信息,员工可以通过以下方式进行转正:nn1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。转正申请将按照试用期满评估程序进行评估。nn2. 按期转正:根据试用期满评估程序进行转正。在试用期结束时,人事行政部将发放《试用期转正述职报告模板》给新员工填写,并将《试用期转正评估表》发给部门负责人。新员工需要填写述职报告并提交给部门负责人审核,然后由人事行政部安排转正述职评审。nn3. 终止试用:如果试用期员工在试用期间不能达到公司岗位任职要求,公司可以随时停止试用并予以辞退。此外,如果试用期员工在试用期间严重违反公司规章制度并造成不良影响,公司也可以随时停止试用并予以辞退。nn请注意,具体的转正流程和要求可能会根据公司的规定而有所不同。建议您与人事部门或直接上级咨询,以了解公司的具体转正政策和程序。'}
print(result)
print(result.keys())   # dict_keys(['question', 'chat_history', 'answer'])
"""
根据提供的信息,员工可以通过以下方式进行转正:

1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。转正申请将按照试用期满评估程序进行评估。

2. 按期转正:根据试用期满评估程序进行转正。在试用期结束时,人事行政部将发给新员工《试用期转正述职报告模板》和《试用期转正评估表》,新员工需要填写述职报告并提交给部门负责人审核,然后参与转正述职评审。

3. 终止试用:如果试用期员工在试用期间不能达到公司岗位任职要求,公司可以随时停止试用并予以辞退。此外,如果试用期员工在试用期间严重违反公司规章制度并造成不良影响,公司也可以随时停止试用并予以辞退。

请注意,具体的转正流程和要求可能会根据公司的规定而有所不同。建议您咨询人事部门或相关负责人以获取准确的信息。
"""
print(result["answer"])

返回retriever搜索到的源文档

构建ConversationalRetrievalChain对象的时候通过指定return_source_documents:True会在结果中返回retriver通过get_relevent_docs 得到的文档列表

from langchain.memory import ConversationBufferMemory
from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.document_loaders import PyPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.chat_models import ChatOpenAI

loader = PyPDFLoader("试用期评估及转正管理办法.pdf")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents=documents)

embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(texts, embeddings)

# 创建一个内存对象,用于跟踪输入/输出并进行对话
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True, output_key="answer")

qa = ConversationalRetrievalChain.from_llm(ChatOpenAI(temperature=0),
                                           docsearch.as_retriever(),
                                           memory=memory,
                                           return_source_documents=True)
query = "如何转正?"
result = qa({"question": query})
# {'question': '如何转正?', 'chat_history': [HumanMessage(content='如何转正?', additional_kwargs={}, example=False), AIMessage(content='根据提供的信息,员工可以通过以下方式进行转正:nn1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。转正申请将按照试用期满评估程序进行评估。nn2. 按期转正:根据试用期满评估程序进行转正。在试用期结束时,人事行政部将发给新员工《试用期转正述职报告模板》和《试用期转正评估表》,新员工需要填写述职报告并提交给部门负责人审核,然后参与转正述职评审。nn3. 终止试用:如果试用期员工在试用期间不能达到公司岗位任职要求,公司可以随时停止试用并予以辞退。此外,如果试用期员工在试用期间严重违反公司规章制度并造成不良影响,公司也可以随时停止试用并予以辞退。nn请注意,具体的转正流程和要求可能会根据公司的规定而有所不同。建议您与所在公司的人事部门或直接上级咨询,以了解公司的具体转正政策和程序。', additional_kwargs={}, example=False)], 'answer': '根据提供的信息,员工可以通过以下方式进行转正:nn1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。转正申请将按照试用期满评估程序进行评估。nn2. 按期转正:根据试用期满评估程序进行转正。在试用期结束时,人事行政部将发给新员工《试用期转正述职报告模板》和《试用期转正评估表》,新员工需要填写述职报告并提交给部门负责人审核,然后参与转正述职评审。nn3. 终止试用:如果试用期员工在试用期间不能达到公司岗位任职要求,公司可以随时停止试用并予以辞退。此外,如果试用期员工在试用期间严重违反公司规章制度并造成不良影响,公司也可以随时停止试用并予以辞退。nn请注意,具体的转正流程和要求可能会根据公司的规定而有所不同。建议您与所在公司的人事部门或直接上级咨询,以了解公司的具体转正政策和程序。', 'source_documents': [Document(page_content='5.2.6新员工转为正式员工后由人事行政部以邮件形式发出《转正通知单》的形式以通知转正人员相关n事宜。6.转正类别转正可分为提前转正、按期转正、终止试用四个类别。n6.1提前转正:新员工入职三个月以后,确因工作态度、工作绩效、团队融合表现突出者,部门负责人n可为员工申请提前转正。并依据本办法5.2试用期满评估程序实施转正。n6.2按期转正:依据本办法5.2试用期满评估程序实施转正。n6.3终止试用n6.3.1试用期员工在试用期如不能认同企业文化,感到公司状况与个人预期差距较大或者其它原因决定n离开公司的,可提出辞职,但应提前3日以书面形式提交辞职报告。n6.3.2试用期员工在试用期间如不能达到公司岗位任职要求,公司可随时停止试用,予以辞退。n6.3.3试用期员工在试用期间有严重违反公司规章制度行为并造成不良影响的,公司可随时停止试用,n予以辞退。n6.3.4终止试用的员工,至人事行政部办理离职手续。7.转正日期新员工如果提前转正,则转正日期以最终审批的提前转正日期为准;如果按期转正,新员工的转正日期n为试用期满之日。8.其他及附件8.1本办法自发布之日起生效。n8.2附件n《月度工作计划表》n《试用期转正述职报告》n《转正述职与答辩评分表》n《试用期转正评估表》n《员工转正通知书》n二○一九年八月[COMPANY_NAME_CN]', metadata={'source': '试用期评估及转正管理办法.pdf', 'page': 1}), Document(page_content='试用期评估及转正管理办法n1目的n为帮助员工发展、统一员工在试用期内评估及转正要求与流程,明确各相关部门的工作职责,特制定本n办法。n2适用范围n本办法适用于公司所有新进员工。n3定义n3.1试用期:指劳动合同期限内公司与员工为相互了解对方而约定的考察期间。n3.2试用期期限根据相关劳动法律法规及工作岗位性质确定。n3.3转正:指新员工试用期满,达到岗位任职资格,并按规定办完相应手续后,成为公司的正式员工。n4各方权责n4.1试用期员工的权责n主动了解自己的工作职责与工作内容、工作目标、公司文化等各项规章制度;n接受人力部门的入职培训与相关评估;n4.2试用期各部门负责人的权责n在工作中引导新员工领悟并融入公司的文化、督促并指导其执行公司各项任务、工作须知及管理制度;n帮助新员工,使其了解并掌握所任职岗位的岗位职责、内容、目标与岗位应知应会;n4.3人事行政部的权责n培训新员工有关公司的组织、文化、发展、管理制度及日常办公工作须知等;n负责试用期员工的评估执行。n5试用期评估n试用期员工的评估分为两种形式:中间评估与期满综合评估。中间评估自入职之日起试用期内分阶段(每n阶段按月或者按照季度设定)进行,期满综合评估在试用期结束时进行。n5.1中间评估程序n5.1.1部门负责人必须在入职第一个月内与新员工确定阶段性(月度或者季度)工作目标。n5.1.2在阶段结束时,部门负责人与新员工对本阶段学习任务与工作目标达成情况以及下阶段工作计划n与目标进行沟通,对新员工遇到的疑问进行合理解答,对新员工遇到的困难进行及时解决。n5.1.3中间评估完成后由部门负责人批准将表格原件备案至人事行政部,人事行政部对月度评估表的合n理性进行评估确认。n5.2试用期满综合评估n5.2.1试用期期满前10天,人事行政部将试用期《试用期转正述职报告模板》发给新员工,抄送部门n负责人;将《试用期转正评估表》发给部门负责人;n5.2.2新员工填写《试用期转正述职报告模板》,对试用期间的学习与工作心得进行总结,于转正期满n前一周完成并交部门负责人审核,交人事行政部备案;n5.2.3部门负责人初步考核通过后,人事行政部安排新员工转正述职,邀请关联团队经理、部门负责人、n公司负责人、HR作为评审人员参与转正述职评审;n5.2.4评审人员根据新员工现场述职、述职材料、既往的中间评估给予综合评分,最终评分≥3.5分方n为通过转正述职评估;n5.2.5部门负责人需于新员工试用期满前3天完成《试用期转正评估表》;', metadata={'source': '试用期评估及转正管理办法.pdf', 'page': 0})]}
print(result)
print(result.keys())   # dict_keys(['question', 'chat_history', 'answer', 'source_documents'])
print(result["source_documents"]) # [Document(page_content='5.2.6新员工转为正式员工后由人事行政部以邮件形式发出《转正通知单》的形式以通知转正人员相关n事宜。6.转正类别转正可分为提前转正、按期转正、终止试用四个类别。n6.1提前转正:新员工入职三个月以后,确因工作态度、工作绩效、团队融合表现突出者,部门负责人n可为员工申请提前转正。并依据本办法5.2试用期满评估程序实施转正。n6.2按期转正:依据本办法5.2试用期满评估程序实施转正。n6.3终止试用n6.3.1试用期员工在试用期如不能认同企业文化,感到公司状况与个人预期差距较大或者其它原因决定n离开公司的,可提出辞职,但应提前3日以书面形式提交辞职报告。n6.3.2试用期员工在试用期间如不能达到公司岗位任职要求,公司可随时停止试用,予以辞退。n6.3.3试用期员工在试用期间有严重违反公司规章制度行为并造成不良影响的,公司可随时停止试用,n予以辞退。n6.3.4终止试用的员工,至人事行政部办理离职手续。7.转正日期新员工如果提前转正,则转正日期以最终审批的提前转正日期为准;如果按期转正,新员工的转正日期n为试用期满之日。8.其他及附件8.1本办法自发布之日起生效。n8.2附件n《月度工作计划表》n《试用期转正述职报告》n《转正述职与答辩评分表》n《试用期转正评估表》n《员工转正通知书》n二○一九年八月[COMPANY_NAME_CN]', metadata={'source': '试用期评估及转正管理办法.pdf', 'page': 1}), Document(page_content='试用期评估及转正管理办法n1目的n为帮助员工发展、统一员工在试用期内评估及转正要求与流程,明确各相关部门的工作职责,特制定本n办法。n2适用范围n本办法适用于公司所有新进员工。n3定义n3.1试用期:指劳动合同期限内公司与员工为相互了解对方而约定的考察期间。n3.2试用期期限根据相关劳动法律法规及工作岗位性质确定。n3.3转正:指新员工试用期满,达到岗位任职资格,并按规定办完相应手续后,成为公司的正式员工。n4各方权责n4.1试用期员工的权责n主动了解自己的工作职责与工作内容、工作目标、公司文化等各项规章制度;n接受人力部门的入职培训与相关评估;n4.2试用期各部门负责人的权责n在工作中引导新员工领悟并融入公司的文化、督促并指导其执行公司各项任务、工作须知及管理制度;n帮助新员工,使其了解并掌握所任职岗位的岗位职责、内容、目标与岗位应知应会;n4.3人事行政部的权责n培训新员工有关公司的组织、文化、发展、管理制度及日常办公工作须知等;n负责试用期员工的评估执行。n5试用期评估n试用期员工的评估分为两种形式:中间评估与期满综合评估。中间评估自入职之日起试用期内分阶段(每n阶段按月或者按照季度设定)进行,期满综合评估在试用期结束时进行。n5.1中间评估程序n5.1.1部门负责人必须在入职第一个月内与新员工确定阶段性(月度或者季度)工作目标。n5.1.2在阶段结束时,部门负责人与新员工对本阶段学习任务与工作目标达成情况以及下阶段工作计划n与目标进行沟通,对新员工遇到的疑问进行合理解答,对新员工遇到的困难进行及时解决。n5.1.3中间评估完成后由部门负责人批准将表格原件备案至人事行政部,人事行政部对月度评估表的合n理性进行评估确认。n5.2试用期满综合评估n5.2.1试用期期满前10天,人事行政部将试用期《试用期转正述职报告模板》发给新员工,抄送部门n负责人;将《试用期转正评估表》发给部门负责人;n5.2.2新员工填写《试用期转正述职报告模板》,对试用期间的学习与工作心得进行总结,于转正期满n前一周完成并交部门负责人审核,交人事行政部备案;n5.2.3部门负责人初步考核通过后,人事行政部安排新员工转正述职,邀请关联团队经理、部门负责人、n公司负责人、HR作为评审人员参与转正述职评审;n5.2.4评审人员根据新员工现场述职、述职材料、既往的中间评估给予综合评分,最终评分≥3.5分方n为通过转正述职评估;n5.2.5部门负责人需于新员工试用期满前3天完成《试用期转正评估表》;', metadata={'source': '试用期评估及转正管理办法.pdf', 'page': 0})]

手动传入memory

from langchain.chains import RetrievalQA, ConversationalRetrievalChain
from langchain.document_loaders import PyPDFLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.chat_models import ChatOpenAI

loader = PyPDFLoader("试用期评估及转正管理办法.pdf")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_documents(documents=documents)

embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(texts, embeddings)


qa = ConversationalRetrievalChain.from_llm(ChatOpenAI(temperature=0),
                                           docsearch.as_retriever())
chat_history = [("如何转正","根据提供的信息,员工可以通过以下方式进行转正:nn1. 提前转正:如果新员工入职三个月以后,工作态度、工作绩效、团队融合表现突出,部门负责人可以为员工申请提前转正。")]
query = "如何转正?"
result = qa({"question": query, "chat_history": chat_history})
print(result)

这里传给chain的字典中的key必须是chat_history,对应BaseConversationalRetrievalChain的_call 源码中chat_history_str = get_chat_history(inputs[“chat_history”]) 的代码,如果key不使用chat_history那么源码中就取不到历史记录。

源码

Langchain 源码剖析 Chains系列(三)

BaseConversationalRetrievalChain

class BaseConversationalRetrievalChain(Chain):
    """Chain for chatting with an index."""

    combine_docs_chain: BaseCombineDocumentsChain
    question_generator: LLMChain
    output_key: str = "answer"
    rephrase_question: bool = True
    return_source_documents: bool = False
    return_generated_question: bool = False
    get_chat_history: Optional[Callable[[List[CHAT_TURN_TYPE]], str]] = None


    @property
    def input_keys(self) -> List[str]:
        """Input keys."""
        return ["question", "chat_history"]

    @property
    def output_keys(self) -> List[str]:
        _output_keys = [self.output_key]
        if self.return_source_documents:
            _output_keys = _output_keys + ["source_documents"]
        if self.return_generated_question:
            _output_keys = _output_keys + ["generated_question"]
        return _output_keys

    @abstractmethod
    def _get_docs(
        self,
        question: str,
        inputs: Dict[str, Any],
        *,
        run_manager: CallbackManagerForChainRun,
    ) -> List[Document]:
        """Get docs."""

    def _call(
        self,
        inputs: Dict[str, Any],
        run_manager: Optional[CallbackManagerForChainRun] = None,
    ) -> Dict[str, Any]:
        _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
        question = inputs["question"]
        get_chat_history = self.get_chat_history or _get_chat_history
        chat_history_str = get_chat_history(inputs["chat_history"])

        if chat_history_str:
            callbacks = _run_manager.get_child()
            new_question = self.question_generator.run(
                question=question, chat_history=chat_history_str, callbacks=callbacks
            )
        else:
            new_question = question
        accepts_run_manager = (
            "run_manager" in inspect.signature(self._get_docs).parameters
        )
        if accepts_run_manager:
            docs = self._get_docs(new_question, inputs, run_manager=_run_manager)
        else:
            docs = self._get_docs(new_question, inputs)  # type: ignore[call-arg]
        new_inputs = inputs.copy()
        if self.rephrase_question:
            new_inputs["question"] = new_question
        new_inputs["chat_history"] = chat_history_str
        answer = self.combine_docs_chain.run(
            input_documents=docs, callbacks=_run_manager.get_child(), **new_inputs
        )
        output: Dict[str, Any] = {self.output_key: answer}
        if self.return_source_documents:
            output["source_documents"] = docs
        if self.return_generated_question:
            output["generated_question"] = new_question
        return output

属性

“BaseConversationalRetrievalChain类代表一个对话检索链路,它可以被视为聊天机器人或其他交流系统的核心部分。这个类的主要任务是将问题被翻译为更具体的子问题,然后检索相关信息,并最终将答案呈现给用户。下面我们来看一下这个类的各个主要组件:

  • combine_docs_chain:这是一个预定义的操作链,它需要能够接受一组文档并返回合并后的结果。你可以把它想象成一个图书馆员,他的工作是从库中找到与提出的问题相关的所有文本,并把这些文本融合在一起。
  • question_generator:这是一个预定义的链路,它能够接收当前的问题和聊天历史,然后生成一个新的问题,以便进一步检索。如果原始问题太笼统(比如,“告诉我一些关于世界历史的事情?”),question_generator就会生成更具体的问题(例如,“让我们首先探讨一下古罗马历史。”),帮助我们更精准地进行信息检索。
  • output_key:这是一个字符串变量,用于在输出结果中标识答案。你可以把它看作是一把钥匙,用来打开我们需要的答案的宝箱。
  • rephrase_question:这是一个布尔属性,如果为真,则新生成的问题将会替代原问题被传递给下一级的combine_docs_chain。
  • return_source_documents和return_generated_question:这两个布尔属性决定是否要返回源文件和生成的新问题。当他们设为真时,原始检索到的文档和生成的问题会被包含在最终结果中。
  • get_chat_history:这是一个可选的函数,用于获取聊天历史记录。如果没有特定的函数,那么将会使用默认方式获取聊天记录。

BaseConversationalRetrievalChain类就像一个智能的信息检索与处理系统,根据提出的问题,查询相关信息,然后整合这些信息并形成有用的答案。”

方法

BaseConversationalRetrievalChain 包含一个抽象方法_get_docs 需要子类根据子类的定义来实现以及核心模板方法 _call, _call的大致逻辑如下:

  1. 提取问题与聊天历史:从输入数据中提取出用户提问 (question) 和聊天历史 (chat_history)。
  2. 生成新问题:根据是否存在聊天历史,使用 question_generator 生成新问题或直接用原始的问题。
  3. 检索文档:然后根据 _get_docs 方法是否接受 run_manager 参数,选择相应的方式来调用 _get_docs 获取文档。
  4. 复制并修改输入:创建一个新的输入字典 new_inputs,然后根据 rephrase_question 的值决定是否将新生成的问题作为新输入的一部分。
  5. 生成答案:调用 combine_docs_chain.run() 方法处理检索到的所有文档,并结合生成的新问题或者旧问题生成回答,放入 answer 中。
  6. 处理输出:创建输出字典 output,默认将 answer 添加进去,根据设置,可能还会加入源文档和生成的新问题。
  7. 返回结果:最后,返回 output 字典作为结果。

这个流程可以理解为一个查询系统的核心过程:接受问题->理解问题和历史信息->获取与问题相关的信息->处理这些信息生成答案->返回答案(及其他可选的原始信息)。

ConversationalRetrievalChain

这个链接收聊天历史(一系列消息)和新问题,然后返回对该问题的答案。此链的算法包括三个部分:

  1. 利用聊天历史和新问题创建一个”独立问题”。这样做是为了将这个问题传递到检索步骤中以获取相关文档。如果只传入了新问题,那么可能会缺少相关的上下文。如果将整个对话传入检索,那么可能存在一些不必要的信息,这会干扰检索。
  2. 这个新问题被传给检索器,并返回相关的文档。
  3. 检索到的文档会和新问题(默认行为)或原始问题和聊天历史一起传给语言模型(LLM),以生成最终的回应。
class ConversationalRetrievalChain(BaseConversationalRetrievalChain):
    
    retriever: BaseRetriever
    """Retriever to use to fetch documents."""
    max_tokens_limit: Optional[int] = None

    def _reduce_tokens_below_limit(self, docs: List[Document]) -> List[Document]:
        num_docs = len(docs)

        if self.max_tokens_limit and isinstance(
            self.combine_docs_chain, StuffDocumentsChain
        ):
            tokens = [
                self.combine_docs_chain.llm_chain.llm.get_num_tokens(doc.page_content)
                for doc in docs
            ]
            token_count = sum(tokens[:num_docs])
            while token_count > self.max_tokens_limit:
                num_docs -= 1
                token_count -= tokens[num_docs]

        return docs[:num_docs]

    def _get_docs(
        self,
        question: str,
        inputs: Dict[str, Any],
        *,
        run_manager: CallbackManagerForChainRun,
    ) -> List[Document]:
        """Get docs."""
        docs = self.retriever.get_relevant_documents(
            question, callbacks=run_manager.get_child()
        )
        return self._reduce_tokens_below_limit(docs)


    @classmethod
    def from_llm(
        cls,
        llm: BaseLanguageModel,
        retriever: BaseRetriever,
        condense_question_prompt: BasePromptTemplate = CONDENSE_QUESTION_PROMPT,
        chain_type: str = "stuff",
        verbose: bool = False,
        condense_question_llm: Optional[BaseLanguageModel] = None,
        combine_docs_chain_kwargs: Optional[Dict] = None,
        callbacks: Callbacks = None,
        **kwargs: Any,
    ) -> BaseConversationalRetrievalChain:
        combine_docs_chain_kwargs = combine_docs_chain_kwargs or {}
        doc_chain = load_qa_chain(
            llm,
            chain_type=chain_type,
            verbose=verbose,
            callbacks=callbacks,
            **combine_docs_chain_kwargs,
        )

        _llm = condense_question_llm or llm
        condense_question_chain = LLMChain(
            llm=_llm,
            prompt=condense_question_prompt,
            verbose=verbose,
            callbacks=callbacks,
        )
        return cls(
            retriever=retriever,
            combine_docs_chain=doc_chain,
            question_generator=condense_question_chain,
            callbacks=callbacks,
            **kwargs,
        )

ConversationalRetrievalChain类是从BaseConversationalRetrievalChain基础类继承,并实现了抽象方法_get_docs。这个方法在执行过程中会被__call__函数调用来生成文档(docs)。

我们可以通过类方法from_llm了解到更多关于ConversationalRetrievalChain的实现细节。在from_llm函数内部,它创建了两个链(Chain)对象。第一个是condense_question_chain,该对象根据聊天历史(history_str)和当前的问题(question)生成新的、更具体的问题(new_question), 下面是condense_question_chain使用的condense_question_prompt:

_template = """Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question, in its original language.

Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(_template)

第二个是StuffDocumentChain对象,它负责提供文档生成时所需的上下文字符串(context)以及新生成的问题(new_question),并基于这些信息生成最终的答案,这个chain使用的prompt:

Langchain 源码剖析 Chains系列(三)

prompt_template = """Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.

{context}

Question: {question}
Helpful Answer:"""
PROMPT = PromptTemplate(
    template=prompt_template, input_variables=["context", "question"]
)

总之,ConversationalRetrievalChain类采取了一种组合和迭代的方式对问题进行精化,同时利用相关文档的内容生成满足用户需求的答案。下面的一系列截图是ConversationalRetrievalChain带history的debug:

Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

根据chat_history_str和question调用condense_question_llm调用大模型生成新问题:
Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

最终转换后的新问题是:请问转正的具体流程和要求会根据公司的规定而有所不同吗?
Langchain 源码剖析 Chains系列(三)

调用retriver获取docs:
Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

利用新问题和从retrieve获取的docs调用combine_docs_chain获取答案:

Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

Langchain 源码剖析 Chains系列(三)

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

大模型之争:OpenAI即将推出GPT-vision,与谷歌多模态模型竞争

2023-11-29 23:33:14

AI教程

机器学习超参数调优的方法和实战

2023-11-30 1:03:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索