1. 为什么单 Agent 干不完的活,多 Agent 也未必行
很多人第一次接触 OpenClaw 的多 Agent 玩法,脑子里想的是「我多开几个 Agent,一个写代码、一个写文案、一个做审核,不就 24 小时自动干活了吗」。真跑起来才发现,问题根本不在 Agent 数量,而在任务怎么流转。三个 Agent 各自聪明,但彼此不知道对方在干什么,Builder 写完的方案 Executor 没收到,Executor 执行完的结果 Orchestrator 不知道,最后你还是得手动复制粘贴、手动触发、手动盯进度。
我试过最原始的搞法:开三个终端窗口,每个窗口跑一个 Agent,靠人肉在中间传话。结果一天下来光协调就花掉一个多小时,任务状态全靠脑子记,漏一个就卡住整条链路。后来换成 Kanban 看板 + Daemon 常驻调度的组合,才真正把「AI 员工 24 小时自主工作」这件事跑通。核心思路是把 Agent 之间的协作从「人传话」变成「看板驱动」:Builder 负责拆任务写进看板,Orchestrator 负责监听看板并派活,Executor 负责领活执行并回写状态,Daemon 则像一个不下班的调度员,每隔几十秒扫一遍看板,把该流转的任务推下去。
这套东西适合谁?适合已经用过 OpenClaw 单 Agent、想进一步做自动化工作流的人;适合手里有多个重复性任务(内容生产、代码流水线、数据巡检)想交给 AI 自主跑的人;也适合想理解多 Agent 协作底层机制、不想被「一键智能体」黑盒糊弄的开发者。下面我把 config.toml 骨架、TaoToken 统一 Key 接入、Daemon 启动、看板流转验证和排错清单完整给出来,你可以直接照着搭。
2. TaoToken 前置:一个 Key 打通所有 Agent 的模型调用
多 Agent 系统最烦的一件事是每个 Agent 都要配一遍模型通道。Builder 用这个模型、Executor 用那个模型,Key 散落在各个配置文件里,换一次就得改一圈。TaoToken 在这里的价值就是统一 Key + 统一 API 通道:你只需要在 TaoToken 控制台创建一个 API Key,所有 Agent 的模型请求都走同一个入口,模型切换、额度查看、调用日志都在一个地方管。
具体操作路径是这样的:先到官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册并登录,进入控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 创建一个新 Key。创建完先别急着关页面,把 Key 复制到环境变量里,后面 config.toml 会引用它。
这里有个细节要注意:TaoToken 的 API 基地址是https://taotoken.net/api,不带任何 UTM 参数,配置的时候别把官网地址和 API 地址搞混。官网地址是给人看的,API 地址是给程序调的。如果你用的是 OpenAI 兼容的 SDK,直接把 base_url 指向这个地址即可。
注意:API Key 不要硬编码进 config.toml 或任何会提交到 Git 的文件里。用环境变量
TAOTOKEN_API_KEY引用,Daemon 启动时从环境读取。这是多 Agent 系统最基本的安全底线。
配好 Key 之后,建议先去模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 发一条测试消息,确认 Key 能用、模型能回。这一步花不了一分钟,但能帮你排除掉后面 80% 的「Agent 不响应」问题——很多时候不是 Agent 配置错了,是 Key 根本没通。
3. 可复制配置:config.toml 骨架与三角色定义
OpenClaw 的多 Agent 协作配置核心在一个 config.toml 文件里。下面这份骨架是我实测能跑通的版本,你可以直接复制后按需改。重点看三个角色的定义和 Kanban、Daemon 两块的参数。
# config.toml - OpenClaw 多 Agent 协作系统骨架 [global] api_base = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" default_model = "claude-sonnet-4" log_level = "info" # ---------- 角色定义 ---------- [[agents]] id = "builder" name = "Builder 构建者" role = "builder" model = "claude-sonnet-4" system_prompt = """ 你是任务构建者。收到新主题后,拆解为可执行的子任务清单, 每个子任务写明目标、输入、预期输出、验收标准,写入 Kanban 的 todo 列。 不要自己执行任务,只负责规划和拆解。 """ [[agents]] id = "orchestrator" name = "Orchestrator 协调者" role = "orchestrator" model = "claude-sonnet-4" system_prompt = """ 你是任务协调者。每隔一个调度周期扫描 Kanban 看板, 把 todo 列中已就绪的任务移动到 doing 列并指派给合适的 Executor。 监控 doing 列任务是否超时,超时则回退到 todo 并记录原因。 """ [[agents]] id = "executor" name = "Executor 执行者" role = "executor" model = "claude-sonnet-4" system_prompt = """ 你是任务执行者。从 Kanban 的 doing 列领取指派给你的任务, 执行后把结果写回任务 payload,并将状态更新为 done。 执行失败时把状态改为 needs_input 并附上失败原因。 """ # ---------- Kanban 看板 ---------- [kanban] backend = "postgres" dsn_env = "KANBAN_DSN" poll_interval_sec = 30 columns = ["todo", "doing", "needs_input", "done"] claim_strategy = "atomic" # 原子领取,防止多 Worker 重复执行 max_retry = 3 # ---------- Daemon 常驻调度 ---------- [daemon] enabled = true tick_interval_sec = 60 heartbeat_interval_sec = 180 task_timeout_sec = 1800 on_timeout = "requeue" max_concurrent_tasks = 4 # ---------- 任务流转规则 ---------- [workflow] auto_dispatch = true require_review = false notify_on_done = true这份配置里几个参数值得单独说。claim_strategy = "atomic"是关键,它保证多个 Executor 同时抢一个任务时只有一个能成功领取,避免重复执行——这是多 Agent 系统最容易踩的坑之一。task_timeout_sec = 1800表示单个任务超过 30 分钟没完成就判定超时,on_timeout = "requeue"让它回到 todo 列重新排队。heartbeat_interval_sec = 180是 Agent 心跳间隔,Daemon 靠这个判断 Agent 是否在线。
数据库这边,Kanban 需要两张表:任务表和状态变更日志表。任务表存任务本身,日志表存每次状态流转的记录,方便排错时回溯。
-- 任务表 CREATE TABLE tasks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, description TEXT, status TEXT DEFAULT 'todo', assigned_to TEXT, priority INT DEFAULT 3, payload JSONB, retry_count INT DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); -- 状态变更日志 CREATE TABLE task_history ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), task_id UUID REFERENCES tasks(id), old_status TEXT, new_status TEXT, changed_by TEXT, changed_at TIMESTAMP DEFAULT NOW() ); -- 原子领取函数 CREATE OR REPLACE FUNCTION claim_task(p_task_id UUID, p_agent TEXT) RETURNS BOOLEAN AS $$ DECLARE updated INT; BEGIN UPDATE tasks SET status = 'doing', assigned_to = p_agent, updated_at = NOW() WHERE id = p_task_id AND status = 'todo'; GET DIAGNOSTICS updated = ROW_COUNT; RETURN updated > 0; END; $$ LANGUAGE plpgsql;claim_task这个函数是整个协作系统的锁。Executor 领任务时调用它,只有把 status 从 todo 改成 doing 成功的那一个才算领到,其他并发请求会拿到 false 自动跳过。没有这个,两个 Executor 同时干同一个任务,结果互相覆盖,你会排查到怀疑人生。
4. 启动 Daemon 与验证看板任务流转
配置和数据库准备好之后,启动 Daemon 是让系统「活」起来的一步。Daemon 的本质是一个常驻进程,按tick_interval_sec的节奏循环执行:扫描看板、派发任务、检查超时、更新心跳。下面是一个最小可用的 Daemon 实现,你可以直接跑。
# daemon.py import os, time, signal, logging from supabase import create_client logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") log = logging.getLogger("openclaw-daemon") SUPABASE_URL = os.environ["SUPABASE_URL"] SUPABASE_KEY = os.environ["SUPABASE_KEY"] TICK = int(os.environ.get("DAEMON_TICK", "60")) sb = create_client(SUPABASE_URL, SUPABASE_KEY) running = True def handle_signal(signum, frame): global running running = False log.info("收到退出信号,准备优雅停止") signal.signal(signal.SIGTERM, handle_signal) signal.signal(signal.SIGINT, handle_signal) def scan_todo(): """扫描 todo 列,交给 Orchestrator 派发""" rows = sb.table("tasks").select("*").eq("status", "todo").order("priority").execute().data for task in rows: log.info(f"发现待派发任务: {task['id']} - {task['title']}") # 这里调用 Orchestrator Agent 决定指派给谁 dispatch(task) def dispatch(task): """原子领取并指派""" agent = pick_executor(task) ok = sb.rpc("claim_task", {"p_task_id": task["id"], "p_agent": agent}).execute().data if ok: log.info(f"任务 {task['id']} 已指派给 {agent}") sb.table("task_history").insert({ "task_id": task["id"], "old_status": "todo", "new_status": "doing", "changed_by": agent }).execute() else: log.warning(f"任务 {task['id']} 领取失败,可能已被其他 Worker 抢走") def pick_executor(task): return task.get("assigned_to") or "executor" def check_timeout(): """检查 doing 列超时任务""" rows = sb.table("tasks").select("*").eq("status", "doing").execute().data now = time.time() for task in rows: updated = task["updated_at"] # 简化处理,实际用时间戳比较 if task.get("retry_count", 0) >= 3: log.warning(f"任务 {task['id']} 重试超限,标记 needs_input") sb.table("tasks").update({"status": "needs_input"}).eq("id", task["id"]).execute() def heartbeat(): log.info("daemon heartbeat ok") def main(): log.info("OpenClaw Daemon 启动") while running: try: scan_todo() check_timeout() heartbeat() except Exception as e: log.error(f"调度循环异常: {e}") time.sleep(TICK) log.info("Daemon 已停止") if __name__ == "__main__": main()启动命令很简单,把环境变量带上就行:
export TAOTOKEN_API_KEY="你的 TaoToken Key" export SUPABASE_URL="你的数据库地址" export SUPABASE_KEY="你的数据库 Key" export DAEMON_TICK=60 nohup python3 daemon.py > daemon.log 2>&1 & echo $! > daemon.pid启动后验证三件事。第一,看日志有没有OpenClaw Daemon 启动和周期性的heartbeat ok,有就说明 Daemon 活着。第二,往 tasks 表插一条测试任务,状态设为 todo,等一个 tick 周期,看它有没有被改成 doing 并写入 task_history。第三,把这条任务手动改成 done,再插一条新任务,确认 Daemon 能持续处理而不是只跑一次。
# 插入测试任务 psql $KANBAN_DSN -c "INSERT INTO tasks (title, status, priority) VALUES ('测试任务:生成周报', 'todo', 2);" # 观察状态流转 watch -n 5 "psql $KANBAN_DSN -c \"SELECT id, title, status, assigned_to FROM tasks ORDER BY created_at DESC LIMIT 5;\""如果看到测试任务从 todo 变成 doing,task_history 里多了一条记录,说明整条链路通了。这时候你可以把 Builder 接进来:给 Builder 一个主题,让它拆解任务写进 todo 列,剩下的交给 Daemon 和 Orchestrator 自动流转。
5. 本篇常见错排查清单
多 Agent 系统跑不起来,九成问题出在下面这几个地方。我按排查优先级列出来,你对着查。
Daemon 启动了但任务不动。先看 Daemon 日志有没有报错,再看数据库连接是否正常。最常见的原因是SUPABASE_KEY用的是 anon key 而不是 service key,导致 RPC 调用claim_task权限不足静默失败。换成 service key 再试。
任务被重复执行。检查claim_task函数是否真的生效。如果你在代码里是先 select 再 update,而不是用原子 UPDATE,那并发下必然重复。确认用的是UPDATE ... WHERE status='todo'加ROW_COUNT判断的写法。
Agent 不响应派发。先确认 TaoToken Key 通了——去模型对话页面发一条消息测试。如果 Key 没问题,检查 Agent 的 system_prompt 是否让它误以为自己要执行任务而不是等待派发。Orchestrator 的 prompt 里要明确写「只负责派发,不执行」。
任务卡在 doing 列不动。看task_timeout_sec设置是否合理。如果任务本身就要跑很久,超时设太短会被反复 requeue。另外检查 Executor 执行完有没有回写状态,很多新手写的 Executor 干完活不更新数据库,任务永远停在 doing。
心跳正常但看板不更新。检查poll_interval_sec和tick_interval_sec是不是设得太大,导致你观察的窗口内还没到下一个周期。调试阶段建议把 tick 设成 10 秒,跑通后再调回 60。
Cron 任务超时拖垮调度。如果你在 Daemon 里直接跑耗时任务,一个任务卡住整个循环就停了。正确做法是 Daemon 只负责派发,实际执行交给独立的子进程或子 Agent,Daemon 派发完立刻返回继续下一轮。
注意:调试多 Agent 系统时,把日志级别开到 debug,并且给每个 Agent 的日志加上 agent_id 前缀。不然三个 Agent 的日志混在一起,你根本分不清是谁在报错。
6. 把 Key 和通道固定下来,再谈 24 小时自主
多 Agent 协作系统能不能真正 24 小时跑,取决于两个东西稳不稳:任务流转的锁机制,和模型调用的通道。锁机制靠claim_task和状态机保证,通道就靠 TaoToken 统一 Key 来兜底。所有 Agent 走同一个 API 入口,你换模型、查额度、看调用日志都在一个控制台里,不用挨个改配置文件。
如果你还在调试接入阶段,建议先把 API Keys 和接入文档过一遍:API Keys 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。文档里有 OpenAI 兼容 SDK 的完整示例,直接抄 base_url 和鉴权头就行。
等你把三角色跑顺了,下一步可以往 Coding Plan 方向走——让 Builder 拆需求、Executor 写代码、Orchestrator 调度测试和部署,整条开发流水线交给 Agent 自主跑。Coding Plan 的入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,适合长期跑编码类 Agent 任务的场景。Claude Code 相关的接入配置可以参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有针对 Anthropic 通道的说明。
最后说个我踩过的坑:别一上来就追求全自动无人值守。先把 Daemon 的 tick 调大、把require_review打开,让关键节点需要人工确认,跑几天观察任务流转是否稳定、有没有重复执行、超时重试是否合理。等日志干净了,再逐步关掉人工审核,把节奏交给系统自己。真正的 24 小时自主工作,是建立在你对每个环节都心里有数的基础上,而不是把开关一开就撒手不管。