LangChain : 破解长文本问答挑战:智能切分与上下文增强的解决方案

1. 长文本切分信息丢失问答精度曲线

![[Pasted image 20250316183931.png]]

长文本的头尾部分,问答的精度较高,而在中间部分则出现了精度下降的问题。这通常是因为在文本切分过程中,重要信息的上下文可能被丢失,尤其是在长段落的中间部分。

2. 长文本切分信息丢失解决思路

  1. 重叠切割策略(Overlapping Chunks)

    • 在切割文本时设置重叠区域(overlap),确保相邻块之间共享一定的内容

    • 通过增加重叠区域大小,可以减少中间部分上下文丢失的问题

    • 在LangChain中可以通过设置overlap参数来实现:

      text_splitter = RecursiveCharacterTextSplitter(    chunk_size=1000,    chunk_overlap=200  # 增加重叠区域)
      
  2. 分层切割方法(Hierarchical Splitting)

    • 先将文本分割为大的语义单位(如章节),然后再进行细粒度切割
    • 这样可以保留章节内的上下文关系,减少中间部分信息丢失
    • LangChain中可以使用HierarchicalTextSplitter或组合多个切割器实现
  3. 语义感知切割(Semantic-aware Splitting)

    • 使用语义边界(如段落、句子)而非简单的字符数来切割文本

    • 这样可以避免切割点破坏语义完整性

    • 可以通过调整RecursiveCharacterTextSplitter的分隔符优先级来实现:

      text_splitter = RecursiveCharacterTextSplitter(    separators=["\n\n", "\n", ". ", " ", ""],    chunk_size=1000)
      
  4. 动态调整切割大小(Adaptive Chunk Sizing)

    • 对文本不同部分采用不同的切割策略
    • 例如,对文本的中间部分使用更小的chunk_size和更大的overlap
    • 这需要自定义切割器来实现
  5. 切割后的后处理(Post-processing)

    • 为每个切片添加前后文的摘要或关键信息
    • 使用LangChain的Document Transformers对切片进行增强
    • 例如,可以将每个片段的前后位置信息作为元数据添加
  6. 检索增强(Retrieval Enhancement)

    • 在检索阶段改进策略,不仅获取最匹配的片段,还获取其前后文
    • 使用LangChain的ContextualCompressionRetriever进行上下文感知检索
    • 或者使用多查询检索,从不同角度获取相关信息
  7. 句子窗口方法(Sentence Window Retrieval)

    • 将文档切割为句子级别,检索时返回匹配句子及其周围的句子

    • 这可以通过自定义的处理管道实现:

      from langchain.text_splitter import CharacterTextSplitter# 先按句子切割sentence_splitter = CharacterTextSplitter(    separator=".",     chunk_size=10000,    chunk_overlap=0)# 检索时获取句子及其上下文
      

3. LangChain 解决长文本切分信息丢失 综合实例

需求分析

背景案例: 某法律科技公司需要构建一个智能法律助手,用于分析和检索长篇法律文件(如判决书、法律法规等)来回答用户问题。团队发现在测试过程中存在一个明显问题:系统在处理长文本时表现出明显的"位置偏见"——能够准确回答关于文档开头和结尾部分的问题,但对中间部分的回答质量明显下降。

具体问题:

  1. 判决书通常长达数万字,按常规切分方法(如固定字符数)导致中间部分的上下文信息丢失
  2. 法律文件中的因果关系和逻辑推理常常跨越多个段落,简单切分会破坏这些联系
  3. 关键信息(如判决结果)可能在文档多处被引用,但切分后这种联系被切断
  4. 检索系统倾向于返回文档首尾的内容,中间部分的召回率较低

性能指标:

  • 提高中间部分内容的问答准确率(从现有的62%提升至85%以上)
  • 减少"不知道"或错误回答的比例
  • 保持系统响应时间在可接受范围内(<3秒)

解决方案设计

基于上述需求,我们可以设计以下三种方案组合使用:

方案1:分层递进切分 + 重叠窗口

思路: 根据法律文档的结构特点,先按照大的语义单位(如章节)进行切分,再对每个章节进行更细粒度的切分,同时在细粒度切分时保持足够的重叠度。

优势:

  • 保留文档的层次结构信息
  • 通过重叠窗口保证上下文连贯性
  • 适合具有明确章节结构的法律文档

方案2:语义感知切分 + 上下文增强

思路: 使用语义边界(如自然段落、完整句子)进行切分,同时为每个切片添加前后文的摘要信息作为元数据。

优势:

  • 避免切断自然语义单位
  • 通过元数据保留上下文关联
  • 特别适合逻辑关系复杂的法律推理文本

方案3:动态调整切片大小 + 多查询检索

思路: 根据文本位置和内容复杂度动态调整切片大小和重叠度,并在检索时使用多角度查询策略。

优势:

  • 对文档不同部分采用不同的处理策略
  • 通过多角度查询提高中间部分的检索概率
  • 适应不同类型法律文档的特点

代码构建思路

我们将综合使用以上三种方案,构建一个完整的解决方案。核心思路如下:

  1. 实现分层递进切分器,根据文档结构进行自适应切分
  2. 创建语义感知的切分策略,保留自然语义单位
  3. 设计动态参数调整机制,根据文本位置调整切分参数
  4. 实现上下文增强处理器,为每个切片添加关键上下文
  5. 构建多策略检索系统,提高中间部分的召回率

完整实现

下面是基于LangChain的完整实现代码:

import re
from typing import List, Dict, Any, Optional, Tuple
import numpy as np
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate


class AdaptiveDocumentProcessor:
    """自适应文档处理系统,实现分层递进切分与上下文增强"""
    
    def __init__(
        self,
        base_chunk_size: int = 1000,
        base_chunk_overlap: int = 200,
        embeddings_model: str = "text-embedding-ada-002",
        llm_model: str = "gpt-3.5-turbo",
        db_directory: str = "./chroma_db"
    ):
        self.base_chunk_size = base_chunk_size
        self.base_chunk_overlap = base_chunk_overlap
        self.embeddings = OpenAIEmbeddings(model=embeddings_model)
        self.llm = ChatOpenAI(model_name=llm_model, temperature=0)
        self.db_directory = db_directory
        
        # 定义结构识别的正则表达式
        self.section_patterns = {
   
            "header": r"^#+\s+.+$|^.+\n[=\-]+$",  # Markdown标题或下划线式标题
            "paragraph_break": r"\n\n+",          # 段落分隔
            "list_item": r"^\s*[\*\-\+]\s+.+$",   # 列表项
            "code_block": r"```[\s\S]+?```",      # 代码块
            "table": r"\|.+\|.+\|",               # 简单表格识别
        }
    
    def detect_document_structure(self, text: str) -> Dict[str, List[Tuple[int, int]]]:
        """分析文档结构,返回各类结构元素的位置信息"""
        structure_map = {
   }
        
        for element_type, pattern in self.section_patterns.items():
            matches = list(re.finditer(pattern, text, re.MULTILINE))
            if matches:
                structure_map[element_type] = [(m.start(), m.end()) for m in matches]
                
        return structure_map
    
    def calculate_position_weights(self, text: str, position: int) -> float:
        """根据文本位置计算权重,用于动态调整切分参数
        
        文档开头和结尾通常包含更重要的信息,给予更高权重
        """
        total_length = len(text)
        relative_pos = position / total_length
        
        # 使用高斯分布使开头和结尾获得更高权重
        if relative_pos < 0.3:
            # 文档开头部分
            weight = 1.5 - (relative_pos / 0.3) * 0.5
        elif relative_pos > 0.7:
            # 文档结尾部分
            weight = 1.0 + ((relative_pos - 0.7) / 0.3) * 0.5
        else:
            # 文档中间部分
            weight = 1.0
            
        return weight
    
    def adaptive_chunk_parameters(self, text: str, position: int) -> Tuple[int, int]:
        """根据文本位置动态调整chunk大小和重叠参数"""
        weight = self.calculate_position_weights(text, position)
        
        # 调整chunk大小和重叠参数
        adjusted_chunk_size = int(self.base_chunk_size * weight)
        adjusted_overlap = int(self.base_chunk_overlap * weight)
        
        return adjusted_chunk_size, adjusted_overlap
    
    def hierarchical_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI Agent首席体验官

您的打赏是我继续创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值