专用、高效、节省资源的单机 RAG:重预处理,轻运行时。
入库阶段通过 LLM 把原始文档转成“富语义切片”(摘要 / 表格叙述 / 关键词),查询阶段使用 Qdrant 混合检索(Dense + Sparse)+ 可选 Rerank + 时间衰减,兼顾质量与成本。
- FastAPI 无状态服务,内置 1 小时 TTLCache 查询缓存。
- 入库 ETL:
chonkie切分 + LLM 三合一分析 + metadata 注入。 - 检索链路:Hybrid Retrieval -> 可选 Rerank -> 内容过滤 -> 关键词过滤 -> 时间衰减。
- 存储:单集合 Qdrant(Dense
text-dense+ Sparse BM42)。 - 前端:内置 HTMX + Jinja2 控制台(仪表盘、上传、查询、文档管理)。
/stupidsimplerag
├── app/
│ ├── __init__.py # 自动加载 .env + 初始化日志
│ ├── main.py # FastAPI 路由(API + UI)
│ ├── core.py # Qdrant、检索引擎、Rerank、后处理器
│ ├── ingest.py # 文档分析与切分
│ ├── models.py # Pydantic 请求/响应模型
│ ├── openai_utils.py # OpenAI 兼容配置与 LLM 包装
│ ├── logging_config.py # Loguru/stdlib 日志桥接
│ ├── templates/ # Jinja2 页面与 HTMX partial
│ └── static/ # CSS/Favicon
├── offline_ingest.py # 离线批量入库脚本
├── reset_qdrant.py # 危险:删除并重建集合
├── preload_models.py # 预下载 FastEmbed 稀疏模型
├── test/ # 集成与功能测试
├── Dockerfile
├── docker-compose.yml
└── AGENTS.md
git clone <repo>
cd stupidsimplerag
cp .env.example .env
docker compose up -d默认 docker-compose.yml 使用镜像 highkay/stupidsimplerag:latest:
- API:
http://localhost:8005(容器内 8000) - Qdrant:
http://localhost:6333
本地开发(不走容器):
pip install -r requirements.txt
uvicorn app.main:app --reload| Endpoint | Method | 说明 |
|---|---|---|
/ingest |
POST |
单文件入库(multipart/form-data) |
/ingest/batch |
POST |
批量入库(files 多字段) |
/ingest/text |
POST |
直接上传文本(application/json) |
/ingest 与 /ingest/batch:
- 支持
.md/.txt force_update(可选):true时先删同名文档再重建scope(可选):逻辑命名空间,作为文档身份的一部分写入 metadata- 可选请求头
X-File-Mtime:支持 Unix 时间戳(秒/毫秒)或 ISO 时间,按APP_TIMEZONE(或TZ)归一为日期
/ingest/text 请求体:
{
"content": "# 文档正文",
"filename": "optional.md",
"force_update": false,
"scope": "reports/2025"
}| Endpoint | Method | 说明 |
|---|---|---|
/chat |
POST |
标准检索 + 可选生成 |
/chat/lod |
POST |
两阶段 LOD 检索(先选文档,再聚焦生成,支持 filename/filename_contains 过滤) |
/chat 支持字段:
query(必填)start_date/end_datefilename(精确匹配)filename_contains(不区分大小写模糊匹配)keywords_any/keywords_allscopeskip_rerank(跳过重排)skip_generation(仅返回切片,不调用生成)
/chat 与 /chat/lod 的响应 sources[] 会返回 filename/date/score/keywords/text/scope,
其中 scope 从切片 metadata 透传;无该字段时为 null。
示例:
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{
"query": "英伟达最新财报表现",
"start_date": "2025-01-01",
"keywords_any": ["NVDA", "GPU"],
"skip_generation": true
}'| Endpoint | Method | 说明 |
|---|---|---|
/documents |
GET |
列出文档(按 filename + scope 聚合) |
/documents/{filename} |
DELETE |
删除指定文档切片;可选查询参数 scope,未传时仅删除无 scope 文档 |
| 页面/接口 | 说明 |
|---|---|
/ |
Dashboard(Qdrant 状态、缓存状态、配置快照) |
/ui/upload |
单文件上传页面 |
/ui/upload/batch |
批量上传页面 |
/ui/chat |
查询页面 |
/ui/documents |
文档列表 partial |
/ui/documents/{filename} |
删除文档(HTMX) |
说明:UI 表单默认 accept=.md,但后端 API 实际支持 .md 和 .txt。
- 去重与幂等
doc_hash = sha256(content)。- 若同一
scope内已存在相同doc_hash且未force_update,入库返回status=skipped。 - 节点 ID 使用
md5(doc_hash + scope + chunk_idx),避免跨 scope 冲突。 scope参与文档身份;同内容可在不同 scope 中并存。
- 检索后处理顺序
APIReranker(可跳过)ContentFilterPostprocessorKeywordFilterPostprocessorTimeDecayPostprocessor
- Rerank 协议兼容
RERANK_API_URL包含/rerank:按 rerank endpoint 协议调用。- 否则按 OpenAI Chat Completions 协议调用重排模型。
- 缓存行为
/chat结果使用 TTLCache(1h)。- 缓存键包含 query、日期范围、文件过滤、关键词过滤、skip 标志、scope。
- 任一入库接口成功写入新切片后会清空查询缓存。
/chat/lod当前不缓存。
- 时区基线
- 应用统一使用
APP_TIMEZONE(回退TZ,默认Asia/Shanghai)。 - 时间戳解析、
/ingest/text默认日期、时间衰减均按同一时区计算。 - 依赖
tzdata保证无系统 IANA 时区数据库环境下的行为一致性。
LLM_MODEL,OPENAI_API_KEY,OPENAI_API_BASEEMBEDDING_MODEL,EMBEDDING_API_KEY,EMBEDDING_API_BASE,EMBEDDING_DIMRERANK_API_URL,RERANK_API_KEY,RERANK_MODELQDRANT_HOST,QDRANT_PORT,QDRANT_URL,QDRANT_API_KEY,COLLECTION_NAMEAPP_TIMEZONE(默认Asia/Shanghai)、TZ(容器系统时区,建议与APP_TIMEZONE一致)
TOP_K_RETRIEVALTOP_N_RERANKFINAL_TOP_KSPARSE_TOP_KTIME_DECAY_RATE
LLM_CONTEXT_WINDOW,LLM_CONCURRENCY,LLM_MAX_RETRIES,LLM_RETRY_BACKOFFEMBEDDING_MAX_RETRIES,EMBEDDING_RETRY_BACKOFFRERANK_TIMEOUT,RERANK_MAX_RETRIES,RERANK_RETRY_BACKOFF,RERANK_RETURN_DOCUMENTSINSERT_BATCH_SIZE,INGEST_INSERT_MAX_RETRIES,INGEST_INSERT_RETRY_BACKOFFQUERY_MAX_RETRIES,QUERY_RETRY_BACKOFFBATCH_INGEST_CONCURRENCY,BATCH_MAX_FILESQDRANT_CLIENT_TIMEOUT
APP_LOG_LEVEL(优先)LOG_LEVEL
offline_ingest.py
- 递归导入目录下
.md/.txt或单文件。 - 支持
--batch-size、--skip-completed、--progress-file、--max-retries、--dry-run。 - 批量模式对超时采用指数退避重试。
reset_qdrant.py
- 删除并重建集合(危险操作)。
- 默认会交互确认;
-y跳过确认。
preload_models.py
- 提前下载 FastEmbed 稀疏模型到
FASTEMBED_CACHE_PATH。
pytest -q当前测试构成:
test/test_features_scope.py:Mock 测试(scope 透传、LOD 路由)。test/test_features_scope.py额外覆盖 scope 去重、文档列表与 scoped 删除行为。test/test_offline_ingest.py:离线批量入库脚本的批次结果处理测试。test/test_api.py:真实链路测试(依赖可用的 LLM/Embedding/Qdrant)。
提示:test/conftest.py 会把 COLLECTION_NAME 改为 pytest_integration_test,避免污染默认集合。
- Dockerfile 使用
python:3.13-slim,启动命令为 Gunicorn + Uvicorn worker。 - 镜像内默认
TZ=Asia/Shanghai,并安装tzdata;docker-compose同步为api/qdrant注入TZ。 - 镜像构建阶段会执行
preload_models.py预热 BM42。 - GitHub Actions(
.github/workflows/docker-publish.yml)在main相关文件变更时自动推送镜像:highkay/stupidsimplerag:latesthighkay/stupidsimplerag:<commit_sha>
- 维护者/代理协作规则与代码约束:
AGENTS.md