数据截至 (上游 commit 2ebb9390fe07)
04 · 横切设施 · 巧妙之处 · 边界 · 代码地图
本章讲什么: 前三章是主线(Agent / Crew / Flow);这章讲贯穿它们的基础设施(LLM、记忆、事件、提示词组装),再提炼可借鉴的设计、诚实列出边界,最后给跳转用的代码地图。
4.1 LLM 抽象层
CrewAI 不绑死某家模型,而是有一层 BaseLLM(llms/base_llm.py)和默认实现 LLM(llm.py:? 处定义 class LLM)。LLM 底层走 litellm(统一上百家供应商 API 的库)。
一个真实的工程细节:litellm 被懒加载。因为 litellm 在 import 时会执行 dotenv.load_dotenv(),可能有副作用,所以 CrewAI 用 _ensure_litellm() 在首次用到时才加载(llm.py:74-156):
# llm.py:113-123(摘)
def _ensure_litellm() -> bool:
"""Lazy-load litellm on first use. Returns True if available."""
global _litellm_loaded, LITELLM_AVAILABLE
if _litellm_loaded:
return LITELLM_AVAILABLE
...
LLM 还向上层暴露能力查询:supports_function_calling()(决定走原生还是 ReAct,见 01 章)、supports_stop_words()、supports_multimodal()、supports_response_schema(llm.py)。这层「能力探测」让上层循环能因模型而异地选路径。
4.2 提示词怎么组装
Agent 的系统提示词由 Prompts.task_execution() 拼装(utilities/prompts.py:93-134)。它从「片段(slice)」拼起:role_playing(角色扮演,把 role/goal/backstory 填进去)、tools 或 no_tools、再加任务片段(task / native_task / task_no_tools)。
两种产物(utilities/prompts.py:27-50):
SystemPromptResult:分system/user两条消息(当模型适合用 system prompt)。StandardPromptResult:合成单条prompt。
这解释了 01 章 _setup_messages 里为何要分别处理 "system" in self.prompt 与否(agents/crew_agent_executor.py:182-204)。提示词里是否包含 tools 片段,直接决定 LLM 会不会以 ReAct 文本格式调工具。
4.3 统一记忆(Memory)
memory/unified_memory.py 的 Memory(:76)是一套重新设计的记忆系统,docstring 自述其特点(:76-82):
Uses LLM to infer scope, categories, importance on save. Uses RecallFlow for adaptive-depth recall.
拆成两条线:
写入(remember)用 LLM 分析。 remember(...)(:430)存一条记忆时,LLM 会推断它的 scope(作用域)、category、importance。还有consolidation(合并):若新记忆与已有记忆相似度超过 consolidation_threshold(默认 0.85),触发合并而非重复存(:116-123)。
读取(recall)自适应深度。 recall(...)(:681)用一个 RecallFlow(本身是个 Flow!memory/recall_flow.py)做召回:先快速检索,若置信度低于阈值(confidence_threshold_low 默认 0.5)则触发更深的 LLM 驱动探索(exploration_budget 轮)。最终相关性是 recency / semantic / importance 三者加权(默认 0.3/0.5/0.2,:100-111)的复合分。
默认存储后端是 LanceDB(:92-95),embedder 默认 OpenAI。记忆可挂在 Crew 上,也可作为工具(recall/remember)注入给 Agent(crew.py:1790-1802 _add_memory_tools)。
注:本节据类字段与方法签名描述其结构;完整的召回评分与合并算法未逐行走读。
4.4 事件总线
crewai_event_bus(events/event_bus.py,class CrewAIEventsBus :94)是一个全局发布订阅总线。全流程关键节点都 emit 事件:CrewKickoffFailedEvent、TaskStartedEvent/TaskCompletedEvent、ToolUsageStartedEvent/Finished/Error、AgentLogsStartedEvent 等。
用 bus.on(EventType)(handler) 订阅(:244),bus.emit(source, event)(:570)发布。它支持运行时作用域(_enter_runtime_scope / _exit_runtime_scope),kickoff 进出时管理(crew.py:1047、1086)。可观测 性(tracing)、verbose 日志、评测监听器都挂在这条总线上,做到了「核心逻辑不感知监听者」的解耦。
4.5 巧妙之处(可借鉴的技术)
- 协作降维成工具调用。 多 Agent 委派不是另起一套调度,而是把「派活给同事」做成一个普通工具(
DelegateWorkTool),复用 Agent 的工具循环(tools/agent_tools/agent_tools.py:26、02 章 §2.5)。新机制最小化。 - 两套范式互相嵌套,连执行器都跑在 Flow 上。
AgentExecutor(Flow[...], BaseAgentExecutor)(experimental/agent_executor.py:173)——最底层的 Agent 循环也被表达成 Flow。(01 章 §1.8、03 章 §3.8) - 原生→文本的运行时降级。 模型声称支持 function calling 但实际不行时,当场切回 ReAct 而非失败(
crew_agent_executor.py:576-579)。 - 每轮只执行一个原生工具调用 + 反思。 串行 + 反思保证 LLM 能基于结果调整,只有在无特殊工具的多调用批次才并行(
crew_agent_executor.py:667-784)。 - JSON 容错。 LLM 吐的工具参数常是坏 JSON,
_safe_repair_json用json_repair修复,修不好就原样返回而非崩溃(parser.py:161-179)。 - 缓存断点对齐 ReAct 循环。
_setup_messages在 system / user 提示词末尾打 prompt-cache 断点,跨循环迭代复用稳定前缀(crew_agent_executor.py:189-199)。 - litellm 懒加载 规避其 import 副作用(
llm.py:113)。
4.6 边界与局限(诚实)
consensual流程未实现。Process里它还是注释掉的 TODO(process.py);只有sequential和hierarchical能用,其余raise NotImplementedError(crew.py:1056)。CrewAgentExecutor已弃用。 旧执行器构造即发DeprecationWarning,将来会移除;新默认是实验性的AgentExecutor(crew_agent_executor.py:145-151)。即「当前最稳的循环代码」与「未来的循环代码」处于过渡期。- 经理 Agent 不能带工具。 层级模式下给 manager 传普通工具会先警告清空再抛异常(
crew.py:1522-1528)——这是刻意约束,经理只负责调度。 - 强依赖 LLM 守规矩。 ReAct 路径完全依赖 LLM 输出可解析的
Action/Final Answer格式;格式错就靠「喂回纠错 + 重试」,但仍可能在max_iter内耗尽(crew_agent_executor.py:462-466)。 - 记忆默认要外部依赖。 统一记忆默认用 LanceDB + OpenAI embedding,且写入/召回都可能额外打 LLM,成本与延迟需自己权衡(
memory/unified_memory.py:88-95)。 - 大量运行期依赖外部服务(litellm、各供应商、tracing)。框架本身的「智能」全在编排;真正的能力来自所接的模型。
4.7 横向对比(同 shelf 兄弟项目)
| 维度 | CrewAI | 典型对照 |
|---|---|---|
| 核心隐喻 | 「角色化队伍(Crew)」+「事件流程(Flow)」双范式 | LangGraph 单一「图」范式;AutoGen 「会话式多 Agent」 |
| 多 Agent 协作 | 委派/提问作为工具,或经理调度 | AutoGen 靠 Agent 间消息往返 |
| 控制 vs 自治 | 显式二分:Crew 自治 / Flow 可控,可嵌套 | LangGraph 偏可控,自治需自己拼 |
| 模型接入 | litellm 统一上百家 | 各家 SDK 直连或各自抽象 |
(对照项为框架取向的概括,具体以各兄弟子库 doc 为准。)
4.8 代码地图(导航索引)
| 主题 | 文件路径(相对克隆根 lib/crewai/src/crewai/) | 符号名 |
|---|---|---|
| Agent 干活入口 | agent/core.py | Agent.execute_task / Agent._execute_without_timeout |
| ReAct 循环 | agents/crew_agent_executor.py | CrewAgentExecutor._invoke_loop_react |