【免费下载链接】Context-Gateway
Context Gateway is an agentic proxy that enhances any AI agent workflow with instant history compaction and context optimization tools
Context Gateway 是一个开源的 AI Agent 上下文压缩代理(LLM Proxy),部署在 AI 编程助手(Claude Code、Cursor 等)与大模型 API 之间,自动完成历史压缩(history compaction)与上下文优化。本文将带你拆解它的三大核心架构:Pipes 压缩管道、Adapter 适配器 与 多 Provider 路由设计,帮你快速理解一个生产级 LLM 网关是如何设计的。
一、整体架构:四层协作的上下文压缩网关
Context Gateway 的架构可以概括为四个层次,每一层职责单一、边界清晰:
| 层次 | 核心模块 | 职责 |
|---|---|---|
| 路由层 | internal/gateway/ | 接收请求、识别 Provider、安全校验 |
| 适配层 | internal/adapters/ | 抹平各厂商 API 协议差异 |
| 管道层 | internal/pipes/ | 工具输出压缩、工具发现、任务输出处理 |
| 存储层 | internal/store/ | 影子上下文存储,支持按需"还原" |
一个请求的完整生命周期如下:
AI Agent (Claude Code / Cursor)
│
▼
① 路由层:识别 Provider(OpenAI / Anthropic / Bedrock / Ollama ...)
▼
② 适配层:Adapter 从请求中提取工具输出 / 工具定义 / 用户意图
▼
③ 管道层:task_output → tool_output ∥ tool_discovery 并行压缩
▼
④ 转发给 LLM API,影子内容存入 Store 供 expand_context 还原
这种"路由 → 适配 → 管道 → 存储"的单向数据流,是整个架构最优雅的地方:任何一层都可以独立替换,互不污染。
二、多 Provider 路由:一次识别,自动适配
2.1 六级优先级的 Provider 识别策略
网关面对的第一个问题是:这个请求到底是发给哪家的?不同厂商的 API 格式(消息结构、认证头、路径)各不相同,但 Context Gateway 用一个集中式入口 IdentifyAndGetAdapter 一次解决,它按六级优先级逐步探测:
| 优先级 | 识别信号 | 判定结果 |
|---|---|---|
| 1 | X-Provider 请求头(显式声明) | 7 种 Provider 之一 |
| 2 | Bedrock URL 路径特征(/model/.../invoke) | Bedrock |
| 3 | anthropic-version 请求头 | Anthropic |
| 4 | API Key 前缀(sk-ant- vs sk-) | Anthropic / OpenAI |
| 5 | 路径特征(/v1/messages、/v1/chat/completions 等) | 对应厂商 |
| 6 | 兜底默认 | OpenAI(最通用格式) |
这里有一个精妙的细节:Bedrock 路径检测被刻意放在 anthropic-version 头检测之前,因为 AWS SDK 客户端常常在 Bedrock 请求中同时携带 anthropic-version 头,如果头检测先触发会导致误判(见 provider_identification.go 中的注释说明)。
识别失败时不会报错,而是优雅降级到 OpenAI 适配器——因为 OpenAI Chat Completions 是最通用的事实标准格式。
2.2 内置七个适配器,注册表统一管理
所有适配器在启动时通过 Registry 统一注册,内置 7 个:
- Anthropic / OpenAI(含 Responses API)
- Bedrock / Gemini / MiniMax
- Ollama(本地模型)/ LiteLLM(网关套网关)
注册表基于 map[string]Adapter + 读写锁实现,支持热插拔——新增一个 Provider 只需实现接口并调用 Register,路由层代码零改动。
2.3 SSRF 防护:请求白名单机制
作为代理,网关还承担安全责任。gateway.go 维护了一份目标域名白名单(allowedHosts),只放行 api.openai.com、api.anthropic.com、bedrock-runtime.amazonaws.com 等受信任的 LLM 域名,防止恶意配置把请求转发到内网地址。本地开发可通过环境变量 GATEWAY_ALLOW_LOCAL=true 显式开启 localhost 访问。
三、Adapter 适配器:用一个接口抹平所有协议差异
3.1 核心设计:2×2 的 Extract / Apply 方法对
Adapter 接口 是整个系统的"协议翻译官"。它定义了成对的方法,每对方法对应一类管道:
| 方法对 | 服务管道 | 示例差异 |
|---|---|---|
ExtractToolOutput / ApplyToolOutput | tool_output | OpenAI 是 role="tool" 消息;Anthropic 是 type="tool_result" 内容块 |
ExtractToolDiscovery / ApplyToolDiscovery | tool_discovery | 各家 tools 定义结构不同 |
ExtractUserQuery / ExtractAssistantIntent | 压缩上下文 | 提取用户原话和 LLM 调用工具的"意图" |
ExtractUsage / ExtractModel | 计费统计 | OpenAI 用 prompt_tokens,Anthropic 用 input_tokens |
除了压缩相关方法,Adapter 还负责响应侧的工作:从 LLM 响应中提取工具调用、过滤"幽灵工具"调用、构建下一轮请求消息、归一化停止原因信号(ExtractTurnSignal)等。
关键约束:适配器必须无状态、线程安全,因为管道池会并发复用它们。
3.2 管道与适配器分离:为什么这样设计?
Pipe 接口 的注释明确写着:"Pipes 中不得包含 Provider 特定逻辑——那由 Adapter 负责"。这意味着:
- Pipes 只写一遍压缩算法(如"压缩工具输出并打引用标记"),通过调用
adapter.Extract*()取数据、adapter.Apply*()写回结果; - 新增 Provider 时不需要改任何管道代码,只要新 Adapter 正确实现接口即可。
这是典型的策略模式 + 依赖倒置:压缩策略(管道)与协议实现(适配器)双向解耦,是理解整个项目的架构钥匙。
四、Pipes 管道:三条压缩流水线与并行调度
4.1 三条管道各管一摊
Context Gateway 内置三条独立的压缩管道,配置集中在 pipes/config.go:
| 管道 | 目录 | 作用 |
|---|---|---|
| task_output | pipes/task_output/ | 识别子 Agent / 任务工具的结果,按客户端 Schema 单独处理 |
| tool_output | pipes/tool_output/ | 压缩冗长的工具输出(如超长文件内容),注入 expand_context 还原入口 |
| tool_discovery | pipes/tool_discovery/ | 动态过滤不相关工具定义,节省 tools[] 占用的 token |
每条管道通过 Strategy() 方法声明执行策略,常见策略有:
passthrough:完全透传,不做处理api(compresr):调用 Compresr 压缩 APIexternal_provider:直连你自己的 OpenAI/Anthropic 兼容模型relevance/trimming:本地相关性过滤 / 尾部保留裁剪
4.2 Router 调度:先串行、后并行的两阶段执行
调度核心在 Router.ProcessAll,它实现了非常有讲究的两阶段执行模型:
阶段一(串行):task_output
└── 认领子 Agent 的工具结果 ID,写入 TaskOutputHandledIDs
阶段二(并行):tool_output ∥ tool_discovery
├── tool_output 修改 messages[] 路径
└── tool_discovery 修改 tools[] 路径
最终:sjson 合并两个结果(不重叠的 JSON 路径,可安全合并)
并行设计有三个保障:
- 路径不重叠:两条管道分别改写
messages[]和tools[],互不干扰; - 上下文隔离:router.go 为并行分支深拷贝
PipeContext,并将只属于对方的字段置空,避免数据竞争; - 故障隔离:每个 worker 都用
recover()兜住 panic,单条管道失败时回退到原始请求体,绝不让网关把正常请求搞挂。
4.3 智能跳过:便宜模型不压缩
tool_output 管道 内置了一个反直觉但非常实用的机制:如果目标模型是廉价模型(如 gpt-4o-mini),自动跳过压缩——因为压缩 API 本身的成本可能超过省下的 token 费用。压缩还有 token 阈值(默认 min_tokens: 512,max_tokens: 50000),太小不值得压,太大可能压不动。
五、影子存储与 expand_context:压缩了还能"还原"
这是 Context Gateway 区别于简单截断方案的核心能力。压缩后的工具输出以 [REF:shadow_id] 引用标记的形式留在对话中,原文存入 store 模块 的"影子存储",并采用双 TTL 策略:
- 原文 5 小时 TTL:会话期间 LLM 发现信息不够时,可调用注入的
expand_context工具按 ID 取回原文; - 压缩内容 24 小时 TTL:保证跨会话 KV-Cache 前缀稳定——已压缩的历史轮次不会被再次改写,避免缓存全部失效。
存储层用 LRU 上限控制内存(原文 1000 条 ≈ 5MB,压缩缓存 2000 条 ≈ 4MB),当前是内存实现,接口预留了 Redis 等多实例扩展点。
六、预压缩:让历史压缩"零等待"
preemptive 模块 实现了 README 中宣传的"即时历史压缩":一个后台 Worker 持续监控各会话的 token 使用量,当接近触发阈值(默认 75%)时提前调用摘要模型生成历史摘要。等会话真的触及上下文上限时,摘要已经就绪,压缩动作瞬时完成——用户永远不会卡在一次漫长的摘要生成上。
七、热更新与配置驱动
所有管道都支持配置热加载:Router.UpdateConfig 在收到新配置时先构建全新的 worker 池,再原子替换引用,进行中的请求继续使用旧池完成,做到无中断热更新。每个管道池默认 10 个 worker,通过 channel 实现获取/归还,天然限流。
八、总结:值得借鉴的五个架构模式
回顾整个 Context Gateway 的设计,有五个模式尤其值得做 LLM 基础设施的同学参考:
- 集中式识别入口:Provider 检测只有一个入口(IdentifyAndGetAdapter),规则集中、优先级显式,杜绝散落各处的 if-else;
- 无状态适配器 + 注册表:新增 Provider 是"加文件"而非"改代码";
- 管道/适配器解耦:压缩算法只写一遍,协议差异全部下沉到适配器;
- 路径不重叠的并行管道 + 故障隔离:性能与稳定性兼得;
- 压缩可逆:影子存储 + expand_context,让"省 token"和"保信息"不再二选一。
如果你想动手验证,可以通过 make 或脚本 scripts/start_gateway.sh 启动网关,再运行 tests/anthropic/ 下的端到端测试观察压缩效果;压缩明细会写入 logs/history_compaction.jsonl,方便逐请求审计。
📌 关键源码地图:internal/pipes/pipe.go(管道接口)、internal/adapters/adapter.go(适配器接口)、internal/gateway/router.go(调度核心)、internal/store/store.go(影子存储)。
【免费下载链接】Context-Gateway
Context Gateway is an agentic proxy that enhances any AI agent workflow with instant history compaction and context optimization tools
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



