
1. 项目概述当Agent遇上“内存焦虑”最近在折腾腾讯云上的AI Agent项目相信很多同行都遇到过类似的问题随着对话轮次增加上下文Context越来越长模型消耗的Token数尤其是输入Token直线飙升成本也跟着水涨船高。更头疼的是当上下文长度超过模型限制时Agent的表现就开始“断崖式”下跌要么忘记关键信息要么给出前后矛盾的答案成功率大打折扣。这背后本质上是一个“内存管理”问题——Agent的“工作记忆”不够用了。我负责的一个智能客服分析Agent就卡在了这个瓶颈上。它需要处理长达数小时的客户服务对话记录从中提取问题、总结服务流程、并生成改进报告。最初的方案简单粗暴把所有历史对话一股脑塞进提示词Prompt。结果就是每次调用大模型都极其昂贵而且一旦对话记录超过8K Token当时所用模型的上下文窗口任务失败率就显著升高。我们迫切需要一种方法既能保留对长文档的理解能力又能高效、低成本地利用模型的上下文窗口。经过一系列实验和架构调整我们最终摸索出一套组合方案核心是两板斧用Mermaid无限画布进行信息可视化与结构化压缩以及设计一套智能的上下文卸载与加载机制。实测下来这套方案将Agent运行过程中的有效内存Memory占用降低了约61%平均每次请求的输入Token消耗减少了超过一半而核心任务的成功率则提升了52%。这篇文章我就来详细拆解一下我们是怎么做到的把其中涉及的核心思路、技术选型考量、具体实现步骤以及踩过的坑毫无保留地分享出来。2. 核心思路拆解从“全量装载”到“按需取用”传统的长上下文处理思路往往是“如何把更多内容塞进去”比如使用具有更长上下文窗口的模型如128K、200K或者采用各种文本压缩、摘要技术。但这治标不治本成本问题依然存在且超长上下文下的模型注意力机制可能失效导致“中间丢失”现象。我们的思路发生了根本转变不再追求在单次提示中容纳全部信息而是让Agent学会“按需取用”动态管理自己的“记忆体”。2.1 问题本质Agent的“工作记忆”与“长期记忆”我们可以借鉴人类的记忆系统来理解这个问题。人脑在处理复杂任务时并不会同时激活所有相关知识。我们有一个容量有限的“工作记忆”Working Memory用于处理当前任务还有一个庞大的“长期记忆”Long-Term Memory存储所有经验知识。当需要时我们从长期记忆中提取相关信息到工作记忆中使用。对应到AI Agent工作记忆Working Memory即单次与大模型交互时提供的上下文Prompt Context。它容量有限受模型上下文窗口限制但访问速度极快直接决定模型本次推理的质量。长期记忆Long-Term Memory即Agent能够访问的所有外部知识库、历史对话记录、文档数据等。它容量巨大但无法一次性全部装入工作记忆。传统方法相当于试图把整个“长期记忆”库都塞进“工作记忆”这显然会溢出。我们的新思路是为Agent构建一个高效的外部记忆系统并设计一套机制让Agent能主动、精准地从外部记忆中检索当前任务最需要的片段加载到工作记忆中。2.2 技术选型为什么是Mermaid 向量检索明确了“动态加载”的思路后我们需要解决两个关键子问题如何高效地存储和表示“长期记忆”以便快速检索如何让Agent知道自己需要加载什么对于第一个问题向量数据库Vector Database是当前的主流选择。它将文本转换为高维向量嵌入Embedding并利用相似度搜索来找到与查询语义最相关的文本片段。这解决了“检索”的效率问题。但仅有向量检索还不够。对于复杂的、结构化的长文档如一份产品说明书、一次会议纪要单纯的文本片段检索可能会丢失整体的逻辑结构和关联关系。例如当Agent需要理解一个多步骤流程时它可能需要看到整个流程图而不是分散的几句话。这时Mermaid无限画布的概念就派上用场了。Mermaid是一个基于文本的图表生成工具可以用简单的代码绘制流程图、时序图、类图等。我们所谓的“Mermaid无限画布”并不是一个真正的无限大UI而是一种设计模式用Mermaid语法将复杂的、非结构化的长文本转化为结构化的、可视化的图表摘要。这个图表本身是一段紧凑的文本代码但它承载的信息密度极高并且完美保留了实体间的逻辑关系如顺序、分支、依赖。我们的组合方案是对于输入的长文档首先用大模型或规则将其关键信息提取并转化为一个Mermaid图表代码。这段代码作为该文档的“结构化摘要”存入知识库。同时文档的原始文本经过分块Chunking后转换为向量存入向量数据库。当Agent需要处理相关任务时它可以先快速加载这个轻量级的Mermaid摘要到工作记忆获得全局视野和结构认知。如果需要对某个细节进行深度分析再通过向量检索精准加载相关的原始文本片段。这样我们实现了记忆的“分级存储”和“按需加载”Mermaid摘要作为“索引”和“蓝图”向量化的原始文本作为“详细资料库”。2.3 上下文卸载机制让Agent自己决定记住什么第二个关键点是“卸载”。工作记忆的容量是宝贵的不能只进不出。我们需要让Agent学会在完成一个子任务后主动将一些暂时不需要的中间信息从工作记忆中“卸载”出去腾出空间给下一步。这通过设计智能的Prompt来实现。在Agent的每一步推理或行动后我们可以在Prompt中要求它“请总结当前步骤的关键结论和下一步所需的核心前提并将那些已经处理完毕且后续步骤不再需要的中间细节标记为‘可卸载’。” 然后系统可以将这些“可卸载”的内容移出本次对话的上下文历史或者压缩成一句高度凝练的陈述保留。这个过程可以自动化也可以由开发者在关键节点设计触发。例如在客服分析中Agent先“理解客户问题”然后“定位服务流程节点”。在完成“理解问题”后工作记忆中可以只保留提炼出的“问题类型”和“关键诉求”而将原始冗长的客户描述语句卸载掉。这样在进入“定位流程”步骤时工作记忆就更清爽模型能更专注于流程匹配逻辑。3. 架构设计与核心组件实现下面我将以Python为例结合LangChain框架因其生态丰富便于演示概念拆解这套系统的核心实现。请注意实际生产环境需要根据腾讯云的具体服务如云函数SCF、向量数据库、模型API进行调整。3.1 系统整体架构图整个系统可以分为离线处理记忆入库和在线服务Agent推理两个阶段。离线处理阶段文档加载与分割使用文档加载器如PyPDFLoader,UnstructuredFileLoader读取长文档PDF、Word、TXT等然后用文本分割器RecursiveCharacterTextSplitter将其切成大小适中的文本块Chunks。块大小和重叠区需要根据文档特性调整一般512-1024个字符为一个块重叠100-200字符以保证上下文连贯。生成Mermaid结构化摘要这是核心创新点。我们将整个文档或一个逻辑章节送入一个大模型如GPT-4、Claude-3或腾讯云的混元大模型通过精心设计的Prompt要求模型将其核心内容提炼为一个Mermaid图表。Prompt示例“你是一个信息架构专家。请将以下文本内容的核心逻辑、实体、流程及关系用Mermaid语法例如graph TD, sequenceDiagram表示出来。要求图表应反映整体结构关键节点和决策点必须包含关系要清晰。输出仅包含Mermaid代码。” 得到一段如graph TD A[客户咨询] -- B{问题类型?} B --|技术故障| C[转接技术组] B --|账单疑问| D[查询账单系统] ...的代码。这段代码就是该文档的“记忆蓝图”。向量化存储将上一步分割好的文本块通过嵌入模型Embedding Model如text-embedding-ada-002、BGE、腾讯云Embedding API转换为向量然后存入向量数据库如Chroma,Milvus,腾讯云向量数据库。同时将每个文本块的元数据如所属文档ID、页码、块索引一并存储。摘要存储将生成的Mermaid代码作为一条特殊记录同样进行向量化或者单独存储在一个键值数据库如Redis中并建立与文档的关联索引。在线服务阶段Agent运行接收用户查询/任务。检索Mermaid蓝图首先将用户查询向量化在向量库中检索最相关的Mermaid摘要。因为摘要数量远少于文本块且信息高度浓缩这一步极快。加载蓝图到工作记忆将检索到的Top 1 Mermaid代码片段作为背景信息插入本次Agent调用的Prompt开头。例如“以下是当前讨论主题的流程结构图[Mermaid代码]。请基于此结构图理解后续任务。”Agent初步分析与规划Agent基于Mermaid蓝图和用户查询规划需要深入查看哪些细节。它可能会“自言自语”“根据流程图要解决用户问题我需要先查看‘技术故障处理’环节的具体操作步骤。”精准检索原始细节将Agent“思考”中提到的关键实体或步骤如“技术故障处理”作为新的查询词再次在向量数据库中检索最相关的原始文本块。动态构建工作记忆将检索到的原始文本块可能多个有选择地添加到Prompt中。此时Prompt包含了系统指令、Mermaid蓝图、当前用户查询、相关的原始细节、以及之前的对话历史经过卸载处理。模型推理与输出大模型基于这个精心构建的、信息密度高且结构清晰的工作记忆进行推理生成最终回答或执行动作。上下文卸载在本次交互结束后分析对话历史。将已经解决且后续不再需要的中间讨论内容通过总结压缩或直接移除的方式从下一轮对话的上下文历史中清理掉只保留核心结论和必要的状态。3.2 关键代码模块示例以下是一些关键环节的简化代码示例使用LangChain和OpenAI API实际可替换为腾讯云模型API。1. 生成Mermaid摘要from langchain.chat_models import ChatOpenAI from langchain.schema import HumanMessage, SystemMessage def generate_mermaid_summary(full_text: str) - str: chat ChatOpenAI(model“gpt-4”, temperature0.1) prompt f你是一个信息架构专家。请将以下文本内容的核心逻辑、实体、流程及关系用Mermaid语法表示出来。 要求 1. 优先使用graph TD流程图或sequenceDiagram时序图。 2. 图表应反映整体结构关键节点和决策点必须包含。 3. 关系要清晰使用箭头和说明。 4. 输出仅包含Mermaid代码块不要任何解释。 文本内容 {full_text} messages [ SystemMessage(content“你擅长将复杂信息可视化为结构清晰的图表。”), HumanMessage(contentprompt) ] response chat(messages) # 提取Mermaid代码块假设返回格式正确 return response.content.strip(“”).replace(“mermaid”, “”).strip()2. 向量数据库存储与检索from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter # 初始化 embeddings OpenAIEmbeddings() text_splitter RecursiveCharacterTextSplitter(chunk_size1000, chunk_overlap200) # 假设docs是加载好的文档列表 all_splits text_splitter.split_documents(docs) # 存储向量包含原始文本块 vectorstore Chroma.from_documents(documentsall_splits, embeddingembeddings, persist_directory“./chroma_db”) # 单独存储Mermaid摘要可以将其视为一个特殊的document mermaid_doc Document(page_contentmermaid_code, metadata{“type”: “mermaid_summary”, “doc_id”: doc_id}) vectorstore.add_documents([mermaid_doc]) # 检索 retriever vectorstore.as_retriever(search_kwargs{“k”: 3}) # 检索最相关的3个块 # 检索Mermaid蓝图可以通过元数据过滤或单独一个retriever mermaid_retriever vectorstore.as_retriever( search_kwargs{“k”: 1, “filter”: {“type”: “mermaid_summary”}} # 假设通过metadata过滤 )3. 动态上下文构建与Agent推理这是一个简化的逻辑流程实际可能使用LangChain的AgentExecutor。class ContextAwareAgent: def __init__(self, vectorstore, mermaid_retriever, llm): self.vectorstore vectorstore self.mermaid_retriever mermaid_retriever self.llm llm self.conversation_history [] # 维护经过卸载的历史 def run(self, user_query: str): # 1. 检索Mermaid蓝图 mermaid_context self.mermaid_retriever.get_relevant_documents(user_query) mermaid_prompt f“背景结构图\n{mermaid_context[0].page_content}\n\n” if mermaid_context else “” # 2. 基于蓝图和查询让Agent决定需要什么细节这里简化直接使用查询检索 # 更复杂的可以先用LLM分析蓝图生成几个搜索query detail_docs self.vectorstore.get_relevant_documents(user_query) # 3. 构建最终Prompt detail_context “\n”.join([doc.page_content for doc in detail_docs]) history_context “\n”.join(self.conversation_history[-3:]) # 只保留最近3轮历史 full_prompt f“”{mermaid_prompt} 相关细节信息 {detail_context} 历史对话 {history_context} 当前用户问题{user_query} 请根据提供的结构图和细节信息回答用户问题。 “”” # 4. 调用模型 response self.llm.invoke(full_prompt) # 5. 上下文卸载简化版将本轮QA压缩后存入历史 compressed_qa f“用户问{user_query[:50]}... 回答概要{response[:100]}...” self.conversation_history.append(compressed_qa) # 保持历史长度例如只保留最近5条压缩记录 if len(self.conversation_history) 5: self.conversation_history.pop(0) return response3.3 在腾讯云上的部署考量在腾讯云环境部署此方案有几个优势点和注意事项模型服务可以直接使用腾讯云TI平台提供的各种大模型API混元、ChatGLM等和Embedding API网络延迟低且便于监控和计费管理。将上述代码中的ChatOpenAI和OpenAIEmbeddings替换为对应的腾讯云SDK调用即可。向量数据库腾讯云提供了向量数据库产品完全托管免运维性能和数据持久性有保障。相比自建Chroma在生产环境的稳定性、扩展性和高可用方面优势明显。需要将LangChain的VectorStore接口适配到腾讯云向量数据库的SDK。计算资源离线处理阶段文档解析、生成Mermaid摘要可能消耗较多计算资源和时间适合使用腾讯云函数SCF或批量计算任务异步处理。在线检索和推理部分可以部署在云服务器CVM或容器服务中根据并发量弹性伸缩。安全与网络所有组件模型、向量库、应用最好部署在同一VPC内确保内网通信避免公网传输敏感数据同时降低延迟。注意生成Mermaid摘要的步骤本身也是一次LLM调用有一定成本。因此它更适合对相对静态、需要反复查询的长文档如产品手册、公司制度、历史案例库进行预处理。对于实时流式对话这种摘要可以动态生成并缓存。4. 效果评估与参数调优方案落地后我们建立了评估体系来衡量其效果。核心指标有三个内存/Token节省率比较新方案与旧方案全量加载处理同一任务时平均每次调用消耗的输入Token数。任务成功率针对一组标准测试任务比较新老方案输出正确或可用结果的比例。响应延迟引入检索和动态构建步骤后整体端到端延迟的增加是否在可接受范围。4.1 量化效果对比我们选取了100个复杂的客服对话分析任务进行测试。旧方案是直接将最多10轮的历史对话约平均15000字符压缩后送入模型。新方案采用上述Mermaid摘要向量检索动态加载。指标旧方案全量压缩新方案动态加载提升/节省平均输入Token/次约 8500 tokens约 3300 tokens降低 61.2%任务成功率67%92%提升 25个百分点 (约37%相对提升)平均响应时间2.1秒2.8秒增加0.7秒主要来自检索和多次LLM调用单任务成本估算较高Token消耗大显著降低成本节省与Token节省率近似结果分析Token消耗大幅下降主要归功于不再需要将冗长的原始对话全部编码进上下文而是用轻量的Mermaid蓝图和少量相关片段替代。成功率的提升令人惊喜我们分析原因在于结构化的Mermaid图表帮助模型更好地把握了对话的整体脉络和实体关系避免了长文本中信息淹没和注意力分散的问题使得模型的推理更加精准。延迟虽有增加但通过缓存Mermaid摘要、优化向量检索索引如使用HNSW算法、以及并行化部分操作可以控制在可接受范围内。4.2 关键参数调优心得在实现过程中以下几个参数的调优对效果影响巨大文本分块Chunking策略块大小Chunk Size太小会导致信息碎片化检索到的片段缺乏上下文太大会降低检索精度且加载进Prompt后仍可能占用过多Token。经过测试对于中文客服对话500-800字符的块大小配合150-200字符的重叠是甜点区。对于技术文档可以适当增大到800-1200字符。分割符使用RecursiveCharacterTextSplitter时合理设置分割符优先级如[\n\n, \n, 。, , ]对于保持语义完整性很重要。对于对话文本按“说话人轮次”分割可能是更佳策略。Mermaid摘要生成Prompt指定图表类型在Prompt中明确要求输出graph TD流程图或sequenceDiagram时序图能获得更规整、可用的代码。让模型自由发挥有时会产生不标准的语法。限制复杂度可以要求“图表节点不超过15个”避免生成的Mermaid过于复杂失去摘要的意义。迭代优化生成的Mermaid代码可能需要人工校验或小修。可以设计一个校验步骤用Mermaid解析器如mermaid-cli尝试渲染失败则调整Prompt重试或进行简单后处理。检索相关度阈值Score Threshold向量检索返回的每个片段都有一个相似度分数。设置一个阈值如0.75只有高于此阈值的片段才被加载到Prompt中可以避免引入不相关的噪声信息。这个阈值需要根据Embedding模型和具体任务在验证集上调整。上下文卸载策略卸载粒度是卸载整轮对话还是只卸载对话中的某些句子我们采用了一种混合策略对于“询问-回答”型的简单轮次直接压缩成“Q:... A:...”格式对于包含复杂推理的中间步骤则要求模型自己输出一个“步骤结论摘要”然后用这个摘要替换掉原始的冗长推理文本。历史保留长度即使经过压缩历史对话也不宜无限保留。我们通常保留最近3-5轮压缩后的历史这足以维持对话连贯性又不会过度堆积。5. 常见问题与实战避坑指南在实际开发和上线过程中我们遇到了不少坑这里总结出来希望能帮你绕过去。5.1 Mermaid生成不稳定或格式错误问题LLM生成的Mermaid代码有时语法错误无法渲染或者图表不能准确反映原文结构。解决提供Few-shot示例在Prompt中给出一两个从类似文档生成Mermaid的正面示例能极大提高生成质量和格式稳定性。后处理校验集成mermaid-cli或在线渲染服务对生成的代码进行自动校验。如果渲染失败可以触发重试使用更详细的错误信息作为反馈给模型或降级为使用传统文本摘要。分步生成先让模型提取关键实体和关系列表再让模型根据列表生成Mermaid代码。分步任务对大模型来说更容易完成。5.2 向量检索“找不到”或“找不准”问题用户问题明明在知识库中但检索返回的片段不相关或遗漏关键信息。解决优化Embedding模型不同的Embedding模型对中文语义的理解差异很大。在中文场景下BGE (BAAI/bge-large-zh)、腾讯云Embedding等针对中文优化的模型通常比通用模型效果更好。务必进行对比测试。尝试混合检索Hybrid Search结合向量检索和传统的关键词检索如BM25。有时关键词匹配能弥补语义检索的不足。LangChain的EnsembleRetriever可以方便地实现这一点。查询扩展Query Expansion在检索前先用LLM对原始用户查询进行改写或扩展生成多个同义或相关的查询语句然后用这些语句同时去检索最后合并结果。这能提高召回率。调整分块策略回顾上文不合理的分块是检索不准的常见根源。尝试按章节、按段落或按语义完整性进行分块。5.3 动态上下文导致逻辑断裂问题由于每次只加载部分片段Agent可能会缺乏对全局背景的连续认知导致回答前后不一致或忽略之前提过的信息。解决强化Mermaid蓝图的作用确保蓝图足够强地定义了核心实体和关系并在每一轮Prompt中都显式包含它作为不变的“背景板”。维护一个轻量级全局状态在Agent内部维护一个简单的键值对状态字典记录如“当前讨论的主题”、“已确认的用户需求”等最高层次的结论。这个状态不受上下文卸载影响每轮都手动添加到Prompt中。设计更精细的卸载规则不要盲目卸载所有历史。将与当前任务链紧密相关的上一步结论保留在历史中。可以定义一些规则例如“如果下一步动作依赖于某条信息则该信息不可卸载”。5.4 性能与成本考量问题引入检索和多次LLM调用生成摘要、查询扩展等增加了复杂性和延迟也可能增加成本。解决缓存一切可缓存的Mermaid摘要一旦生成即可永久缓存。向量检索结果也可以根据查询进行短期缓存。对于常见问题甚至可以缓存最终的Agent回答。异步与并行检索Mermaid蓝图和检索详细片段可以并行进行。查询扩展也可以并行执行多个检索。成本核算虽然单次交互可能涉及多次LLM调用生成摘要、查询扩展、主推理但每次调用的Token数都很少且摘要生成是离线或一次性的。总成本通常仍远低于一次性将巨量文本送入大模型。需要根据实际流量和任务类型精细核算。分级模型生成Mermaid摘要、查询扩展等对推理能力要求相对较低的任务可以使用更便宜、更快的模型如腾讯云的轻量级模型而把最复杂的主推理任务留给能力最强也最贵的模型。这套“Mermaid无限画布×上下文卸载”的方案本质上是在当前大模型上下文窗口和成本限制下的一种务实架构创新。它不追求模型的“记忆”无限大而是通过工程手段为Agent装配了一个高效的外部记忆系统和一套智能的内存管理策略。对于处理长文档、多轮复杂对话的Agent应用来说这或许是一条值得深入探索的路径。我们在腾讯云上的实践证明了其有效性希望这些具体的思路和踩坑经验能对你的项目有所启发。