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 发给模型
所以最核心的事实只有两个:
-
初始化阶段:Crew 和 Agent 分别建立自己的 Knowledge实例 -
执行阶段: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 知识装配点”。
它的职责可以概括为:
-
检查 agent 是否已经有现成的 knowledge -
如果没有,但有 knowledge_sources,则构造Knowledge(...) -
在需要时继承 crew 传下来的 embedder -
完成 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 相关步骤大致顺序是:
-
_prepare_task_execution() -
handle_knowledge_retrieval() -
_finalize_task_prompt() -
把最终 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 层主要参与的是:
-
内容读入 -
内容分块/预处理 -
交给 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. 一张总表:各步骤在哪个文件
|
|
|
|
|---|---|---|
|
|
|
lib/crewai/src/crewai/crew.py:397 |
|
|
|
lib/crewai/src/crewai/agent/core.py:323 |
|
|
|
lib/crewai/src/crewai/agent/core.py:434
lib/crewai/src/crewai/task.py:794 |
|
|
|
lib/crewai/src/crewai/agent/core.py:1217 |
|
|
|
lib/crewai/src/crewai/agent/utils.py:117 |
|
|
|
lib/crewai/src/crewai/knowledge/utils/knowledge_utils.py:4 |
|
|
|
lib/crewai/src/crewai/agent/utils.py:160-174 |
|
|
|
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。
夜雨聆风