Skip to content

个人知识库(RAG) ​

gg 自带一个最小、无依赖的检索层,服务于用户自己的文档:agent 可以通过 kb_search 工具对个人知识库做语义检索。不用向量数据库,不用额外服务——就是 ~/.gg/kb/<name>/index.json 一个 JSON 文件。索引留在用户本机;注意索引构建时文档文本会发给配置的 embeddings provider(本地只存生成的向量)。

它和长期记忆是互补关系,不是重复:

记忆(memory_search)知识库(kb_search)
内容关于用户的提炼事实:偏好、人物、日常日志用户文档、笔记、手册、收藏资料的原文(单文件上限 512 KiB)
体量小而精大而全
注入MEMORY.md 注入(按预算);人物、群组和旧日志按需检索按需检索,绝不整库注入
查询关键词语义(embeddings)

agent 的经验法则:"用户喜欢什么 / X 是谁 / 我们之前聊过什么" → memory_search。"用户的文档里 Y 是怎么说的" → kb_search。"这段代码是干什么的" → read / grep,别走知识库。

流水线 ​

gg kb index <dir>            gg kb search <query> / kb_search tool
┌──────────────┐              ┌──────────────┐
│ walk <dir>   │              │ embed query  │
│  skip .git,  │              │  (index's    │
│  node_modules│              │   own model)  │
└──────┬───────┘              └──────┬───────┘
       ▼                             ▼
┌──────────────┐              ┌──────────────┐
│ chunk text   │              │ cosine search│
│  paragraphs, │              │  brute force │
│  overlap     │              │  top-k       │
└──────┬───────┘              └──────┬───────┘
       ▼                             ▼
┌──────────────┐              ┌──────────────┐
│ embed chunks │              │ return chunk │
│  batched     │              │  text+source │
│  /embeddings │              │  +score      │
└──────┬───────┘              └──────────────┘
       ▼
┌──────────────┐
│ save index   │
│  index.json  │
└──────────────┘

组件 ​

组件位置说明
分块internal/kb/chunk.go按空行切段落,代码围栏保持完整,约 1200 字符一块,重叠 150 字符。超长段落在 UTF-8 边界硬切。
Embeddingsinternal/kb/embed.goEmbedder 接口;OpenAIEmbedder 调任意 OpenAI 兼容的 /embeddings 端点,每次请求 64 条文本。
索引构建internal/kb/build.go遍历目录树,跳过 VCS/依赖/构建目录,只收文本扩展名,未知文件先探测是否为二进制,单文件上限 512 KiB。
向量存储internal/kb/store.goJSON 持久化 chunks + vectors,带 manifest(name、model、dim、timestamp)。临时文件 + rename 原子写。
检索internal/kb/store.go暴力余弦相似度。精确、无依赖,几万个 chunk 也够快。
Agent 工具internal/tools/kb_search.gokb_search(query, top_k);只有默认索引存在时才注册,工具列表保持干净。工具描述会引导 agent:用户文档 → kb_search,提炼过的用户事实 → memory_search,实时代码 → read/grep。用索引自己的模型 embed 查询,避免悄无声息的维度/模型错配。
CLIinternal/cliapp/kb.go`gg kb index

关键设计决策 ​

  • 查询用索引的模型 embed,不用 flag 指定。 manifest 记录 embedding 模型;工具和 kb search 都复用它。混用模型会悄无声息地污染余弦分数,所以这里是强制,不是建议。
  • 暴力检索是故意的。 文档量级(几千个 chunk)下,float32 向量的精确余弦是亚毫秒到毫秒级。以后可以用 ANN 索引(HNSW)替换 Index.Search,文件格式和工具契约都不用变。
  • chunk 之间重叠。 一个概念被切在 chunk 边界上时,相邻 chunk 共享约 150 字符,照样能检回来。
  • 查询时只读。 除了 embed 查询的那一次网络调用,工具不碰网络;索引就是本地文件。
  • 没有索引就没有工具。 只有 ~/.gg/kb/default/index.json 存在时才注册 kb_search,没建知识库的用户看到的工具列表和以前一样。
  • 查询端点必须和构建端点一致。 manifest 记录构建索引用的 embeddings 端点。如果 agent/CLI 后来配了别的端点,kb_search / kb search 会拒绝运行(fail closed),而不是去查错的服务或用不兼容的向量排序。维度对不上的查询向量同样会被拒绝。
  • 磁盘上是私有的。 索引里有文档全文,所以 KB 目录建 0700,index.json 建 0600。
  • 入库时做 UTF-8 清洗。 读取时把文件规范成合法 UTF-8,坏文件既卡不住分块器,也破坏不了 embeddings 请求体。

用法 ​

bash
# 索引你自己的文档:笔记、手册、收藏的文章(纯文本格式)
# (需要支持 embeddings 的 API key)
export OPENAI_API_KEY=<key>          # 或传 --embed-api-key
gg kb index ~/Documents/notes        # -> ~/.gg/kb/default/index.json
gg kb index ~/Documents/manuals --name manuals --embed-model text-embedding-3-large

# 独立的 embeddings 端点(聊天 provider 不提供 embeddings 时):
# flag --embed-base-url / --embed-api-key,或环境变量 GG_EMBED_BASE_URL / GG_EMBED_API_KEY。
# embeddings API key 的解析顺序:--embed-api-key > ~/.gg/credentials.json 的
# "embed" 条目 > GG_EMBED_API_KEY > 聊天 provider 的 key——`gg kb` 和 agent 的
# kb_search 工具走完全相同的链路。注意:只用 flag 传 key
# (--embed-api-key 且无任何持久化配置)时,该 key 仅对当次 CLI 调用可见;
# `gg kb index` 会在这种情况下告警,因为 agent 将查不了新索引。
# 查询(agent 或 CLI)必须配同一个端点;
# 端点不一致会 fail closed 并给出指引,而不是悄悄出错。
export GG_EMBED_BASE_URL=https://embed.example.com/v1
export GG_EMBED_API_KEY=<key>
gg kb index ./docs

# 手动查询
gg kb search "what did my Tokyo trip notes say about hotels?" --top-k 3

# 在 agent 里:默认知识库建好后 kb_search 工具自动出现。

# 评测检索质量
gg kb eval --cases docs/kb-eval-sample.jsonl --top-k 5

Cases 文件格式(JSONL,# 注释允许):

json
{"query": "which hotel did I book in Tokyo?", "expect": "Park Hyatt"}

一条 case 只要 top-k 里有命中包含 expect(大小写不敏感)就算过。报告打印每条 PASS/FAIL 和 recall@k。

取舍与后续工作 ​

  • 分块按段落,不感知语义也不感知 AST;代码多的语料以后可能需要按符号切。
  • 没有 reranker:top-k 余弦就是最终排序。加 cross-encoder 重排是显而易见的下一个质量抓手。
  • 没有增量更新:重建索引就是全量重建。文档量级够用;大语料以后要做变更检测。
  • agent 只看得到 default 知识库;命名知识库目前只能走 CLI。

由 gg 仓库的 README 与 docs/ 生成,构建时同步。