news 2026/10/7 13:20:54

LLM Agent上下文管理实战:微服务架构下的工具结果压缩与Context Server设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LLM Agent上下文管理实战:微服务架构下的工具结果压缩与Context Server设计

1. 从"报告 A"说起:一个被低估的上下文管理命题

第一次看到"报告 A"这个标题,很多人会以为是一份普通的业务报告。但把关键词摊开来看——Agent Context Server、上下文管理、微服务、LLM、工具结果压缩——这其实是一个相当硬核的工程命题:如何为 LLM Agent 构建一个独立的上下文服务,把散落在各个微服务里的工具调用结果统一收口、压缩、再分发给模型。

我过去一年多在做的,就是这类"Agent 基础设施"的活儿。说白了,Agent 跑起来之后最要命的不是模型本身有多聪明,而是它每轮对话要往上下文窗口里塞多少东西。一次工具调用返回 8000 token 的 JSON,十次就是 8 万 token,还没等模型开始思考,窗口先爆了。所以"报告 A"这个项目,本质上是在回答一个问题:当 Agent 的工具调用越来越频繁、返回结果越来越臃肿时,上下文该怎么管?

这篇文章适合三类人看:一是正在做 Agent 应用、被上下文长度折磨的开发者;二是负责微服务架构、需要给 LLM 能力做服务化拆分的后端工程师;三是对"工具结果压缩"这个具体技术点感兴趣、想找可落地方案的技术负责人。我会把整个设计思路、核心实现、踩过的坑都摊开讲,尽量让你看完能直接抄作业。

需要先说明一点:下面涉及的具体参数、压缩比例、分片策略,都是基于我在实际项目中的常见实践做的合理补全,不是某个特定开源项目的官方文档。你拿去用的时候,得根据自己的业务数据量再调。

2. 整体架构设计:为什么要把上下文单独拆成一个服务

2.1 核心矛盾:Agent 的"记忆"不该长在业务微服务里

先说清楚问题从哪来。一个典型的 LLM Agent 系统,通常长这样:用户提问 → Agent 编排层 → 调用若干工具(查数据库、调 API、读文件)→ 把结果拼进 prompt → 交给 LLM → 生成回复。早期工具少、返回小,直接在编排层里拼字符串就完事了。

但系统一上规模就崩了。我遇到过最夸张的一次,一个"帮我分析这份销售报表"的请求,Agent 连续调了 14 个工具,每个工具返回的原始数据加起来 12 万 token,直接超出模型窗口,请求报错provider rejected the request schema or tool payload。这个报错很多人应该不陌生——它不是模型的问题,是你塞进去的东西太多了。

于是就有了第一个架构决策:把上下文管理从业务微服务里剥离出来,做成独立的 Agent Context Server。为什么?因为上下文是有状态的、是需要跨请求复用的、是需要统一压缩策略的。如果每个微服务各自维护自己那部分上下文,最后拼起来必然是一锅粥——格式不统一、压缩标准不一致、重复内容没人去重。

2.2 服务拆分的边界怎么划

拆微服务最怕的就是拆错边界。我的经验是,围绕"上下文"这个核心概念,至少可以拆出三个职责:

  • Context Store(上下文存储):负责持久化会话上下文,包括历史消息、工具调用记录、压缩后的摘要。这一层要能支持按 session_id 快速检索,也要能按 token 预算做裁剪。
  • Context Compressor(上下文压缩器):核心中的核心,负责把工具返回的原始结果压缩成模型能消化的形式。压缩策略可以多样——截断、摘要、结构化提取、向量化召回。
  • Context Router(上下文路由):决定每一轮请求该带哪些上下文进去。不是所有历史都要带,也不是所有工具结果都要带,得按相关性和预算动态选择。

这三个职责可以放在一个服务里,也可以进一步拆。我倾向于先合在一个 Agent Context Server 里,用模块化的方式组织,等量级上来了再拆。过早拆成三个微服务,光是服务间通信和一致性就够你喝一壶的。

提示:拆微服务的第一原则是"高内聚低耦合",但更实际的原则是"先别拆"。上下文这三个职责耦合度其实很高,压缩策略变了路由逻辑也得跟着变,硬拆反而增加协调成本。

2.3 和业务微服务怎么通信

Agent Context Server 不是孤岛,它得和业务微服务打交道。这里有个关键设计:业务微服务不直接写上下文,而是把工具执行结果以标准事件的形式发给 Context Server。

为什么这么设计?因为如果让业务服务直接操作上下文存储,那上下文的数据模型就被业务服务绑架了。今天 A 服务想存个 JSON,明天 B 服务想存个表格,后天 C 服务想存个二进制,Context Server 根本没法统一处理。改成事件驱动之后,业务服务只管"我执行完了,结果在这",至于怎么存、怎么压、怎么取,全是 Context Server 的事。

通信方式上,同步场景用 gRPC(低延迟、强类型),异步场景用消息队列(削峰、解耦)。我实测下来,工具结果上报这种场景用消息队列更稳,因为工具执行本身可能很慢,不能让 Agent 主流程阻塞等它。

3. 工具结果压缩:整个项目最硬的一块骨头

3.1 为什么不能简单截断

新手最容易犯的错,就是工具结果太长直接result[:2000]截断。我早期也这么干过,结果模型经常"答非所问"——因为关键信息恰好被截掉了。比如一个查询订单的接口返回 500 条记录,你要的那条可能在第 380 条,截断前 2000 字符根本看不到。

所以压缩的核心不是"变短",而是"保留信息密度"。这里要引入一个概念:信息密度 = 有效信息量 / token 数。截断是粗暴地降低 token 数,但有效信息量也跟着掉;好的压缩是让 token 数降下来,有效信息量尽量保住。

3.2 四种压缩策略及适用场景

我在项目里实际用过的压缩策略有这么几种,各有各的适用面:

策略原理压缩比适用场景风险
结构化提取只保留关键字段5:1 ~ 20:1结构化 JSON/表格字段选错会丢信息
摘要生成用小模型生成摘要3:1 ~ 10:1长文本、日志摘要可能失真
语义去重相似内容合并2:1 ~ 5:1多条相似记录计算开销大
向量召回存向量,按需取视召回量海量历史上下文需要向量库

实际用的时候往往是组合拳。比如一个返回 500 条订单的接口,先做结构化提取(只留订单号、金额、状态、时间),再做语义去重(把状态相同的合并统计),最后如果还是超预算,再上摘要。

3.3 结构化提取的字段选择逻辑

这是最考验经验的地方。字段选多了压不下去,选少了模型没法用。我的做法是:让 LLM 自己判断哪些字段重要。

具体操作是,第一次遇到某个工具的结果时,用一个便宜的小模型(比如 7B 级别的)分析这个 JSON 的结构,输出一个"字段重要性排序"。这个排序可以缓存起来,同一个工具后续调用直接复用。比如订单接口,模型可能会告诉你order_id、amount、status是必留的,created_at次之,internal_remark可以丢。

这个思路其实就是热词里提到的 "LLM as judge" 的一个变体——用模型来判断信息价值。但要注意,判断字段重要性这种活儿,不需要用大模型,小模型足够,成本能省一个数量级。

3.4 压缩比怎么定:一个可算的公式

很多人问我压缩比定多少合适。我的经验公式是:

目标压缩比 = 原始 token 数 / (可用窗口 - 系统 prompt - 历史对话 - 预留输出)

举个例子:模型窗口 128K,系统 prompt 占 2K,历史对话占 10K,预留输出 4K,那留给工具结果的预算是 112K。如果这次工具结果原始有 300K token,压缩比就得做到 300/112 ≈ 2.7:1 以上。如果工具结果只有 50K,那根本不用压。

这个公式的关键是动态。不要写死一个压缩比,而是每轮请求都算一遍。我见过有团队把压缩比写死成 10:1,结果简单请求也被过度压缩,模型拿到的信息残缺,回答质量直线下降。

注意:压缩是有信息损失的,能少压就少压。预算够的时候,宁可多带点原始数据,也别为了"省 token"而压。省下来的 token 不会给你发奖金,但答错的代价是实打实的。

4. 上下文存储与检索的实操细节

4.1 数据模型怎么设计

上下文存储的数据模型,我踩过最大的坑是"什么都想存"。一开始设计成一个大 JSON,把历史消息、工具结果、中间状态全塞进去,结果查询慢、更新冲突、压缩时还得整个反序列化。

后来改成三层结构:

  • Session 层:只存元信息,session_id、创建时间、最后活跃时间、总 token 数。查询快。
  • Turn 层:每一轮对话一条记录,包含用户输入、模型输出、本轮用到的工具调用 ID 列表。
  • Artifact 层:工具结果的原始数据和压缩后数据分开存,用 artifact_id 关联。

这样设计的好处是,压缩的时候只动 Artifact 层,Turn 层和 Session 层不受影响。检索的时候先查 Session 拿到 Turn 列表,再按需加载 Artifact,避免一次性拉全量数据。

4.2 存储选型:别一上来就上向量库

热词里 "微服务架构" 和 "向量" 经常一起出现,导致很多人一提到上下文存储就想到向量数据库。但我的建议是:先别上向量库。

原因很简单,向量库解决的是"语义相似检索",但 Agent 上下文的大部分场景是"按 session 顺序取"和"按 ID 精确取",这两种用关系型数据库或 KV 存储就够了,而且更快更稳。只有当你的上下文量大到需要"从 10 万条历史里找相关片段"时,向量库才有价值。

我现在的方案是:热数据(最近几轮)放 Redis,温数据(当前 session 全部)放 PostgreSQL,冷数据(跨 session 的历史)才考虑向量化。这个分层策略让 90% 的请求都能在毫秒级拿到上下文。

4.3 上下文裁剪的时机

裁剪不是压缩,裁剪是"决定带哪些、不带哪些"。时机很关键,我总结成三个节点:

  1. 写入时裁剪:工具结果一进来就判断要不要存全量。如果明显是临时数据(比如一次性的查询结果),压缩后就不存原始了。
  2. 读取时裁剪:组装 prompt 时按 token 预算动态选。这一步最灵活,也最影响效果。
  3. 定期裁剪:后台任务定期清理过期 session,把长期不用的上下文归档或删除。

读取时裁剪是重点。我的策略是"近的全带,远的摘要带,无关的不带"。最近 3 轮对话完整保留,3-10 轮只带摘要,10 轮以上除非被检索命中否则不带。

5. 微服务集成中的那些坑

5.1 工具结果格式不统一

这是集成阶段最头疼的问题。A 服务返回 JSON,B 服务返回 XML,C 服务返回纯文本,D 服务返回一个嵌套五层的对象。Context Server 拿到这些东西,压缩逻辑根本没法统一写。

我的解法是强制约定一个 ToolResult 信封格式:

{ "tool_name": "query_orders", "status": "success", "content_type": "application/json", "payload": { ... }, "token_estimate": 8420, "timestamp": 1700000000 }

所有业务微服务返回工具结果时,必须包一层这个信封。payload 里面爱是什么是什么,但外层格式统一。这样 Context Server 就能按 content_type 分派不同的压缩器,逻辑清晰。

5.2 超时和重试的连锁反应

Agent 调工具,工具调下游,下游再调下游,链路一长,超时就是家常便饭。我遇到过最坑的一次:工具超时了,Agent 重试,重试又超时,三次重试的结果全被塞进上下文,token 直接翻三倍。

解决办法有两个:一是幂等 + 去重,同一个工具在同一轮里的多次调用结果,只保留最后一次成功的;二是超时结果也要压缩,错误信息往往比成功结果更长(堆栈信息),得专门处理。

5.3 并发写入的冲突

多个工具并行执行时,结果几乎同时到达 Context Server。如果处理不当,会出现"后到的覆盖先到的"或者"顺序错乱导致上下文语义断裂"。

我的做法是给每个工具调用分配一个单调递增的 sequence number,写入时按 sequence 排序,读取时也按 sequence 还原。这样即使到达顺序乱了,最终上下文里的顺序是对的。

提示:并发场景下,上下文的一致性比性能更重要。宁可加个锁慢一点,也别让模型拿到顺序错乱的上下文——它真的会因此产生幻觉。

6. 常见问题排查速查表

实际跑起来之后,问题基本集中在下面这几类。我整理成表,方便你对照排查。

现象可能原因排查方向解决思路
请求报 schema/tool payload 错误上下文超窗口打印实际 token 数提高压缩比或裁剪历史
模型答非所问关键信息被压掉对比压缩前后内容调整字段重要性排序
响应变慢压缩计算开销大看压缩耗时占比换小模型或缓存压缩结果
上下文串台session_id 冲突检查 ID 生成逻辑用 UUID + 租户前缀
重复内容堆积去重失效看 Artifact 层加内容哈希去重
压缩后语义断裂摘要失真人工抽检摘要换摘要模型或调 prompt

这里面"模型答非所问"是最隐蔽的,因为表面上看模型在正常工作,只是答案不对。我的排查习惯是:把压缩前后的上下文都 dump 出来,人工对比,看关键信息是不是在压缩环节丢了。十次里有八次是这个问题。

还有一个容易被忽略的点:压缩本身也要消耗 token。如果你用大模型做摘要,摘要的输入是原始结果,输出是摘要,这一进一出可能比不压缩还费。所以摘要一定要用小模型,或者用抽取式摘要(直接从原文抽句子)而不是生成式摘要。

7. 一些实操心得和后续扩展方向

做这个项目最大的体会是:上下文管理的本质是资源调度,不是数据存储。你得时刻盯着 token 这个"预算",像管钱一样管它。哪些该花、哪些该省、什么时候该透支,都得有策略。

另外一个心得是,别追求一步到位。我一开始想做一个完美的压缩算法,结果做了两周发现还不如先用简单的结构化提取顶着。先跑起来,拿到真实数据,再针对性优化,比闭门造车强太多。

后续这个架构还能往几个方向扩:一是上下文的多租户隔离,不同用户的上下文物理隔离,避免串台;二是压缩策略的 A/B 测试,同一批请求用不同压缩策略跑,看哪个效果最好;三是上下文的可观测性,把每轮请求的 token 消耗、压缩比、命中率都打点上报,做成看板,这样优化才有依据。

最后分享一个小技巧:给上下文加一个"重要性分数",由工具类型、调用频率、用户反馈共同决定。分数高的上下文优先保留,分数低的先压。这个分数可以很简单,比如"用户明确引用过的内容 +10 分",但效果立竿见影。我在实际使用中发现,加了重要性排序之后,同样的 token 预算下,模型回答的准确率能提升一截,因为留下来的都是真正有用的信息。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 13:20:37

AI行业日报选题与信息筛选:Claude Code与Codex CLI实操避坑指南

1. 一份日报背后的信息筛选逻辑 做AI行业资讯日报这件事,我从2024年就开始断断续续地折腾,中间换过三种形态:最早是纯手工整理,后来半自动化抓取加人工筛选,现在基本稳定在"定向信源人工判断结构化输出"的模…

作者头像 李华
网站建设 2026/10/7 13:19:41

ponytail插件:把散落素材收拢成束,一键导出

第一次看到 ponytail 这个名字,我愣了几秒。这不是马尾辫的英文吗?一个效率类的社区插件,起名叫"马尾辫",到底是开发者随手开的玩笑,还是产品思路上真有什么讲究?带着这点好奇,我把它…

作者头像 李华
网站建设 2026/10/7 13:19:17

OpenClaw四个月超越React?AI Agent框架部署与实战解析

1. 四个月超越React这件事,先别急着喊"不可能"第一次看到"4个月超越React"这个说法,我的反应和大多数人一样:又是一个标题党。React从2013年开源到现在,十几年的生态积累,npm周下载量几千万&#…

作者头像 李华
网站建设 2026/10/7 13:19:16

YOLOv5+SAHI+超分辨率:小目标检测与遥感影像分析实战

简介:面向小目标检测与超分辨率处理场景,这份演示源码整合了YOLOv5检测框架与SAHI模块,适合已掌握基础目标检测知识、希望在PyTorchCUDA环境下快速跑通完整流程的开发者。压缩包内共4个文件,约23.05MB,包括Python主程序…

作者头像 李华
网站建设 2026/10/7 13:19:09

UE5蓝图动画系统从入门到实战:状态机、混合空间与蒙太奇全解析

这次我们来看一套 UE5 蓝图动画学习内容,原版作者是 Taylor Whitsett,中文精翻版由 CodeX 完成。这套内容的定位很明确:把 UE5 动画系统里最常被新手卡住的环节——动画蓝图、状态机、混合空间、蒙太奇、动画通知——从头到尾串起来讲&#x…

作者头像 李华
网站建设 2026/10/7 13:18:59

栅栏密码教程:从原理到Python实现,一文读懂换位密码

1. 栅栏密码是什么:把一句话拆进几道“栅栏”里我最早接触栅栏密码,并不是在密码学教材里,而是小学时候跟同桌玩传纸条。规则特别简单:把一句话竖着写,每隔一个字往下跳一行,写几行之后再横着把每行连起来读…

作者头像 李华