
1. 多智能体跑起来之后为什么反而更不敢改crewAI 最让人上头的地方是几个 Agent 一串联任务真的能自己跑完。但最让人心虚的地方也在这——它跑完了你不知道它到底怎么跑完的。三个 Agent、五个 Task中间调了哪些工具、哪个 Agent 反复绕圈、哪一步把 Token 吃掉了大半全靠最后那段输出猜。我试过在一个研究型 Crew 里把verboseTrue打开控制台瞬间刷出几百行看着热闹但真要定位“为什么分析师这一步花了 40 秒”还是得一行行翻。这就是多智能体调试的核心痛点执行链路是黑盒性能瓶颈没有归因。这篇要解决的就是这件事。我会用 crewAI v1.11.0 搭一个可复制的追踪骨架把config.toml和settings.json两个配置文件写清楚再通过 TaoToken 的统一 Key 和 API 通道接入模型让每个 Agent 的耗时、工具调用、Token 消耗都能被采集下来最后生成一张能看的调用图。适合已经在写 crewAI、但被“跑得动却看不清”卡住的同学。核心检索词先摆出来crewAI 执行链路追踪、流程可视化、性能剖析、多智能体调试。下面所有配置都围绕这几个词展开你可以直接抄。2. TaoToken 前置统一 Key 与 API 通道怎么接在讲追踪之前得先把模型通道理顺。crewAI 默认走 OpenAI 兼容接口如果你每个 Agent 配一个 Key追踪时根本分不清哪次调用属于哪个 Agent。用 TaoToken 的统一 Key 接入好处是所有 Agent 走同一条 API 通道调用日志天然带统一的请求标识后面做链路归因会轻松很多。TaoToken 的 API 地址是https://taotoken.net/api兼容 OpenAI 的/v1/chat/completions格式。你需要在官网注册后拿到 Key注册入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Key 的管理页面在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。拿到 Key 之后不要硬编码在 Python 里。crewAI 支持从环境变量读也支持从settings.json读。我建议两层都配环境变量放 Keysettings.json放模型和追踪开关。这样换 Key 不用改代码换追踪策略不用动环境。注意TaoToken 是合规的 API 聚合通道所有调用走标准 HTTPS不需要任何额外网络配置。你只需要保证base_url和api_key正确即可。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里面有完整的模型列表和参数说明。如果你只是想先验证模型通不通可以直接用模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 发一条消息确认 Key 有效再往下走。3. 可复制配置config.toml 与 settings.json 骨架这一节是全文的核心两个文件直接给全。先建目录结构mkdir -p crew_trace/config crew_trace/logs cd crew_trace3.1 config.toml追踪与性能参数config.toml负责声明追踪开关、采样率和输出路径。crewAI 本身不直接读 toml但我们可以用它做项目级配置再由 Python 加载。# crew_trace/config/config.toml [llm] provider openai base_url https://taotoken.net/api model gpt-4o-mini temperature 0.2 max_tokens 2048 [trace] enabled true sample_rate 1.0 output_dir ./logs # 每个 Agent 的耗时阈值超过就标红 slow_threshold_sec 15.0 # 是否采集工具调用参数 capture_tool_input true # 是否采集 Token 明细 capture_token_usage true [agents] # 全局迭代上限防止单个 Agent 绕圈 default_max_iter 10 # 只对需要调试的 Agent 开 verbose verbose_agents [researcher] [report] # 生成可视化调用图的格式 graph_format mermaid # 是否输出 JSON 明细 export_json true这里几个参数值得说清楚。sample_rate设成 1.0 表示全量采集生产环境可以降到 0.1 减少开销。slow_threshold_sec是性能剖析的关键超过这个值的步骤会被单独标记方便你一眼看到瓶颈。verbose_agents只对研究员开详细日志避免所有 Agent 一起刷屏。3.2 settings.json运行时注入settings.json负责把 Key 和追踪回调注入到 crewAI 运行时。{ llm: { api_key_env: TAOTOKEN_API_KEY, base_url: https://taotoken.net/api, model: gpt-4o-mini }, trace: { enabled: true, output_dir: ./logs, capture_tool_input: true, capture_token_usage: true, slow_threshold_sec: 15.0 }, agents: { default_max_iter: 10, verbose_agents: [researcher] }, report: { graph_format: mermaid, export_json: true } }环境变量这样设export TAOTOKEN_API_KEY你的Key提示不要把 Key 写进settings.json提交到仓库。用api_key_env指向环境变量是更安全的做法。3.3 追踪器实现采集每个 Agent 的耗时下面这段 Python 是追踪的核心它读取两个配置挂载step_callback和任务级回调把每一步的耗时、工具、Token 都记下来。# crew_trace/tracer.py import json import time import tomllib from pathlib import Path from datetime import datetime class CrewTracer: def __init__(self, config_path./config/config.toml): with open(config_path, rb) as f: self.cfg tomllib.load(f) self.trace_cfg self.cfg[trace] self.events [] self.task_times {} self.output_dir Path(self.trace_cfg[output_dir]) self.output_dir.mkdir(parentsTrue, exist_okTrue) def on_step(self, step_output): 每个推理步骤完成后调用 if not self.trace_cfg[enabled]: return event { ts: datetime.now().isoformat(), type: step, agent: getattr(step_output, agent, unknown), tool: getattr(step_output, tool, None), tool_input: ( str(getattr(step_output, tool_input, ))[:200] if self.trace_cfg[capture_tool_input] else None ), result_preview: str(getattr(step_output, result, ))[:120], } self.events.append(event) def on_task_start(self, task_desc): self.task_times[task_desc] {start: time.time()} def on_task_end(self, task_desc, output): rec self.task_times.get(task_desc) if not rec: return rec[end] time.time() rec[duration] rec[end] - rec[start] rec[slow] rec[duration] self.trace_cfg[slow_threshold_sec] usage getattr(output, token_usage, None) if usage and self.trace_cfg[capture_token_usage]: rec[tokens] { prompt: getattr(usage, prompt_tokens, 0), completion: getattr(usage, completion_tokens, 0), total: getattr(usage, total_tokens, 0), } self.events.append({ ts: datetime.now().isoformat(), type: task_end, task: task_desc[:80], duration: round(rec[duration], 2), slow: rec[slow], tokens: rec.get(tokens), }) def export(self): if self.cfg[report][export_json]: path self.output_dir / trace.json with open(path, w, encodingutf-8) as f: json.dump(self.events, f, ensure_asciiFalse, indent2) return self.events def build_graph(self): 生成 mermaid 调用图 lines [graph TD] for ev in self.events: if ev[type] step and ev.get(tool): node f{ev[agent]} --|{ev[tool]}| Tool_{ev[tool]} if node not in lines: lines.append(node) return \n.join(lines)这段代码的关键点on_step挂在 Agent 上on_task_start/on_task_end挂在 Task 上。耗时统计在 Task 级别工具调用链在 Step 级别两者合起来就是完整的执行链路。4. 组装 Crew 并验证追踪数据完整性配置和追踪器都有了现在把它们组装成一个能跑的 Crew。# crew_trace/main.py import os from crewai import Agent, Task, Crew from tracer import CrewTracer tracer CrewTracer() researcher Agent( role研究员, goal收集 crewAI 执行链路追踪相关资料, backstory你是一名擅长技术调研的工程师, verboseTrue, max_iter10, step_callbacktracer.on_step, llm_config{ base_url: https://taotoken.net/api, api_key: os.environ[TAOTOKEN_API_KEY], model: gpt-4o-mini, }, ) analyst Agent( role分析师, goal分析调研结果并提炼要点, backstory你擅长从资料中提取关键结论, verboseFalse, max_iter10, step_callbacktracer.on_step, ) t1 Task( description调研 crewAI 的链路追踪能力列出三种以上方法, expected_output一份包含方法名称和适用场景的清单, agentresearcher, ) t2 Task( description基于调研结果给出追踪配置建议, expected_output三条可落地的配置建议, agentanalyst, context[t1], ) crew Crew( agents[researcher, analyst], tasks[t1, t2], verboseTrue, ) result crew.kickoff() # 采集任务级耗时 for i, task_output in enumerate(result.tasks_output): tracer.on_task_end(ftask_{i}, task_output) events tracer.export() print(f采集到 {len(events)} 条追踪事件) print(tracer.build_graph())跑完之后logs/trace.json里会有完整的事件流。验证数据完整性看三个动作第一检查task_end事件数量是否等于 Task 数量。如果少了说明某个 Task 的回调没触发。第二检查每个task_end是否带tokens字段。如果缺失说明capture_token_usage没生效或者该 Task 的输出对象没有token_usage属性。第三检查step事件里agent字段是否覆盖了所有 Agent。如果某个 Agent 一条 step 都没有说明它的step_callback没挂上。# 快速校验 python -c import json events json.load(open(logs/trace.json)) tasks [e for e in events if e[type]task_end] steps [e for e in events if e[type]step] print(task_end:, len(tasks)) print(step:, len(steps)) print(agents:, set(e[agent] for e in steps)) print(slow tasks:, [t[task] for t in tasks if t.get(slow)]) 如果输出里slow tasks有内容恭喜你性能瓶颈已经被定位到了。接下来就是针对那个 Task 优化 description 或者调max_iter。5. 本篇常见错排查报错一tomllib找不到。Python 3.11 以下没有tomllib。装tomli然后改导入import tomli as tomllib。报错二step_callback不触发。检查 Agent 初始化时是否真的传了step_callback。crewAI 的step_callback是 Agent 级别参数不是 Crew 级别。传错位置不会报错但也不会触发。报错三token_usage全是 0。确认settings.json里capture_token_usage为 true且模型返回里带 usage 字段。部分兼容接口默认不返回 usage需要在请求里显式带上stream_options或类似参数。报错四调用图节点重复。build_graph里用if node not in lines去重但如果 Agent 名或工具名带空格mermaid 会解析失败。把名字里的空格换成下划线。报错五Key 无效。先用模型对话页面发一条测试消息确认 Key 本身可用。如果对话页面正常但代码报 401检查base_url是否漏了/api后缀。注意追踪本身有开销。全量采集时step_callback会在每个推理步骤后同步执行如果回调里做了网络请求会明显拖慢整体速度。生产环境建议把事件先写内存队列异步落盘。6. 把追踪变成习惯而不是救火工具链路追踪这件事最怕的是“出问题了才想起来开”。我的做法是新写一个 Crew先把CrewTracer挂上跑一遍看调用图长什么样。如果图里出现某个 Agent 反复调同一个工具或者某个 Task 耗时明显高于其他当场就改不要等它上线。长期跑编码类 Agent 的话可以考虑用 Coding Plan 把模型调用和追踪统一管起来入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你更习惯在编辑器里直接调 AgentClaudeCode 的接入方式在 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 有说明。最后留一个实用技巧把trace.json按天归档每周扫一次slow标记的 Task。你会发现真正拖慢 Crew 的往往不是模型本身而是某个 Task 的 description 写得太模糊导致 Agent 反复试错。改一句话省一半时间这就是链路追踪带来的最直接的收益。