Serendipity 405303e82c fix: 修复 44 个代码审查问题 (CRITICAL/HIGH/MEDIUM/LOW)
Batch 1 — CRITICAL (1):
- 提取 is_safe_path() 到 src/core/security.py 公共模块
- CLI 和 ingest_obsidian.py 统一添加路径遍历防护

Batch 2 — HIGH (13) + 架构重构:
- CLI 复用 deps.py AppState, 消除 30 行重复代码
- AppState/get_state 添加线程安全锁
- serve 命令传递 --config 到 uvicorn (H1)
- OpenAIEmbedder 懒创建+复用 HTTP 客户端 (H2)
- DashscopeEmbedder import 移到模块顶部 (H3)
- 路径检查改用 os.path.commonpath (H4)
- embedder.embed() 返回值长度检查 (H5)
- 健康检查不泄露内部错误详情 (H7)
- /api/v1/collections 添加 API Key 认证 (H8)
- API Key 使用 hmac.compare_digest 恒定时间比较 (H9)
- 添加 CORS 中间件 (H10)
- ServerConfig 支持 SSL 配置 (H11)
- HF_ENDPOINT 修改添加详细注释 (H12)

Batch 3 — MEDIUM (20) + Splitter Protocol:
- 定义 Splitter(Protocol) 接口, DocumentIngestor 接受可选 splitter
- DashScope 响应添加结构验证 (M2)
- ingest_obsidian.py 支持 CLI 参数和 OBSIDIAN_DIRS 环境变量 (M6)
- scripts/serve.py 添加废弃警告 (M7)
- content 限制 500KB, collection 正则限制字符集 (M12-M14)
- 默认监听地址 127.0.0.1 (M16)
- 添加安全响应头中间件 (M17)
- verify_api_key 认证失败记录日志 (M19)

Batch 4 — LOW (10):
- CLI emoji 清理为纯文本标记 (L5)
- logging.basicConfig 移到 FastAPI lifespan (L1)
- VectorDB 添加 write_guard() 上下文管理器 (L3)
- IngestRequest file_path/content 互斥校验 (L10)
- ingest_obsidian.py 注释修正 (L6)

测试: 46 → 70 (+24)
- tests/test_security.py: 11 个路径安全测试
- tests/test_deps.py: 11 个依赖注入测试

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-06 16:56:38 +08:00
2026-07-05 01:46:57 +08:00
2026-07-05 01:25:37 +08:00

md-vector-db

Markdown 文档向量数据库 — 将 Markdown 文件自动分块、嵌入、存入 ChromaDB,通过语义检索快速查找相关内容。提供 CLI 命令行工具HTTP API 两种使用方式供其他项目集成。

功能特性

  • 文档入库: 支持单文件、目录批量导入 Markdown 文档,自动按标题+段落智能分块
  • 语义检索: 自然语言查询,返回最相关的文档片段及来源定位(文件名、章节标题)
  • 多 Provider: 本地模型 + 云端 APIOpenAI、阿里云 DashScope、硅基流动等 OpenAI 兼容服务)
  • GPU 加速: 本地模型自动检测 CUDARTX 4060 实测:729 chunks 嵌入仅 1.8s
  • 多集合: 支持多项目数据隔离,不同知识库存入不同 ChromaDB collection
  • HTTP API: FastAPI 提供 RESTful 接口,附带 Swagger 文档
  • 安全: 可选 API Key 认证、速率限制、路径遍历防护
  • 去重: 同一文件重复入库自动覆盖旧版本(基于路径 SHA256 哈希)

快速开始

1. 安装

git clone git@lhy-git.liuhangyv.top:Serendipity/md-vector-db.git
cd md-vector-db
uv sync

2. 配置

# 复制环境变量模板(API 密钥等)
cp .env.example .env

编辑 config.yaml 选择嵌入模式:

embed:
  mode: local                              # 本地模型(默认,无需联网)
  # 或使用云端 API
  # mode: api
  # provider: openai                       # openai | dashscope
  # api_base: https://api.siliconflow.cn/v1   # 可选:覆盖默认 API 地址
  # model: BAAI/bge-large-zh-v1.5             # 可选:覆盖默认模型名

3. 入库文档

# 单文件
uv run md-vector-db ingest docs/intro.md

# 批量导入目录(递归扫描所有 .md 文件)
uv run md-vector-db ingest-dir ./md_docs/

# 指定集合(多项目数据隔离)
uv run md-vector-db ingest docs/intro.md -C my_project

# 查看统计
uv run md-vector-db stats

4. 搜索

# 搜索(注意指定集合,默认为 default)
uv run md-vector-db search "如何配置向量数据库" -k 5 -C my_project

# JSON 输出(供 MCP 等工具消费)
uv run md-vector-db search "Docker 部署" -k 3 -C obsidian_blog --json

输出示例:

--- 结果 1 (相似度: 0.8231) ---
📄 来源: setup-guide.md
📑 章节: 配置向量数据库
## 配置向量数据库
编辑 config.yaml 中的 embed 段来选择嵌入模式...

--- 结果 2 (相似度: 0.6104) ---
📄 来源: faq.md
📑 章节: 常见问题
...

5. 启动 HTTP 服务

uv run md-vector-db serve --port 8000

浏览器打开 http://localhost:8000/docs 查看 Swagger UI,可直接在页面中调试所有 API。


HTTP API

方法 路径 需要认证 说明
GET / 重定向到/docs (Swagger UI)
GET /api/v1/health 健康检查(验证 ChromaDB + Embedder 可用性)
GET /api/v1/collections 列出集合和已入库的源文件
POST /api/v1/ingest API Key 入库文档(file_pathcontent + file_name,可选 collection
POST /api/v1/search API Key 语义检索(query + top_k,可选 collection
DELETE /api/v1/documents/{file_name} API Key 按文件名删除所有关联 chunks

调用示例

# 入库内容
curl -X POST http://localhost:8000/api/v1/ingest \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-secret-key" \
  -d '{"content": "# 服务配置\n端口设置为 8080。", "file_name": "config.md"}'

# 语义搜索
curl -X POST http://localhost:8000/api/v1/search \
  -H "Content-Type: application/json" \
  -H "x-api-key: your-secret-key" \
  -d '{"query": "端口设置", "top_k": 5}'

# 删除文档
curl -X DELETE http://localhost:8000/api/v1/documents/config.md \
  -H "x-api-key: your-secret-key"

其他项目集成示例

# Python
import httpx

resp = httpx.post(
    "http://localhost:8000/api/v1/search",
    json={"query": "向量数据库", "top_k": 5},
    headers={"x-api-key": "your-secret-key"},
)
for r in resp.json()["results"]:
    print(f"[{r['score']:.2f}] {r['source_file']}{r['section_title']}")

CLI 命令参考

所有命令均支持 --config/-c(配置文件)、--collection/-C(集合名,默认 default)。

命令 说明
ingest <文件路径> 入库单个 .md 文件,支持-C 指定集合
ingest-dir <目录路径> 递归入库目录下所有 .md 文件
search <查询> -k <数量> 语义检索,-k 默认 10、最大 100--json JSON 输出
stats 显示 chunks 总数、源文件列表
serve -p <端口> 启动 HTTP 服务(默认 8000

配置说明

config.yaml

chroma:
  persist_dir: ./data              # ChromaDB 数据存储目录
  collection_name: markdown_docs   # 集合名称

embed:
  mode: local                      # local(本地模型) | api(云端 API)
  local_model: BAAI/bge-small-zh-v1.5   # 本地模型名
  provider: openai                 # api 模式下的服务商
  api_base: ""                     # 覆盖默认 API 地址
  model: ""                        # 覆盖默认模型名

chunk:
  max_size: 1000                   # 分块最大字符数
  overlap: 100                     # 相邻 chunk 重叠字符数

server:
  host: 0.0.0.0                    # 服务监听地址
  port: 8000                       # 服务监听端口

.env 环境变量

EMBED_API_KEY=sk-xxx          # 嵌入 API 密钥(api 模式必需)
MD_VECTOR_API_KEY=secret      # HTTP API 访问密钥(不设置则跳过认证)

Provider 速查

provider 默认 API 地址 默认模型 维度
local 本地(sentence-transformers BAAI/bge-small-zh-v1.5 512
openai https://api.openai.com/v1 text-embedding-3-small 1536
dashscope 阿里云 DashScope text-embedding-v4 1536

使用 OpenAI 兼容的第三方服务(硅基流动、智谱、DeepSeek 等)只需设置 provider: openai 并覆盖 api_basemodel:

embed:
  mode: api
  provider: openai
  api_base: https://api.siliconflow.cn/v1
  model: BAAI/bge-large-zh-v1.5

项目结构

md-vector-db/
├── config.yaml              # 主配置文件
├── .env.example             # 环境变量模板
├── pyproject.toml
├── src/
│   ├── core/                # 核心逻辑(不依赖 server/cli
│   │   ├── config.py        # YAML + .env → dataclass
│   │   ├── db.py            # ChromaDB 封装(线程安全)
│   │   ├── embedder.py      # 嵌入器(Local/OpenAI/Dashscope
│   │   ├── ingest.py        # 混合分块 + 入库
│   │   └── search.py        # 语义检索
│   ├── server/              # FastAPI HTTP 层
│   │   ├── app.py           # 路由 + 中间件
│   │   ├── deps.py          # 依赖注入(AppState
│   │   └── auth.py          # 认证 + 速率限制
│   └── cli/
│       └── main.py          # Typer CLI
├── data/                    # ChromaDB 持久化目录
├── md_docs/                 # 待入库文档目录
├── scripts/
│   ├── serve.py                  # 快速启动脚本
│   └── ingest_obsidian.py        # 批量入库 Obsidian 知识库
└── tests/                   # 测试(46 个)

测试

uv run pytest tests/ -v                      # 全部测试 (46 个)
uv run pytest tests/test_embedder.py -v      # 嵌入器测试
uv run pytest tests/ -v -k "search"          # 按名称过滤
uv run pytest tests/ -v --cov=src --cov-report=term-missing  # 覆盖率

架构

数据流: MD 文件 → MarkdownSplitter.split() (标题→段落分块) → batch_embed() (分批嵌入) → ChromaDB collection.add()Searcher.search() 查询

依赖方向: configdbembedderingest / searchserver / cli

分块策略(混合切分): 先按 #/##/### 标题拆分 → 章节超过 1000 字符则按段落边界继续拆 → 单个段落仍超限则按标点符号硬切,所有拆分保留 100 字符重叠。

线程安全: 所有 ChromaDB 写操作(add/delete)通过 threading.Lock 保护,支持 FastAPI 多请求并发。


GPU 加速

本地嵌入模式自动检测 CUDA 设备,优先使用 GPU。实测性能对比(RTX 4060 Laptopbge-small-zh-v1.5):

文件大小 chunks CPU 耗时 GPU 耗时
60KB 320 数分钟至卡死 0.7s
96KB 729 卡死 1.8s

GPU 环境前提:安装 CUDA 版 torch。本项目 pyproject.toml 已配置从本地 wheel 目录获取 CUDA 版 torchuv sync 即可。

[tool.uv]
find-links = ["D:/settings/Language/Python/库"]   # 本地 CUDA wheel
index-strategy = "unsafe-best-match"              # 允许跨源查找

License

MIT

S
Description
Markdown 文档向量数据库,支持文档入库、语义检索,可通过 HTTP API 供其他项目调用。
Readme MIT 1.5 MiB
Languages
Python 94.1%
HTML 5.3%
Dockerfile 0.6%