乐于分享
好东西不私藏

CrewAI Knowledge 源码调用链深度解读

CrewAI Knowledge 源码调用链深度解读

CrewAI Knowledge 源码调用链深度解读

本文不再只讲“有哪些配置项”,而是专注回答一个更具体的问题:

当你在 Crew 或 Agent 上配置了 knowledge_sources 之后,CrewAI 在运行时到底经过哪些源码调用,把这些 knowledge 初始化、查询,并最终注入到模型 prompt 中?

重点覆盖:

  • Crew knowledge 的初始化调用链
  • Agent knowledge 的初始化调用链
  • 任务执行时 knowledge 的检索调用链
  • query rewrite 的调用位置
  • knowledge 最终进入 prompt 的准确位置
  • Agent knowledge 与 Crew knowledge 在运行时如何汇合

1. 先说结论

如果只看标准 Agent.execute_task() 路径,knowledge 的主链可以压缩成下面这几步:

用户配置 knowledge_sources    -> Crew 创建 crew-level Knowledge    -> Crew 关联每个 Agent,并调用 agent.set_knowledge(...)    -> Agent 执行 task 时先构造 task_prompt    -> 用 task_prompt 生成 knowledge search query    -> 分别查询 agent.knowledge 和 crew.knowledge    -> 把召回结果 append 到 task_prompt    -> executor 把 task_prompt 作为 user message 发给模型

所以最核心的事实只有两个:

  1. 初始化阶段:Crew 和 Agent 分别建立自己的 Knowledge 实例
  2. 执行阶段:Agent 同时查询这两份 knowledge,并把结果拼进 user prompt

2. 全链路总览图

先给一张总图:

[用户声明 knowledge_sources]        |        +--> Crew.knowledge_sources        |        |        |        +--> Crew.create_crew_knowledge()        |                 |        |                 +--> crew.knowledge = Knowledge(...)        |        +--> Agent.knowledge_sources                 |                 +--> Crew 组装 Agent                          |                          +--> agent.crew = crew                          +--> agent.set_knowledge(crew_embedder=crew.embedder)                                   |                                   +--> agent.knowledge = Knowledge(...)[运行 task]        |        +--> Agent.execute_task()                 |                 +--> _prepare_task_execution()                 |        |                 |        +--> task.prompt()                 |        +--> build_task_prompt_with_schema(...)                 |        +--> format_task_with_context(...)                 |                 +--> handle_knowledge_retrieval()                 |        |                 |        +--> _get_knowledge_search_query(task_prompt, task)                 |        +--> query agent.knowledge                 |        +--> query crew.knowledge                 |        +--> append retrieval text to task_prompt                 |                 +--> _finalize_task_prompt()                 |                 +--> executor.invoke({"input": task_prompt, ...})                          |                          +--> user message: Current Task: {input}

3. 第一步:用户从哪里配置 knowledge

knowledge 的配置入口主要有两层。

3.1 Crew 层

Crew 上可以声明:

  • knowledge_sources
  • knowledge
  • embedder

最关键的是:

  • lib/crewai/src/crewai/crew.py:397create_crew_knowledge()

也就是说,Crew 是可以直接拥有一份共享 knowledge 的。


3.2 Agent 层

Agent 上也可以声明:

  • knowledge_sources
  • knowledge
  • knowledge_storage
  • knowledge_config

初始化入口在:

  • lib/crewai/src/crewai/agent/core.py:323set_knowledge()

这意味着 Agent 自己也可以有一份私有 knowledge。


4. 第二步:Crew knowledge 是怎么初始化的

Crew knowledge 的直接入口是:

  • lib/crewai/src/crewai/crew.py:397create_crew_knowledge()

核心逻辑可以概括成:

ifself.knowledge_sources:self.knowledge = Knowledge(        sources=self.knowledge_sources,        embedder=self.embedder,        collection_name="crew",    )

这里有几个重要含义。

4.1 只有存在 knowledge_sources 才会创建 crew knowledge

也就是说:

  • 只声明了 crew.knowledge=None 不会自动生成内容
  • 真正常见的入口是 knowledge_sources

4.2 crew knowledge 有明确的 collection namespace

这里显式传了:

collection_name="crew"

后续 storage 层会把它映射到类似:

  • knowledge_crew

这样的 collection 名字。

所以 crew knowledge 天然就是一份共享逻辑命名空间。


4.3 crew embedder 会向下游传播

创建 crew knowledge 时会带上:

embedder=self.embedder

这意味着 Crew 层的 embedding 配置,不只是给自己用,也经常会成为 Agent knowledge 初始化时的默认来源。


5. 第三步:Agent knowledge 是怎么初始化的

Agent knowledge 的入口是:

  • lib/crewai/src/crewai/agent/core.py:323set_knowledge()

这个方法是整个链路里最关键的“Agent 知识装配点”。

它的职责可以概括为:

  1. 检查 agent 是否已经有现成的 knowledge
  2. 如果没有,但有 knowledge_sources,则构造 Knowledge(...)
  3. 在需要时继承 crew 传下来的 embedder
  4. 完成 agent 私有知识库的初始化

5.1 为什么 set_knowledge() 通常不是孤立调用

因为 Agent 往往不是单独存在,而是由 Crew 管理。

在 crew 组装 agent 的过程中,你前面已经定位到类似链路:

agent.crew = crewagent.set_knowledge(crew_embedder=embedder)

这说明:

  • Agent 会先获得它所属的 crew
  • 然后再根据自身 knowledge_sources 和 crew embedder 补全 knowledge

这一步非常关键,因为它建立了 agent knowledge 与 crew runtime context 的连接


5.2 Agent knowledge 和 Crew knowledge 在初始化上彼此独立

虽然 Agent 会继承 Crew 的一些配置,但它不会直接把 crew knowledge 拷贝成自己的 knowledge。

更接近的真实模型是:

  • agent.knowledge 是 agent 自己的知识库
  • crew.knowledge 是 crew 自己的共享知识库
  • 执行时同时查两者

也就是说它们是并存关系,不是覆盖关系。


6. 第四步:Knowledge 对象内部长什么样

不管是 agent knowledge 还是 crew knowledge,最终都落到同一个核心对象:

  • lib/crewai/src/crewai/knowledge/knowledge.py

关键结构可以压缩成:

classKnowledge(BaseModel):    sources: list[BaseKnowledgeSource]    storage: KnowledgeStorage | None = None    embedder: EmbedderConfig | None = None

所以它内部的责任边界是:

  • sources:提供原始文本
  • embedder:负责 embedding 配置
  • storage:负责向量存储和相似度检索

对外最关键的运行时接口是:

query(...)

因此在调用链上可以把 Knowledge 理解成:

Agent/Crew 上层配置 与 底层向量检索实现 之间的统一门面。


7. 第五步:任务执行时从哪里触发 knowledge 检索

标准执行入口在:

  • lib/crewai/src/crewai/agent/core.py:663execute_task()

在这条主链里,knowledge 相关步骤大致顺序是:

  1. _prepare_task_execution()
  2. handle_knowledge_retrieval()
  3. _finalize_task_prompt()
  4. 把最终 prompt 交给 executor

其中真正负责 knowledge 检索的是:

  • lib/crewai/src/crewai/agent/utils.py:117handle_knowledge_retrieval()

也就是说:

knowledge 检索不是在初始化时把整库塞进 prompt,而是在每次 task 执行时,按当前任务动态检索。


8. 第六步:为什么要先构造 task_prompt

在做 knowledge retrieval 前,Agent 会先把当前任务整理成一份较完整的 task_prompt

主要链路在:

  • lib/crewai/src/crewai/agent/core.py:434_prepare_task_execution()
  • lib/crewai/src/crewai/task.py:794Task.prompt()

这个阶段会先把这些内容组织起来:

  • task description
  • expected output
  • schema 指令
  • context
  • memory(稍后路径里也会补)
  • 其他运行时信息

这样做的原因是:

knowledge 检索需要围绕“当前任务真实语义”来做

而不是只拿用户最初的一句 description 去查。

所以 query rewrite 和 retrieval 的输入,通常是已经比较完整的 task_prompt


9. 第七步:knowledge query rewrite 是怎么接进去的

在真正查知识库之前,会调用:

  • lib/crewai/src/crewai/agent/core.py:1217_get_knowledge_search_query()

它会发起一次独立的小型 LLM 调用。

其结构大致是:

messages = [    {"role""system""content": rewriter_prompt},    {"role""user""content": query},]rewritten_query = self.llm.call(messages)

对应模板在:

  • lib/crewai/src/crewai/translations/en.json:35
  • lib/crewai/src/crewai/translations/en.json:36

9.1 这一步的作用

目标是把“面向生成的任务描述”改写成“面向检索的搜索查询”。

因为原始 task prompt 往往:

  • 很长
  • 包含输出要求
  • 含有很多非检索关键词
  • 不一定适合直接做向量检索

所以 CrewAI 会先做一层 query rewrite。


9.2 这一步不是主 prompt 本身

这个点非常容易混淆。

这里的 system/user message:

  • 只是给 query-rewriter 这个 LLM 调用用的
  • 不是最终主 agent 对话的 system prompt / user prompt

所以应理解为:

knowledge 检索前置有一轮辅助 LLM,用来优化检索 query。


10. 第八步:运行时如何同时查 agent knowledge 和 crew knowledge

真正的双路检索发生在:

  • lib/crewai/src/crewai/agent/utils.py:117handle_knowledge_retrieval()

这里会分别处理:

10.1 agent knowledge

如果 agent.knowledge 存在,就基于 search query 调用它的 query。


10.2 crew knowledge

如果 agent.crew 存在且 agent.crew.knowledge 存在,也会再查一遍 crew knowledge。


10.3 两份结果不会互相覆盖

而是分别格式化,然后按顺序 append 到当前任务 prompt。

这意味着运行时语义是:

task context+ agent private knowledge snippets+ crew shared knowledge snippets

而不是:

merge all knowledge first -> one single store -> one retrieval result

这点对于理解调试结果很重要。


11. 第九步:检索结果如何格式化

knowledge 的检索结果会通过工具函数格式化成文本块。

你前面已经定位到:

  • lib/crewai/src/crewai/knowledge/utils/knowledge_utils.py:4

格式大致是:

Additional Information: <snippet1><snippet2>...

也就是说,knowledge 检索结果在进入主 prompt 前,会先被压平成纯文本补充信息,而不是以结构化 JSON message 的形式发送。


12. 第十步:knowledge 最终注入到哪里

这是整条链最值得反复确认的一点。

在:

  • lib/crewai/src/crewai/agent/utils.py:160
  • lib/crewai/src/crewai/agent/utils.py:164
  • lib/crewai/src/crewai/agent/utils.py:171
  • lib/crewai/src/crewai/agent/utils.py:174

召回结果被 append 到:

  • task_prompt

而不是 append 到 system prompt。


12.1 后续 executor 怎么处理这个 task_prompt

在 executor 里,task_prompt 会作为:

{"input": task_prompt, ...}

传给 prompt builder,最终落到默认用户消息模板:

Current Task: {input}

见:

  • lib/crewai/src/crewai/agents/crew_agent_executor.py:194
  • lib/crewai/src/crewai/translations/en.json:9

所以最终结论非常明确:

knowledge 在标准 Agent 执行路径里,默认进入 user message,而不是 system prompt。


13. 把主执行链串成一张更细的图

Crew kickoff / task orchestration    |    +--> Crew.create_crew_knowledge()    |       |    |       +--> crew.knowledge = Knowledge(..., collection_name="crew")    |    +--> for each agent:            |            +--> agent.crew = crew            +--> agent.set_knowledge(crew_embedder=crew.embedder)                    |                    +--> if agent.knowledge_sources:                            agent.knowledge = Knowledge(...)Agent.execute_task(task, context)    |    +--> _prepare_task_execution(task, context)    |       |    |       +--> task.prompt()    |       +--> build_task_prompt_with_schema(...)    |       +--> format_task_with_context(...)    |    +--> handle_knowledge_retrieval(agent, task_prompt, task)    |       |    |       +--> _get_knowledge_search_query(task_prompt, task)    |       |       |    |       |       +--> LLM rewrite query    |       |    |       +--> if agent.knowledge: agent.knowledge.query(...)    |       +--> if crew.knowledge: crew.knowledge.query(...)    |       +--> format snippets    |       +--> task_prompt += knowledge_context    |    +--> _finalize_task_prompt(...)    |    +--> executor.invoke({"input": task_prompt, ...})            |            +--> user message = Current Task: {input}

14. storage 层在这条链中的位置

knowledge query 最终不会直接在 source 上做字符串查找,而是走 storage。

相关文件:

  • lib/crewai/src/crewai/knowledge/storage/base_knowledge_storage.py
  • lib/crewai/src/crewai/knowledge/storage/knowledge_storage.py

你前面已经确认两个关键点:

returnself._client ifself._client else get_rag_client()
collection_name = f"knowledge_{self.collection_name}"ifself.collection_name else"knowledge"

这意味着在调用链上:

上层看到的是 Knowledge.query(...)

下层真正执行的是某个 RAG client 的向量检索

所以 Knowledge 是业务层门面,KnowledgeStorage 是后端适配层。


15. source 层在这条链中的位置

source 主要负责“知识从哪里来”。

关键抽象:

  • lib/crewai/src/crewai/knowledge/source/base_knowledge_source.py
  • lib/crewai/src/crewai/knowledge/source/base_file_knowledge_source.py

尤其文件型 source 会把字符串路径解释为 knowledge/ 目录下的路径。

这说明 source 层主要参与的是:

  1. 内容读入
  2. 内容分块/预处理
  3. 交给 storage 建索引

一旦进入运行时 query 阶段,主角就从 source 切换成了 storage / query pipeline。


16. 为什么说这是“动态检索”,不是“静态注入”

因为从链路上看,knowledge 并不会在 Agent 初始化时一次性全部塞进 prompt。

真正发生的是:

  • task 来了
  • 先构造 task prompt
  • 用 task prompt 生成 search query
  • 按本轮任务实时检索
  • 只把最相关的 snippets 塞回 prompt

所以 knowledge 的语义是:

面向当前任务的按需检索上下文

而不是:

Agent 恒定拥有的一大段固定说明文本

这也是为什么它更适合放在 user prompt,而不是 system prompt。


17. 调试 knowledge 问题时,最值得检查的几个节点

如果以后要排查“为什么 knowledge 没生效”,按这条链看最有效:

17.1 配置层有没有 source

  • crew.knowledge_sources 是否真的有值
  • agent.knowledge_sources 是否真的有值

17.2 初始化层有没有构造出 Knowledge

  • crew.knowledge 是否成功建立
  • agent.knowledge 是否成功建立

17.3 query rewrite 是否生成了合理检索词

  • _get_knowledge_search_query() 结果是否偏题

17.4 双路检索是否都执行了

  • agent knowledge 有没有查
  • crew knowledge 有没有查

17.5 检索结果是否被 append 回 prompt

  • task_prompt 最终是否包含 Additional Information 块

17.6 分数阈值是不是过高

  • KnowledgeConfig.score_threshold
  • Crew.query_knowledge() 的显式参数

因为这条链是逐层串起来的,任何一层掉链子,最终模型都像“完全没看到知识”。


18. 一张总表:各步骤在哪个文件

阶段
作用
关键位置
Crew knowledge 初始化
创建 crew 共享知识库
lib/crewai/src/crewai/crew.py:397
Agent knowledge 初始化
创建 agent 私有知识库
lib/crewai/src/crewai/agent/core.py:323
Task prompt 初始构造
形成检索前的任务语义上下文
lib/crewai/src/crewai/agent/core.py:434

 / lib/crewai/src/crewai/task.py:794
Query rewrite
把 task prompt 改写成检索 query
lib/crewai/src/crewai/agent/core.py:1217
Knowledge retrieval
同时查 agent/crew knowledge
lib/crewai/src/crewai/agent/utils.py:117
Retrieval formatting
把命中结果变成文本块
lib/crewai/src/crewai/knowledge/utils/knowledge_utils.py:4
Prompt append
把 knowledge 文本拼回 task_prompt
lib/crewai/src/crewai/agent/utils.py:160-174
Executor message build
把 task_prompt 发成 user message
lib/crewai/src/crewai/agents/crew_agent_executor.py:194

19. 如果你只想记一句话

CrewAI 的 knowledge 调用链本质是:Crew 和 Agent 在初始化阶段分别构造自己的 Knowledge 实例;任务执行时,Agent 先基于当前 task_prompt 生成检索 query,再分别查询 agent knowledge 和 crew knowledge,并把召回结果追加回 task_prompt,最终作为 user message 发给模型,而不是写进 system prompt。