第 38 章 · Agno Knowledge 与 RAG
手册在那里,模型却不知道
还是那个跨境卖家。他有一份厚厚的《选品与合规手册》——哪些品类进哪个国家要认证、哪些材质在欧盟受限、锂电池玩具的运输和申报要求是什么。上一章接上工具和 MCP 之后,Agent 已经能联网搜索、能查自家销量,看起来“能干活”了。可他随手一问:“这款含锂电池的玩具能发德国吗?”
Agent 答得头头是道,却是编的。联网搜索能查到公开新闻,查不到他压在电脑里的这份内部手册;自定义工具能查销量,却没人告诉它这份 PDF 存在。模型的博学止步于训练数据和能调用的工具,公司内部资料它一无所知——这正是第 11 章开篇讲的问题:模型不知道你的私有资料。
第 11 章给出的答案是 RAG(检索增强生成):回答前先查资料,再基于资料作答。这一章要做的,是看 Agno 怎么把这套模式变成几行可运行的代码。
概念与主书映射:Agno 把 RAG 做成一等能力
第 11 章讲得很清楚:pi 不内建知识库,它把 RAG 当作一种“接入的能力”——可以做成工具(search_knowledge)、可以接一个 RAG 类 MCP Server、也可以打包成技能。三种方式共享同一个内核:Agent 本身不存知识,需要时“去查”,检索就是一次工具调用。
Agno 的取向不同。它把 Knowledge、Vector DB(向量数据库)、Embedder(嵌入模型)这些组件直接放进框架里,构成 Agno 五级能力模型里靠后的一级:给 Agent 装上外部知识。用 Agno 的话说,这是一等能力(first-class citizen)——不需要你自己写检索工具、自己管理向量库客户端,配置好 Knowledge 对象、挂到 Agent(knowledge=...) 上,剩下的检索、拼接上下文、生成回答都由框架接管。
落到术语上,Agno 和主书用的是同一套词汇,只是包装方式不同:
- Embedding(文本向量):把文字转成能表示语义的一串数字。在 Agno 里对应
OpenAIEmbedder之类的嵌入器,负责把文档片段和用户问题都转成向量。 - Vector DB(向量数据库):存向量、支持“语义最接近”检索的数据库。Agno 支持多种向量库后端,
LanceDb是文档里最常见的一种,轻量、可本地跑。 - 切片(Chunking):长文档要先切成小段再分别生成 Embedding。Agno 的知识库加载器在读取 PDF、网页等资料时会做这一步,具体切法可以配置。
- Hybrid Search(混合检索):关键词匹配 + 语义匹配一起用。
LanceDb支持SearchType.hybrid,查“CE 认证”这种精确术语和查“能不能安全出口”这种模糊问法都能兼顾。
这四个概念串起来,就是“建库 + 检索”两阶段的数据流,和第 11 章的描述完全一致——只是在 Agno 里,这条流水线被封装成了几个类的组合,而不是你手写的 embed() / vectorDb.search() 调用。
可运行的 Agno 代码
示例一:菜谱知识库(最小可跑通版本)
先看一个最小示例,来自 Agno 的常见用法:读取一份在线 PDF 菜谱,切片、嵌入、存进向量库,Agent 基于资料回答做菜问题。
from agno.agent import Agent
from agno.embedder.openai import OpenAIEmbedder
from agno.knowledge.pdf_url import PDFUrlKnowledgeBase
from agno.models.openai import OpenAIChat
from agno.vectordb.lancedb import LanceDb, SearchType
knowledge = PDFUrlKnowledgeBase(
urls=["https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf"],
vector_db=LanceDb(
uri="tmp/lancedb", # 本地向量库存储位置
table_name="recipe_knowledge",
search_type=SearchType.hybrid, # 关键词 + 语义混合检索
embedder=OpenAIEmbedder(id="text-embedding-3-small"),
),
)
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
knowledge=knowledge,
instructions=[
"你是一个中文资料问答助手。",
"回答资料相关问题时,优先使用知识库内容。",
"资料里没有就明确说明没有找到。",
],
markdown=True,
)
# 首次运行需要显式加载知识库:解析 PDF → 切片 → 生成向量 → 写入 LanceDb
if agent.knowledge is not None:
agent.knowledge.load()
agent.print_response("如何制作 Pad Thai?", stream=True)
这段代码把第 11 章的“建库两阶段”封装了起来:PDFUrlKnowledgeBase 负责资料解析和切片,LanceDb + OpenAIEmbedder 负责生成向量并存储,agent.knowledge.load() 触发整条建库流程,之后每次 print_response 调用都会自动检索、拼接上下文、再回答。
示例二:跨境电商合规手册知识库
把同样的模式换到贯穿案例上——那份《选品与合规手册》。假设手册已经整理成公司内部可访问的 PDF(或多份 PDF),做法完全一致,只是资料源和 instructions 换成合规场景:
from agno.agent import Agent
from agno.embedder.openai import OpenAIEmbedder
from agno.knowledge.pdf_url import PDFUrlKnowledgeBase
from agno.models.openai import OpenAIChat
from agno.vectordb.lancedb import LanceDb, SearchType
compliance_knowledge = PDFUrlKnowledgeBase(
urls=[
"https://internal.example.com/docs/selection-compliance-manual.pdf",
"https://internal.example.com/docs/eu-battery-regulation.pdf",
],
vector_db=LanceDb(
uri="tmp/lancedb",
table_name="compliance_knowledge", # 单独一张表,和菜谱知识库物理隔离
search_type=SearchType.hybrid, # 型号、认证编号这类术语需要关键词精确匹配
embedder=OpenAIEmbedder(id="text-embedding-3-small"),
),
)
compliance_agent = Agent(
model=OpenAIChat(id="gpt-4o"),
knowledge=compliance_knowledge,
instructions=[
"你是跨境电商合规助手,负责回答选品、认证、运输相关问题。",
"回答必须基于知识库中的手册内容,禁止使用未经检索验证的常识。",
"如果资料里没有明确条款,直接说明“手册中未找到相关依据”,不要猜测。",
"涉及具体国家、认证标准(如 CE、UN38.3)时,注明来自手册的哪个章节。",
],
markdown=True,
show_tool_calls=True, # 观察 Agent 是否真的触发了检索
)
if compliance_agent.knowledge is not None:
compliance_agent.knowledge.load()
compliance_agent.print_response(
"这款含锂电池的儿童玩具可以发德国吗?需要哪些认证?",
stream=True,
)
和菜谱示例的差别不在 API,而在工程约束:table_name 单独隔离、instructions 里明确“没有依据就拒答”、show_tool_calls=True 便于确认检索确实发生了。这几点正是“能跑通的 demo”和“能上生产的 RAG”之间的距离,后面几节会展开。
示例三:结合已有工具,让 Agent 同时具备检索和查询能力
延续上一章的自定义工具,Knowledge 和 Tools 并不冲突——一个 Agent 可以同时具备“查手册”和“查销量”两种能力,模型自己判断该用哪个:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
def query_sales(start: str, end: str, limit: int = 10) -> dict:
"""查询指定时间段内销量最高的 SKU。只读,不修改任何数据。"""
rows = sales_db.readonly_top_skus(start=start, end=end, limit=limit)
return {"rows": rows, "source": "sales_read_replica"}
research_agent = Agent(
model=OpenAIChat(id="gpt-4o"),
knowledge=compliance_knowledge, # 复用示例二里建好的知识库
tools=[query_sales],
instructions=[
"回答合规问题时查知识库;回答销量问题时查销量工具;两者都需要时都要查。",
"合规结论必须引用手册依据,销量数据必须说明统计口径。",
],
markdown=True,
show_tool_calls=True,
)
research_agent.print_response(
"这批含锂电池玩具最近卖得怎么样?如果要追加发德国的库存,有什么合规要求?",
stream=True,
)
这一段呼应第 11 章“RAG 只是众多工具能力之一”的定位——即便 Agno 把 Knowledge 做成了一等能力,它在 Agent 眼里仍然是和其他工具并列的一种“可调用的信息来源”,而不是取代工具系统的独立机制。
深入一层:参数怎么选,和 pi 的做法有什么不同
Embedder 怎么选? 决定检索质量的第一个变量是嵌入模型。text-embedding-3-small 便宜、够用,适合大多数中英文混合的业务文档;如果资料里专业术语密集(法规条文、化学成分表),可以考虑更大的嵌入模型或者领域专用模型。这一步在 pi 里对应你自己调用 embed() 时选的模型——Agno 只是把这个选择放进了 OpenAIEmbedder(id=...) 这一个参数位。
Vector DB 怎么选? LanceDb 适合起步和中小规模场景,本地文件即可跑起来,不需要额外部署服务。生产规模变大、需要多副本或者更强的运维能力时,通常会换成托管的向量数据库服务。这个决策和 pi 里“你自己挑一个向量库、自己维护客户端”是同一个决策,只是 Agno 把切换成本压低到了换一个类的构造参数。
Hybrid Search 什么时候必须开? 只用语义检索,查“CE-EN71”这种精确编号命中率会明显下降,因为语义相近的词很多,编号却要求精确匹配。SearchType.hybrid 把关键词检索和语义检索的结果做融合,这正是第 11 章反复强调的“精确术语用关键词、模糊问法用语义”。合规手册、产品型号这类资料,hybrid 几乎是必选项,不是可选项。
引用追踪怎么做? 这是 Agno 默认封装和主书 pi 做法差别最大的地方。pi 的标准做法是让检索工具显式返回 {text, source, score, updatedAt} 这样的结构化字段(回顾第 11 章的返回结构),后续的引用展示、评测、观测都基于这些字段。Agno 把检索到回答这一步做得更“黑盒”——框架自动完成检索和上下文拼接,你不需要手写这部分逻辑,但也意味着如果要在最终回答里显式标注“这段话来自手册第几节”,需要你在 instructions 里明确要求模型引用来源,或者在知识库入库时把 source、章节信息作为文档元数据附加上去,让模型在生成时“看得到”这些信息。换句话说:pi 把引用信息当作检索结果的必选字段来设计;Agno 默认给你的是便捷,引用的颗粒度需要你自己在资料整理和 instructions 里补回来。
知识库更新怎么处理? agent.knowledge.load() 是一次性触发,資料变了要重新调用它才能反映最新内容(部分场景可以配置增量加载,避免每次全量重建)。这和 pi 里“把建库做成可重跑的流程”是同一个工程要求——区别只是 pi 需要你自己写这个重跑脚本,Agno 提供了 load() 这个统一入口。
动手看看
拿示例二的合规知识库做起点,做两个小实验:
- 把
search_type从SearchType.hybrid改成纯向量检索(如果LanceDb支持对应选项),问一次“UN38.3 认证要求”,对比命中的片段有什么变化——你会直观感受到 hybrid 对精确术语的帮助。 - 给
compliance_agent追加第二份资料(比如一份关于包装标识的 PDF),重新调用load(),问一个跨两份文档的问题,观察 Agno 是否能同时检索到两份资料里的相关片段并综合回答。
实战中的几个坑
坑一:切片粒度不当,检索回来的片段要么太碎要么太大。
- 现象:命中的资料片段读起来上下文不完整,或者一次塞进太长一段导致回答变得笼统。
- 原因:知识库加载时的默认切片策略不一定适合你的文档结构,法规条文和菜谱说明的理想切片长度并不一样。
- 对策:关注知识库加载器是否支持自定义切片参数,按语义单元(条款、小节)切,而不是按固定字数硬切。
坑二:以为接了 Knowledge 就自动会引用来源。
- 现象:回答听起来很像基于资料,但完全看不出是手册第几节、哪份文档,无法核实真伪。
- 原因:Agno 默认只是把检索到的内容拼进上下文让模型参考,不会自动在回答里标注来源,这一步需要显式要求。
- 对策:在
instructions里明确要求“注明引用的文档和章节”,并在文档入库前给内容打好元数据(文件名、版本号、章节标题)。
坑三:忘记调用 load(),或者资料更新后没有重新加载。
- 现象:换了新版手册,Agent 还在按旧条款回答;或者第一次跑起来时 Agent 说“知识库里没有任何内容”。
- 原因:
load()是显式触发的,不是资料一放进urls就自动生效;也不会自动感知源文件变化。 - 对策:把“资料更新 → 重新
load()”做成可重复执行的运维步骤,而不是手工一次性操作。
坑四:把 Knowledge 和 Memory 混在一起用。
- 现象:某次检索命中的政策片段,被误当作“用户偏好”或“长期事实”存进了记忆系统,后续对话里反复被引用,即使政策已经过期。
- 原因:没分清 Knowledge(外部资料证据)和 Memory(用户偏好、长期事实)的边界,见下一节详述。
- 对策:知识库命中的内容只应该进入本轮 Context,不要写入 Memory;需要长期记住的是“用户是谁、偏好什么”,不是“某次查到的资料内容”。
Knowledge 不是 Memory:四个概念的边界
Agno 初学者最容易把 Knowledge、Memory、Storage、Context 混在一起,尤其是在 Knowledge 已经“检索到内容”之后,很容易觉得“顺手存进记忆就好”。第 11 章和附录 B 的边界在 Agno 里同样适用,而且更要小心,因为框架把检索这一步做得很顺滑,容易让人忘了这是四个完全不同的机制:
| 概念 | 回答的问题 | 在 Agno 里的形态 |
|---|---|---|
| Knowledge | 外部资料里有什么证据? | PDFUrlKnowledgeBase + LanceDb 等,检索命中即证据 |
| Memory | 下次还值得记住什么用户偏好/长期事实? | Memory + enable_user_memories=True(第 39 章展开) |
| Storage | 这次聊过什么、状态如何恢复? | SqliteStorage 等会话持久化(第 39 章展开) |
| Context | 这一轮模型实际看见什么? | 检索命中的片段被临时拼进本轮提示 |
一个具体的判断标准:知识库命中的片段,无论多么“重要”,都不应该自动流入长期记忆。政策、手册、产品资料会更新,今天检索命中的一段条款明天可能已经作废;而 Memory 应该保存的是相对稳定的东西——这个用户偏好哪类产品、上次沟通中确认过的业务规则。把二者分开管理,才不会让 Memory 变成一个装满过期资料的噪声仓库。
Agno vs 主书 pi 做法
| 能力 | Agno 怎么做 | pi(harness)怎么做 | 各自适合谁 |
|---|---|---|---|
| 知识库定义 | Knowledge 对象(如 PDFUrlKnowledgeBase)+ vector_db= 一站式配置 | 自己维护向量库客户端,封装 search_knowledge 工具 | Agno 适合快速搭建;pi 适合要完全掌控索引和检索逻辑的场景 |
| 检索触发方式 | 挂到 Agent(knowledge=...),框架自动判断何时检索并拼接上下文 | 作为工具/MCP/技能显式接入,检索即一次工具调用 | Agno 更省心;pi 让“是否检索、检索了什么”在 ReAct 轨迹里显式可见 |
| Hybrid Search | LanceDb(search_type=SearchType.hybrid) 一个参数开启 | 自己在检索服务层实现关键词 + 语义融合逻辑 | Agno 一行代码;pi 灵活但要自己写融合与排序 |
| 引用追踪 | 需要在 instructions 和文档元数据里自行补齐来源信息 | 检索结果结构化返回 {text, source, score, updatedAt},天然带来源 | pi 对“可审计”要求高的场景更省心;Agno 需要额外设计 |
| 知识库更新 | agent.knowledge.load() 显式重新加载 | 把建库做成可重跑的独立流程/脚本 | 两者都要求“更新可重跑”,Agno 提供了统一入口 |
分野和上一章一致:Agno 是应用框架,把 RAG 的“建库 + 检索 + 拼接上下文”整体封装成开箱即用的组件,几行代码就能有一个能回答资料问题的 Agent;pi 是 harness,把 RAG 当作“你接入的一种能力”,检索本身是 ReAct 循环里一次显式的、可审计的工具调用。两者背后是同一套 RAG 方法论,只是抽象层次不同——Agno 换来速度,代价是引用颗粒度、检索融合逻辑这些细节需要你在它的接口里主动补齐;pi 换来掌控力,代价是你要多写这部分工程代码。
小结
- Agno 把 Knowledge/RAG 做成一等能力:
PDFUrlKnowledgeBase+LanceDb+OpenAIEmbedder几行代码就能搭出一个能检索资料的 Agent,对应主书第 11 章的 RAG 模式。 - 核心概念和主书一致:Embedding(语义向量)、Vector DB(向量库)、切片(Chunking)、Hybrid Search(混合检索),只是被封装进了几个类的构造参数。
- Knowledge 不是 Memory:检索命中的片段只应进入本轮 Context,不该被当作长期偏好写进 Memory——四个概念(Knowledge/Memory/Storage/Context)各自回答不同的问题。
- 生产级 RAG 需要在 Agno 默认封装之上自己补齐引用来源、切片策略、更新机制——框架给的是便捷,工程边界仍要你设计。
- 和主书 pi 相比,Agno 换速度,pi 换掌控力:同一套 RAG 方法论,落地深度不同,选哪个取决于你是要快速搭应用,还是要把检索每一步都做成可审计的工具调用。
Agent 现在既能动手(工具)又能查资料(知识库),但这些能力都只在“当前这一次调用”里有效——对话一结束,它就把用户是谁、聊过什么、进行到哪一步全部忘光。下一章,我们看 Storage、Memory、State 如何让 Agent 从一次性调用变成一个记得住事情的长时系统。