此笔记由idea插件辅助生成
idea插件推荐 AnNote - IntelliJ IDEs Plugin | Marketplace 75 折折扣:
MGRYF-TJW4N-WZMSJ-MZDLD-LVGJH
BTKQ8-XZLPH-L3QH3-MPKBH-BP9RR
之前我们讲到langchain的rag问答,有兴趣的同学可以做下回顾
今天我们来了解下如何基于前文的方案实现长文本总结
为什么需要文本总结
通常会议内容是冗长的,如果能够提取关键信息的话,能够帮我们节省大量的时间
模型不能总结吗,为什么单独提出来长文本这个概念
大部分模型都会限制输入长度,如果会议长度超出了模型的限制则无法进行总结
方案
langchain提供了多种方案供我们选择,https://python.langchain.com/v0.1/docs/use_cases/summarization/
- stuff:全文本总结,将整个文本全部投入模型;这样仍然可能会超出模型
- MapReduce:将文本拆成n个小段,每个小段分别总结,然后再将最终的内容一起总结;这样虽然能解决问题,但是可能会破坏文本的上下文导致最终的结果不理想
- refine:和MapReduce相似的是将文本拆成n个小段,但是会以循环的方式先总结第一段,然后将第一段的总结结果和第二段再总结以此类推,此方法能够更好的保留原文的语义
难点
- 代码实现
- 流式返回
- 如何确定是最后一轮的返回(在流式响应的情况下,每轮都会返回总结结果,那么入会确定是最后一轮并返回个前端)
实现
由于langchain的部分实现比较紧凑,导致做二次开发不是很方便,所以可能有部分修改源码的地方
- 创建文本加载工具,用于加载文本
AttachCode
from typing import Dict, Optional
from langchain.chains.combine_documents.base import AnalyzeDocumentChain
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.callbacks import CallbackManagerForChainRun
from modules.BasinessException import BusinessException
from modules.resultCodeEnum import ResultCodeEnum
from service.SubtitleService import SubtitleService
from utils import constants
from utils.logger import logger
class download_summarize_chain(AnalyzeDocumentChain):
def _call(
self,
inputs: Dict[str, str],
run_manager: Optional[CallbackManagerForChainRun] = None,
) -> dict[str, str]:
docs = self.get_docs(inputs, run_manager)
# Other keys are assumed to be needed for LLM prediction
other_keys: Dict = {k: v for k, v in inputs.items() if k != self.input_key}
other_keys[self.combine_docs_chain.input_key] = docs
_run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager()
return self.combine_docs_chain(
other_keys, return_only_outputs=True, callbacks=_run_manager.get_child()
)
def get_docs(self, inputs, run_manager):
file_download_url = str(inputs[constants.TRANSCRIPTION_FILE_URL])
if file_download_url is not None and file_download_url.startswith("http"):
# 通过下载地址下载文件
loader = WebBaseLoader(file_download_url, None, False)
"""Split document into chunks and pass to CombineDocumentsChain."""
document = loader.load()[0].page_content
if len(document) <= 0:
logger.error(f"file not exists:{file_download_url}")
raise BusinessException.new_instance_with_rce(400, ResultCodeEnum.EMPTY_CONTENT)
else:
# 通过企业id和会议id获取字幕
enterprise_id: str = run_manager.metadata.get(constants.ENTERPRISE_ID)
meeting_id: str = run_manager.metadata.get(constants.MEETING_ID)
logger.info(f"process task with llm:{enterprise_id}-{meeting_id}")
document = SubtitleService().fetch_subtitles(enterprise_id=enterprise_id, meeting_id=meeting_id)
docs = self.text_splitter.create_documents([document])
logger.info("number of splitting doc parts:{}", len(docs))
return docs
- 创建重写chain,实现迭代次数的记录
AttachCode
"""Load summarizing chains."""
from typing import Any, Mapping, Optional, Protocol
from langchain.chains.combine_documents.base import BaseCombineDocumentsChain
from langchain.chains.combine_documents.map_reduce import MapReduceDocumentsChain
from langchain.chains.combine_documents.reduce import ReduceDocumentsChain
from langchain.chains.combine_documents.stuff import StuffDocumentsChain
from langchain.chains.llm import LLMChain
from langchain.chains.summarize import map_reduce_prompt, refine_prompts, stuff_prompt
from langchain_core.callbacks import Callbacks
from langchain_core.language_models import BaseLanguageModel
from langchain_core.prompts import BasePromptTemplate
from adapters.langchain.chains.refine import RefineDocumentsChain
clas