当前位置: 首页 > news >正文

LLM 能力集成:多轮对话的上下文压缩与长文本处理策略

LLM 能力集成:多轮对话的上下文压缩与长文本处理策略

一、Token 账单与注意力衰减:长对话场景的双重困境

在将 LLM 能力集成到业务系统时,多轮对话的上下文管理是一个绕不开的工程难题。一方面,随着对话轮次增加,输入 Token 数线性增长,API 调用成本随之飙升——一个 20 轮的客服对话,Token 消耗可能达到初始请求的 5 倍以上。另一方面,研究表明当上下文长度超过模型有效注意力范围时,模型对中间位置信息的提取准确率显著下降,即所谓的"Lost in the Middle"现象。

这两个问题形成了一个矛盾:为了保持对话连贯性,需要尽可能多的上下文;但过多的上下文既增加成本,又降低生成质量。简单的滑动窗口方案虽然控制了 Token 数量,却会丢失早期对话中的关键信息——用户在第一轮提到的偏好,到第十轮时可能已经被窗口淘汰。如何在压缩上下文的同时保留关键信息,是 LLM 集成工程中的核心挑战。

二、上下文压缩的三大策略:摘要、检索与结构化提取

上下文压缩的核心思路是将原始对话信息转化为更紧凑的表示形式,同时保留对后续对话有用的语义信息。三种主流策略各有适用场景。

flowchart LR subgraph 原始对话 M1[第1轮: 用户偏好] M2[第2-5轮: 闲聊] M3[第6-8轮: 问题排查] M4[第9轮: 解决方案] M5[第10轮: 新问题] end subgraph 策略一: 滚动摘要 M1 --> S1[摘要1: 用户偏好+闲聊要点] M2 --> S1 S1 --> S2[摘要2: 偏好+排查进展] M3 --> S2 S2 --> S3[摘要3: 偏好+已解决问题] M4 --> S3 S3 --> CTX1[压缩上下文: 摘要3 + 第10轮] M5 --> CTX1 end subgraph 策略二: 检索增强 M1 --> V1[向量化存储] M2 --> V2[向量化存储] M3 --> V3[向量化存储] M4 --> V4[向量化存储] M5 --> Q[查询向量] Q --> |Top-K| CTX2[压缩上下文: Top-K片段 + 第10轮] V1 --> Q V3 --> Q V4 --> Q end subgraph 策略三: 结构化提取 M1 --> E1[偏好: 暗色主题] M3 --> E2[问题: 登录失败] M4 --> E3[方案: 重置密码] E1 --> CTX3[压缩上下文: 结构化摘要 + 第10轮] E2 --> CTX3 E3 --> CTX3 end

滚动摘要:每隔 N 轮对话,调用 LLM 对早期对话生成摘要,用摘要替代原始消息。优点是信息损失可控,缺点是每次摘要调用本身消耗 Token,且摘要错误会累积传播。

检索增强:将所有历史对话向量化存储,每轮对话时根据当前问题检索最相关的 K 个片段。优点是无需丢弃任何信息,缺点是检索噪声可能引入无关上下文。

结构化提取:在对话过程中实时提取关键实体、用户偏好、已解决问题等结构化信息,维护一个动态更新的状态表。优点是信息密度最高,缺点是需要针对业务场景定制提取规则。

三、混合压缩引擎的实现

# context_compressor.py — 多轮对话上下文压缩引擎 import time import json from dataclasses import dataclass, field from typing import Optional import numpy as np @dataclass class ConversationTurn: """单轮对话的数据模型""" turn_id: int role: str # user / assistant content: str timestamp: float = field(default_factory=time.time) token_count: int = 0 entities: list[dict] = field(default_factory=list) # 提取的结构化实体 summary: Optional[str] = None embedding: Optional[np.ndarray] = None class RollingSummarizer: """滚动摘要:每 N 轮生成一次摘要,替代原始对话""" def __init__(self, summarize_fn, window_size: int = 6, max_summary_tokens: int = 200): self._summarize_fn = summarize_fn self.window_size = window_size self.max_summary_tokens = max_summary_tokens self._pending_turns: list[ConversationTurn] = [] self._summaries: list[str] = [] def add_turn(self, turn: ConversationTurn) -> None: """添加一轮对话,达到窗口大小时触发摘要""" self._pending_turns.append(turn) if len(self._pending_turns) >= self.window_size: self._generate_summary() def _generate_summary(self) -> None: """对累积的对话轮次生成摘要""" conversation_text = "\n".join( f"{t.role}: {t.content}" for t in self._pending_turns ) prompt = ( f"请将以下对话压缩为不超过{self.max_summary_tokens}Token的摘要," f"保留关键决策、用户偏好和未解决问题:\n\n{conversation_text}" ) summary = self._summarize_fn(prompt) self._summaries.append(summary) # 为已摘要的轮次标记,避免重复处理 for turn in self._pending_turns: turn.summary = summary self._pending_turns.clear() def get_context(self, current_turn: ConversationTurn, max_tokens: int = 4096) -> list[dict]: """构建压缩后的上下文:历史摘要 + 未摘要轮次 + 当前轮""" context = [] # 历史摘要部分 for summary in self._summaries: context.append({ "role": "system", "content": f"[对话摘要] {summary}" }) # 未摘要的待处理轮次 for turn in self._pending_turns: context.append({ "role": turn.role, "content": turn.content }) # 当前轮 context.append({ "role": current_turn.role, "content": current_turn.content }) return context def flush(self) -> None: """强制将剩余待处理轮次生成摘要""" if self._pending_turns: self._generate_summary() class StructuredExtractor: """结构化提取:从对话中实时提取实体和状态""" def __init__(self, extract_fn, schema: dict): self._extract_fn = extract_fn self.schema = schema # 定义需要提取的字段和类型 self._state: dict = {} def extract(self, turn: ConversationTurn) -> list[dict]: """从一轮对话中提取结构化信息""" prompt = ( f"从以下对话中提取结构化信息,按 JSON Schema 输出:\n" f"Schema: {json.dumps(self.schema, ensure_ascii=False)}\n" f"对话: {turn.role}: {turn.content}\n" f"仅输出 JSON,不要解释。" ) try: result = self._extract_fn(prompt) entities = json.loads(result) if isinstance(entities, dict): entities = [entities] turn.entities = entities # 更新全局状态 for entity in entities: entity_type = entity.get("type", "unknown") entity_key = f"{entity_type}:{entity.get('name', entity.get('id', ''))}" self._state[entity_key] = { "value": entity, "updated_at": time.time(), "source_turn": turn.turn_id, } return entities except (json.JSONDecodeError, Exception): return [] def get_state_summary(self) -> str: """生成当前结构化状态的文本摘要""" if not self._state: return "" lines = ["[当前状态]"] for key, info in self._state.items(): entity = info["value"] lines.append(f"- {key}: {json.dumps(entity, ensure_ascii=False)}") return "\n".join(lines) class HybridCompressor: """混合压缩引擎:结合摘要、检索和结构化提取""" def __init__(self, summarize_fn, extract_fn, embed_fn, entity_schema: dict, max_context_tokens: int = 4096): self.summarizer = RollingSummarizer(summarize_fn, window_size=6) self.extractor = StructuredExtractor(extract_fn, entity_schema) self._embed_fn = embed_fn self._turns: list[ConversationTurn] = [] self._embeddings: list[np.ndarray] = [] self.max_context_tokens = max_context_tokens def process_turn(self, role: str, content: str) -> list[dict]: """处理一轮新对话,返回压缩后的上下文""" turn = ConversationTurn( turn_id=len(self._turns), role=role, content=content, token_count=len(content), # 简化估算 ) # 1. 结构化提取 self.extractor.extract(turn) # 2. 向量化存储 if self._embed_fn: turn.embedding = self._embed_fn(content) self._embeddings.append(turn.embedding) # 3. 滚动摘要 self.summarizer.add_turn(turn) self._turns.append(turn) # 4. 构建混合上下文 return self._build_context(turn) def _build_context(self, current_turn: ConversationTurn) -> list[dict]: """构建混合压缩上下文""" context = [] token_budget = self.max_context_tokens # 优先级1: 结构化状态摘要(信息密度最高) state_summary = self.extractor.get_state_summary() if state_summary: context.append({"role": "system", "content": state_summary}) token_budget -= len(state_summary) # 优先级2: 滚动摘要(保留对话主线) for summary in self.summarizer._summaries: if token_budget <= 0: break context.append({"role": "system", "content": f"[摘要] {summary}"}) token_budget -= len(summary) # 优先级3: 语义检索补充(填充关键细节) if self._embed_fn and current_turn.embedding is not None and token_budget > 0: relevant = self._semantic_search(current_turn.embedding, top_k=3) for turn in relevant: if token_budget <= 0: break context.append({ "role": "system", "content": f"[相关历史] {turn.role}: {turn.content}" }) token_budget -= turn.token_count # 当前轮对话 context.append({ "role": current_turn.role, "content": current_turn.content }) return context def _semantic_search(self, query_embedding: np.ndarray, top_k: int = 3) -> list[ConversationTurn]: """基于向量相似度检索相关历史对话""" if len(self._embeddings) < 2: return [] matrix = np.array(self._embeddings[:-1]) # 排除当前轮 query_norm = query_embedding / (np.linalg.norm(query_embedding) + 1e-8) matrix_norm = matrix / (np.linalg.norm(matrix, axis=1, keepdims=True) + 1e-8) similarities = matrix_norm @ query_norm top_indices = np.argsort(similarities)[-top_k:][::-1] return [self._turns[i] for i in top_indices if similarities[i] > 0.6]

混合压缩引擎按优先级组合三种策略:结构化状态优先(信息密度最高),滚动摘要次之(保留对话主线),语义检索补充(填充关键细节)。这种分层策略在保证关键信息不丢失的前提下,将 20 轮对话的 Token 消耗压缩到原始的 30%-40%。

四、压缩策略的精度损失与延迟代价

上下文压缩本质上是一种有损压缩,必然引入信息损失。滚动摘要的累积误差是最突出的问题——每次摘要都是对前一次摘要的再压缩,经过多轮后,早期对话中的细节可能被完全抹去。缓解方案是在摘要 Prompt 中显式要求保留"未解决问题"和"用户偏好"两类信息,并在结构化提取层做兜底。

延迟代价同样需要关注。每次结构化提取和摘要生成都需要额外的 LLM 调用,在串行模式下会增加 500ms-2s 的响应延迟。生产环境中应将提取和摘要操作异步化——用户请求先基于当前上下文生成响应,提取和摘要在后台异步完成,供下一轮对话使用。

适用边界方面,混合压缩策略适用于客服、咨询等长对话场景。对于简单的指令式交互(如代码补全、翻译),滑动窗口即可满足需求,引入压缩引擎反而增加了不必要的复杂度。

五、总结

多轮对话的上下文压缩是 LLM 工程化落地的关键环节。滚动摘要、检索增强和结构化提取三种策略各有优劣,混合使用可以在 Token 压缩率和信息保留率之间取得平衡。落地时建议优先实现结构化提取(投入产出比最高),再逐步引入滚动摘要和语义检索。异步化是控制延迟的核心手段,压缩操作不应阻塞主响应路径。

http://www.jsqmd.com/news/1015029/

相关文章:

  • 法考报名流程|报名入口|资料已整理
  • Java程序员转大模型:做Agent工程化,我成了部门“AI负责人“ [特殊字符]
  • 2026年福建印染化工原料供应商实力评测:口碑、渠道与真实案例全解析 - 优质品牌商家
  • AI 辅助代码生成质量评估与自动审查:从“能用就行“到“工程级可靠“
  • 20254108 2025-2026-2 《Python程序设计》实验4报告
  • 国内制冷快商用冷柜批发厂家实力排行盘点 - 互联网科技品牌测评
  • 2026 北京管道疏通与异味治理机构精选 5 家 马桶 / 厨卫下水 / 地漏除臭服务参考 - 宅安选房屋修缮
  • 2026年6月专业的长春奥迪Q5L隐形车衣门店找哪家推荐,TPU漆面保护膜、全车改色膜、太阳隔热膜选择指南 - 海棠依旧大
  • 计算机Java毕设实战-基于 SpringBoot 框架的智能健康数据管理系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 医疗数据合规:电子病历作为特殊电子合同的法律认定标准
  • 5步打造专属AI伙伴:SillyTavern角色卡片终极实用指南
  • 鸿蒙原生应用从0到1:项目搭建与首页开发实战
  • Java程序员转大模型:微调Qwen+本地部署,我在自己电脑上跑了一个“ChatGPT“
  • 深度学习模型推理优化:从算子融合到 KV Cache 的全链路加速
  • GSV5800@ACP#Serdes 高速延长芯片,物理 AI 分布式显示的传输骨干
  • 2026年CPE硫化剂厂家选型参考:技术参数、应用场景与主流供应商分析 - 优质品牌商家
  • 2026年6月山东地区诚信可靠的管链输送设备直销厂家与选择分析 - 品牌鉴赏官2026
  • 如何让老款Mac运行最新macOS?OpenCore Legacy Patcher完整指南
  • 2026年西北地区消防水箱与生活水箱供应商综合评估:从技术实力到项目案例的全景分析 - 优质品牌商家
  • GY001-WiFiBLE+4G转CAN总线或RS485中高速通信 - 4G通信CAN数据发送到UDP, GPS上传数据, 4G转CAN总线的1毫秒一帧通信测试,实际做到了微秒级速率
  • 宴会餐厅厨用设备厂家排行 实测性能与服务对比 - 互联网科技品牌测评
  • 2026年镀锌铁皮架空保温钢管厂家怎么选?四川、西藏、贵州市场深度分析与真实案例参考 - 优质品牌商家
  • 计算机Java毕设实战-基于 SpringBoot 框架的足球俱乐部赛事管理系统的设计与实现 前后端分离架构下足球俱乐部综合管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • Java毕设选题推荐:基于 Web 的随机组卷数学题库管理系统的设计与实现 辅助教学的 Web 数学试题智能生成系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • trace.moe:如何用AI瞬间定位任意动漫场景
  • 自助打印机怎么选?2026年主流厂商与场景化方案全解析 - 优质品牌商家
  • 计算机Java毕设实战-基于 SpringBoot 框架的高校校园信息交互系统的设计与实现 面向师生的校园信息共享服务系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 2026 年 6 月泰州 GEO/SEO 优化公司实测:十家头部服务商真实转化效果对比 - 936品牌测评网
  • 2026年6月专业的石家庄三角钢琴搬运公司口碑推荐:立式钢琴、三角钢琴、自动演奏钢琴搬运选择指南 - 海棠依旧大
  • 企业加密软件排行榜,6款企业透明加密软件分享,亲测推荐