CrewAI manager + specialists 代码说明

这份脚本和前面两份材料是一个连续关系:

所以它真正想讲清楚的是:

这份脚本整体在做什么

你可以把它理解成一个四步工作流:

  1. manager 先读取项目记忆,再把题拆成一个 checklist
  2. 链路预算 specialist 调工具做计算
  3. 调制 specialist 再根据结果给建议
  4. manager 把两位 specialist 的结果收回来,写出最终报告

最后会把完整结果写到本目录下的 Markdown 报告里。


第一部分:导入库和基础设置

脚本开头先导入:

这里有两个很实用的小动作:

warnings.filterwarnings(...)

它的作用是把当前环境里那个 logfire-plugin 的导入警告压掉。
这个警告不影响 demo 运行,但会把终端刷得很乱。

os.environ.setdefault("OTEL_SDK_DISABLED", "true")

这行是为了减少 tracing / telemetry 相关的干扰。
对课堂 demo 来说,我们只想把例子跑通,不想让同学一上来就被 tracing 配置打断。


第二部分:DEFAULT_PROMPTMODEL_PRESETS

DEFAULT_PROMPT

这是默认题目。
内容和前面两版保持一致,还是那道无线链路分析题。

这样做的好处是:

MODEL_PRESETS

这里定义了两个模型预设:

这相当于一个方便的开关。
如果小模型卡住,就可以直接切到 full


第三部分:clear_proxy_env()

这个函数会清掉:

以及对应的大写版本。

为什么要这样做?

因为很多同学电脑里装过代理。
一旦这些环境变量存在,本来应该访问本地 http://localhost:11434/v1 的请求,就可能被错误地送到代理上去。

所以这一步的本质是:


第四部分:两个工具的参数结构

class LinkAnalysisArgs(BaseModel)

这是第一个工具的参数定义。

它列出了链路预算要用到的全部输入:

这里用 Pydantic 的意义是:

class ModulationArgs(BaseModel)

这是第二个工具的参数定义。
它非常简单,只需要一个:

因为第二个工具只负责把 SNR 变成调制建议。


第四点五部分:memory 在这里怎么体现

这份 demo 没有上复杂的数据库 memory, 而是用了一个更适合课堂的最小版本:

里面写的是:

然后 manager 通过 read_project_memory 工具先把它读进来。

这能帮助同学区分三件事:


第五部分:第一个工具 AnalyzeWirelessLinkTool

这一类继承自 BaseTool

name

name: str = "analyze_wireless_link"

这是工具名。
模型调用工具时,看到的就是这个名字。

description

description: str = "计算无线链路的路径损耗、接收功率、噪声底、SNR 和 Shannon 容量。"

这是给模型看的工具说明。
说明写得越清楚,模型越容易正确选工具。

args_schema

args_schema: type[BaseModel] = LinkAnalysisArgs

这表示:

_run(...)

这是工具真正执行的函数。

它逐行做了这些事:

path_loss = 92.45 + ...

这是自由空间路径损耗的近似公式。

作用:

received = ...

这是接收功率的链路预算公式。

它把:

放在一起,得到最终的接收功率。

bandwidth_hz = bandwidth_mhz * 1_000_000

把 MHz 转成 Hz。
因为后面 Shannon 容量公式要用 Hz。

noise = -174 + 10 * math.log10(bandwidth_hz) + noise_figure_db

这是热噪声底的常见写法。

含义是:

snr_value = received - noise

接收功率减噪声底,就是 SNR

capacity_bps = bandwidth_hz * math.log2(1 + 10 ** (snr_value / 10))

这是 Shannon 容量公式。

注意这里的 SNR 要先从 dB 转成线性值。


第六部分:第二个工具 SuggestModulationTool

这和第一个工具是同样的写法,只是功能更简单。

它输入:

输出:

这样就能把“计算”和“工程判断”分成两个工具。


第六点五部分:第三个工具 ReadProjectMemoryTool

这个工具非常简单:

但它很重要, 因为它把 memory 变成了一个课堂上能看见、能解释的东西。

在这份 demo 里, memory 的作用不是“神奇地记住一切”, 而是:


第七部分:build_llm(...)

这里把 CrewAI 用的模型统一接到本地 Ollama /v1

和前面一样:


第八部分:build_crew(...)

这是整份脚本最核心的一部分。

manager_agent

这个角色是:

它的工作不是自己去算, 而是:

这就是这份脚本和 7/2-prog 最本质的不同。


第八点一部分:这份 demo 里的 hierarchical 到底怎么工作

很多同学看到:

process=Process.hierarchical
manager_agent=manager_agent

会以为背后是三个 agent 在“自由聊天、自由协商”。

但这份 demo 里,真实机制其实更具体,也更工程化:

  1. 你先手工定义了 3 个 agent

  2. 你再手工定义了 4 个 task

  3. 每个 task 都明确绑定了一个 agent
    所以系统不是“临时决定谁来做”,而是你已经写清楚了谁负责什么。

  4. hierarchical 的作用是:
    manager_agent 处在更高层的“统筹 / 把关”位置, 而不是把它当成一个普通执行者。

所以这份 demo 里, hierarchical 的真实含义更接近:

而不是:


第八点二部分:这份 demo 真正把三个 agent 串起来的是什么

真正让三个 agent 连成一条工作流的, 不是 “hierarchical” 这一个词本身, 而是:

尤其是 context

在这份脚本里:

所以实际发生的事情是:

  1. manager 先输出拆题结果
  2. 预算 specialist 读取这份拆题结果再计算
  3. 调制 specialist 再读取前面两步结果
  4. manager 最后读取前三步结果,做最终综合

也就是说:

两者一起,才构成了真正的多 Agent workflow。


第八点三部分:process、context、memory 各自负责什么

这三个词很容易混在一起, 但在这份 demo 里其实分工很清楚:

process

解决的是:

在这里就是:

context

解决的是:

在这里它负责:

memory

解决的是:

在这里它体现为:

所以一句话说:

budget_specialist

这个角色是:

它只负责:

modulation_specialist

这个角色是:

它只负责:

第一个 task:planning_task

这是 manager 的第一步任务。

它不做数值计算, 只做一件事:

这一步的教学价值非常大, 因为它把“多 Agent 不是乱分工”这件事显式写出来了。 同时这里还把 memory 放到了最前面:

这样同学更容易理解, memory 为什么会影响后面的 workflow。

第二个 task:budget_task

这是链路预算专家的任务。

它和 7/2-prog 里的第一步很像, 但这次它会带着 manager 的 checklist 去执行。

这表示:

第三个 task:modulation_task

这是调制专家的任务。

它会读取:

然后再调用 suggest_modulation

所以这一步体现的是:

第四个 task:final_task

这一步又回到 manager。

它会读取前三步结果, 输出最终报告。

注意这里写得很明确:

这就是 manager + specialists 模式里最典型的一步。

Crew(...)

这里把三个 agent 和四个 task 组装起来。

这次最重要的改动是:

process=Process.hierarchical

并且还加了:

manager_agent=manager_agent

这表示:

这比 7/2-prog 更接近你在课堂上真正想讲的“manager + specialists”。

它仍然没有去追求“炫技式并行”, 而是把重点放在:

这些才是多 Agent 真正值得先学的部分。


第九部分:summarize_task_output(...)

这个函数的作用是:

这样最后写 Markdown 报告时, 我们就能看到每一步任务到底产出了什么。


第十部分:write_markdown_report(...)

这个函数会把运行结果写成本目录下的:

里面包括:

这对教学特别有帮助, 因为你不只看到最后答案, 还看到 manager 和 specialist 分别做了什么。


第十一部分:main()

这里主要做 4 件事:

  1. 解析命令行参数
  2. 选模型
  3. 建 Crew 并运行
  4. 把结果打印并写入 Markdown

其中最关键的一行是:

result = crew.kickoff(inputs={"user_prompt": args.prompt})

这行就是真正启动整个多 Agent 工作流的地方。


这份脚本真正想让同学理解什么

最重要的不是“记住 CrewAI 的 API”。

而是下面这几件事:

  1. manager 和 specialist 的职责应该分开
  2. task 是任务交接和上下文传递的关键
  3. 工具不只是“能不能调”,还要“谁来调”
  4. memory 可以先用“本地经验记录”这种最小形式讲清楚
  5. Process.hierarchical 更适合讲 manager + specialists
  6. context 才是这份 demo 里真正的信息传递机制
  7. 多 Agent 的价值很多时候来自分工与把关,而不是单纯并行
  8. CrewAI 更适合讲工作流和多角色协作