docs: 更新 README/CLAUDE 文档 + 配置 uv CUDA 兼容

- README: 补充 GPU 加速章节、多集合、CLI 新选项 (-C/--json)
- CLAUDE: 修正 uv run 可用说明、补充 [tool.uv] 配置文档
- pyproject.toml: 添加 [tool.uv] 配置, 通过 find-links + unsafe-best-match
  让 uv 从本地 CUDA wheel 解析 torch, 不再覆盖 GPU 环境
- uv.lock: 更新 lockfile, Windows 平台锁定 torch 2.6.0+cu124
This commit is contained in:
2026-07-06 12:44:06 +08:00
parent eebdef739c
commit 7e77c31e16
4 changed files with 260 additions and 247 deletions
+68 -24
View File
@@ -9,17 +9,23 @@ Markdown 文档向量数据库 — 将 .md 文件分块 → 嵌入 → 存入 Ch
## 常用命令
```bash
uv sync # 安装依赖
uv run pytest tests/ -v # 全部测试 (46 个)
uv run pytest tests/test_api.py -v # 单个测试模块
uv run pytest tests/ -v -k "test_search" # 按名称过滤
# --- 安装与测试 ---
uv sync # 安装依赖(lockfile 已锁定 CUDA torch
uv run pytest tests/ -v # 全部测试 (46 个)
uv run pytest tests/test_api.py -v # 单个测试模块
uv run pytest tests/ -v -k "test_search" # 按名称过滤
# CLI(所有命令支持 --config/-c 指定配置文件)
uv run python -m src.cli.main ingest <file.md>
uv run python -m src.cli.main ingest-dir ./md_docs/
uv run python -m src.cli.main search "关键词" -k 5
uv run python -m src.cli.main stats
uv run python -m src.cli.main serve --port 8000
# --- CLI 命令(所有命令支持 --config/-c 和 --collection/-C ---
uv run md-vector-db ingest <file.md> # 入库单文件(或: python -m src.cli.main ingest
uv run md-vector-db ingest-dir ./md_docs/ # 入库目录
uv run md-vector-db search "关键词" -k 5 # 搜索 default 集合
uv run md-vector-db search "关键词" -C obsidian_blog -k 5 # 搜索博客知识库
uv run md-vector-db search "..." -C obsidian_blog --json # JSON 输出(MCP 用)
uv run md-vector-db stats -C obsidian_blog # 集合统计
uv run md-vector-db serve --port 8000 # 启动 HTTP API
# --- 批量入库 Obsidian ---
uv run python scripts/ingest_obsidian.py
```
## 架构
@@ -46,17 +52,26 @@ cli/main.py # Typer CLI5 个命令 + --config 选项
## 关键实现细节
### GPU 支持 (embedder.py)
`LocalEmbedder.__init__` 自动检测 CUDA`torch.cuda.is_available()` → 优先使用 GPU(RTX 4060),否则 CPU。模型首次加载用 `local_files_only=True`,未缓存时自动走 `hf-mirror.com` 镜像下载。
**GPU vs CPU 对比**bge-small-zh-v1.5, 96KB 文件 / 729 chunks):
- CPU: 文件几秒完成,但超 15 chunks 的文件逐渐变慢,320 chunks 以上可能几分钟
- GPU: 729 chunks 嵌入仅 1.8s(含分块+嵌入+ChromaDB 写入)
### 嵌入 Provider 架构 (embedder.py)
策略模式,接口 `Embedder(Protocol)`:
| Provider | 类 | API 格式 | 默认模型 |
|----------|-----|---------|----------|
| `local` | `LocalEmbedder` | sentence-transformers | BAAI/bge-small-zh-v1.5 |
| `openai` | `OpenAIEmbedder` | OpenAI 兼容 | text-embedding-3-small |
| `dashscope` | `DashscopeEmbedder` | 阿里云自定义 HTTP | text-embedding-v4 |
| Provider | 类 | API 格式 | 默认模型 | 向量维度 |
| ------------- | --------------------- | --------------------------- | ---------------------- | -------- |
| `local` | `LocalEmbedder` | sentence-transformers + GPU | BAAI/bge-small-zh-v1.5 | 512 |
| `openai` | `OpenAIEmbedder` | OpenAI 兼容 | text-embedding-3-small | 1536 |
| `dashscope` | `DashscopeEmbedder` | 阿里云自定义 HTTP | text-embedding-v4 | 1536 |
`config.api_base` / `config.model` 可覆盖默认值。`batch_embed()` 分批嵌入(每批 32 条)防 OOM。本地模型未缓存时自动通过 `hf-mirror.com` 下载。
`config.api_base` / `config.model` 可覆盖默认值。`batch_embed()` 分批嵌入(每批 32 条)防 OOM。
### 线程安全 (db.py + ingest.py + search.py)
@@ -83,11 +98,40 @@ cli/main.py # Typer CLI5 个命令 + --config 选项
## HTTP API
| 方法 | 路径 | 认证 | 说明 |
|------|------|------|------|
| GET | `/` | - | 重定向到 /docs |
| GET | `/api/v1/health` | - | 真实健康检查 (ChromaDB + Embedder) |
| GET | `/api/v1/collections` | - | 集合和源文件列表 |
| POST | `/api/v1/ingest` | API Key | 入库 (file_path 或 content+file_name) |
| POST | `/api/v1/search` | API Key | 语义检索 (query + top_k 1-100) |
| DELETE | `/api/v1/documents/{file_name}` | API Key | 按文件名删除 |
| 方法 | 路径 | 认证 | 说明 |
| ------ | --------------------------------- | ------- | ------------------------------------- |
| GET | `/` | - | 重定向到 /docs |
| GET | `/api/v1/health` | - | 真实健康检查 (ChromaDB + Embedder) |
| GET | `/api/v1/collections` | - | 集合和源文件列表 |
| POST | `/api/v1/ingest` | API Key | 入库 (file_path 或 content+file_name) |
| POST | `/api/v1/search` | API Key | 语义检索 (query + top_k 1-100) |
| DELETE | `/api/v1/documents/{file_name}` | API Key | 按文件名删除 |
## 已入库知识库
| 集合名 | 来源 | 文件数 | chunks | 说明 |
|--------|------|--------|--------|------|
| `obsidian_blog` | `D:\Code\Obsidian` | 51 | 3,611 | 博客笔记(GPU bge-small-v1.5 |
| `default` | 测试文件 | 2 | ~30 | test-guide.md + stdin-doc.md |
搜索时务必用 `-C obsidian_blog` 指定博客集合,否则只会搜到 default 中的测试数据。
## 已知问题 / 注意事项
### uv 与 CUDA torch 的兼容配置
本机全局 `UV_INDEX_URL` 指向阿里云镜像(只有 CPU 版 torch),`pyproject.toml` 通过以下配置让 uv 从本地 wheel 取 CUDA 版:
```toml
[tool.uv]
find-links = ["D:/settings/Language/Python/库"] # 本地 CUDA wheel 目录
index-strategy = "unsafe-best-match" # 允许跨源查找
```
`uv.lock` 已锁定:Windows 平台 → torch 2.6.0+cu124(本地),Linux/Mac → CPU torch(清华源)。
**不要删除 `[tool.uv]` 配置**,否则 `uv sync` 会重新解析为 CPU 版 torch。
### MarkdownSplitter 边界情况
`_split_single_paragraph` 中,当段落分隔符(。!?等)距 chunk 起点 < overlap(100) 时,
`start` 会回退为负数,Python `str.rfind` 的负索引会绕回文本末尾,造成死循环。
此 bug 已被修复(`start = max(start + 1, next_start)`),但给超长段落测试时需留意类似问题。