Day 2 你写的 read_file 工具有个致命限制:你得知道文件名。
真实场景是这样的:
三种"曾经的方案",都不行:
| 方案 | 为什么不行 |
|---|---|
| 把所有笔记塞进 prompt | 200 篇 = 几百 KB,早爆 context 了;每次调用还要重发所有内容,token 爆炸 |
用 grep 关键词搜 | 你问"记忆机制",笔记里写的是"短期记忆"、"conversation state" —— 关键词搜不到 |
| 让 LLM 逐个读文件 | 200 次 API 调用,慢得要死 |
RAG(Retrieval-Augmented Generation) 是当下的标准答案:
把一段文字塞给 embedding 模型,它吐出一个浮点数数组(叫"向量")。
"Agent 是能自主决策的智能程序"
↓ embedding 模型
[0.023, -0.181, 0.442, 0.007, ..., -0.093] ← 1024 个数字
关键性质:语义相近的文本,向量在高维空间里挨得近。
文本 A: "Agent 是智能程序" → 向量 A
文本 B: "AI Agent 有自主决策能力" → 向量 B(跟 A 很近)
文本 C: "今天午饭吃什么" → 向量 C(跟 A/B 都很远)
余弦相似度(cosine similarity):
A · B / (|A| × |B|) ← 越接近 1 越像,接近 0 或负数就完全无关
向量数据库,专门存这些向量并高效搜索"最近邻"。
对比:
SELECT ... WHERE id = 42 —— 精确匹配找跟这个向量最像的 5 条 —— 相似度匹配Chroma 的优点:纯 Python 库,本地文件存储,零部署。pip install chromadb 就能用。
notes/*.md
↓ 读取
"Agent 是..." "记忆机制..." "工具调用..."
↓ 切分(chunking)
[chunk1, chunk2, chunk3, ...] ← 每段 ~500 字
↓ embedding 模型
[vec1, vec2, vec3, ...] ← 每段一个向量
↓ 存入 ChromaDB
{id: uuid, text: chunk, vector: vec, metadata: {file, chunk_idx}}
用户提问:"Agent 怎么记住上下文?"
↓ 同一个 embedding 模型
[0.021, -0.15, ...] ← 问题的向量
↓ ChromaDB 检索 top-k
[chunk7 (相似度 0.89), chunk12 (0.83), chunk3 (0.79)]
↓ 拼进 prompt
"根据以下笔记回答:\n[chunk7]\n[chunk12]\n[chunk3]\n\n问题:Agent 怎么记住上下文?"
↓ LLM
"根据你的笔记,Agent 通过 messages 数组累积历史..."
cd ~/Documents/practice/agents
mkdir -p day3
cd day3
python3 -m venv venv
source venv/bin/activate
# 新增:chromadb(向量库)
pip install openai python-dotenv chromadb
onnxruntime 等,稍等一分钟。
ep-2024xxxx-xxxxxARK_API_KEY=你的APIKey # 跟 Day 1/2 一样
ARK_ENDPOINT_ID=你的Chat接入点ID # 跟 Day 1/2 一样
ARK_EMBEDDING_ENDPOINT_ID=你新建的Embedding接入点ID # 🆕 今天新增
ARK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
先不做工程,玩一下 embedding,感受"向量相似度"是什么。
见 day3/demo1_embedding_basics.py(已经建好,直接跑):
python demo1_embedding_basics.py
=== 3 段文本的 embedding ===
📝 文本 1: "Agent 是能自主决策的智能程序"
向量前 5 维: [0.021, -0.153, 0.089, -0.007, 0.212, ...]
向量长度: 2048
📝 文本 2: "AI 助手可以理解意图并调用工具"
向量前 5 维: [0.019, -0.148, 0.091, ...]
📝 文本 3: "今天中午吃什么好"
向量前 5 维: [-0.088, 0.234, -0.011, ...]
=== 两两相似度(余弦) ===
文本1 vs 文本2: 0.783 ← 都在讲 AI Agent,很相似
文本1 vs 文本3: 0.412 ← 无关,低
文本2 vs 文本3: 0.398 ← 无关,低
把 notes/ 目录下所有 .md 文件切分 → embedding → 存 ChromaDB。
我们准备了 3 篇示例笔记在 day3/notes/,你也可以塞自己的笔记进去。
for md_file in notes/*.md:
text = read(md_file)
chunks = split_by_paragraph(text, max_chars=500) # 简单按段落切
for i, chunk in enumerate(chunks):
vector = embed(chunk)
chroma.add(
ids=[f"{md_file}#{i}"],
documents=[chunk],
embeddings=[vector],
metadatas=[{"file": md_file, "chunk_idx": i}],
)
python demo2_ingest.py
预期输出:
📁 找到 3 个笔记文件
├─ notes/agent-basics.md (2 chunks)
├─ notes/rag-notes.md (3 chunks)
└─ notes/go-tips.md (2 chunks)
✅ 入库完成:共 7 个 chunk 存入 ChromaDB
📊 数据库路径: ./chroma_db/
切分是 RAG 的隐藏关键。我们今天用最简单的"按段落切+字数限制"策略。有几个原则:
命令行 REPL,你输入问题 → 显示最相似的 3 段笔记。
python demo3_query.py
试试这些问题:
问题: Agent 是怎么记住上下文的?
↑ 你的笔记里可能写着"messages 数组累积历史" —— 关键词完全不匹配
↑ 但 embedding 应该能找到
问题: Go 里怎么处理错误
问题: 什么是向量数据库
🔍 检索: "Agent 是怎么记住上下文的?"
[1] 相似度 0.847 来源: notes/agent-basics.md#1
内容: LLM 本身是无状态的,Agent 通过 messages 数组累积对话历史...
[2] 相似度 0.732 来源: notes/agent-basics.md#0
内容: Agent = LLM + 记忆 + 工具 + 规划。所谓记忆有两种...
[3] 相似度 0.512 来源: notes/rag-notes.md#2
内容: 长期记忆通常放向量库,短期放消息数组...
让相邻 chunk 重叠 50 字。测试:"段落被切断的信息还能否被检索到?"
让 query 支持 "只搜 notes/go-tips.md"—— ChromaDB 支持 where={"file": "..."}。
把 notes/ 替换成你自己的 10 篇笔记,看看检索效果如何。
| 坑 | 解决 |
|---|---|
| Chroma 每次跑重复入库 | 入库前先 collection.delete();或用文件 hash 做 id,重复自动覆盖 |
| 相似度都很低(<0.3) | 可能笔记内容跟问题真的无关;或 chunk 切太碎;或用错了 embedding 模型 |
| Chroma 版本报错 | Chroma 0.4 → 0.5 API 有变化。我们代码用 PersistentClient,兼容新版 |
| 中文 embedding 效果差 | 确认用的是 doubao-embedding-text,支持中文;如果是 base 模型可能只对英文好 |
cd ~/Documents/practice/agents
git status # 确认 .env、chroma_db/ 都在 .gitignore 里
git add day3/
git commit -m "Day 3: RAG 向量检索(embedding + ChromaDB)"
git push
Day 4 是大合体:把 Day 2 的工具调用 + Day 3 的向量检索融合,做出知识助手 v1。你会看到:
search_notes 检索read_full_note那才是真正让人惊艳的 Agent 时刻 ✨