上周五凌晨两点,我盯着终端里一行诡异的报错发呆——CrewAI 跑出来的结果里,两个 Agent 居然互相覆盖了对方的输出字段。一个负责写技术文档的 Researcher,把另一个负责代码审查的 Reviewer 的结论给吞了。这不是 bug,是我没搞清楚 CrewAI 的角色隔离机制。
如果你也遇到过“多智能体协作时,A 干了 B 的活”或者“最终结果里某个 Agent 的输出莫名其妙消失了”,那这篇笔记就是为你写的。
角色分配:别把 Agent 当函数用
很多人刚接触 CrewAI 时,习惯把每个 Agent 当成一个独立的 API 调用——定义角色、给个 prompt、扔进去跑。这其实是在用单智能体的思维做多智能体协作。
CrewAI 的角色分配核心在于职责边界。不是“这个 Agent 负责写代码”,而是“这个 Agent 在什么场景下、以什么身份、对什么类型的输入做出响应”。我踩过的一个坑:给两个 Agent 都设置了allow_delegation=True,结果它们互相把任务甩给对方,陷入了死循环。
正确的做法是给每个 Agent 一个明确的“领地”。比如:
# 这里踩过坑:不要用模糊的 role 描述researcher=Agent(role="技术调研员",goal="从给定技术文档中提取关键信息",backstory="你只负责阅读和总结,不参与任何代码编写或决策",allow_delegation=False# 别让它把活甩给别人)backstory字段不是装饰品。CrewAI 的底层会用这个字段来约束 Agent 的行为边界。如果你写“你是一个全能的工程师”,那它就会试图干所有事,包括抢别人的活。
任务委派:不是发指令,是签合同
任务委派最容易出问题的地方在于上下文传递。我见过最典型的错误:把一个大任务拆成几个子任务,然后每个子任务都重新加载一遍完整的上下文。结果 Agent 们各自为政,输出之间毫无关联。
CrewAI 的任务委派机制更像是一种“契约”——每个 Task 不仅定义了要做什么,还定义了依赖什么和产出什么。
task1=Task(description="分析用户提供的日志文件,提取所有错误码",expected_output="一个包含错误码及其出现次数的字典",agent=researcher)task2=Task(description="基于错误码字典,生成修复建议",expected_output="每个错误码对应的修复步骤列表",agent=fixer,context=[task1]# 别这样写:把 task1 的整个输出塞进去)注意context参数。很多人以为这里传的是“前一个任务的输出”,其实传的是前一个任务的完整执行记录,包括中间思考过程。如果你的 Agent 的 prompt 里没有明确告诉它“只关注最终输出,忽略思考过程”,它可能会被上下文里的废话带偏。
我习惯在expected_output里明确指定输出格式,并且在description里加一句“只输出最终结果,不要包含推理过程”。这能省掉很多后期解析的麻烦。
结果聚合:最容易被忽视的环节
多智能体协作的最终输出,往往不是某个 Agent 的单独结果,而是多个 Agent 输出的组合体。CrewAI 默认会把所有 Agent 的输出按顺序拼接,但实际场景中,我们需要的是结构化聚合。
举个例子:一个写代码的 Agent 和一个写测试的 Agent,它们的输出应该合并成一个完整的项目文件,而不是两段独立的文本。
我踩过的一个大坑:用Crew的output属性直接拿结果,发现两个 Agent 的输出字段名冲突了。CrewAI 内部用 Agent 的名字作为 key,如果你两个 Agent 名字相似(比如coder_agent和tester_agent),解析时很容易搞混。
解决方案是显式定义输出处理器:
defaggregate_results(outputs):# 这里踩过坑:不要假设 outputs 的顺序code=outputs.get("coder_agent","")tests=outputs.get("tester_agent","")returnf"# 代码\n{code}\n\n# 测试\n{tests}"crew=Crew(agents=[coder,tester],tasks=[code_task,test_task],output_processor=aggregate_results)注意output_processor这个参数。CrewAI 的较新版本支持自定义聚合函数,但文档里写得很隐晦。如果你用的是旧版本,可能需要手动遍历crew.tasks来收集结果。
调试技巧:让 Agent 开口说话
多智能体协作最头疼的问题是黑盒——你不知道哪个 Agent 在哪个环节出了问题。我的经验是:在 Agent 的backstory里加一句“在每次输出前,先输出当前任务的名称和你的角色”。
debug_agent=Agent(role="调试助手",backstory="你会在每次输出前打印'[DEBUG] 当前任务: xxx, 角色: xxx'",...)这样在最终输出里,你能看到每个 Agent 的执行轨迹。如果某个 Agent 的输出缺失,看日志就能定位到是它没执行,还是执行了但结果被覆盖了。
另一个实用技巧:给每个 Task 设置verbose=True。CrewAI 会打印出 Agent 的思考过程,虽然信息量大,但排查“为什么这个 Agent 没按预期工作”时非常有用。
个人经验
别迷信“多智能体一定比单智能体好”。我见过太多项目,明明一个 Agent 加一个精心设计的 prompt 就能搞定,非要拆成三四个 Agent,结果引入了一堆协调问题。
CrewAI 的真正价值在于角色隔离——当你的任务确实需要不同专业背景的知识时,才值得用多智能体。比如一个 Agent 负责读技术文档,另一个负责写代码,第三个负责测试。如果只是把同一个任务拆成几步,那用 pipeline 模式比多智能体更稳定。
最后,记得给每个 Agent 设置max_iter参数。默认情况下,Agent 可能会无限循环思考,尤其是当任务描述不够清晰时。我一般设成 3-5 次,超过就强制输出当前结果。宁可结果不完美,也比卡死强。