1. 队友各跑各的 WORK 循环,问题到底出在哪
如果你跟着 learn-claude-code 第十一课写到了 Autonomous Agents 这一段,大概率会遇到一个很具体的现象:TeammateManager.spawn出来的 alice、bob 各自起线程跑_loop,WORK 阶段反复调client.messages.create处理tool_use,IDLE 阶段轮询收件箱和scan_unclaimed_tasks认领任务。每个队友都是一条独立长会话,上下文被压缩后还要靠make_identity_block重注入身份。团队一扩,消耗全落在这几条队友循环上。
这时候如果ANTHROPIC_BASE_URL没配对,你会看到几种典型症状:WORK 阶段拿不到tool_use、/team里状态卡在 working 不动、/tasks上 owner 写不回去,或者直接 401。这篇就按原文第十一课的代码结构,把.env里ANTHROPIC_BASE_URL和 Key 的改法讲清楚,让 alice、bob 能正常跑完 WORK 和 IDLE 两个阶段。
适合谁看:已经跑通第九、第十课队友指派、现在想上自治智能体(Autonomous Agents)的读者;或者 spawn 之后发现队友循环报错、想排查 Base URL 写法的人。核心检索词就三个:Autonomous Agents、WORK 循环、ANTHROPIC_BASE_URL。
2. 前置:TaoToken 供 Key 和统一通道
原文的_loop里,每个队友线程都会独立构造请求:
response = client.messages.create( model=MODEL, system=sys_prompt, messages=messages, tools=tools, max_tokens=8000, )client是在模块顶层用Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))建的。也就是说,只要.env里的ANTHROPIC_BASE_URL指向一个统一通道,alice、bob、lead 三条循环就都走同一个入口,不用给每个队友单独配。
TaoToken 在这里的角色很明确:只负责供 Key 和统一通道。任务认领、身份重注入、看板扫描这些逻辑,仍然按原文代码走,一行都不用改。落地时先打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册并创建一把 Key,再回到原文load_dotenv那一步改配置。
注意:TaoToken 不是让你替换编辑器或改业务逻辑,它只解决「请求往哪发、用哪把 Key」这一层。队友循环的自治行为完全由原文的
_loop决定。
3. 可复制配置:改 .env 里的 ANTHROPIC_BASE_URL
原文开头这段是判断逻辑,不需要动:
load_dotenv(override=True) if os.getenv("ANTHROPIC_BASE_URL"): os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)它做的是:如果设了ANTHROPIC_BASE_URL,就把ANTHROPIC_AUTH_TOKEN清掉,避免两个鉴权变量打架。你要改的是.env文件本身。
在项目根目录的.env里保留MODEL_ID,加上 Base URL 和 Key:
# .env MODEL_ID=你的模型ID ANTHROPIC_BASE_URL=https://taotoken.net/api ANTHROPIC_API_KEY=你刚创建的那把Key三个关键点,踩过坑的都懂:
第一,ANTHROPIC_BASE_URL结尾不加/v1。SDK 内部会自己拼/v1/messages,你再加一层就变成/v1/v1/messages,直接 404 或重复路径。
第二,不带任何查询参数。不要写成https://taotoken.net/api?xxx=yyy,查询串会干扰路径拼接。
第三,Key 填刚创建的那把,变量名用ANTHROPIC_API_KEY。原文if os.getenv("ANTHROPIC_BASE_URL")那段会把ANTHROPIC_AUTH_TOKEN弹掉,所以别指望用 AUTH_TOKEN 传 Key。
改完确认一下client的构造还是原文那行:
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL")) MODEL = os.environ["MODEL_ID"]MODEL从MODEL_ID读,队友循环里client.messages.create(model=MODEL, ...)用的就是它。这两行保持原样即可。
4. 验证请求:spawn 两个队友看 WORK 与 IDLE 切换
配置改完,照原文 2.5 的应用示例来验证。先在看板上创建任务,再 spawn alice 和 bob:
# 在 lead 的交互里执行 spawn_teammate(name="alice", role="researcher", prompt="Scan the board and claim work.") spawn_teammate(name="bob", role="coder", prompt="Scan the board and claim work.")然后盯三个观察点。
观察点一:WORK 阶段能否正常拿到tool_use。队友线程进入_loop后,第一段是 WORK PHASE,for _ in range(50)里调client.messages.create。如果 Base URL 和 Key 都对,response.stop_reason会是"tool_use",response.content里能解析出block.type == "tool_use",控制台会打印类似:
[alice] claim_task: Claimed task #1 for alice [bob] claim_task: Claimed task #2 for bob如果这里一直拿不到tool_use,或者stop_reason直接是end_turn,先怀疑通道没通。
观察点二:/team里状态在 working 与 idle 之间切换。输入/team,输出来自TEAM.list_all():
Team: default alice (researcher): working bob (coder): idleWORK 阶段_set_status(name, "working"),进入 IDLE 前_set_status(name, "idle")。状态能来回切,说明_loop两个阶段都在正常跑。
观察点三:/tasks上 owner 是否被正确写回。输入/tasks,看claim_task有没有把 owner 写进task_*.json:
[ ] #1: 调研任务 @alice [>] #2: 编码任务 @bob [ ] #3: 依赖任务[>]表示 in_progress,@alice、@bob就是 owner。owner 写回成功,说明claim_task里的_claim_lock和文件写入都正常,跟 Base URL 无关,但能反证整条链路是通的。
IDLE 阶段还有个细节值得看:scan_unclaimed_tasks找到任务后,如果len(messages) <= 3(说明上下文被压缩过),会插入make_identity_block:
if len(messages) <= 3: messages.insert(0, make_identity_block(name, role, team_name)) messages.insert(1, {"role": "assistant", "content": f"I am {name}. Continuing."})身份重注入生效时,队友不会「忘了自己是谁」,能继续认领下一个任务。这一步跟通道无关,但验证时值得确认它没被跳过。
5. 本篇常见错排查
报 401。最常见。先回官网核对 Key 是不是复制完整、有没有多余空格。再确认.env里用的是ANTHROPIC_API_KEY而不是ANTHROPIC_AUTH_TOKEN——原文那段os.environ.pop("ANTHROPIC_AUTH_TOKEN", None)会把 AUTH_TOKEN 清掉,你填了也白填。
出现重复/v1。报错里如果看到/v1/v1/messages或 404,就是ANTHROPIC_BASE_URL结尾多写了/v1。改成https://taotoken.net/api,结尾不带/v1、不带查询参数。
队友状态卡在 working 不动。先看控制台有没有[alice] xxx:的打印。如果 WORK 阶段第一次client.messages.create就抛异常,_loop里那个except Exception会把状态设成 idle 然后 return,线程直接结束。这种情况多半还是通道问题,回官网核对 Key 与 Base URL 写法。
/tasks上 owner 一直是空。检查claim_task是否被调用。如果队友根本没进 WORK 阶段,自然不会认领。先解决通道,再看认领逻辑。
IDLE 阶段不认领新任务。看scan_unclaimed_tasks的过滤条件:status == "pending"、无owner、无blockedBy。三个条件缺一不可。任务被阻塞或有 owner 时不会被认领,这是设计如此,不是 bug。
身份重注入没触发。len(messages) <= 3才插入身份块。如果上下文没被压缩,messages 很长,就不会触发。这是正常的,不用强行改。
6. 接入与后续
排障和接入相关的,直接看 API Keys 和接入文档:
- 创建 Key:https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite
想先验证模型通不通、不写代码直接对话,用模型对话页:
- 模型对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite
如果你打算长期跑多 Agent、长会话、任务编排这类场景,Coding Plan 更合适:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite
最后提醒一句:TaoToken 只负责供 Key 和统一通道,任务认领与身份重注入仍按原文代码走。改完.env后 spawn 两个队友,看 WORK 阶段能否正常拿到tool_use、/team里状态是否在 working 与 idle 之间切换、/tasks上的 owner 是否被正确写回,这三步过了,自治智能体的循环就算跑通了。