Anima Zero Tutorials
🔍 搜索功能尚未开启,敬请期待。

1.1 v0.1 封版:AWI 框架与 sim-desk

欢迎来到《Anima Zero 开发教程》。这是一份开发日志——它不教你怎么用某个库,而是记录 anima-zero 这个项目「为什么这么设计、一步步是怎么搭出来的」。anima-zero 是一台具身机器人的「脑」:它只想、不动手;它决定「做什么」,身体决定「怎么动」。配套的身体在另一个仓库 soma-zero

这一节讲的是已经打了 tag v0.1封版版本。封版意味着「这部分跑通了、定下来了,后面在它上面长新东西,但不再回头改它」。所以这一节适合从头读:它把整个框架的骨架交代清楚。下一节(1.2)讲的是 v0.1 之后、正在开发还没提交的 v0.2(下棋)。

为什么先做「脑」、而且先做框架?因为机器人难的从来不是「手能不能动」,而是「该让手做什么、做错了怎么纠正」。把这套会思考、能纠错的脑做成一个干净的框架,将来换个身体、换个任务都能复用——这就是 anima-zero 想立住的东西。

一、先把它摆正:ANIMA 是「会想、不动手」的那半个大脑

认知科学里有个经典的二分法叫 System 1 / System 2:System 1 是快、反射、不假思索的那套(你伸手接住掉下来的杯子);System 2 是慢、深思、要算一算的那套(你算一道数学题)。机器人也是这么分工的——而且这是当下主流机器人大脑的共识(π0.5、GR00T N1、Figure Helix 都这么切)。

ANIMA 就是 System 2:慢、深思,每个决策跑一次,把一个目标变成一串安全、经过核验的动作,再根据反馈判断成没成、要不要重试。它绝不碰电机。真正动手的那只「快手」(System 1,一个学习式的视觉-语言-动作策略 VLA)藏在身体那一侧。

图 1.1.4 慢脑快手:ANIMA 是 System 2(想),身体 SOMA 是 System 1(动)

说白了,ANIMA 本质就是一个 agent(智能体)——和 Claude Code、Codex 这类编码 agent 是同一类东西,只不过把「眼」换成相机、「手」换成机械臂。理解了这一点,后面所有设计就都顺理成章了:它要会看、会想、会调工具、会闭环纠错。

二、核心设计:把「大脑」和「世界」彻底分开

整个 v0.1 最关键的一个决定,就一句话:「世界」是一个独立运行的程序(仿真器或真机),ANIMA 不碰它的内部,只隔着一套「看 / 动」的接口去观测、操作它——就像我们看真实世界一样

人在这套里负责开会话、选世界、选大脑、看结果;ANIMA 在中间想和编排;世界在另一头自己跑,自己声明能力、自己感知和执行。三者各待各的位置:

图 1.1.1 人 — ANIMA(脑) — 世界(独立进程):三者关系

这个分离有个特别漂亮的验证方式:人甚至可以绕过大脑,直接在世界自己的界面里拨弄它(比如拖动桌面上的笔),ANIMA 下一次「看」的时候就会发现世界变了。这就证明了「世界是真独立的,ANIMA 只是个观测者 + 指挥者」,而不是它自己脑补出来的一个内部状态。

这样设计的回报是:换世界只换一个地址,认知端一行代码都不用改。今天连虚拟桌面,明天连棋盘,后天连真机——对大脑来说都一样。

三、脑和世界怎么对话:AWI 协议

大脑和世界之间这套「看 / 动」的接口,我们给它起了个名字叫 AWI(Anima World Interface)。它的思路和 MCP、ROS 一样:定一个标准,谁符合这个标准谁就能接进来。ANIMA 这边用一个很薄的客户端,按 URL 连上远程世界就行。

AWI 的核心就三件事,外加几个辅助端点:

图 1.1.3 AWI 三端点:capabilities 问能力、perceive 看、invoke 动;工具按 kind 决定要不要过安全闸
  • capabilities(问能力):这个世界有哪些高层动作?注意是「把 e2 走到 e4」这种语言可读的动作,不是关节角度。能力在连接时握手问一次,之后缓存,不每拍重问。
  • perceive(看):要一帧当前画面(图),外加结构化的真值状态。
  • invoke(动):执行一个动作,返回结果。

还有一个小而重要的设计:每个工具自带一个 kind。会改变世界的动作(kind="tool")必须过一道安全检查;而只读、裁判这类不改世界的(kind="read"/"judge")直接放行。这一笔让「安全」这件事有了清楚的边界,后面真机会用得上。

四、它怎么思考:看 → 想 →(过安全闸)→ 动 → 再看

顶级 agent 有一条共识:主循环简单到就是个 while 循环,复杂度全在外围(记忆、验证、安全)。ANIMA 照这个来。一条用户消息进来,主循环最多转 N 拍(默认 8),每一拍就是同一套动作,直到大脑只出文字 = 收尾。

图 1.1.2 通用认知主循环:看 → 想 →(过安全闸)→ 动 → 再看,闭环纠错

顺着图走一遍:先能力和画面,把「系统说明 + 历史对话 + 工具清单 + 这帧画面」一起交给大脑去。大脑只有两种结果——要么只回话(那就收尾),要么要调某个工具。要调工具的话,先过一道不经过大模型的安全闸(打招呼、只读、裁判类不算改世界,直接放行),再把动作交给世界。动完之后回到开头重新看一眼——这就是闭环纠错:它不靠「我以为我做对了」,而是真的再看一眼世界变成什么样了。

每一拍还会产出一份结构化轨迹:这拍看到的图 + 真值、想了什么、调了哪些工具、最终回复。前端把它做成两层折叠,方便排查「它到底看到了什么、怎么想的」。代码在 src/orchestrator.pyhandle()(整段返回)和 handle_stream()(边跑边流式推给前端)。

五、一个容易做错的细节:工具调用不写进提示词

「让大模型调工具」有两种做法,差别很大:

  • 老办法:在提示词里写「请按 {"action":...,"args":...} 这样输出 JSON」,然后模型吐一段文本,你自己用正则或 json.loads 去抠。这种做法又脆又容易跑偏。
  • 原生工具调用(我们用的):把工具清单作为一个独立参数交给大模型 API,模型经过专门训练,会把调用放进一个专门的结构化字段返回。格式由 API 保证,我们直接读字段,不解析正文。

这里还有个关键的心智模型,要分清两件事:「要不要调、何时调」是行为问题,归系统提示词和工具描述管;「怎么把这次调用表达成 JSON」是机制问题,归 API 那层管。把这两件事分开,就不会再用「在提示词里硬写格式」这种又管行为又管机制的糊涂做法。OpenAI、本地 Ollama、Claude 三条路在 src/llm/ 里各有一个适配器,对上层暴露同一套「拿到 tool_calls」的结果。

六、围着主循环的一圈「外围件」

前面说「复杂度全在外围」,这个「外围」具体指什么?就是围着那个 while 循环的一圈 hook:会话、上下文、安全闸、多大脑、注册表、监控面板。

图 1.1.5 认知外围件:会话、上下文滑窗、安全闸、多大脑、注册表、AWI 监控面板
  • 会话(session.py):一次任务一个会话,记忆存在本地。同一个世界同时只允许一个活跃会话(按世界单活 + 冻结),这是为了安全——别让两个会话同时指挥一个世界。
  • 上下文(context.py):发给大脑的历史是一个滑动窗口,而且只发最新一张图,老图只存不发——否则一堆历史图片会把上下文撑爆、还会让它「上下文腐烂」。
  • 安全闸(safety.py):动作下发前一道不经过大模型的确定性检查。仿真默认放行,上真机时把「默认放行」显式关掉、再填硬检查。
  • 多大脑(llm/factory.py):5 个视觉大脑登记在一张表里,网页里下拉就能换,别处一行不动;其中一个能在本地离线免费跑。
  • 注册表(registry.py):登记有哪些世界(名字 + URL),加世界就是加一行配置。
  • AWI 监控面板(/awi):把所有连接、各世界的能力清单、实时 AWI 流量都摊开给你看,流量还落盘到 logs/awi-*.jsonl 方便追溯。

另外还有个「裁判」的概念:它是世界提供的一个确定性工具,大脑学会去调它来确认「成没成」,而不是让大模型自己看图说「我做好了」。这条在下棋那一节会变得非常重要。

七、第一个世界:用 sim-desk 把整套协议跑通

光有协议不行,得有个真实例子把它端到端验证一遍。v0.1 自带的例子是 sim-desk——一张虚拟桌面 + 一支笔 + 一块可涂画的画布,声明三个工具:移动笔、绘制、擦除。

图 1.1.6 首个示例世界 sim-desk:桌面 + 笔 + 画布,端到端验证整套 AWI

你可以对它说「把笔移到右上角」,大脑就会看画面、决定调 move_pen、把笔移过去、再看一眼确认。它还提供一个给人用的画板界面,你可以亲手拖笔、涂画——然后看 ANIMA 那边能不能观测到这个变化。这就把第二节说的「世界独立」用一个能跑的东西证明了。

更重要的是它立了个样板:以后把世界换成「棋盘 + 棋臂」,高层动作就变成「把 e2 走到 e4」;换成 MuJoCo 人形,高层动作就变成「走到门口 / 左转 90°」。所谓 sim2sim、上真机,本质就是让大脑去连不同的世界,接口一样,大脑一行不改。

八、代码长什么样,以及「封版」意味着什么

图 1.1.7 v0.1 封版的代码模块结构:脑 src/ + 表现层 presentation/ + 世界 world/

三大块:src/ 是脑(AWI 接口、瘦客户端、主循环、各种外围件、大脑适配层);presentation/ 是表现层(FastAPI 后端 + Next.js 15 前端,三栏布局、流式输出、两层折叠轨迹);world/ 装世界(v0.1 只有 sim-desk 一个子模块)。跑起来是三个服务各占一个端口:世界 8100、后端 8000、网页 3000。

2026-06-27 这一天把这套框架打磨到封版,首次正式提交并打 tag v0.1。封版的范围是:世界独立 + 会话 + 主循环 + 外围 hook + 原生工具调用这一整套已经跑通、定下来。真机的安全硬检查、视觉裁判、技能库这些外围件,按依赖顺序往后做——其中「技能库」就是下一节 v0.2 要讲的下棋。

顺带一提一个历史包袱:更早还有个叫「ANIMA O1」的原型,是按 L0–L5 六层、五因子评估、脑电波信号那套医疗/通用愿景写的。v0.1 是完全重写,把那套整体删掉了(git 可恢复),不复用其代码。所以如果你在别处看到 L0–L5 的说法,那是旧故事,和现在这套 AWI 框架没关系。

九、这一版真正踩过的坑(开发日志的价值所在)

开发日志最有价值的部分,往往不是「做对了什么」,而是「差点做错了什么」。v0.1 封版这天记下了几条教训:

  • 「打招呼也乱调工具」:弱模型(gpt-4.1-nano)开局对它说「你好」,它 8 次有 8 次乱调工具。一开始以为是模型笨,后来发现根因是系统提示词 + 工具呈现没写好——把画面 framing 成「环境背景」、重写工具描述之后,从 8/8 乱调降到 16 次里 0 次。教训:先怀疑提示词工艺,别急着怪模型。
  • 复现 bug 要用对场景:同一个 bug,在「开局」和「带历史」两种场景下结论可能正好相反。不挑对场景去复现,会得出错误结论。
  • 改了公共参数要在新模型上验证:曾经在 OpenAI 路径上动了 max_tokens,结果某个只认新参数名的模型直接报 400——而当时还误以为它「跑得很干净」,其实是它在报错。改公共参数,务必在最新的模型上验一遍。

这几条看着琐碎,但它们正是「框架能不能稳」的真实门槛。把它们记下来,下一节做下棋时就不会再栽同样的跟头。

下一节:1.2 v0.2 封版:Chess Mode —— 在这套封版框架上,长出第一个「技能 + 行为树」应用:陪你下棋。