ARES 源码解析
++ withmartian/ares —— + 把 LLM Agent 当成 RL 问题的"考场+监考系统"。 + 用 asyncio.Queue 拦截 Agent 的 LLM 调用,让线性 Agent 代码无感地被 RL 环境托管。 + Python 8.3K 行 + Go HTTP 代理,双栈容器(Daytona / Docker),双栈 Agent(mini-swe-agent / terminus2)。 +
+ +§1TL;DR
+一页纸看懂 ARES 是什么 / 不是什么 / 值得抄什么。
+ +🎯 一句话
++ ARES 不是 Agent 产品,也不是训练算法库——它是 Agent RL 的基础设施层: + 把每个 LLM 请求变成 observation,把 LLM 响应变成 action,让训练框架(trl / verl / openpipe)能按 RL 循环驱动 Agent。 +
+概述
-待补充研究内容...
+核心抽象
+dm_envasync
+LLMRequest = observation,LLMResponse = action,reward 从容器 /reward.txt 或 /reward.json 读出。
核心发现
-待补充...
+最关键模式
+50 行全框架杠杆点
+QueueMediatedLLMClient 用 asyncio.Queue + Future 拦截 Agent 调用。Agent 线性代码无感被 RL 环境托管。
双栈容器
+云端默认本地备用
+Daytona(10 次 retry + auto-stop)+ Docker(本地 build + tar 传输)。Janitor 用 atexit 兜底清理,防云资源泄露。
+双栈 Agent
+轻量生产级
+MiniSWECodeAgent(~260 行,SWE-bench 跑分)vs Terminus2Agent(1,110 行,tmux 持续会话 + 主动概括)。
+双栈拦截
+同进程跨进程
+in-process Python Queue(0 延迟)+ ares-proxy Go HTTP(跨容器/跨机器,RTT 10-100ms)。
+附加彩蛋
+mech_interpTUI
+transformer-lens 集成、激活抓取、线性探针、CAA 干预;Textual TUI 评估看板。
§2生态位 · 它不是什么
+理解 ARES 的第一步是搞清楚它和相邻层的边界。
+ +| 常见误解 | 实际 |
|---|---|
| 成品 Agent 产品(类 Manus) | ❌ 不是终端产品,不直接服务用户 |
| 训练算法库(类 trl / verl) | ❌ 不做 PPO / GRPO 权重更新 |
| LLM 路由器(withmartian 主业) | ❌ 那是另一条产品线 |
它在哪一层
+谁会用它
+-
+
- 做 Agent 后训练(post-training / fine-tuning)的研究员 +
- SWE-bench Verified 类基准的大规模并行评估者 +
- 想把自家 Agent 接入"统一评估 + 统一沙箱"的工程团队 +
- withmartian 自家的 Router / Agent 产品线 +
§3核心抽象:Agent 当成 RL
+ARES 实现了 dm_env 的 async 版本。一次 episode 就是 Agent 从接到任务到 reward 出现的全过程。
+ +Environment 协议
+class Environment(Protocol[ActionType, ObservationType, RewardType, DiscountType]):
+ async def reset(self) -> TimeStep[ObservationType, RewardType, DiscountType]: ...
+ async def step(self, action: ActionType) -> TimeStep[...]: ...
+ async def close(self) -> None: ...
+ 来源:src/ares/environments/base.py:71-138
+ +TimeStep 的三种状态
+| step_type | 语义 | reward | observation |
|---|---|---|---|
FIRST | episode 开始 | None(强制) | 首个 LLMRequest |
MID | episode 进行中 | 0.0(稀疏奖励) | 下一个 LLMRequest |
LAST | episode 结束 | 从 /reward.* 读出 | None |
来源:src/ares/environments/base.py:31-69
+§4架构全图
+从公开 API 到 Go 代理的五层栈。
+ +§5Environment 层
+RL 主循环的编排者。管容器生命周期、驱动 Agent Task、收集 reward。
+ +CodeEnvironment 的签名
+class CodeEnvironment(base.Environment[
+ response.LLMResponse, # ActionType
+ request.LLMRequest | None, # ObservationType
+ float, # RewardType
+ float, # DiscountType
+])
+
+ reset() 流程 code_env.py:95-127
+-
+
- 清空步数,停旧容器(:104-108) +
- 随机选任务
_reset_task()(:110)
+ - 启动容器
_start_container()(:111)
+ - 启动 agent 作为独立 asyncio Task(:112)——Agent 代码线性,环境在后台跑它 +
- 等 agent 发出第一个 LLM 请求
_get_time_step()(:114)
+ - 包装成
FIRSTTimeStep 返回
+
step(action) 流程 code_env.py:129-161
+-
+
- 步数 +1(:138) +
- 把
action喂回 agent:_llm_req_future.set_result(action)(:142)—— 唤醒 agent 的 await
+ - 等 agent 下一个 LLM 请求或 agent 任务完成(:146) +
- 步数超限(默认 250)强制
LAST,取消 agent task(:148-153)
+ - 否则返回
MID(reward=0.0),episode 终止才算分
+
Reward 双格式读取 code_env.py:302-315
+/reward.txt
+直接 float(content)。最简单场景用。
/reward.json
+解析 JSON,取唯一 key 的 value。Harbor 数据集约定。
+Episode 终止三条路
+| 触发 | 位置 | 终局 |
|---|---|---|
| Agent Task 完成 | :174-193 | LAST(reward 从 /reward.* 读) |
| 步数超限 | :148-153 | LAST(reward=上一 reward) |
| 已 LAST 再 step | :135-136 | 抛异常,要求 reset |
§6Container 层
+隔离 Agent 执行环境。双实现覆盖云 / 本地,Janitor 兜底防泄露。
+ +Container Protocol containers/containers.py:24-131
+async def start(env: dict[str, str] | None) -> None
+async def exec_run(command, workdir, env, timeout_s) -> ExecResult
+async def upload_files/download_files/upload_dir/download_dir
+def stop_and_remove() -> None # 唯一同步方法,给 atexit 用
+
+ 双实现对比
+| 特性 | Daytona(云,默认) | Docker(本地) |
|---|---|---|
| 启动介质 | 云 API | docker-py,本地 build |
| 重试 | 10 次指数退避 daytona.py:35-46 | 无 |
| 超时处理 | 抛 TimeoutError,不重试 | asyncio.wait_for |
| 文件传输 | 原生 SDK sbx.fs.upload_files() | tar 打包 put_archive() |
| 资源配置 | CPU / Memory / Disk / GPU | ❌ TODO 未支持 |
| 清理 | auto_stop(30min) + auto_delete(0) | force remove |
| 挂起方式 | Sandbox 自管 | tail -f /dev/nulldocker.py:83 |
tail -f /dev/null,容器 CMD 执行完就会退出。这是常见的"容器秒退"问题的标准解法。
+ Janitor atexit 兜底 code_env.py:348-389
+关键设计约束:atexit 不能跑 async,所以 Container 协议强制提供同步的 stop_and_remove()。
§7Code Agent 层 · 双栈 Agent 对比
+ARES 内置两种 Agent:轻量跑分的 MiniSWECodeAgent 和生产级复杂度的 Terminus2Agent。
+ +协议
+class CodeAgent(Protocol):
+ async def run(self, task: str) -> None
+
+ MiniSWECodeAgent
+轻量级,封装 mini-swe-agent 库
+-
+
- 步数上限:250 +
- 会话:每步无状态 +
- 循环:step → query → execute_action +
- bash 解析:markdown 代码块 +
- 错误:异常分层(
_NonTerminatingError/_TerminatingError)
+ - 代码量:约 260 行 +
- 适用:SWE-bench 快速跑分 +
mini_swe_agent.py:156-258
+Terminus2Agent
+生产级,Terminal-Bench 的 tmux 会话
+-
+
- 步数上限:1,000,000 +
- 会话:tmux 持续会话,160×40 分辨率,50k 历史 +
- 循环:tmux check → query → parse → execute → 两步完成确认 +
- 上下文:主动概括(200k token)+ 被动救援(context_length_exceeded) +
- 输出追踪:增量定位(rfind 锚点) +
- Parser:JSON / XML 可切换,三级降级 +
- 代码量:1,110 行 +
- 适用:长期交互、超长轨迹 +
terminus2/terminus2_agent.py:482-849
+Parser 三级降级 terminus2/json_parser.py:75-99
+容错 Parser 模板(可直接抄)
+try:
+ data = json.loads(json_str)
+except JSONDecodeError:
+ json_str = self._auto_fix_json(json_str) # Level 2: 补括号 / 引号
+ try:
+ data = json.loads(json_str)
+ except JSONDecodeError:
+ fallback = self._parse_with_regex(original) # Level 3: regex 降级
+ XML 侧还有 salvage_truncated_response,从被截断的响应中抢救合法标签。
2 字符 = 1 token 估算都是硬编码。对英文 / Unicode 混杂的 tmux 输出不完全准确。生产需按模型实际 tokenizer 调。
+ §8Queue-Mediated ⭐ 全框架杠杆点
+
+ 整个 ARES 最精妙的 50 行代码。它让 线性 Agent 代码(随手写一堆 await llm(...))和 RL 环境协议(强制 reset/step/close 循环)无感接合。
+
核心 50 行 queue_mediated_client.py:47-50
+class QueueMediatedLLMClient:
+ q: asyncio.Queue[ValueAndFuture[LLMRequest, LLMResponse]]
+
+ async def __call__(self, req: LLMRequest) -> LLMResponse:
+ future = asyncio.Future[LLMResponse]()
+ await self.q.put(ValueAndFuture(value=req, future=future))
+ return await future # ← Agent 挂在这里
+
+ 它是怎么工作的
+结果:
+-
+
- Agent 作者:正常写
response = await llm(req),完全不知道自己被托管
+ - 训练者:拿到符合 dm_env 规范的
reset/step,可以喂给任何训练框架
+ - 零妥协:不需要 Agent 实现"RL-aware 接口",也不需要训练器懂 Agent 内部 +
支撑抽象:ValueAndFuture async_utils.py
+@dataclasses.dataclass(frozen=True)
+class ValueAndFuture[ValType, FutureType]:
+ value: ValType
+ future: asyncio.Future[FutureType]
+ 8 行泛型 dataclass。任何"把值和响应承诺打包传递"的场景都能抄:模拟器、游戏引擎、多租户推理队列、RPC 中间件。
+ +💡 值得抄
+这是那种"看一眼就该记住"的模式。比 RxJS 的 Subject 简单得多,但在 async Python 场景里解决了"外部事件驱动 + 内部线性代码"的终极矛盾。
+§9LLM Client 层
+多个实现共享 LLMClient 协议。拦截版、API 版、本地版、可解释性版各司其职。
协议
+class LLMClient(Protocol):
+ async def __call__(self, request: LLMRequest) -> LLMResponse
+
+ QueueMediatedLLMClient ⭐
+核心
+RL 拦截版,把请求塞进 queue 等 future。见 §8。
+ChatCompletionCompatibleLLMClient
+API 默认
+OpenAI 兼容 HTTP 客户端。Martian API 默认后端。线程局部 httpx + tenacity 3 次重试 + 成本追踪。
+LlamaCppLLMClient
+本地 GGUF
+对接本地 GGUF 模型。asyncio.to_thread() 包装阻塞推理。
HookedTransformerLLMClient
+mech_interp
+底层 transformer-lens,支持抓中间激活 / 钩子干预。
+ChatCompletionCompatibleLLMClient 的四个亮点
+ +1. 线程局部 httpx 客户端 chat_completions_compatible.py:22-41
+_thread_local = threading.local()
+
+def _get_llm_client(base_url, api_key):
+ key = (base_url, api_key)
+ clients = getattr(_thread_local, "clients", {})
+ if key not in clients:
+ clients[key] = openai.AsyncClient(...)
+ _thread_local.clients = clients
+ return clients[key]
+ 为什么要这样写:httpx.AsyncClient 绑创建线程的 event loop。跨线程用同一个实例会死锁。线程局部是最优雅的解。
2. Tenacity 装饰器 :44-53
+@tenacity.retry(
+ stop=tenacity.stop_after_attempt(3),
+ wait=tenacity.wait_exponential(min=1, max=60) + tenacity.wait_random(min=0, max=1),
+ before_sleep=tenacity.before_sleep_log(_LOGGER, logging.INFO),
+)
+ 3 次尝试 + 指数退避 + 随机抖动。异步 API 客户端通用模板。
+ +3. GPT-5 特判 :66-67
+GPT-5 不支持 temperature,动态移除参数。提醒你:模型家族碎片化在 API 适配层是常态。
4. 成本内置 :72 + accounting.py:70-97
+每个 LLMResponse 带 cost 字段。价目表从 Martian API 拉取(LRU 缓存),按 prompt/completion token 累加。
cached_tokens 和 reasoning_tokens(Martian 当前未区分)。对 GPT-o3 / Claude 3.7 thinking 场景要另行处理。
+ 转换层:两份而非一份
+OpenAI 的 Chat Completions API 和 Responses API 的消息/工具结构完全不同,单一转换器写起来会很乱,所以 ARES 拆了两份各司其职。
+ +| openai_chat_converter.py (395 行) | openai_responses_converter.py (435 行) | |
|---|---|---|
| 目标 API | Chat Completions | Responses |
| system prompt | messages[0] = system 角色 | instructions 参数 |
| 工具调用 | 展平成 AssistantMessage.tool_calls | 多态 input 数组 |
| 损失检测 | top_k、stop_sequences>4 | stop_sequences 完全不支持 |
§10ares-proxy · Go HTTP 版 Queue-Mediated
++ in-process Queue 只在同一 Python 进程内有效。Agent 在隔离容器跑时,需要把队列搬到 HTTP。 + ares-proxy 就是这个跨进程版本,用 Go 实现。 +
+ +为什么要 ares-proxy
+in-process Queue
+Agent 和环境同一个 Python 进程。
+-
+
- 0 网络延迟 +
- asyncio 原生 +
- 单机单进程才能用 +
ares-proxy (Go HTTP)
+Agent 跑在 Docker / Daytona 容器,通过 HTTP 跨进程通信。
+-
+
- RTT 10-100ms +
- goroutine + channel +
- 跨进程 / 跨容器 / 跨机器都能用 +
三端点数据流
+端点实现对照
+| 端点 | 文件:行 | 行为 |
|---|---|---|
POST /v1/chat/completions |
+ main.go:34-59 broker.go:36-73 |
+ 生成 UUID,创建 responseChan,加入 map + 队列,阻塞等(默认 15min timeout) |
+
GET /poll |
+ main.go:64-80 broker.go:90-102 |
+ 原子读整个 requestQueue,立即清空(:99),返回 JSON 数组 |
+
POST /respond |
+ main.go:85-109 broker.go:106-122 |
+ 查 ID,responseChan <- response,关闭通道 |
+
Broker 数据结构 broker.go:14-22
+type Broker struct {
+ mutex sync.Mutex
+ pendingRequests map[string]chan json.RawMessage // ID → 响应通道
+ requestQueue []PendingRequest // 待轮询队列
+}
+
+ 为什么选 Go
+-
+
- goroutine + channel 天然适合队列代理 +
- 纯 stdlib,无外部依赖 +
- 单二进制部署,扔进任何容器都能跑 +
- Python 做这个反而要装 httpx / aiohttp / uvicorn 一堆 +
responseChan 缓冲大小 = 1(broker.go:41)。如果 Agent 不及时读取响应,会堵塞后续处理。高并发场景建议调大。
+ §11Registry + Presets + 任务切片
+用字符串魔法 "sbv-mswea:0:10" 精准定位 "SWE-bench Verified 上 mini-swe-agent 的前 10 个任务"。
三种 Selector registry.py:31-217
+| Selector | 构造 | 行为 |
|---|---|---|
IndexSelector(5) | :47-58 | tasks[5] |
SliceSelector(0, 10) | :62-75 | tasks[0:10] |
ShardSelector(2, 8) | :79-109 | 均匀分 8 片取第 2 片 |
语法糖 parse_selector:112-217
+"sbv-mswea" # 全选 → SliceSelector(None, None)
+"sbv-mswea:5" # 单任务 → IndexSelector(5)
+"sbv-mswea:0:10" # 切片 → SliceSelector(0, 10)
+"sbv-mswea:5:" # 从 5 到末尾 → SliceSelector(5, None)
+"sbv-mswea@2/8" # 第 2/8 片(分布式评估) → ShardSelector(2, 8)
+
+ 已注册预设 presets.py
+HarborSpec 系列 :39-82
+从 code_env.list_harbor_datasets() 动态枚举所有 Harbor 数据集,× {mini_swe_agent, terminus2_agent} 笛卡尔积。
命名:{dataset_id}-{agent_id}
例如:sbv-mswea、sbv-terminus2
TwentyQuestionsSpec :85-119
+20 Questions 猜谜游戏(无容器,纯文本)。
+125 个内置对象。
+展示 ARES 非 SWE-bench 的能力边界。
+§12Examples · 渐进式学习梯度
+四个示例精心设计,每一步只换一个组件,体现 ARES 的模块化。
+ +01_sequential_eval_with_local_llm.py
+最小
+最小循环:async with ares.make("sbv-mswea:0")。用 llama_cpp 加载本地 Qwen2-0.5B。默认 Docker 容器。
02_sequential_eval_with_api.py
+API 切换
+唯一差别:agent 换成 ChatCompletionCompatibleLLMClient(model="openai/gpt-5-mini")。环境/容器代码一模一样。
03_parallel_eval_with_api.py
+并行核心
+Semaphore(20) + gather + TUI 看板。几百任务同时跑。见下方剖析。
+20q_case_study/
+可解释性
+5 阶段:采集激活 → 训探针 → 方向识别 → CAA 干预 → 因果验证。展示非 SWE-bench 应用。
+并行机制拆解 examples/03_parallel_eval_with_api.py
+# Semaphore 流控
+sem = asyncio.Semaphore(args.num_parallel_workers) # 默认 20
+
+# 装饰器包装:每个任务抢信号量
+async def _await_with_semaphore(coro):
+ async with sem:
+ return await coro
+
+# gather 批量启动
+tasks = [_await_with_semaphore(run_one(task_id)) for task_id in task_ids]
+results = await asyncio.gather(*tasks, return_exceptions=True)
+
+ 并行瓶颈
+-
+
- num_parallel_workers:Semaphore 上限(默认 20) +
- 容器工厂配额:Daytona API 并发创建配额 +
- 单点 CPU / 内存:TUI Dashboard 渲染 + asyncio 调度 +
§13测试 + Mock 体系
+单元测试用 mock,集成测试用真容器。
+ +单元测试 · Mock
+MockContainer testing/mock_container.py:10-130
+记录所有 exec_commands / uploaded_files / downloaded_files。支持 exec_handler 回调动态生成响应。
MockLLMClient testing/mock_llm.py:10-72
+循环预设响应列表 / 自定义 response_handler。记录全部请求,get_last_request() 断言入口。
集成测试 · 真 Daytona
+test_default_workdir.py integration_tests/:L10-48
+验证 SWE-bench /testbed vs TerminalBench /app 工作目录。
流程:ares.make(preset) → reset() → exec_run("pwd") → 断言
用 Daytona(避开本地 Docker 兼容问题)。
+§14StatTracker · 三实现一协议
+时序指标和标量的非侵入式追踪。
+ +协议 stat_tracker.py:16-21
+class StatTracker(Protocol):
+ @contextlib.contextmanager
+ def timeit(self, name: str) -> Generator: ...
+ def scalar(self, name: str, value: float) -> None: ...
+
+ 三种实现
+| 实现 | 位置 | 机制 |
|---|---|---|
NullStatTracker | stat_tracker.py:23-30 | 无操作,生产低开销路径 |
LoggingStatTracker | :33-62 | 后台任务每 60s 打分位数(p0/p25/p50/p75/p100),np.percentile() |
TensorboardStatTracker | tensorboard.py:14-42 | 60s 周期 SummaryWriter.add_histogram() |
· 无 MLflow / wandb 集成(仅 tensorboard) +
· 60s 周期硬编码 +
· 无接口配置 +
· 想接 wandb 只能自己实现 Protocol +
§15mech_interp · 机制可解释性附加
+ARES 不仅能跑分,还支持可解释性研究的完整闭环。这是 withmartian 的亮点附加。
+ +三个核心组件 contrib/mech_interp/
+| 文件 | 行 | 作用 |
|---|---|---|
hooked_transformer_client.py | 13-140 | 实现 LLMClient,底层 transformer-lens.HookedTransformer.generate() |
activation_capture.py | 13-89 | TrajectoryActivations:列表存每步 ActivationCache,torch.save/load 持久化 |
hook_utils.py | 20-100 | 零融合钩子(ablate 位置/头)+ 路径补丁钩子(clean → corrupted 替换做因果分析) |
和训练什么关系?
+不是直接训练反馈,而是 离线可解释性研究:
+§16关键设计模式
+从 ARES 源码提炼的七个高杠杆率模式。
+ +1. Queue-Mediated Communication
+⭐ 最重要
+asyncio.Queue + Future 让线性代码与外部控制器无感接合。50 行代码,但抽象力巨大。
2. Protocol-Oriented Design
+结构子类型
+几乎所有核心类型都是 typing.Protocol。无继承树,duck typing + 类型检查两全。
3. Factory Pattern
+依赖注入
+环境收"工厂"而非"实例"。container_factory / code_agent_factory。便于 A/B 切换。
4. Async Context Manager
+生命周期
+所有资源都 async with。保证 __aexit__ 清理。
5. Frozen Dataclass
+并发安全
+大部分 dataclass frozen=True。async 并发下避免状态污染。
6. Atexit Janitor
+兜底清理
+异常退出时清理外部资源(容器、临时文件)。atexit.register + 同步版 stop_and_remove。
7. YAGNI
+哲学
+CLAUDE.md 明说:不做过度抽象。CodeEnvironment 直接实现 Environment,不搞继承塔。
§17亮点 · 坑点 · 可抄
+最值得拿的三类清单。
+ +✨ 亮点 8 条
+Queue-Mediated 50 行
+让 Agent 线性代码无感被 RL 托管。全框架最关键杠杆点。
+Parser 三级降级
+JSON → auto-fix → regex。XML 还有 salvage_truncated_response。容错极强。
+增量输出追踪
+rfind 锚点定位新增内容。适配超长 tmux 会话不爆上下文。
+双保险概括
+主动 200k 阈值 + 被动 context_length_exceeded 捕获。
双栈拦截
+in-process Python Queue + out-of-process Go HTTP。覆盖所有部署形态。
+成本内置
+每个 LLMResponse 带 cost。精细计费无痛。
线程局部 httpx
+规避 async 事件循环跨线程死锁的最佳实践。
+Janitor atexit
+云资源兜底清理,防泄露。任何管外部资源的系统都该抄。
+⚠️ 坑点 8 条
+-
+
- Terminus2Agent tmux 初始化复杂(:196-319):动态 apt-get 装 tmux,建议生产镜像预装 +
- 200k token 阈值硬编码(:666):
2 字符 = 1 token估算粗糙,对 Unicode 不准
+ - ares-proxy 响应通道大小 = 1(broker.go:41):agent 不及时取会延迟后续处理 +
- Chat 与 Responses 转换器有重复(tool_choice 部分) +
- 增量输出定位失败兜底(:453-456):rfind=-1 时输出整屏,可能重复 +
- Docker 不支持资源配置(CPU/Memory TODO 未完成) +
- StatTracker 周期 60s 硬编码,无配置接口 +
- 无 wandb / mlflow,仅 tensorboard +
💎 可抄片段 6 条(直接能用)
+| 片段 | 位置 | 适用场景 |
|---|---|---|
| Queue-Mediated 50 行 | queue_mediated_client.py:47-50 | 任何"线性代码 + 外部控制"场景:模拟器、多租户推理、游戏 AI |
| 三级降级 Parser | json_parser.py:75-99 | LLM 输出解析的最佳实践模板 |
| Tenacity 重试装饰器 | chat_completions_compatible.py:44-53 | 异步 API 客户端通用重试 |
| 线程局部 httpx | chat_completions_compatible.py:22-41 | 多线程 async 场景规避事件循环冲突 |
| Janitor atexit | code_env.py:348-389 | 任何管外部资源(容器、临时文件、远程 session)的系统 |
| ValueAndFuture | async_utils.py | 8 行泛型 dataclass,"值 + 响应 future"原子单位 |
§18对标 / 启发
+放到 Agent + RL 生态里看,ARES 占哪块地,能给其他项目什么。
+ +vs 其他 Agent RL 框架
+| ARES | Verl | OpenPipe Mini-ART | Gymnasium | |
|---|---|---|---|---|
| 定位 | 环境层 | 训练器 + 环境 | Fine-tune + eval 一体 | 通用 RL 标准 |
| Agent 支持 | SWE + terminal | SWE | 多场景 | 非 LLM |
| 沙箱 | Daytona + Docker | 自研 | 自研 | 无 |
| 拦截机制 | asyncio.Queue + Go proxy | RPC | 直接调用 | N/A |
| 可解释性 | mech_interp 附加 | 无 | 无 | 无 |
对标 Manus
+边界与重叠
+Manus = 成品 Agent(应用层);ARES = 训练/评估基础设施(基础设施层)。角色不同。
+
+ 但 ARES 里的 Agent 运行内核(terminus2_agent + ares-proxy + Daytona 沙箱)
+ ≈ 一个 Manus-like 的 Agent 运行器。
+
+ 把 ARES 的 RL 训练钩子(reward 读取、并发 rollout、gather 聚合)拆掉,剩下的部分可以当独立 Agent 运行时复用。 + 这是最值得拿的"后半"。 +
+对个人项目的启发
+| 项目 | 可借鉴 |
|---|---|
| HiClaw / OpenClaw 魔改 | QueueMediatedLLMClient + ares-proxy 的双栈拦截可直接借鉴,给多 Agent 编排做统一观察接口 |
| 手机 GUI Agent | Terminus2Agent 的 tmux 增量输出追踪 + 主动概括策略可迁移到 GUI 长轨迹 |
| Hermes Personal | ChatCompletionCompatibleLLMClient 的线程局部客户端 + tenacity 重试模板直接抄 |
| Manus 逆向 | 参考 ARES 的 Agent runtime 设计,对比 Manus 公开行为里哪些已经实现、哪些还差 |
| 通用 | Parser 三级降级 + Janitor atexit 属于"看过一次就该用"的基础模式 |
§19阅读路径推荐
+想理解 ARES,按以下顺序读最省脑。
+ +-
+
CLAUDE.md—— 13,139 字节,仓库自带,密度比 README 高 3 倍
+ src/ares/__init__.py—— 公开 API 清单
+ src/ares/environments/base.py—— Environment 协议 + TimeStep
+ src/ares/llms/queue_mediated_client.py—— 50 行核心,看一眼就懂
+ src/ares/async_utils.py—— ValueAndFuture 抽象
+ src/ares/environments/code_env.py—— 250 行 RL 主循环
+ src/ares/code_agents/mini_swe_agent.py—— 简单 Agent
+ src/ares/containers/docker.py—— 熟悉容器抽象
+ examples/03_parallel_eval_with_api.py—— 端到端用法
+ ares-proxy/*.go—— 跨进程版队列中介
+ src/ares/code_agents/terminus2/terminus2_agent.py—— 1,110 行生产级 Agent
+ src/ares/contrib/mech_interp/*—— 可解释性加成
+