Unreal Agent:把工具调用彻底异步化的 Agent 运行时,成本比 Codex 低 40%

Unreal Agent:把工具调用彻底异步化的 Agent 运行时,成本比 Codex 低 40%

大部分 Agent 框架在模型之外叠了一层又一层:工作流引擎、子 Agent、任务队列、状态机。Unreal Labs 走了相反的路——他们把「工具调用」这件事从模型身上彻底拿掉,做成异步的运行时,然后开源了。unreallabsai/unreal-agent 建仓两天冲到 952 星,MIT 协议,Go 写的。

一句话概括:模型不再需要等工具返回、轮询状态、处理心跳。发完工具调用就可以继续干活,工具在后台跑完再把结果写回会话日志。

为什么值得看一眼

Unreal Labs 是一家做 Agent 产品的公司(背靠 Sequoia 与 First Round,工程团队来自 CERN、Meta、Snap、Bloomberg、DeepMind)。他们官方博客给出的数字是:

  • 在生产负载和编程/科研基准上,成本比 Codex 低最多 40%,比 Pi 低约 20%,通过率没有下降

这不是靠换更便宜的模型做到的。他们给的归因是两条:

1. Harness 本身足够薄 —— 提示词简单,工具结果做了 token 优化,没有子 Agent、没有工作流引擎。

2. 每个模型轮次能塞进更多有用的工具活 —— 因为模型不用花 token 去轮询和等待。

异步工具调用到底怎么工作

这是整个项目的核心设计。传统做法是:模型发一个工具调用 → 阻塞等结果 → 结果回来才继续。Unreal Agent 把它拆成两步事件:

1. 模型发出工具调用,harness 立刻在事件日志里追加一条「工具已返回」的记录,状态是 in-progress,同时工具在后台继续执行。

2. 工具真正跑完,把最终结果追加进会话日志,这时才发起下一次 LLM 调用。

由此带来两个直接收益:

  • 用户随时可以插话:不需要等工具跑完,steering 消息立刻就能被接受
  • 模型轮次之间可以排更多工具活:比如一边起一个要跑几分钟的开发环境初始化,一边并行探索代码库、搜网页,不额外付 token 税

顺带一个工程细节:作者提到「在不破坏 KV Cache 的前提下做这件事本身是个有意思的挑战」。他们引用了 KV Cache Rules Everything Around Me 那篇讨论。

架构:每个组件只做一件事

这个 harness 的设计克制得有点反潮流。README 里列了完整组件表:

组件 职责
Session inbox 会话级、内存中的输入幂等去重
Coordinator 持久化已接受的输入、跑 LLM 轮次、通过注册表解析工具翻译器、派发已提交的操作
Session store 持久化规范会话历史与操作状态;支持恢复与 fork;把工具调用状态与操作原子化记录
Context builder 在内存中有状态地组装模型输入;同时返回「被省略/截断/压缩了什么」的记录;不做 I/O
LLM Adapter 把准备好的输入发给 provider,返回归一化响应;自己负责认证、取消、provider 错误
Tool registry 持有固定的 Bash、ViewImage、skill-use 定义及其翻译器
Tool translator 校验工具调用、产出状态和操作;不做 I/O,也不能阻塞事件循环
Operation manager 持久化操作的 actor 运行时,本地实现可替换

几个值得抄的点:

  • Tool translator 严禁 I/O。它只在 coordinator 的事件循环上做同步的校验和翻译,把工具调用翻译成一个或多个可序列化的 Operation,交给操作管理器异步执行。翻译和执行彻底分离。
  • Context builder 没有任何持久化依赖。它只负责组装输入,并且必须如实报告哪些内容被裁剪了。
  • 每个字段都是可序列化的、带版本号的。会话存储格式、操作格式都有版本;恢复一个不支持的版本会明确报错,而不是悄悄降级。

内置能力与扩展点

模型侧只有三个静态工具,非常克制:

  • bash —— 后台执行 shell 命令,一个轮次里可以并行发多个独立命令
  • view_image —— 查看本地 JPEG/PNG/BMP/TIFF/WebP
  • skill_use —— 按名字加载已注册技能的指令

扩展的方式不是加钩子,而是替换接口实现。作者的例子很直白:写一个 代理操作管理器,把序列化的 Operation 发到远端沙箱里运行的本地操作管理器,工具就在那边执行了——这不需要改 harness 的任何核心代码。

LLM 适配器已经内置了 openai、openai-codex(带 OAuth 凭据)、openrouter、fireworks、ollama 五家,另外有一层 Responses API 适配(harness/llm/responsesapi),带重试策略和流式处理。

上手:两条命令

Runner 是个命令行可执行文件,行为类似 claude -p / codex exec:从 prompt 或 JSON 请求跑一个 Agent,事件以 JSONL 写到 stdout,任务结束就退出。

go install github.com/unreallabsai/unreal-agent/cmd/unreal-agent-runner@latest

export OPENAI_API_KEY="..."
unreal-agent-runner -p 'Inspect this project and explain how to run its tests.'

指定工作区并把输出存下来:

unreal-agent-runner -workspace ./my-project -p 'Summarize this project.' > run.jsonl

也可以直接喂 JSON 请求(参数或 stdin 都行):

unreal-agent-runner '{"prompt":"Summarize this project."}'
unreal-agent-runner < request.json

换 provider 和模型靠环境变量,OpenAI 是默认值:

export UNREAL_HARNESS_LLM_PROVIDER=openrouter   # openai | openai-codex | openrouter | fireworks | ollama
export UNREAL_HARNESS_LLM_MODEL=...

不想装 Go 也行,官方镜像支持 Linux AMD64/ARM64,每个 release 都会发布对应的 Git tag(比如 v0.1.0)方便锁版本:

docker run --rm -i --user "$(id -u):$(id -g)" \
  -e OPENAI_API_KEY -v "$PWD:/workspace" \
  unrea1labs/unreal-agent:latest -p 'Summarize this project.'

用 Go 直接当库用

harness/ 就是那个库,可以直接嵌进你自己的代码库里。会话模型长这样:

package session

type Session struct {
    ID        ID
    CreatedAt time.Time
}

type Turn struct {
    ID             TurnID
    PreviousTurnID TurnID
    Type           TurnType   // regular 或 compaction
}

会话存储是 append-only 的,只有五种条目:forkinputturnmodel_responsetool_call_status。fork 一个会话就是记一条带父会话 ID 的 fork 记录——不需要复制历史。

输入的类型也刻意做得很小,就三种:external(外部消息)、control(可以硬停、空闲停、心跳、更新设置)、crash(恢复)。每条输入都要求调用方提供一个全局唯一 ID,重投递时保持不变,inbox 靠它去重。

基准测试:他们把自己的账本公开了

这可能是最值得称赞的部分——作者把每次跑的 Harbor 任务链接都贴出来了,可以自己去核。

Terminal-Bench 4.0(GPT-6 Astra · xhigh)

Agent 通过率 总花费 输入/trial 轮次 工具调用
unreal-agent 57.9% 1428 1.73M 28 37
Codex(榜单基线) 57.9% 2350
Pi 55.0% 1827 2.83M 44 57

SWE-Atlas Codebase QnA

Agent 通过率 总花费 输入/trial 轮次 工具调用
unreal-agent 65.8% 936 898k 16 27
Codex 63.3% 1303 1.69M 22 21
Pi 64.0% 1033 1.29M 24 60

DeepSWE 1.1

Agent 通过率 总花费 输入/trial 轮次 工具调用
unreal-agent 72.4% 1367 1.60M 26 38
Codex 69.0% 1633 2.19M 30 29
Pi 69.6% 1584 2.21M 40 75

Agents' Last Exam(ALE-CLI)

Agent 全通过率 平均分 总花费 输入/task 轮次 工具调用
unreal-agent 30.0% 59.7 217 0.76M 18 23
Codex 29.0% 58.1 292 1.59M 21
Pi 29.0% 59.2 262 1.19M 27 37

规律很清晰:通过率基本打平,输入 token 和总花费明显下降。比如 ALE-CLI 上输入 token 只有 Codex 的一半,总花费低 26%。作者自己也说通过率的边际差异归因于基准方差,不邀功。

作者对现有 Agent SDK 的吐槽(挺扎心)

博客里有一节专门讲「为什么不直接用现成 SDK」,几条挺实在:

  • CLI 导向的 SDK 带着本地会话、子进程、资源限制的假设,搬进生产环境往往不成立。要可靠地处理完成、取消、后台任务,通常得自己在外面再包一层生命周期管理。
  • 换 provider 要额外做兼容工作:切 API 模式可能弄坏工具或压缩逻辑;SDK 升级会改消息格式,逼你重写集成。
  • 依赖树太重,给本来就难以理解和打补丁的运行时又加了一层维护和供应链风险。
  • 靠 harness 钩子和专用工具做的安全审批,不如确定性的环境/沙箱约束可靠——允许/禁止的主机、细粒度访问令牌、带审批门禁的代理,这些在 harness 外面做更稳。

还有一条技术上的发现:他们在 Responses API 上用「两个工具结果条目」(一个 in-progress、一个 final)来表达进度,但这个用法在文档里其实没有明确定义——他们在一些非 OpenAI 的推理 provider 上遇到了拒绝,function_call_output 的 status 字段也不起作用。作者认为这个模式应该被 Responses API 显式支持,并在各家 provider 上一致实现。

局限与注意事项

  • 项目非常新:2026-09-21 建仓(UTC),README 主要是架构术语表和组件说明,没有中文文档。
  • 偏底层:这是给你「拿去嵌进自己产品」的库,不是开箱即用的聊天终端。想快速体验就用 unreal-agent-runner
  • 需要 Go 1.27+ 才能从源码构建;官方 Docker 镜像可以绕过这个要求。
  • 没有子 Agent、没有工作流是设计选择而不是缺失——如果你需要多 Agent 协作,这套架构得自己在上层搭。
  • 目前模型的基准数据都基于 GPT-6 Astra,其他模型的实际表现需要自己测。

值得关注的理由

Agent 框架这两年堆得越来越厚,Unreal Agent 是少见的「往下拆」的思路:把工具调用的等待成本从模型身上拿走,让 harness 变薄,把复杂度转到异步运行时和可序列化的操作上。它给出的成本数据有可复核的任务链接,架构约束(无 I/O 的 translator、无持久化的 context builder、可替换的操作管理器)也写得清楚。

如果你在自建 Agent 运行时,或者正被「模型把大量 token 花在等工具上」这件事困扰,这个仓库值得花半小时读一遍 harness/ 目录。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容