目录

07 - 知识库与 RAG:让 Agent 基于你的数据回答

一句话总结:用 Knowledge + 向量数据库把你的文档灌进去,Agent 就能基于你的数据回答问题,而不是瞎编。


问题:LLM 只知道训练数据

大模型很聪明,但它只知道训练截止日期之前的公开数据。你公司的内部文档?你自己写的技术笔记?上周刚发布的产品说明?它一概不知。

你问它”我们公司的年假政策是什么”,它只能瞎猜或者礼貌地说”我不知道”。

这时候你需要 RAG – Retrieval Augmented Generation,检索增强生成。

名字听着唬人,原理很简单:

  1. 把你的文档切成小块,存到一个可以搜索的数据库里
  2. 用户提问时,先搜索相关的文档片段
  3. 把搜到的内容塞给 LLM,让它基于这些内容来回答

打个比方:LLM 是一个聪明的学生,Knowledge 是他的教科书,RAG 就是”先翻书再答题”这个动作。不给教科书,再聪明也只能凭记忆瞎蒙;给了教科书,答案就靠谱多了。


核心组件

在 Agno 里实现 RAG 需要三样东西:

组件 作用 类比
Knowledge 知识库,管理文档的导入和检索 图书馆
VectorDb 向量数据库,存储文档的向量表示 书架
Embedder 嵌入器,把文本转成向量 编目员

向量是什么?简单说就是一串数字,用来表示文本的”语义”。两段话意思越接近,它们的向量就越相似。所以你搜”休假制度”,也能找到写着”年假政策”的文档 – 因为它们语义相近。


搭建知识库:用 ChromaDb

ChromaDb 是一个轻量级的向量数据库,不需要额外安装服务,开箱即用,非常适合本地开发和学习。

先装依赖:

pip install agno openai chromadb

然后搭建知识库:

from agno.knowledge.knowledge import Knowledge
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.vectordb.chroma import ChromaDb
from agno.vectordb.search import SearchType

knowledge = Knowledge(
    vector_db=ChromaDb(
        collection="my_docs",
        path="tmp/chromadb",
        persistent_client=True,
        search_type=SearchType.hybrid,
        embedder=OpenAIEmbedder(id="text-embedding-3-small"),
    ),
)

几个参数说明一下:

  • collection – 集合名称,类似数据库里的表名
  • path – 数据存储路径,persistent_client=True 表示数据持久化到磁盘,脚本重启不丢
  • search_type – 搜索方式,后面细讲,先用 hybrid 就对了
  • embedder – 用 OpenAI 的 text-embedding-3-small 模型来生成向量,便宜又好用

往知识库里灌数据

知识库搭好了,接下来往里塞内容。Agno 支持多种数据源:

# 从 URL 加载
knowledge.insert(
    url="https://docs.agno.com/introduction.md",
    skip_if_exists=True,  # 已存在就跳过,避免重复导入
)

# 直接塞文本
knowledge.insert(
    name="Company FAQ",
    text_content="我们公司成立于2020年,主要做AI产品开发。办公地点在北京中关村。",
)

# 从本地文件加载(支持 PDF、TXT、MD 等)
# knowledge.insert(name="Report", path="path/to/document.pdf")

insert() 做了什么?它把你的内容切成小块(chunking),用 Embedder 把每块转成向量,然后存到 ChromaDb 里。这些都是自动的,你不用操心细节。


给 Agent 接上知识库

知识库有了数据,接下来让 Agent 用起来:

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.knowledge.knowledge import Knowledge
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.vectordb.chroma import ChromaDb
from agno.vectordb.search import SearchType

# 1. 创建知识库
knowledge = Knowledge(
    vector_db=ChromaDb(
        collection="my_docs",
        path="tmp/chromadb",
        persistent_client=True,
        search_type=SearchType.hybrid,
        embedder=OpenAIEmbedder(id="text-embedding-3-small"),
    ),
)

# 2. 导入内容
knowledge.insert(
    url="https://docs.agno.com/introduction.md",
    skip_if_exists=True,
)

# 3. 创建 Agent 并绑定知识库
agent = Agent(
    model=OpenAIChat(id="gpt-4o-mini"),
    knowledge=knowledge,
    search_knowledge=True,
    instructions="你是一个文档问答助手,基于知识库中的信息回答问题。如果知识库中没有相关信息就说不知道。",
    markdown=True,
)

agent.print_response("Agno是什么?", stream=True)

关键参数:

  • knowledge – 绑定知识库
  • search_knowledge=True – 让 Agent 在回答前自动搜索知识库(默认就是 True,写出来是为了明确意图)

当用户提问时,Agent 会自动调用知识库搜索相关内容,把搜到的文档片段放进上下文,然后基于这些内容来回答。整个流程是自动的。


搜索类型:keyword vs vector vs hybrid

知识库搜索有三种方式,通过 search_type 参数控制:

keyword(关键词搜索)

按字面匹配。搜”年假”就找包含”年假”这个词的文档。简单粗暴,但如果用户说的是”休假天数”就搜不到”年假政策”了。

vector(向量搜索)

按语义匹配。把问题和文档都转成向量,计算相似度。搜”休假天数”也能找到”年假政策”,因为它们语义接近。但有时候精确匹配反而不如关键词。

hybrid(混合搜索)

两种都用,然后用 RRF(Reciprocal Rank Fusion)算法把结果融合排序。兼顾精确匹配和语义理解,大多数场景下效果最好。

from agno.vectordb.search import SearchType

# 三种搜索类型
SearchType.keyword   # 关键词搜索
SearchType.vector    # 向量搜索(默认)
SearchType.hybrid    # 混合搜索(推荐)

没有特殊需求的话,用 hybrid 就完事了。


实战:公司 FAQ 机器人

来做一个实际的例子 – 把公司的常见问题灌进知识库,做一个内部问答机器人:

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.knowledge.knowledge import Knowledge
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.vectordb.chroma import ChromaDb
from agno.vectordb.search import SearchType

# 创建知识库
knowledge = Knowledge(
    vector_db=ChromaDb(
        collection="company_faq",
        path="tmp/chromadb",
        persistent_client=True,
        search_type=SearchType.hybrid,
        embedder=OpenAIEmbedder(id="text-embedding-3-small"),
    ),
)

# 导入公司 FAQ
knowledge.insert(
    name="FAQ",
    text_content="""
Q: 公司的工作时间是什么?
A: 工作时间是早9点到晚6点,弹性工作制。

Q: 年假有多少天?
A: 入职第一年10天,之后每年增加1天,最多15天。

Q: 如何申请报销?
A: 在OA系统提交报销申请,附上发票照片,部门经理审批后财务处理。

Q: 远程办公政策是什么?
A: 每周可以远程办公2天,需要提前在系统上报备。

Q: 试用期多长?
A: 试用期3个月,表现优秀可以提前转正。
""",
    skip_if_exists=True,
)

# 创建问答 Agent
agent = Agent(
    model=OpenAIChat(id="gpt-4o-mini"),
    knowledge=knowledge,
    search_knowledge=True,
    instructions=[
        "你是公司的HR助手,专门回答员工关于公司政策的问题。",
        "基于知识库中的信息回答,不要编造不存在的政策。",
        "如果问题超出知识库范围,建议员工联系HR部门。",
        "回答要简洁友好。",
    ],
    markdown=True,
)

# 测试几个问题
agent.print_response("年假有多少天?")
agent.print_response("我想报销出差费用,怎么操作?")
agent.print_response("公司有健身房吗?")  # 知识库里没有这个信息

第三个问题”公司有健身房吗”在知识库里找不到相关内容,Agent 应该会告诉用户它不知道,建议联系 HR – 因为我们在指令里明确说了”不要编造”。这就是 RAG 的优势:有据可查的就回答,查不到的就坦诚说不知道,而不是瞎编一个答案。


知识库 vs 上下文窗口:该选哪个

你可能会想:为什么不直接把所有文档塞进 system prompt 里?干嘛还要搞向量数据库这么复杂?

简单对比一下:

方案 优势 劣势
直接塞 prompt 简单,不需要额外组件 文档多了放不下,token 费用高
知识库 + RAG 支持海量文档,只检索相关部分 需要向量数据库,检索可能遗漏

经验法则:

  • 文档总量在几千字以内 – 直接塞 prompt 就行,简单省事
  • 文档总量上万字或持续增长 – 用知识库,否则上下文窗口撑不住,钱也烧不起

几个实用建议

数据质量比什么都重要。 垃圾数据灌进去,搜出来的也是垃圾。确保你导入的内容是干净、准确、有价值的。

给 insert 加上 skip_if_exists=True。 避免每次运行脚本都重复导入数据。

persistent_client=True 别忘了。 否则数据只存在内存里,脚本一关就没了。

instructions 里写清楚”不知道就说不知道”。 否则 Agent 搜不到相关内容时可能会编造答案。

生产环境考虑用 PgVector。 ChromaDb 适合开发和小规模使用,生产环境建议用 PostgreSQL + PgVector,更稳定,支持更大规模的数据。


今天学了什么

回顾一下关键概念:

  • RAG 的核心思路是”先检索,再生成” – 让 LLM 基于你的数据回答,而不是凭空猜测
  • Knowledge 是 Agno 的知识库类,负责管理文档的导入和检索
  • ChromaDb 是一个轻量级向量数据库,本地开发首选,不需要额外服务
  • Embedder 把文本转成向量,让语义搜索成为可能
  • search_type 有三种:keyword(字面匹配)、vector(语义匹配)、hybrid(两者结合,推荐)
  • search_knowledge=True 让 Agent 自动检索知识库,默认就是开启的

一个快速对照表:

你想做的事 用什么
让 Agent 基于自己的文档回答 Knowledge + VectorDb
本地开发快速验证 ChromaDb
生产环境大规模部署 PgVector
兼顾精确匹配和语义搜索 SearchType.hybrid

下一篇预告

08 - 安全护栏:别让 Agent 胡说八道

Agent 能力越大,风险越大。它可能泄露敏感信息、生成不当内容、或者被用户的 prompt injection 带跑偏。下一篇我们聊 Guardrails – 给 Agent 装上安全护栏,在输入和输出两端做检查,确保它不越界、不胡说、不被利用。从”能干活”进化到”安全地干活”。