关于协议
第一个要想清楚的问题:agent 怎么和 tbrain 通信?
一开始想过用 WebSocket,实时性更好。但想了想,任务调度对实时性要求没那么高,agent 每隔几秒来拉一次任务完全够用,换来的是协议简单、接入方便。就用 HTTP 了。agent 的工作流程大概是这样:
- 注册自己,告诉 tbrain 我是谁、能做什么,拿到一个 token
- 每 30 秒发一次心跳,证明自己还在线
- 每 3 秒拉一次任务,有活就认领
- 执行过程中可以上报进度,UI 上能实时看到
- 做完上报结果,或者上报失败并说明原因
tbrain 通过心跳判断 agent 是否在线。心跳超时的 agent 会被标记为离线,手里的任务重新放回队列,等其他同角色的 agent 来认领,不需要人工介入。
任务计划谁来出
调度是确定性的,但"这个任务该怎么拆、交给谁"不是——这需要判断力。
有些场景流程是固定的,比如"先开发、再测试、再部署",每步交给谁很明确,提前写死就行。但有些场景不好提前定死,需求一来,得先看看现在有哪些 agent 在线、各自能干什么,再临时决定怎么安排。
所以支持了三种模式:


静态模式:提交 Job 时自己写好任务列表和依赖关系,tbrain 照着跑。
AI 规划模式:只提交一个目标,brain agent 看到目标后自己规划出任务列表,发回给 tbrain 执行。
混合模式:骨架是静态的,某些步骤交给 brain agent 来决定怎么做。
任务失败了怎么处理
设计失败处理的时候,想了一下失败都有哪些情况:网络抖一下超时了、agent 做错了需要重来、碰到了没权限处理的情况需要人来拍板……这几种情况的处理方式完全不一样,不能一刀切。
最后让 agent 自己在上报失败时指定失败等级:
- 自动重试:瞬态错误,系统自动重试,不超过两次
- 打回重做:业务失败,打回上游任务重新来过
- 人工审核:需要人来决定,任务挂起等人处理
- 致命错误:整个 Job 直接失败
agent 自己最清楚为什么失败,让它定级比 tbrain 猜要靠谱。
让接入省事一点
引擎写完之后,想了一下接入体验——每个 agent 都要自己实现注册、心跳、轮询这一套,挺烦的。
做了一个 Go SDK 把这些封装掉,接入时只需要告诉它用什么命令执行任务:
client := sdk.New(sdk.Config{
ServerURL: "http://localhost:29794",
Role: "brain",
Command: "claude",
Args: []string{"--dangerously-skip-permissions", "--output-format", "stream-json"},
})
client.Run(context.Background())
不想写代码的话,还有一个命令行工具 tbrain-agent,直接把任意程序接进来:
./tbrain-agent \
-server http://localhost:29794 \
-role dev \
-cmd claude \
-args "--dangerously-skip-permissions,--output-format,stream-json" \
-resume
-resume 让 agent 处理下一个任务时恢复上次的会话上下文,不用每次从零开始。
跑起来
一个完整的流程大概是这样:
用户跟对话 agent 说想要什么 → 对话 agent 理解清楚,向 tbrain 提交一个 Job → tbrain 把规划任务派给 brain agent → brain agent 返回任务列表 → tbrain 按顺序把各个 task 分发给 dev、test、researcher 等 agent → 每个 agent 做完上报结果,tbrain 继续下一个 → 全部完成,通知回来。
tbrain 那边只有状态机在转。确定性的,不会乱。
tbrain 源码在这里:tbrain
最兼容 tbrain 的 Agent 运行时是 tclaw,感兴趣可以去 tclaw-releases 体验。