Dave Ebbelaar · 27:28 · 发布 2026-05-10 · 2.4万次观看(截至抓取) · 观看原视频
主旨
用纯 Python + 三个文件操作工具(list / grep / read)+ 任意 agent 框架,搭出一个能自主多轮检索的最小 Agentic RAG 系统,作为对 RAG 的 agentic 升级。
核心论点
- Agentic RAG 与 semantic RAG 不是替代,是分工:低延迟/低预算仍用 semantic,有余量就用 agentic,因为 agent 的反馈环能自查自纠。(→ 详解1)
- RAG 的 agent 化本质上就是给 LLM 三个文件工具——列表、搜索、读——让模型在循环里决定调什么、传什么参数,跟 Claude Code / Cursor 这类 coding agent 用同一套原语。(→ 详解2)
- agent 调试必须看 agent 在干什么,不是看结果:
agent.iter这类流式接口把每一步 tool call 暴露出来,否则你只能看到最终答案和「为什么答错了」。 (→ 详解4) - 生产化三件事就够:ripgrep 替 Python 正则提速、用 Pydantic 模型给工具签名/输出定 schema、错误返回(而不是 raise)让 agent 能自我纠正。(→ 详解5、详解6)
- 结构化输出 + 引用元数据是产品化分水岭:把 answer 与 citation(文件 + 行号 + 原文)包成 Pydantic 模型,下游 UI 才能让用户点开引用。(→ 详解5)
- 存储后端可换,思想不变:markdown 文件只是最易上手的载体,PostgreSQL / S3 / VPS / serverless 都可以,只调工具函数。(→ 详解7)
知识点详解
1. 从 semantic RAG 到 agentic RAG:差别在「反馈环」 00:45
Semrag 的形态是「一次性召回 → 一次性 LLM 调用」,信息聚合得再多,模型也只看一次就出答案——如果第一轮召回的 chunk 不对,错就错到底。“Classic semantic rag is definitely not dead as some people on the internet may claim. If you’re looking at very low-latency scenarios where you need raw speed or you really need to optimize for cost, semantic rag is probably still a better starting point.”
Agentic RAG 反过来:把检索拆成三个工具函数交给 LLM,模型自己决定先列哪些文件、用什么模式 grep、再 read 哪几段,中间每一步拿到的结果都重新喂回 LLM 让它接着决定。“Agentic rag will generally outperform semantic rag just because of the feedback loop that the agent can use.” ——这跟 Harness 那套强调「让 agent 自己在反馈环里纠错」是同一思想,只是这视频里没有「harness」这个抽象词,而是直接把它落到三个 Python 函数上。
代价就是 latency 和 token:本片 demo 跑一次问题大概要 10-15 秒、5 次 tool call。低频高价值的查询才划算,实时客服就别上。
2. 三件工具原语:list / grep / read 02:28
“The simplest way to get started with agentic rag is by using simple markdown files that live on the file system in combination with three tools. A tool that can list files, a tool that can search files, and a tool that can read files. And these are the exact same tools that all of the popular agent harnesses are built around nowadays.” ——作者直接点明:这就是 Claude Code、Cursor、Codex 的最小骨架。
2.1 list_files:glob + relative_to 节省 token 04:39
pathlib.Path.glob('**/*.md') 拿到所有 markdown 全路径,然后用 .relative_to(notes_dir) 只保留文件名。“For the AI agent, we don’t want to provide all of the context, because it doesn’t really need to know the full path. So, that’s why we can use relative to. So, it has a shorter hand of going through all of the files, which will save us tokens in the long run.”
这一步看似小动作,实际是 静态上下文与动态上下文 的体现:不让文件路径的冗余字符串污染 prompt。
2.2 grep:正则 + enumerate + 行号 06:08
grep 工具要做四件事:re.compile(pattern, re.IGNORECASE)、对每个文件 splitlines()、用 enumerate(start=1) 拿人类可读的行号、命中后追加 (文件名, 行号, 原文) 三元组。返回结构就是 [(file, line_no, line_text), ...]。
注意命名变量:start=1 不是 0,这样模型拿到的行号跟文本里写的行号对得上;原片 demo 用 “connection pool” 模式 grep engineering wiki,在两个文件里命中,带原始行号。
2.3 read_file:is_relative_to 守 containment 10:57
读文件前必须做一次路径安全检查:把目标字符串转成 Path 之后,用 target.is_relative_to(notes_dir) 验证目标确实在允许的目录树下,失败就 raise 或返回错误。“This is a safety precaution that we can add to contain the agent within a particular folder.” ——这是 agent 系统最常见的「目录穿越」漏洞的最低防线:agent 拿到工具调用权后,理论上能拼出任意路径(../../etc/passwd),一行 is_relative_to 把攻击面收回到 whitelist。
3. 用 Pydantic AI 把工具组装成能动 RAG 12:41
” I’m using Pydantic AI, and it is simply because it simplifies the way that we can make the tools available for a language model and perform that execution loop. But, you can do this with any agent framework and also build your own using the model APIs directly, working with OpenAI, Claude, whatever you want to use.”
实现层面就 5 行:实例化 agent(OpenAIModel('gpt-3.5-turbo', ...))→ tools=[list_files, grep, read_file] → agent.run_sync(question)。然后问 “Why does our nightly deploy job run specifically at this time?“,agent 自己跑 5 次工具调用再回来给答案。
强调一个判断:框架可换。Pydantic AI 的价值在于它把 tool schema + OpenAI function calling + 消息循环封一层,但你用 LangChain、LlamaIndex、甚至自己写 60 行 while 循环都能复现同样效果——本片对框架刻意中立。
4. 用 agent.iter 看模型在循环里干啥 14:09
上面 agent.run_sync 是个黑盒:只能看到最终答案 + 总 token + 总工具调用次数,中间每一步调了哪个工具、传了什么参数、拿到了什么结果全看不到。Pydantic AI 提供 agent.iter() 入口把每一步拆成节点,你可以 for-loop 遍历它、打印每一步的 tool_call 参数和 tool_return 结果。
“With this we can actually also see, look, if we do a grep, so here it runs grep, you can see that it got 14 results, 14 times the document, the line number and then that particular line for this particular search over here.” ——demo 把 debug=True 打开后,grep 那一步就显出 “got 14 results” 而不是只看到 “called grep”。
调试 agentic RAG 系统时,这一步是必修课:模型答错了,先看它第一步 grep 的 pattern 对不对、再看它拿到的文件列表是不是该看的、最后看它读哪一段——错在哪一步决定改 prompt 还是改工具签名。
5. 结构化输出:answer + citation 封装成 Pydantic 模型 19:20
把 agent 的输出用 Pydantic BaseModel 强约束:顶层是 SearchAgent 类,内嵌 answer: str + citations: list[Citation];Citation 含 file_name: str + quote: str + line_number: int。
class Citation(BaseModel):
file_name: str
quote: str
line_number: int
class SearchAgent(BaseModel):
answer: str
citations: list[Citation]
agent = Agent(model, tools=[...], output_type=SearchAgent)下游拿到这个对象就能直接 obj.answer 渲染文本、obj.citations 渲染可点击的引用块。“This is perfect for putting it into another system in a software environment where downstream processes or functions can rely on this particular data format.” ——这是从「玩具 demo」跨到「产品代码」的关键一步:让别的进程能 schema-validate 这份输出,而不是再写正则去 parse 一坨字符串。
6. 生产化最佳实践:Ripgrep + 错误返回 + safety 预算 22:17
6.1 用 Ripgrep 替 Python 正则 22:36
把 grep 函数从「Python re 模块」换成「subprocess.run(['rg', ...]) 调 Ripgrep」。“Ripgrep is built in Rust. So, this is a grep tool that all of the pretty much all of the modern agent harnesses are using because it’s really fast, and it has some out of the box built-in things where it doesn’t go through hidden files. It will ignore files that are within gitignore.”
收益三层:Ripgrep 本身就是 10x+ 提速;自动 skip .gitignore 里的文件(避免把 node_modules 喂给 agent);macOS brew install ripgrep / Windows winget 一行命令搞定,生产环境必须先装。
6.2 错误返回(不 raise)+ try/except 24:56
list_files / grep / read_file 全部用 try/except 兜住,然后 return 一段人类可读的错误字符串,不 raise。“If our AI agent, for example, would make an error, it would put something into a function that results into some type of error because a file does not exist or any other edge case that is going on, it would simply stop. With the return error, it will simply return a human readable error message that the model can then intercept and course correct on.”
逻辑:raise 会让整个 agent loop 崩;return error 字符串让模型看到「文件不存在」然后自己换个文件名再试——这正是 Harness 里讲的 “let the agent self-correct”。
6.3 Safety 预算 22:17
agent_request_limit 限制单次对话总工具调用次数(防 agent 死循环烧光 token)、read_max_lines 限制单次读文件的最大行数(防一次 read 整个 100MB 日志把 context window 撑爆)。两个常量配置,不复杂但生产环境必加。
7. 部署形态适配与收尾 26:19
“This can all work and tailor to whatever situation you are using. You may just need to adjust a few things.” ——本片的工具函数是抽象的,真要换存储只需要改 list/grep/read 的实现:文件 → PostgreSQL 把 markdown 存表里、grep 换成 SQL ILIKE %pattern%;VPS / container app / serverless 部署,只要环境里装了 ripgrep 跟 Python 依赖就能跑。
最后一锤:Dave 明说 “all of the lessons that I share on this channel, also this one, are coming from the real world where I built and implement solutions for my clients”——他强调的是「6 年 freelancer + Datalumina 客户实战」,而不是实验室 demo。但这是商业声明,具体方法是否站得住见「立场与利益」节。
可执行步骤
- 把工程团队/产品文档按 markdown 形式落到一个目录,文件名/标题简洁,避免下划线和奇字符。
- 写
list_files(notes_dir):pathlib.Path(notes_dir).glob('**/*.md')配合.relative_to(notes_dir)返回纯文件名。 - 写
grep(pattern, notes_dir):re.compile(pattern, re.IGNORECASE)+splitlines()+enumerate(start=1)+ 命中追加(file, line, line_text),空目录返回[]。 - 写
read_file(target, notes_dir):把 target 字符串转 Path 后target.is_relative_to(notes_dir)守 containment,然后read_text()。 - 用 Pydantic AI(或任意框架)把三件工具装到 agent 上,跑一次 5-10 行 Python demo 验证闭环。
- 调试时改用
agent.iter()看每步 tool call 参数 + tool return 结果,而不是只看最终答案。 - 输出加 Pydantic 模型(
SearchAgent(answer, citations))让下游能 schema 验证。 - 生产化:
brew install ripgrep(或 Windows 等价命令)、grep切到 subprocess 调 ripgrep、错误用 return 字符串不 raise、加agent_request_limit与read_max_lines。 - 真要换存储后端(PostgreSQL / S3 / Notion API),只改三个工具的实现,agent 层不动。
关联
- RAG 的 agentic 变体 — 印证:两者都解决「给 LLM 喂私有知识」的问题,本片论证 agentic 形态在「允许反馈环自查自纠」这个维度优于纯向量召回的 semantic 形态;但向量召回在延迟/成本上仍占优,适用场景互补而非互斥。
- 用 LightRAG 搭 Graph RAG — 互补:LightRAG 走「向量检索 + 知识图谱」双层架构解决跨文档关系查询;本片走「纯文件系统 + 三件工具 + agent 循环」,覆盖「无需建图、纯文本检索 + 反复读」的轻量场景。两者共享「把私有数据喂给 LLM」的目标,工具集完全不同,按数据复杂度二选一。
- Harness — 进阶:本片是 harness 思想的最简实现——把 agent 的工具/反馈环/guardrails 用三个 Python 函数暴露出来;先看 Harness 理解 harness 作为概念的存在理由,再看本片看「最小骨架能有多瘦」(≈ 80 行 Python)。
一手来源与延伸
- GitHub: daveebbelaar/ai-cookbook · knowledge/agentic-rag ——视频对应的代码仓库,六份文件(build_tools / utils/tools / basic_agent / streaming_steps / structured_output / production)即本片提到的全部 demo 源码。
术语
- Agentic RAG:检索增强生成的 agentic 形态,把搜索/读拆成工具交给 LLM 让其在循环里自主决定调用次序与参数。
- Tool / Function Calling:LLM 不只输出文本、还能按预定义 schema 生成结构化参数让外部代码执行的接口,是 agentic RAG 的技术地基。
- Pydantic AI:Pydantic 团队出的轻量 Python agent 框架,把 tool schema、模型调用、消息循环封一层,核心卖点是类型安全与 OpenAI/Anthropic 切换成本低。
- Ripgrep (
rg):Rust 写的命令行文本搜索工具,速度比 GNU grep/Pythonre快一个数量级,自动尊重 .gitignore,主流 coding agent harness 的标配 grep 后端。 - Pathlib
is_relative_to:Python 3.9+ 的路径方法,判断某路径是否在指定父目录之下,是 agent 工具读文件时防目录穿越的最小防线。 agent.iter():Pydantic AI 的流式入口,把 agent 的执行过程拆成可遍历的节点,每个节点对应一次 LLM 输出或一次 tool call + tool return,用来调试 agentic 系统。- Structured Output (结构化输出):让 LLM 按预定义 schema(Pydantic 模型、JSON Schema 等)输出而非自由文本,下游代码可以直接 schema-validate 后消费。
- Containment check(目录收容检查):agent 工具在读/写文件前验证目标路径是否在白名单目录树下,避免 prompt injection 借相对路径(
../)逃逸到敏感目录。
金句
“It will loop over all of the files in our notes, it finds two occasions where connection pool is mentioned. First, in the billing run book, and then also in the architectural decisions. You can also see the line numbers in here, as well as the particular line. And that is exactly the same way that your coding agent, like Claude Code or Cursor, can navigate your code base and find what code it actually needs to update and edit when you ask a question.” (09:56) —— 把 grep + 行号 + 文件名三件套对应到「coding agent 在 codebase 里找代码」,是本片给读者的最强心智锚:agentic RAG 不神秘,Claude Code 找你代码跟你找文档是同一动作。
立场与利益
description 含两条 datalumina.com 链接(go.datalumina.com/HbJnpc8 推「data freelancer」项目 + go.datalumina.com/XlYLRjP 推 AI 实战课程),定位为「6 年 freelancer + Datalumina 创始人」,口播中段专门做了一段「想 freelancing 第一个 link」的引流。
- 与利益同向(待印证):「agentic RAG 在多数场景优于 semantic RAG」「ripgrep + Pydantic AI 是 production best practice」「Claude Code 这类 coding agent 已经验证了相同范式」——这些主张帮助他卖 Datalumina 的 AI 教学与外包服务,采信前应外部印证:vendor-neutral 测评(Andrew Ng / LangChain 官方)有无同类结论?GPT-5 / Claude Opus 4.5 在 8k+ chunk 的 semantic RAG 上是否真的被 agentic 反超?片中没有给出量化对比,只有单条 demo query 的 5 次 tool call。
- 利益中性:
pathlib.glob / splitlines / enumerate / is_relative_to这些纯 Python 教程、Pydantic模型定义、subprocess调 ripgrep 的代码片段——与博主卖课/接外包无关,可独立验证,按内容本身采信。 - 与利益反向(可信度最高):作者在中段主动承认 “It will probably take 10 to 15 seconds for it to complete” + “five tool calls” + 「error 要 return 字符串不要 raise 让 agent 自己纠」——这些都把系统的延迟/成本/失败率摆到桌面上,反衬他卖的服务是「真实跑得起来的」而不是 PPT 产品。单独标出。
价值定位
- 适合谁:已经在用 semantic RAG(Pinecone/Chroma + OpenAI embedding + LangChain 链)、想升级到 agentic 但被 LangChain / LlamaIndex 抽象搞晕的人;或者想把团队 wiki / 客户文档 / 公司私有 SOP 直接喂给 LLM 又不想建向量库的工程团队;以及正在评估 Pydantic AI 是否值得引入的小型 agent 项目 owner。
- 解决什么:用 ≈ 80 行 Python 复现「Claude Code / Cursor 怎么读 codebase」这个核心范式,避开 framework 黑盒,让你真正理解 tool-calling loop 在跑什么;以及给一份生产级 checklist(ripgrep 替换、错误返回、agent_request_limit、read_max_lines、Pydantic schema 输出)。
- 认知 vs 实操:双高。半篇在讲 harness 思维(为什么是这三个工具、为什么 agent loop 能自纠),半篇给可抄的代码片段与生产化 checklist。
- 与已有笔记重叠时:与 用 LightRAG 搭 Graph RAG 主题相邻但路径不同——LightRAG 走知识图谱建图,本片走文件系统 + agent 循环,本片独有「无向量库、无 embedding、无数据库」的零基础设施方案 + ripgrep/Pydantic AI 的具体技术栈选择。
自检问题
- Agentic RAG 与 semantic RAG 的核心差异是什么?在什么场景下 semantic RAG 仍优于 agentic RAG? 答案:详解1。Semantic 在低延迟/低预算场景占优,agentic 因反馈环能自查自纠,在「允许多花 10-15 秒 + 多烧 token」的查询里胜出。(00:45)
- 本片 agent 的三个核心工具分别是什么?为什么「read_file」必须做
is_relative_to检查? 答案:详解2。list_files / grep / read_file;is_relative_to防 agent 通过相对路径(../)逃逸到白名单目录之外读敏感文件,这是 containment 最低防线。(10:57) - 调试 agentic RAG 系统为什么不能只看
agent.run_sync的最终答案? 答案:详解4。因为 run_sync 是黑盒,只给最终文本 + 总 token + 总工具调用次数,看不到每一步 tool call 用了什么参数、拿到了什么结果;必须切到agent.iter()看中间步骤才能定位「错在 prompt 还是错在工具签名」。(14:09) - 生产化时为什么要把 Python
regrep 换成 Ripgrep?除了速度还有哪些收益? 答案:详解6.1。除了 Rust 写的 10x+ 提速,Ripgrep 自动尊重 .gitignore(不会把 node_modules / .venv 喂给 agent)、主流 coding agent harness 都用它所以踩坑路径已知。(22:36) - 为什么本片的工具函数在出错时用
return error_string而不是raise? 答案:详解6.2。raise 会中断整个 agent loop,模型没有机会看到错误并自纠;return 人类可读的错误字符串让 LLM 把「文件不存在」当普通文本读进去,然后在下一步换个文件名再调——这是 agent 自我纠错机制的必要前提。(24:56)
