停止向你的 LLM 投喂‘垃圾’令牌。(我构建了一个代理来解决它)
Source: Dev.to – “Stop feeding junk tokens to your LLM – I built a proxy to fix it”
请提供您希望翻译的具体文本内容(包括正文、标题、列表等),我会在保持原始 Markdown 格式、代码块和链接不变的前提下,将其翻译成简体中文。谢谢!
概览
我最近构建了一个用于处理一些 SRE 任务的代理——获取日志、查询数据库、搜索代码。它能够工作,但当我查看追踪时感到非常恼火。
问题不仅在于它很昂贵(账单在攀升),更在于它的低效。
- 单个工具的输出——一次对 Python 文件的搜索——竟然有 40 000 个 token。
- 大约 35 000 个 token 只是
"type": "file"和"language": "python"重复了 2 000 次。
我们在为让最先进的模型读取标准 JSON 样板而支付高额的计算费用。
我找不到一个在不破坏代理的前提下解决此问题的工具,于是自己写了一个。它叫 Headroom。它位于你的应用和 LLM 之间,通过压缩上下文约 85 %,在不丢失语义意义的前提下提升效率。
开源 – Apache‑2.0
代码: (link to repository)
如有需要,请将占位符 “(link to repository)” 替换为 Headroom 代码库的实际 URL。
为什么截断和摘要不起作用
当上下文窗口填满时,行业标准是 截断(删除最旧的消息或文档的中间部分)。对于代理而言,截断是危险的:
- 日志文件: 截断日志的中间部分可能会丢失解释崩溃的唯一错误行。
- 文件列表: 删除条目可能会隐藏用户请求的确切配置文件。
我尝试了 摘要(先使用更便宜的模型对数据进行摘要),但这引入了幻觉。摘要器告诉我部署“看起来不错”,因为它忽略了原始日志中的特定错误代码。
我们需要: 第三种选择——无损压缩,或至少是“意图无损”。
核心理念:统计分析,而非盲目截断
我意识到工具输出中约 90 % 的数据只是模式骨架。LLM 并不需要看到 status: active 重复千次;它需要的是 异常。
Headroom 的 SmartCrusher 在处理数据之前会进行统计分析:
- 常量因子化 – 如果数组中的每个项都有
"type": "file",则该常量只提取一次,而不是重复出现。 - 异常值检测 – 计算数值字段的标准差,并保留超过均值 > 2σ 的突增。这些突增通常才是关键。
- 错误保留 – 硬性规则:绝不丢弃看起来像堆栈跟踪、错误信息或失败的字符串。错误是神圣的。
- 相关性评分 – 保留匹配用户查询的项,使用混合 BM25 + 语义嵌入 评分。
- 首尾保留 – 始终保留前几项和后几项;LLM 需要一些示例,且最新数据很重要。
结果:
40 000 tokens → ~4 000 tokens。信息密度相同,避免幻觉风险。
Source: …
CCR:使压缩可逆
关键洞见: 压缩应当是可逆的。
该架构称为 CCR(Compress‑Cache‑Retrieve)。
| 步骤 | 描述 |
|---|---|
| 1. 压缩 | SmartCrusher 对工具输出进行压缩(例如,2 000 条目 → 20 条)。 |
| 2. 缓存 | 将原始的 2 000 条目本地缓存(5 分钟 TTL,LRU 驱逐)。 |
| 3. 检索 | Headroom 在 LLM 的上下文中注入工具 headroom_retrieve()。如果模型在查看摘要后仍需要更多数据,它可以调用该工具。Headroom 从缓存中获取所需条目并返回。 |
为什么重要
- 你可以 大幅压缩(90 %+ 的削减),因为 永远不会真正丢失——模型随时可以“解压”它需要的内容。
- 风险计算方式发生转变:LLM 可以按需请求完整数据,消除了“信息丢失”问题。
示例对话
Turn 1: "Search for all Python files"
→ 1 000 files returned, compressed to 15 items
Turn 5: "Actually, what was that file handling JWT tokens?"
→ LLM calls headroom_retrieve("jwt")
→ Returns jwt_handler.py from cached data
- 没有额外的 API 调用。
- 不会出现 “抱歉,我已经没有这条信息了” 的情况。
Source: …
TOIN:网络效应
Headroom 通过 TOIN(工具输出智能网络)从压缩模式中学习。它会匿名跟踪压缩后发生的情况:
- 最常被检索的字段是哪些?
- 哪些工具类型的检索率较高?
- 哪些查询模式会触发检索?
这些数据会反馈到压缩建议中。例如,如果 TOIN 学到用户在压缩后经常检索 error_code 字段,它会告诉 SmartCrusher 在下次压缩时更积极地保留 error_code。
内置隐私
| 方面 | 实现方式 |
|---|---|
| 数据值 | 从不存储 |
| 工具标识符 | 以 结构哈希 形式存储 |
| 字段名称 | 以 SHA‑256[:8] 哈希形式存储 |
| 用户追踪 | 无用户标识符 |
网络效应
用户越多 → 压缩事件越多 → 为所有人提供更好的推荐。
记忆:跨对话学习
代理经常需要在不同对话之间记住事实(例如,“我更喜欢暗色模式”,“我的时区是 PST”,“我正在进行身份验证重构”)。Headroom 提供了一套记忆系统,能够自动提取并存储这些事实。
快速记忆(推荐)
- 零额外延迟 – 大语言模型在响应中直接输出
memory块。 - Headroom 解析该块并将记忆存储,以供后续请求使用。
from headroom.memory import with_fast_memory
client = with_fast_memory(OpenAI(), user_id="alice")
# Memories are extracted automatically from responses
# and injected into future requests
背景记忆
- 使用单独的 LLM 调用 异步 提取记忆。
- 更加准确,但会增加少量延迟。
from headroom import with_memory
client = with_memory(OpenAI(), user_id="alice")
两种方法都将记忆本地存储(SQLite),并在后续对话中注入,使模型能够在无需任何外部服务的情况下“记住”。
TL;DR
- Headroom 通过统计分析将工具输出压缩约 85 %,同时保留异常、错误和相关性。
- CCR(Compress‑Cache‑Retrieve)使压缩可逆,允许 LLM 按需获取原始数据。
- TOIN 从集体使用中学习,以改进未来的压缩。
- 内置 memory 让代理在跨会话中保留事实,延迟为零或极低。
试一试,让上下文窗口为你服务,而不是限制你。
Source: …
转换管道
Headroom 对每个请求运行四个转换:
1. CacheAligner
LLM 提供商提供缓存令牌计费(Anthropic:优惠 90%,OpenAI:优惠 50%)。只有当你的提示前缀保持稳定时,缓存才会生效。
问题 – 你的系统提示可能包含时间戳,例如:
Current time: 2024-01-15 10:32:45
这会破坏缓存。
解决方案 – CacheAligner 提取动态内容并将其移动到末尾,使前缀保持稳定。信息仍然保留,但缓存命中率会提升。
2. SmartCrusher
统计压缩引擎。它分析数组,检测模式,保留异常,并因式分解常量。
3. ContentRouter
不同的内容需要不同的压缩方式。代码不是 JSON,日志也不是散文。
ContentRouter 使用基于机器学习的内容检测,将数据路由到专用压缩器:
| 内容类型 | 压缩器 |
|---|---|
| 代码 | AST 感知压缩(tree‑sitter) |
| JSON | SmartCrusher |
| 日志 | LogCompressor(对相似消息进行聚类) |
| 文本 | 可选的 LLMLingua 集成(≈20 倍压缩,增加延迟) |
4. RollingWindow
当上下文超过模型限制时,需要删除一些内容。RollingWindow 会一起丢弃最早的 工具调用 + 响应(永不留下孤立数据),同时保留系统提示和最近的对话轮次。
三种使用方式
选项 1:代理服务器(零代码更改)
pip install headroom-ai
headroom proxy --port 8787
将你的 OpenAI 客户端指向 http://localhost:8787/v1。完成。
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8787/v1")
# No other changes
兼容 Claude Code、Cursor 以及任何 OpenAI‑compatible 客户端。
选项 2:SDK 包装器
from headroom import HeadroomClient
from openai import OpenAI
client = HeadroomClient(OpenAI())
response = client.chat.completions.create(
model="gpt-4o",
messages=[...],
headroom_mode="optimize" # or "audit" or "simulate"
)
模式
| 模式 | 描述 |
|---|---|
| audit | 仅观察。记录将会被优化的内容;不做任何更改。 |
| optimize | 应用压缩——这就是节省 token 的方式。 |
| simulate | 干运行。返回优化后的消息,但不调用 API。 |
先使用 audit 以查看潜在的节省,然后在有信心时切换到 optimize。
选项 3:框架集成
LangChain
from langchain_openai import ChatOpenAI
from headroom.integrations.langchain import HeadroomChatModel
base_model = ChatOpenAI(model="gpt-4o")
model = HeadroomChatModel(base_model, mode="optimize")
# Use in any chain or agent
chain = prompt | model | parser
Agno
from agno.agent import Agent
from headroom.integrations.agno import HeadroomAgnoModel
model = HeadroomAgnoModel(original_model, mode="optimize")
agent = Agent(model=model, tools=[...])
MCP (Model Context Protocol)
from headroom.integrations.mcp import compress_tool_result
# Compress any tool result before returning to LLM
compressed = compress_tool_result(tool_name, result_data)
实际数据
| 工作负载 | 之前(标记) | 之后(标记) | 节省 |
|---|---|---|---|
| 日志分析 | 22 000 | 3 300 | 85 % |
| 代码搜索 | 45 000 | 4 500 | 90 % |
| 数据库查询 | 18 000 | 2 700 | 85 % |
| 长对话 | 80 000 | 32 000 | 60 % |
接下来有什么
更多框架
- CrewAI 集成
- AutoGen 集成
- Semantic Kernel 集成
托管存储
- 云托管 TOIN 后端(可选加入)
- 跨设备内存同步
- 团队共享压缩模式
更佳压缩
- 领域特定配置文件(SRE、编码、数据分析)
- 自定义压缩插件
- 实时工具的流式压缩
为什么我构建了它
我相信我们正处于 AI 炒作周期的 “优化阶段”。让东西能工作是基本要求;让它们 廉价且可靠 地工作才是真正的工程工作。
Headroom 通过统计分析和可逆压缩来解决 “上下文膨胀” 问题——而不是 使用启发式或粗暴截断。它完全在本地运行,因此除常规的 OpenAI/Anthropic 调用外,数据不会离开你的机器。
- 许可证: Apache‑2.0
- 仓库: GitHub – headroom (replace with the actual URL)
如果你发现 bug 或有想法,请提交 issue。我正在积极维护此项目。