【免费下载链接】OpenCollab
A research-friendly, high-performance multi-agent collaboration coding framework.
在长任务中,AI 编码 Agent 经常因为上下文窗口被塞满而"失忆"甚至崩溃。OpenCollab 是一个研究友好的高性能多智能体协作编码框架,它通过**上下文整形(Context Shaping)**和精细的Token 预算分配机制,在读取时动态构建有界的上下文视图,让 Agent 能在数千步的长任务中稳定运行,而不丢失任何原始对话记录。本文带你完整解析这套机制的设计原理与实现细节。
为什么长任务容易"断气"? 🤯
想象一个持续运行数小时的编码 Agent:它不断调用file_read、bash、grep等工具,每一步的工具输出都追加进对话历史。到第几百步时,上下文窗口(context window)被工具输出彻底占满,模型要么被提供商拒绝请求(context overflow),要么在"记忆"里塞满了早已没用的旧文件内容。
大多数框架的应对方式是粗暴截断——但这会丢失关键决策。OpenCollab 的做法完全不同:整形只发生在"发送给模型的那一刻",持久化的完整转录(transcript)永远不动。这意味着:
- 压缩、删除、摘要都只是"读取时的投影",随时可以无损恢复(resume);
- 整形是分级渐进的,压力不大就什么都不做;
- 每一层都只处理上一层的输出,一旦压力解除,后续层自动跳过。
整个运行时的分层架构如图所示:
上图展示了 OpenCollab 的完整运行时:从任务/模型/拓扑/Token 与上下文策略的规范层,到 Single、Team、Workflow 三种协作模式,再到共享的会话循环(Session loop)。右侧的 Evaluation 列则体现了它对每一次运行在角色、行为、结果、成本上的全程审计能力——而上下文整形正是其中 "tokens / context" 策略的核心执行者。
上下文整形四段流水线:ShaperPipeline 🔧
OpenCollab 的所有整形逻辑集中在 opencollab/application/shaping/ 包中,由一个左到右的整形器流水线(ShaperPipeline,定义于 pipeline.py)串联。默认流水线在 container.py 的_build_default_shaper中组装,共 4 层:
| 层 | 整形器 | 触发条件 | 动作 | 损失程度 |
|---|---|---|---|---|
| 1 | PerToolResultBudgetShaper | 无条件 | 单条工具结果超长则截断 + 重读指引 | 极低 |
| 2 | ToolOutputClearShaper | 压力触发 | 清空旧的可重建工具输出内容 | 低 |
| 3 | OldHistorySnipShaper | 压力触发 | 整组删除旧的工具交换轮次 | 中 |
| 4 | AutoCompactShaper | 压力触发 | 用模型摘要替换整段历史 | 高(最后手段) |
第 1 层:单条工具结果预算(Per-tool Result Budget)
实现于 tool_budget.py。任何一条工具结果只要超过预算(默认16,000 字符),就会被截断为"头部内容 + 重读指针",例如附上一条[truncated N chars — re-read a narrower range]的提示,告诉模型如何用更窄的offset/limit范围重新读取。这个设计很妙:模型永远知道自己"看过但没看完",可以按需精确回读,而不是盲目地整块丢失。
第 2 层:压力触发清空旧工具输出(Layer A0)
实现在 reactive.py 的ToolOutputClearShaper。只有当上下文估算值越过触发线时才工作:它保留最近 5 条可压缩工具结果原样不动,把更旧的bash、file_read、grep、git_diff等可重建的只读输出内容替换为一个短占位符[Old tool result content cleared]。注意它只清空"内容",调用与回答的骨架(以及助手轮次的推理)完整保留——零孤儿风险,零模型调用。编辑/写入类工具的返回则从不被触碰。
第 3 层:整组删除旧轮次(Layer A)
OldHistorySnipShaper是最便宜的"真删除":它按"最旧优先"整组移除旧的工具交换组(一个助手的tool_calls轮次连同其所有工具回答),直到估算值降回目标线。删除有严格的安全约束:
- 系统提示组(第 0 组)和最近 4 组永远受保护,只清理"中间地带";
- 删除以"组"为单位,绝不会把
tool_call_id弄成孤儿(提供商要求每次调用都必须有对应回答); - 被删内容仍完整保存在
state.messages和持久化转录中,恢复会话时可完整重建。
第 4 层:模型摘要压缩(Layer B,最后手段)
AutoCompactShaper是重量级手段,默认关闭(不注入 summarizer 时恒等不变)。当更便宜的层做完压力仍然超限时,它把整段可压缩区间交给摘要器,替换成一条带可见标记的消息([Context auto-compacted — summary of N earlier messages]),而不是伪装成原始历史。摘要提示词(compaction_prompt.py)刻意保留意图、决策、错误、用户指令、待办工作和下一步动作,并带有 LRU 摘要缓存,历史不变前缀可以复用旧摘要,避免重复付费。
一个被刻意移除的"直觉方案" 🤔
值得玩味的是 eager.py 中的EagerToolOutputClearShaper——一个每次调用都无条件清空旧工具输出的整形器。它没有被放进默认流水线:因为在低上下文压力时,反复改写旧前缀会让原本可复用的 prompt 缓存失效,还可能诱发模型重复读取文件,代价超过收益。这正是 OpenCollab "压力触发而非预防性清理"哲学的实证:宁可少做,不做无用功。
Token 预算分配:触发线是怎么算出来的? 📐
从模型窗口推导触发线
触发/目标阈值不是拍脑袋的常数,而是由history_trigger_target(见 pipeline.py)从当前模型的真实上下文窗口推导:
有效输入预算 = 上下文窗口 − 输出预留(20,000) − 安全缓冲(13,000) 触发线 trigger = 有效输入预算 目标线 target = trigger × 75%- 输出预留:为下一轮模型的总结/回答腾出空间;
- 安全缓冲:防止估算误差导致真实请求溢出;
- 75% 目标比:压缩到 75% 而非压到触发线以下一点点,留出余量,避免下一轮立刻再次触发压缩(压缩风暴);
- 模型窗口未知时,优雅降级为固定默认值:120,000 触发 / 90,000 目标。
窗口很小时,系统还会按比例缩小预留和保留组数(affordable_groups),保证小窗口模型同样行为合理。
用真实 usage 校准估算(ContextPressure)
Token 估算最难的地方在于:提供商只报告整个请求的 input token 数,不告诉你每条消息各占多少。context_pressure.py 中的ContextPressure用了一个精妙的加性分摊方案:
- 每次成功请求后,把提供商报告的 input tokens按各消息此前的估算比例分摊回每条消息;
- 未改动的消息复用这些权重(保持加性,供各整形层做增量计算);新消息则先用本地估算(ASCII 约 3 字符/token,CJK 约 1 字符/token),等下一次真实 usage 再校准;
- 加密的推理块不按字节数硬算,而是用提供商报告的 output usage 封顶,避免高估。
这样各层看到的 Token 视图是"被真实账单校准过的估算",而不是一句空话。
优先级保护:谁不能被压缩? 🛡️
每个上下文来源都带优先级标签(定义于 domain/context.py):
| 上下文来源 | 默认优先级 |
|---|---|
| 身份 / 角色提示(Identity) | 100 |
| 团队拓扑(Team) | 90 |
| 技能目录(Skill) | 85 |
| 委托任务(Task) | 80 |
| 项目地图(Project) | 30 |
关键常量PIN_FLOOR = 70:优先级 ≥ 70 的来源(身份、团队、技能、任务)被"钉住",永远不会被折叠进摘要;项目地图这类低优先级上下文则在压力下可被优先清理。即使进入"强制压缩"兜底模式(提供商真的拒绝了请求后的最后一搏),钉住的种子消息也照样受保护。
溢出恢复:一次重试,然后体面停
当提供商返回可分类的上下文溢出错误时,系统会强制所有反应层全量压缩并重试一次;若二次溢出则携带原因优雅停止(STOPPED),保留所有部分结果——绝不让一个受保护的巨型种子导致死循环,也绝不把"被限制停止"伪装成"任务成功"。
团队场景下的 Token 预算分配 🧑🤝🧑
多 Agent 团队中,Token 预算不只是窗口问题,还是分配问题。OpenCollab 按运行构造方式分四种分配规则(详见 docs/runtime-design.md):
| 模式 | 分配规则 |
|---|---|
| 预声明团队(共享池) | 每个座位累积上限 =min(total, c × total / N),座位不预占,单轮授权同时受剩余池和座位额度约束 |
| 动态名单 | 子 Agent 创建时同步预留最多min(total, max(10,000, total // 4)) |
| 声明角色预算 | 每个角色独立额度,必须全员声明 |
| Workflow | Agent 调用前从工作流池预留,清理后释放未用部分 |
预声明团队的规则让"团队池 = N 倍单人额度"时,每个座位与独立 Agent 完全可比,还能区分"角色没干活"和"角色额度耗尽"两种情况。下图展示了真实运行中的时间线:预算耗尽(out of budget)如何影响团队的行为轨迹——左边采纳者成功收尾,右边则因预算枯竭导致 Agent 无法结束自己的轮次:
会话预检(precheck)在每次模型调用之间执行 Token 与步数的硬性天花板检查;已在飞行中的调用可能超出剩余额度——这些是优雅的执行限制,而非实时计费上限,这一点在文档中被明确区分,体现了框架在研究可复现性上的严谨。
如何在团队配置中控制上下文策略 ⚙️
普通用户无需读源码,就能通过团队配置文件(如 configs/team.example.yaml)调整上下文策略:
context: default # 或者: context: { policy: no_history_compaction, tool_result_budget: 20000 }default:完整的压力触发历史压缩流水线;no_history_compaction:只保留单层工具结果预算,历史层全部移除——适合希望完整保留历史用于研究审计的场景(策略解析见 context_policy.py);tool_result_budget:直接调大或调小单层工具结果的字符上限。
所有实际生效的触发/目标值都会由session.history_compaction追踪记录落盘,方便跨实验对比(runtime-design.md 中专门强调了用这些记录做比较,而不是用代码里的默认值)。
总结:为什么这套机制让长任务稳定? ✅
OpenCollab 用不到 5 万行核心代码支撑起 SWE-bench Pro 64.25%、Terminal-Bench 2.1 83.15%、DeepSWE 69.91% 的成绩,其上下文工程的要点可以浓缩为四条:
- 读取时整形,存储时不动——压缩永远可逆,恢复永远无损;
- 分级渐进,损失最小优先——清空内容 → 删整组 → 模型摘要,便宜的先上,压力解除即停;
- 预算来自真实窗口与真实 usage——推导式触发线 + 提供商账单校准的估算,杜绝"拍脑袋常量";
- 优先级保护 + 优雅失败——身份/团队/任务永不被折叠,二次溢出就体面停止并保留原因。
如果你想深入阅读,建议从 docs/runtime-design.md 入手,它系统性地解释了上下文装配、压力整形、Token 记账与转向(steering)的全部不变量;而 tests/runtime/ 目录下的 shaping、compaction 与 context-overflow 主题测试,则是每条设计决策的可执行证据。
【免费下载链接】OpenCollab
A research-friendly, high-performance multi-agent collaboration coding framework.
相关推荐
ralph-loop-agent长时运行任务的秘密:RalphContextManager上下文摘要与Token预算管理全解
ralph loop agent长时运行任务的秘密:RalphContextManager上下文摘要与Token预算管理全解 ralph loop agent
httpbin 使用教程:3 个接口跑通 HTTP接口测试 的 mock 对端
httpbin 使用教程:3 个接口跑通 HTTP接口测试 的 mock 对端 测一个接口,要先写个假的对端? 发个请求想确认参数带对了没有,总得有个服务把它原
开发工具测试API设计OpenMAIC 1.2.0 把课程生成搬上服务器之后,它到底是什么
OpenMAIC 1.2.0 把课程生成搬上服务器之后,它到底是什么 看仓库里的 CHANGELOG.md https://link.gitcode.com/i
教育人工智能大模型AI Agent多智能体AI 应用RAG前端后端Agent 工作流
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考