前言
之前遇到一个问题,公司大模型的长度不长,而且业务需要RAG返回不少的相关内容。通常RAG都是取top-10/15,文档由于长度限制不能太多,加上embedding效果不佳,返回的文档不多也不能保证都相关。
我们考虑到返回的文档中真正能被用于求解的文本可能只占总文本的一部分,其他无关部分只会影响大模型,而且会占用上下文长度。
因此我们对RAG文档进行问题相关的总计/query-focused summarization,一方面可以过滤无关内容,另一方面让文本更助于大模型回答问题。此外,同样的top-k设置下,过滤压缩后上下文长度变短了,可以倍数增长top-k,即使总结会有些信息损失,更多的文档可以弥补这方面。
这个文章只介绍基础的总结技术,代码基于langchain实现。
文档摘要的核心原则
在构建摘要生成器时,一个核心问题是:如何将文档呈现给 LLM 的上下文窗口?
主要方法包括:
-
Stuff(完整输入):
直接将整个文档一次性放入上下文窗口。方法简单,但在处理长文档时受限。 -
Map-Reduce(分块合并):
将文档拆分为多个小块,分别对每个部分进行摘要,然后合并各部分摘要得到最终结果。适用于处理大规模数据集。 -
Refine(逐步优化):
按顺序处理文档,并在摘要过程中不断融合先前的摘要和新内容,从而逐步优化总结结果。适用于需要更精细摘要的场景。
import os
import json
from langchain_core.documents import Document
data = []
file_path = './data/data_100.json'
with open(file_path) as f:
for line in f:
a_record = json.loads(line)
data.append(a_record)
print(len(data))
data_indice = 0
a_query = data[data_indice]['query']
a_docs = data[data_indice]['pos']
a_docs = [Document(item) for item in a_docs]
100
Stuff
它将一组文档直接插入提示(prompt),然后将该提示发送给 LLM。
该方法适用于文档较小、且每次调用仅需处理少量文档的应用场景。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = PromptTemplate(
input_variables=["context", "query"],
template=(
"请清晰简明地总结以下文本以回答问题。\n\n"
"在总结时,请注意以下几点:\n"
"- 包含关键事件、重要事实和核心信息。\n"
"- 省略不必要的细节。\n\n"
"[问题-开始]:\n{query}\n[问题-结束]\n\n"
"[需要总结的文本-开始]:\n{context}\n[需要总结的文本-结束]\n"
"摘要:"
)
)
llm = ChatOpenAI(
base_url='http://localhost:5551/v1',
api_key='EMPTY',
model_name='Qwen2.5-7B-Instruct',
temperature=0.2,
)
output_parser = StrOutputParser()
map_chain = prompt | llm | output_parser
result = map_chain.invoke(
{
"query":a_query,
"context":'\n'.join([item.page_content for item in a_docs])
}
)
print(a_query)
print(20*'=')
print(result)
澳大利亚新任外长黄英贤近日访问了哪个国家,他的目的是什么?
====================
澳大利亚新任外长黄英贤访问了所罗门群岛,目的是加强澳大利亚与太平洋国家的安全合作,并强调澳大利亚在该地区的存在感。黄英贤与所罗门群岛总理索加瓦雷进行了会谈,强调了澳大利亚警方在该国骚乱后的援助,并表示澳大利亚不会在所罗门群岛建立军事基地。黄英贤的访问正值中国与所罗门群岛签署安全协议引发地区关切之际,澳大利亚、新西兰和美国等国也在该地区采取行动,试图抗衡中国在太平洋地区的影响力。
事实上既然直接 stuff 的话, 不一点需要先总结再回答, 可以直接回答问题。
在我看来, stuff 可以算作一个 CoT 环节, 先显示找出相关内容, 再求解问题
Map-Reduce
Map-Reduce 摘要是一种有效的长文档压缩技术,主要包含两个阶段:
- Map 阶段:将文档拆分为多个小块,并对每个部分独立生成摘要。
- Reduce 阶段:将各个部分的摘要合并,形成连贯的最终摘要。
该方法在处理超长文档时尤为有用,因为它允许在 Map 阶段对各个块并行处理,从而提高效率。此外,它还能有效规避语言模型的 token 限制,确保每个文本块都能适应模型的上下文窗口。
Map 阶段
在 Map 阶段,通常对每个文本块进行摘要生成。
标准方法是对每个块的内容进行总结,但另一种替代方式是提取关键信息。
由于 Reduce 阶段最终会将所有输出合并为最终摘要,因此这两种方法通常都能有效完成任务,并且对最终结果的影响较小。
在 Map 阶段选择摘要生成还是关键信息提取,可以根据具体任务的目标和需求进行调整。
import asyncio
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
# 定义输出格式
class MapSummary(BaseModel):
reasoning: str = Field(description="关于问题、文本内容、两者之间关联性的思考")
summary: str = Field(description="对文本中与问题相关片段的内容总结")
# 创建解析器
map_parser = PydanticOutputParser(pydantic_object=MapSummary)
# 创建模板
map_prompt = ChatPromptTemplate.from_messages(

最低0.47元/天 解锁文章
1939

被折叠的 条评论
为什么被折叠?



