smart_rag 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.example +252 -0
- data/.rspec +2 -0
- data/AGENTS.md +33 -0
- data/API_DOCUMENTATION.md +828 -0
- data/CHANGELOG.md +16 -1
- data/ER-diagram.mmd +144 -0
- data/Gemfile +50 -0
- data/Gemfile.lock +398 -0
- data/Hybrid_Reranking.md +171 -0
- data/README.en.md +420 -28
- data/README.md +534 -63
- data/Rakefile +268 -0
- data/SETUP_GUIDE.md +650 -0
- data/SmartChunking.md +180 -0
- data/USAGE_EXAMPLES.md +1002 -0
- data/config/llm_config.yml +4 -2
- data/config/smart_rag.yml +45 -1
- data/config.ru +15 -0
- data/db/migrations/006_create_text_search_configs.rb +3 -2
- data/db/migrations/008_create_embeddings.rb +5 -4
- data/db/migrations/012_add_metadata_to_source_sections.rb +11 -0
- data/db/migrations/013_create_media_jobs.rb +25 -0
- data/db/migrations/014_add_media_job_operations_indexes.rb +11 -0
- data/db/migrations/015_add_media_leases_and_objects.rb +80 -0
- data/db/migrations/016_add_document_principals_and_staging_references.rb +38 -0
- data/db/migrations/017_add_media_job_request_fingerprint.rb +48 -0
- data/db/seeds/text_search_configs.sql +3 -3
- data/design.md +1057 -0
- data/docs/API_DOCUMENTATION.md +838 -0
- data/docs/DOCUMENTATION_INDEX.en.md +60 -0
- data/docs/DOCUMENTATION_INDEX.md +65 -0
- data/docs/FIX_SUMMARY.md +256 -0
- data/docs/FIX_SUMMARY_COMPLETE.md +273 -0
- data/docs/Hybrid_Reranking.md +171 -0
- data/docs/MIGRATION_GUIDE.md +151 -0
- data/docs/PERFORMANCE_GUIDE.md +58 -0
- data/docs/SETUP_GUIDE.md +659 -0
- data/docs/SmartChunking.md +180 -0
- data/docs/USAGE_EXAMPLES.md +1008 -0
- data/docs/design.md +1057 -0
- data/docs/evidence_pack.md +211 -0
- data/docs/requirements.md +376 -0
- data/docs/retrieval_plan.md +251 -0
- data/docs/smartrag_improvement_plan.md +201 -0
- data/docs/smartrag_refactor.md +216 -0
- data/docs/todo.md +931 -0
- data/examples/common.rb +1 -1
- data/exe/smart-rag-db +163 -0
- data/exe/smart-rag-media-worker +34 -0
- data/lib/smart_rag/config.rb +12 -0
- data/lib/smart_rag/core/document_processor.rb +80 -16
- data/lib/smart_rag/core/local_content_store.rb +51 -0
- data/lib/smart_rag/core/media_extractors.rb +140 -0
- data/lib/smart_rag/core/media_job_queue.rb +353 -0
- data/lib/smart_rag/core/media_metadata_extractor.rb +188 -0
- data/lib/smart_rag/core/media_object_registry.rb +79 -0
- data/lib/smart_rag/core/media_processor.rb +228 -0
- data/lib/smart_rag/core/media_safety_policy.rb +61 -0
- data/lib/smart_rag/core/s3_content_store.rb +78 -0
- data/lib/smart_rag/core/transcript_normalizer.rb +44 -0
- data/lib/smart_rag/core/video_semantic_extractor.rb +130 -0
- data/lib/smart_rag/http_access_policy.rb +86 -0
- data/lib/smart_rag/http_app.rb +188 -0
- data/lib/smart_rag/models/embedding.rb +1 -1
- data/lib/smart_rag/models/research_topic.rb +1 -1
- data/lib/smart_rag/models/research_topic_section.rb +5 -0
- data/lib/smart_rag/models/research_topic_tag.rb +5 -0
- data/lib/smart_rag/models/search_log.rb +1 -1
- data/lib/smart_rag/models/section_fts.rb +5 -0
- data/lib/smart_rag/models/section_tag.rb +5 -0
- data/lib/smart_rag/models/source_document.rb +1 -1
- data/lib/smart_rag/models/source_section.rb +1 -1
- data/lib/smart_rag/models/tag.rb +1 -1
- data/lib/smart_rag/models/text_search_config.rb +5 -0
- data/lib/smart_rag/retrieve.rb +72 -1
- data/lib/smart_rag/services/embedding_service.rb +1 -1
- data/lib/smart_rag/services/fulltext_search_service.rb +11 -13
- data/lib/smart_rag/services/hybrid_search_service.rb +15 -11
- data/lib/smart_rag/services/summarization_service.rb +1 -1
- data/lib/smart_rag/services/tag_service.rb +1 -1
- data/lib/smart_rag/version.rb +1 -1
- data/lib/smart_rag.rb +264 -30
- data/patch_language.rb +27 -0
- data/requirements.md +376 -0
- data/source_documents_export.json +11072 -0
- data/todo.md +931 -0
- data/workers/analyze_content.rb +6 -2
- data/workers/get_embedding.rb +1 -1
- metadata +151 -38
data/README.md
CHANGED
|
@@ -2,21 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
[English README](README.en.md)
|
|
4
4
|
|
|
5
|
-
SmartRAG 是一个 Ruby 混合检索增强生成(RAG
|
|
5
|
+
SmartRAG 是一个 Ruby 混合检索增强生成(RAG)库,结合向量检索、全文检索、LLM 总结问答与主题/标签管理,提供从文档导入到智能问答的全链路能力。
|
|
6
6
|
|
|
7
7
|
## 项目概览
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
SmartRAG 覆盖了 RAG 系统的完整生命周期:
|
|
10
|
+
|
|
11
|
+
- **文档处理**:支持本地文件与 URL 导入,自动格式转换(PDF/DOCX/HTML → Markdown)
|
|
12
|
+
- **智能切块**:基于 Markdown 标题切分 + 自适应大小切分 + 结构检测
|
|
13
|
+
- **向量嵌入**:通过 Ollama 生成文本向量,存储于 PostgreSQL pgvector
|
|
14
|
+
- **全文索引**:PostgreSQL tsvector 全文搜索,中文使用 pg_jieba 分词
|
|
15
|
+
- **混合检索**:向量语义检索 + 全文关键词检索 → RRF 融合 → 重排序
|
|
16
|
+
- **LLM 问答**:基于检索结果生成结构化回答(支持中/英/日/繁体中文)
|
|
17
|
+
- **主题与标签**:LLM 自动生成标签,支持主题组织和关联推荐
|
|
18
|
+
- **运维工具**:索引重建、去重、回填、搜索日志、系统统计
|
|
19
|
+
|
|
20
|
+
## 架构总览
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────────────────────────────────────┐
|
|
24
|
+
│ 用户 API │
|
|
25
|
+
│ SmartRAG::SmartRAG (lib/smart_rag.rb) │
|
|
26
|
+
├─────────────────────────────────────────────┤
|
|
27
|
+
│ Retrieve 结构化检索 │
|
|
28
|
+
│ SmartRAG::Retrieve (retrieve.rb) │
|
|
29
|
+
├──────────────┬──────────────┬────────────────┤
|
|
30
|
+
│ Core 层 │ Services 层 │ Chunking 层 │
|
|
31
|
+
│ │ │ │
|
|
32
|
+
│ Query │ Hybrid │ Markdown │
|
|
33
|
+
│ Processor │ Search Svc │ Chunker │
|
|
34
|
+
│ │ │ │
|
|
35
|
+
│ Document │ Embedding │ Smart │
|
|
36
|
+
│ Processor │ Service │ Chunking │
|
|
37
|
+
│ │ │ │
|
|
38
|
+
│ Embedding │ Fulltext │ │
|
|
39
|
+
│ (Core) │ Search Svc │ │
|
|
40
|
+
│ │ │ │
|
|
41
|
+
│ Fulltext │ Tag │ │
|
|
42
|
+
│ Manager │ Service │ │
|
|
43
|
+
│ │ │ │
|
|
44
|
+
│ │ Summarization │ │
|
|
45
|
+
│ │ Service │ │
|
|
46
|
+
├──────────────┴──────────────┴────────────────┤
|
|
47
|
+
│ Models 层 (Sequel ORM) │
|
|
48
|
+
│ SourceDocument / SourceSection / Embedding │
|
|
49
|
+
│ Tag / ResearchTopic / SearchLog / SectionFts │
|
|
50
|
+
├─────────────────────────────────────────────┤
|
|
51
|
+
│ Config 层 (YAML + ERB) │
|
|
52
|
+
│ smart_rag.yml / database.yml / llm_config │
|
|
53
|
+
├─────────────────────────────────────────────┤
|
|
54
|
+
│ Workers 层 (SmartPrompt) │
|
|
55
|
+
│ get_embedding / analyze_content │
|
|
56
|
+
└─────────────────────────────────────────────┘
|
|
57
|
+
```
|
|
14
58
|
|
|
15
59
|
## 默认模型配置
|
|
16
60
|
|
|
17
61
|
当前默认配置为本地 Ollama 兼容端点:
|
|
18
62
|
|
|
19
|
-
- Embedding 模型:`qwen3-embedding
|
|
63
|
+
- Embedding 模型:`qwen3-embedding`(4096 维)
|
|
20
64
|
- 文本 LLM 模型:`qwen3`
|
|
21
65
|
- Embedding 端点:`http://localhost:11434/v1/embeddings`
|
|
22
66
|
- LLM 端点:`http://localhost:11434/v1/chat/completions`
|
|
@@ -25,109 +69,412 @@ SmartRAG 是一个 Ruby 混合检索增强生成(RAG)库,结合向量检
|
|
|
25
69
|
|
|
26
70
|
## 快速开始
|
|
27
71
|
|
|
28
|
-
###
|
|
72
|
+
### 方式 A:安装 gem 使用(推荐)
|
|
73
|
+
|
|
74
|
+
1) 安装:
|
|
29
75
|
|
|
30
76
|
```bash
|
|
31
|
-
|
|
77
|
+
gem install smart_rag
|
|
78
|
+
# 或写入 Gemfile:gem "smart_rag"
|
|
32
79
|
```
|
|
33
80
|
|
|
34
|
-
|
|
81
|
+
2) 配置数据库连接(任选其一):
|
|
35
82
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
83
|
+
- 复制 `.env.example` 为 `.env` 并填写(库会自动加载 `.env`):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cp .env.example .env
|
|
87
|
+
```
|
|
39
88
|
|
|
40
|
-
|
|
89
|
+
- 或直接导出环境变量。必填数据库变量:
|
|
41
90
|
|
|
42
|
-
- `SMARTRAG_DB_HOST`
|
|
43
|
-
- `SMARTRAG_DB_PORT`
|
|
44
|
-
- `SMARTRAG_DB_NAME`
|
|
45
|
-
- `SMARTRAG_DB_USER`
|
|
46
|
-
- `SMARTRAG_DB_PASSWORD`
|
|
91
|
+
- `SMARTRAG_DB_HOST`
|
|
92
|
+
- `SMARTRAG_DB_PORT`
|
|
93
|
+
- `SMARTRAG_DB_NAME`
|
|
94
|
+
- `SMARTRAG_DB_USER`
|
|
95
|
+
- `SMARTRAG_DB_PASSWORD`
|
|
47
96
|
|
|
48
|
-
|
|
97
|
+
3) 初始化数据库(建库、迁移、种子数据):
|
|
49
98
|
|
|
50
99
|
```bash
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
smart-rag-db create
|
|
101
|
+
smart-rag-db migrate
|
|
102
|
+
smart-rag-db seed
|
|
103
|
+
# 或一步到位:smart-rag-db reset
|
|
54
104
|
```
|
|
55
105
|
|
|
56
|
-
|
|
106
|
+
4) 在代码中使用:
|
|
57
107
|
|
|
58
|
-
```
|
|
59
|
-
|
|
108
|
+
```ruby
|
|
109
|
+
require "smart_rag"
|
|
110
|
+
|
|
111
|
+
client = SmartRAG::SmartRAG.new # 读取内置默认配置 + 环境变量
|
|
112
|
+
client.add_document("docs/report.md", generate_embeddings: true)
|
|
113
|
+
results = client.search("机器学习是什么?")
|
|
60
114
|
```
|
|
61
115
|
|
|
62
|
-
|
|
116
|
+
> 也可显式指定配置:`SmartRAG::SmartRAG.new("config/smart_rag.yml")` 或传入 Hash。完整用法见「API 参考」。
|
|
117
|
+
|
|
118
|
+
### 方式 B:源码开发
|
|
63
119
|
|
|
64
120
|
```bash
|
|
65
|
-
|
|
66
|
-
|
|
121
|
+
bundle install
|
|
122
|
+
cp .env.example .env
|
|
123
|
+
bundle exec rake db:create db:migrate db:seed
|
|
124
|
+
ruby test/import_doc.rb import # 可选:导入测试文档
|
|
125
|
+
ruby examples/01_quick_start.rb # 运行示例程序
|
|
67
126
|
```
|
|
68
127
|
|
|
69
|
-
|
|
128
|
+
前置依赖见下方「依赖」。
|
|
129
|
+
|
|
130
|
+
## API 参考
|
|
131
|
+
|
|
132
|
+
### 初始化
|
|
70
133
|
|
|
71
134
|
```ruby
|
|
72
135
|
require "smart_rag"
|
|
73
136
|
|
|
74
|
-
|
|
75
|
-
client = SmartRAG::SmartRAG.new(config)
|
|
137
|
+
# 通过配置文件初始化
|
|
138
|
+
client = SmartRAG::SmartRAG.new("config/smart_rag.yml")
|
|
139
|
+
|
|
140
|
+
# 或通过 Hash 配置
|
|
141
|
+
client = SmartRAG::SmartRAG.new({
|
|
142
|
+
database: {
|
|
143
|
+
adapter: "postgresql",
|
|
144
|
+
host: "localhost",
|
|
145
|
+
database: "smart_rag_development",
|
|
146
|
+
user: "rag_user",
|
|
147
|
+
password: "your_password"
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 知识库管理
|
|
153
|
+
|
|
154
|
+
| 方法 | 说明 | 返回值 |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `add_document(path, options)` | 导入文档(本地文件或 URL) | `{ document_id:, section_count:, status: }` |
|
|
157
|
+
| `remove_document(id)` | 删除文档及关联段落/向量 | `{ success:, deleted_sections:, deleted_embeddings: }` |
|
|
158
|
+
| `get_document(id)` | 获取文档详情 | `{ id:, title:, description:, section_count:, metadata: }` |
|
|
159
|
+
| `list_documents(options)` | 分页列表,支持 title 搜索 | `{ documents:, total_count:, page:, per_page:, total_pages: }` |
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# 添加文档并自动生成向量
|
|
163
|
+
client.add_document("docs/report.md", generate_embeddings: true)
|
|
76
164
|
|
|
77
|
-
|
|
78
|
-
|
|
165
|
+
# 从 URL 导入
|
|
166
|
+
client.add_document("https://example.com/article.pdf")
|
|
79
167
|
|
|
80
|
-
|
|
168
|
+
# 分页查询
|
|
169
|
+
client.list_documents(page: 1, per_page: 10, search: "Python")
|
|
81
170
|
```
|
|
82
171
|
|
|
83
|
-
|
|
172
|
+
### 搜索(核心)
|
|
84
173
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
174
|
+
SmartRAG 提供三种搜索模式:**hybrid**(混合,默认)、**vector**(向量)、**fulltext**(全文)。
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
# 统一搜索入口
|
|
178
|
+
results = client.search("机器学习是什么?",
|
|
179
|
+
search_type: "hybrid", # hybrid | vector | fulltext
|
|
180
|
+
limit: 5,
|
|
181
|
+
language: :zh_cn, # :zh_cn | :en | :ja | 自动检测
|
|
182
|
+
alpha: 0.7, # 向量权重 (0.0-1.0),仅 hybrid 有效
|
|
183
|
+
include_content: true,
|
|
184
|
+
include_metadata: true,
|
|
185
|
+
generate_tags: false, # 是否用 LLM 从查询中生成标签
|
|
186
|
+
document_ids: [1, 2], # 限定文档范围
|
|
187
|
+
tags: ["AI", "机器学习"] # 标签过滤/加权
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# 单独使用各搜索模式
|
|
191
|
+
client.vector_search("神经网络架构", limit: 5)
|
|
192
|
+
client.fulltext_search('"deep reinforcement learning"', limit: 5)
|
|
193
|
+
client.hybrid_search("AI应用", language: :zh_cn)
|
|
194
|
+
```
|
|
89
195
|
|
|
90
|
-
|
|
196
|
+
#### 混合搜索流程
|
|
91
197
|
|
|
92
|
-
|
|
198
|
+
```
|
|
199
|
+
用户查询
|
|
200
|
+
↓
|
|
201
|
+
① 语言检测(中/日/英)
|
|
202
|
+
↓
|
|
203
|
+
② 可选:LLM 生成查询标签
|
|
204
|
+
↓
|
|
205
|
+
③ 生成查询向量
|
|
206
|
+
↓
|
|
207
|
+
④ 并行:向量搜索 + 全文搜索
|
|
208
|
+
↓
|
|
209
|
+
⑤ RRF 融合(Reciprocal Rank Fusion, k=60)
|
|
210
|
+
↓
|
|
211
|
+
⑥ 重排序(rerank_limit=64)
|
|
212
|
+
↓
|
|
213
|
+
⑦ 领域加权 + 类别多样性优化
|
|
214
|
+
↓
|
|
215
|
+
⑧ 返回结果
|
|
216
|
+
```
|
|
93
217
|
|
|
94
|
-
|
|
218
|
+
**RRF 融合权重**:向量 0.6 / 全文 0.4(可在 `config/fulltext_search.yml` 调整)。
|
|
95
219
|
|
|
96
|
-
|
|
97
|
-
|
|
220
|
+
#### 多语言支持
|
|
221
|
+
|
|
222
|
+
| 语言 | 全文搜索配置 | 分词方式 |
|
|
223
|
+
|---|---|---|
|
|
224
|
+
| 英文 | `pg_catalog.english` | stemming |
|
|
225
|
+
| 中文 | `jieba` | pg_jieba 分词 |
|
|
226
|
+
| 日文 | `pg_catalog.simple` | 基础分词 |
|
|
227
|
+
| 韩文 | `pg_catalog.simple` | 基础分词 |
|
|
228
|
+
|
|
229
|
+
### 结构化检索(SmartBrain 集成)
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
plan = {
|
|
233
|
+
queries: [
|
|
234
|
+
{ text: "machine learning basics", mode: "semantic", weight: 1.0 },
|
|
235
|
+
{ text: "deep neural networks", mode: "keyword", weight: 0.8 }
|
|
236
|
+
],
|
|
237
|
+
budget: {
|
|
238
|
+
candidate_k: 200,
|
|
239
|
+
per_mode_k: { semantic: 30, keyword: 20 }
|
|
240
|
+
},
|
|
241
|
+
ranking: {
|
|
242
|
+
rerank: { enabled: true }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
evidence_pack = client.retrieve(plan: plan)
|
|
247
|
+
# 返回 EvidencePack 格式:{ evidences:, stats:, explain:, warnings: }
|
|
98
248
|
```
|
|
99
249
|
|
|
100
|
-
|
|
250
|
+
### 标签管理
|
|
101
251
|
|
|
102
|
-
```
|
|
103
|
-
|
|
252
|
+
```ruby
|
|
253
|
+
# LLM 自动生成标签
|
|
254
|
+
result = client.generate_tags("这是一段关于深度学习和神经网络的文本...",
|
|
255
|
+
max_tags: 10
|
|
256
|
+
)
|
|
257
|
+
# => { content_tags: ["深度学习", "神经网络"], category_tags: ["AI"] }
|
|
258
|
+
|
|
259
|
+
# 分页查询标签
|
|
260
|
+
client.list_tags(page: 1, per_page: 20, search: "AI")
|
|
104
261
|
```
|
|
105
262
|
|
|
106
|
-
|
|
263
|
+
### 主题管理
|
|
107
264
|
|
|
108
|
-
```
|
|
109
|
-
|
|
265
|
+
```ruby
|
|
266
|
+
# 创建主题
|
|
267
|
+
client.create_topic("AI 研究",
|
|
268
|
+
description: "人工智能相关研究主题",
|
|
269
|
+
tags: ["AI", "机器学习"],
|
|
270
|
+
document_ids: [1, 3]
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# 查询主题
|
|
274
|
+
client.get_topic(1)
|
|
275
|
+
client.list_topics(page: 1, per_page: 20, search: "AI")
|
|
276
|
+
|
|
277
|
+
# 更新主题
|
|
278
|
+
client.update_topic(1, title: "人工智能研究", tags: ["AI", "深度学习"])
|
|
279
|
+
|
|
280
|
+
# 删除主题
|
|
281
|
+
client.delete_topic(1)
|
|
282
|
+
|
|
283
|
+
# 文档与主题关联
|
|
284
|
+
client.add_document_to_topic(topic_id: 1, document_id: 5)
|
|
285
|
+
client.remove_document_from_topic(topic_id: 1, document_id: 5)
|
|
286
|
+
|
|
287
|
+
# 主题推荐(基于标签共现)
|
|
288
|
+
client.get_topic_recommendations(1, limit: 5)
|
|
110
289
|
```
|
|
111
290
|
|
|
112
|
-
|
|
291
|
+
### 系统运维
|
|
113
292
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
293
|
+
| 方法 | 说明 |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `statistics` | 系统统计(文档/段落/主题/标签/向量数量) |
|
|
296
|
+
| `search_logs(limit:, search_type:)` | 查询搜索历史记录 |
|
|
297
|
+
| `rebuild_fts(document_id)` | 重建全文索引(不传参=全量) |
|
|
298
|
+
| `rebuild_embeddings(document_id)` | 重建向量嵌入(不传参=全量) |
|
|
299
|
+
| `reindex(document_id)` | 一键重建 FTS + 向量 |
|
|
300
|
+
| `dedupe_by_content_hash` | 按内容哈希去重重复文档 |
|
|
301
|
+
| `backfill_source_fields(dry_run:)` | 回填 source_uri/source_type/content_hash 字段 |
|
|
302
|
+
| `prepare_release_indexes(dry_run:)` | 发布前准备:backfill → dedupe → reindex |
|
|
303
|
+
|
|
304
|
+
```ruby
|
|
305
|
+
# 系统统计
|
|
306
|
+
stats = client.statistics
|
|
307
|
+
# => { document_count:, section_count:, topic_count:, tag_count:, embedding_count: }
|
|
308
|
+
|
|
309
|
+
# 搜索日志
|
|
310
|
+
client.search_logs(limit: 50, search_type: "hybrid")
|
|
311
|
+
|
|
312
|
+
# 预演发布准备(不写入)
|
|
313
|
+
client.prepare_release_indexes(dry_run: true)
|
|
314
|
+
|
|
315
|
+
# 执行发布准备
|
|
316
|
+
client.prepare_release_indexes
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## 文档处理管线
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
URL / 文件路径
|
|
323
|
+
↓
|
|
324
|
+
① 下载(支持 301/302 重定向)
|
|
325
|
+
↓
|
|
326
|
+
② 提取元数据(大小、类型、时间)
|
|
327
|
+
↓
|
|
328
|
+
③ 格式转换(Markitdown: PDF/DOCX/HTML → Markdown)
|
|
329
|
+
↓
|
|
330
|
+
④ 创建 SourceDocument 记录
|
|
331
|
+
↓
|
|
332
|
+
⑤ 智能切块(Markdown 标题切分 → 超限大小切分)
|
|
333
|
+
↓
|
|
334
|
+
⑥ 存储 SourceSection 记录
|
|
335
|
+
↓
|
|
336
|
+
⑦ 可选:生成 Embedding + Tag
|
|
337
|
+
↓
|
|
338
|
+
⑧ 标记文档状态为 completed
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
支持的输入格式:`.md` / `.txt` / `.pdf` / `.docx` / `.html`
|
|
342
|
+
|
|
343
|
+
### 切块策略
|
|
344
|
+
|
|
345
|
+
- **MarkdownChunker**(默认):按 H1-H3 标题切分,超限段落再按字符数拆分
|
|
346
|
+
- **SmartChunking**(高级):结构检测 + 文档类型感知(法规/书籍/论文/手册),基于 token 合并
|
|
347
|
+
|
|
348
|
+
配置项(`config/smart_rag.yml`):
|
|
349
|
+
|
|
350
|
+
```yaml
|
|
351
|
+
chunking:
|
|
352
|
+
max_chars: 4000 # 最大块字符数
|
|
353
|
+
overlap: 100 # 块间重叠字符数
|
|
354
|
+
split_by_headers: true # 优先按标题切分
|
|
355
|
+
min_chunk_size: 100 # 最小块大小
|
|
117
356
|
```
|
|
118
357
|
|
|
358
|
+
## 数据模型
|
|
359
|
+
|
|
360
|
+
| 模型 | 表名 | 用途 |
|
|
361
|
+
|---|---|---|
|
|
362
|
+
| `SourceDocument` | `source_documents` | 文档元数据(标题、作者、来源类型、状态) |
|
|
363
|
+
| `SourceSection` | `source_sections` | 文档分段(标题、序号、内容、语言) |
|
|
364
|
+
| `Embedding` | `embeddings` | pgvector 向量存储 |
|
|
365
|
+
| `Tag` | `tags` | 标签(支持层级 parent_id) |
|
|
366
|
+
| `SectionTag` | `section_tags` | 段落到标签的多对多关联 |
|
|
367
|
+
| `ResearchTopic` | `research_topics` | 研究主题 |
|
|
368
|
+
| `ResearchTopicSection` | `research_topic_sections` | 主题到段落的关联 |
|
|
369
|
+
| `ResearchTopicTag` | `research_topic_tags` | 主题到标签的关联 |
|
|
370
|
+
| `SearchLog` | `search_logs` | 搜索记录(查询、耗时、结果数) |
|
|
371
|
+
| `SectionFts` | `section_fts` | 全文搜索物化视图 |
|
|
372
|
+
|
|
373
|
+
## 依赖
|
|
374
|
+
|
|
375
|
+
Ruby 运行时依赖由 gem 自动安装(见 `smart_rag.gemspec`),包括 Sequel、pg、SmartPrompt、concurrent-ruby、dotenv、rack、puma、aws-sdk-s3 等。
|
|
376
|
+
|
|
377
|
+
还需自行准备的外部服务/工具:
|
|
378
|
+
|
|
379
|
+
- **Ruby** >= 2.7
|
|
380
|
+
- **PostgreSQL** + `pgvector` 扩展 + `pg_jieba` 扩展(中文分词,可选;未安装时中文退回 `simple` 分词)
|
|
381
|
+
- **Python 3** + `markitdown`(仅在导入 PDF/DOCX/HTML 等需要格式转换的文档时必需):
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
pip install markitdown
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
- 向量嵌入与 LLM 端点(默认指向本地 Ollama,见「默认模型配置」)
|
|
388
|
+
|
|
389
|
+
## 配置参考
|
|
390
|
+
|
|
391
|
+
主要配置文件:
|
|
392
|
+
|
|
393
|
+
| 文件 | 用途 |
|
|
394
|
+
|---|---|
|
|
395
|
+
| `config/smart_rag.yml` | 主配置(数据库、嵌入、搜索、切块、LLM、日志) |
|
|
396
|
+
| `config/database.yml` | 多环境数据库配置 |
|
|
397
|
+
| `config/llm_config.yml` | LLM 适配器配置(Ollama / SiliconFlow 等) |
|
|
398
|
+
| `config/fulltext_search.yml` | 全文搜索详细配置(语言、索引、性能) |
|
|
399
|
+
|
|
400
|
+
关键环境变量:
|
|
401
|
+
|
|
402
|
+
| 变量 | 默认值 | 说明 |
|
|
403
|
+
|---|---|---|
|
|
404
|
+
| `SMARTRAG_DB_NAME` | `smart_rag_development` | 数据库名 |
|
|
405
|
+
| `SMARTRAG_DB_USER` | `rag_user` | 数据库用户 |
|
|
406
|
+
| `SMARTRAG_DB_PASSWORD` | - | 数据库密码 |
|
|
407
|
+
| `EMBEDDING_MODEL` | `qwen3-embedding` | 嵌入模型 |
|
|
408
|
+
| `EMBEDDING_DIMENSIONS` | `4096` | 向量维度 |
|
|
409
|
+
| `LLM_MODEL` | `qwen3` | LLM 模型 |
|
|
410
|
+
| `DEFAULT_LANGUAGE` | `en` | 默认语言 |
|
|
411
|
+
| `ENABLE_JIEBA` | `true` | 启用中文分词 |
|
|
412
|
+
|
|
119
413
|
## 目录结构
|
|
120
414
|
|
|
121
415
|
```text
|
|
122
416
|
lib/
|
|
123
|
-
smart_rag.rb
|
|
124
|
-
smart_rag/
|
|
125
|
-
smart_rag/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
417
|
+
smart_rag.rb # 主 API 入口(SmartRAG::SmartRAG 类)
|
|
418
|
+
smart_rag/config.rb # 配置加载(YAML + ERB)
|
|
419
|
+
smart_rag/version.rb # 版本号
|
|
420
|
+
smart_rag/errors.rb # 自定义异常类
|
|
421
|
+
smart_rag/retrieve.rb # 结构化检索(RetrievalPlan → EvidencePack)
|
|
422
|
+
smart_rag/models.rb # 模型加载和连接管理
|
|
423
|
+
smart_rag/models/ # Sequel ORM 模型(11 张表)
|
|
424
|
+
smart_rag/core/ # 核心处理逻辑
|
|
425
|
+
query_processor.rb # 查询处理器(搜索 + 问答)
|
|
426
|
+
document_processor.rb # 文档处理器(导入 + 切块 + 存储)
|
|
427
|
+
embedding.rb # 向量嵌入底层操作
|
|
428
|
+
fulltext_manager.rb # 全文索引管理
|
|
429
|
+
markitdown_bridge.rb # 文档格式转换桥接
|
|
430
|
+
smart_rag/services/ # 服务层
|
|
431
|
+
embedding_service.rb # 嵌入服务(CRUD + 批量)
|
|
432
|
+
vector_search_service.rb # 向量检索服务
|
|
433
|
+
fulltext_search_service.rb # 全文检索服务
|
|
434
|
+
hybrid_search_service.rb # 混合检索服务(RRF + 重排序)
|
|
435
|
+
summarization_service.rb # LLM 总结问答服务
|
|
436
|
+
tag_service.rb # 标签生成和管理服务
|
|
437
|
+
smart_rag/chunker/ # 切块器
|
|
438
|
+
markdown_chunker.rb # Markdown 标题切分
|
|
439
|
+
smart_rag/smart_chunking/ # 高级智能切块
|
|
440
|
+
pipeline.rb / parser.rb # 结构检测 + 类型感知切块
|
|
441
|
+
merger.rb / tokenizer.rb # Token 合并策略
|
|
442
|
+
smart_rag/parsers/ # 解析器
|
|
443
|
+
query_parser.rb # 查询解析
|
|
444
|
+
config/ # 运行时配置
|
|
445
|
+
db/ # 数据库迁移和种子 SQL
|
|
446
|
+
examples/ # 示例代码(6 个场景)
|
|
447
|
+
test/ # E2E 测试脚本 + 测试文档
|
|
448
|
+
spec/ # RSpec 测试
|
|
449
|
+
workers/ # SmartPrompt worker 定义
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 开发常用命令
|
|
453
|
+
|
|
454
|
+
```bash
|
|
455
|
+
# 运行测试
|
|
456
|
+
bundle exec rspec # RSpec 单元/集成测试
|
|
457
|
+
SMARTRAG_LIVE_SPECS=1 bundle exec rspec spec/documentation # 真实模型文档示例
|
|
458
|
+
ruby test/test_rag.rb # E2E 测试脚本
|
|
459
|
+
|
|
460
|
+
# 数据库操作
|
|
461
|
+
bundle exec rake db:create # 创建数据库
|
|
462
|
+
bundle exec rake db:migrate # 运行迁移
|
|
463
|
+
bundle exec rake db:seed # 导入种子数据
|
|
464
|
+
bundle exec rake db:reset # 重建数据库
|
|
465
|
+
|
|
466
|
+
# 运维操作
|
|
467
|
+
bundle exec rake db:backfill_source_fields # 回填字段
|
|
468
|
+
bundle exec rake db:prepare_release # 发布前准备
|
|
469
|
+
|
|
470
|
+
# 构建
|
|
471
|
+
gem build smart_rag.gemspec # 构建 gem 包
|
|
472
|
+
|
|
473
|
+
# 导入测试文档
|
|
474
|
+
ruby test/import_doc.rb import
|
|
475
|
+
|
|
476
|
+
# 重建嵌入
|
|
477
|
+
ruby test/reembed_all.rb
|
|
131
478
|
```
|
|
132
479
|
|
|
133
480
|
## 文档导航
|
|
@@ -135,6 +482,18 @@ spec/ # RSpec 测试
|
|
|
135
482
|
完整文档清单、阅读顺序和维护建议见 `docs/DOCUMENTATION_INDEX.md`。
|
|
136
483
|
英文版见 `docs/DOCUMENTATION_INDEX.en.md`。
|
|
137
484
|
|
|
485
|
+
其他重要文档:
|
|
486
|
+
|
|
487
|
+
- `docs/design.md` — 系统设计文档
|
|
488
|
+
- `docs/API_DOCUMENTATION.md` — API 详细文档
|
|
489
|
+
- `docs/SETUP_GUIDE.md` — 环境搭建指南
|
|
490
|
+
- `docs/USAGE_EXAMPLES.md` — 使用示例
|
|
491
|
+
- `docs/Hybrid_Reranking.md` — 混合检索与重排序说明
|
|
492
|
+
- `docs/SmartChunking.md` — 智能切块说明
|
|
493
|
+
- `docs/MIGRATION_GUIDE.md` — 迁移指南
|
|
494
|
+
- `docs/PERFORMANCE_GUIDE.md` — 性能优化指南
|
|
495
|
+
- `ER-diagram.mmd` — ER 图
|
|
496
|
+
|
|
138
497
|
## 说明
|
|
139
498
|
|
|
140
499
|
- 部分历史文档仍保留旧默认值(如 OpenAI 示例)。运行时配置以 `config/smart_rag.yml` 为准。
|
|
@@ -142,3 +501,115 @@ spec/ # RSpec 测试
|
|
|
142
501
|
## 许可证
|
|
143
502
|
|
|
144
503
|
MIT
|
|
504
|
+
# Media HTTP API
|
|
505
|
+
|
|
506
|
+
SmartRAG exposes a small Rack-compatible app for retrieval and media ingestion:
|
|
507
|
+
|
|
508
|
+
```ruby
|
|
509
|
+
require 'smart_rag'
|
|
510
|
+
require 'smart_rag/http_app'
|
|
511
|
+
|
|
512
|
+
rag = SmartRAG::SmartRAG.new(config)
|
|
513
|
+
app = SmartRAG::HttpApp.new(
|
|
514
|
+
rag: rag,
|
|
515
|
+
extractors: {
|
|
516
|
+
audio_transcriber: audio_transcriber,
|
|
517
|
+
video_transcriber: video_transcriber,
|
|
518
|
+
image_describer: image_describer,
|
|
519
|
+
frame_describer: frame_describer,
|
|
520
|
+
ocr_extractor: ocr_extractor
|
|
521
|
+
}
|
|
522
|
+
)
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Endpoints:
|
|
526
|
+
|
|
527
|
+
- `POST /v1/retrieve`: JSON `{ "plan": { ... } }`
|
|
528
|
+
- `POST /v1/media`: JSON URL import or multipart fields `operation`, `options`, `file`
|
|
529
|
+
- `POST /v1/media` with `options.async=true`: queue the import and return HTTP 202
|
|
530
|
+
- `GET /v1/media/jobs?status=failed&limit=20&offset=0`: list and filter jobs
|
|
531
|
+
- `GET /v1/media/jobs/:id`: inspect queued, processing, completed, partial, or failed state
|
|
532
|
+
- `POST /v1/media/jobs/:id/cancel`: cancel a queued job
|
|
533
|
+
- `POST /v1/media/jobs/:id/retry`: manually retry a failed job
|
|
534
|
+
- `GET /v1/media/jobs/stats`: queue counts, oldest queued age, and stale processing count
|
|
535
|
+
- `GET /healthz`
|
|
536
|
+
|
|
537
|
+
Start the bundled Rack entrypoint with a Rack server such as Puma:
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
SMARTRAG_CONFIG_PATH=config/smart_rag.yml bundle exec puma -p 9393 config.ru
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Allowed operations are `add_document`, `add_media`, `add_image`, `add_audio`, and `add_video`. Extractor objects are server-side configuration and are never accepted from HTTP payloads.
|
|
544
|
+
|
|
545
|
+
Run migration `013_create_media_jobs`, then start a worker with:
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
bundle exec smart-rag-media-worker
|
|
549
|
+
# For cron or one-shot processing:
|
|
550
|
+
bundle exec smart-rag-media-worker --once --batch-size 20
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
The `media` section in `config/smart_rag.yml` configures file/duration limits, URL allowlists, command timeouts, the optional local content-addressed store, OpenAI-compatible vision/transcription, Tesseract OCR, and job retry count. Asynchronous multipart uploads are copied to `SMARTRAG_MEDIA_JOB_UPLOAD_DIR`. Completed/canceled uploads are removed immediately; failed uploads remain available for manual retry until terminal jobs are pruned.
|
|
554
|
+
|
|
555
|
+
On startup the worker requeues `processing` jobs older than `MEDIA_JOB_STALE_AFTER_SECONDS`. Once per hour it removes terminal jobs older than `MEDIA_JOB_RETENTION_SECONDS` (defaults: 15 minutes and 7 days). `/healthz` includes the same queue statistics as the stats endpoint, making backlog and expired worker leases observable without inspecting PostgreSQL directly.
|
|
556
|
+
|
|
557
|
+
## Media P3 production controls
|
|
558
|
+
|
|
559
|
+
Migration `015_add_media_leases_and_objects` adds worker heartbeat leases, per-principal idempotency, S3/MinIO storage, reference-counted object cleanup, and optional HTTP authentication/quotas.
|
|
560
|
+
|
|
561
|
+
Migration `016_add_document_principals_and_staging_references` is the data-integrity follow-up. It protects retained failed-job staging objects with an explicit foreign key, makes failed synchronous imports discoverable by object GC, and adds principal ownership to documents. Authenticated retrieval, document reads/lists/deletion, and statistics are owner-scoped; direct embedded calls remain global unless a `principal:` is supplied.
|
|
562
|
+
|
|
563
|
+
Run all migrations through `017_add_media_job_request_fingerprint` before deploying the current queue code. Migration 017 backfills a canonical SHA-256 request fingerprint for existing jobs and makes the new column required.
|
|
564
|
+
|
|
565
|
+
Send `Idempotency-Key` with an asynchronous import to make retries safe. Keys are isolated by authenticated principal. Reusing a key with the same operation, source, and canonicalized options returns the original job with `deduplicated: true`; reusing it with a different payload returns HTTP `409` with `code: "idempotency_conflict"`. Nested hash key order and symbol/string keys do not change the fingerprint, while array order remains significant. Workers update `heartbeat_at` while extraction is running, so long videos are not requeued merely because their original `started_at` is old.
|
|
566
|
+
|
|
567
|
+
For S3 or MinIO:
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
MEDIA_CONTENT_STORE_ENABLED=true
|
|
571
|
+
MEDIA_CONTENT_STORE_PROVIDER=s3
|
|
572
|
+
MEDIA_S3_BUCKET=smart-rag-media
|
|
573
|
+
MEDIA_S3_REGION=us-east-1
|
|
574
|
+
MEDIA_S3_ENDPOINT=http://minio:9000 # omit for AWS S3
|
|
575
|
+
MEDIA_S3_FORCE_PATH_STYLE=true # usually required by MinIO
|
|
576
|
+
MEDIA_S3_ACCESS_KEY_ID=...
|
|
577
|
+
MEDIA_S3_SECRET_ACCESS_KEY=...
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
`aws-sdk-s3` is a runtime dependency and is loaded only when the S3 provider is selected. Stored objects are tracked in `media_objects` and `media_object_references`; deleting a document releases its reference, and the worker removes zero-reference objects during hourly garbage collection. Retained jobs, including failed jobs, protect their staging objects from garbage collection until the job is pruned.
|
|
581
|
+
|
|
582
|
+
Enable Bearer authentication and PostgreSQL-backed quotas with:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
SMARTRAG_HTTP_AUTH_ENABLED=true
|
|
586
|
+
SMARTRAG_HTTP_PRINCIPAL=production-agent
|
|
587
|
+
SMARTRAG_HTTP_TOKEN=replace-with-a-secret
|
|
588
|
+
SMARTRAG_REQUESTS_PER_MINUTE=120
|
|
589
|
+
SMARTRAG_UPLOAD_BYTES_PER_DAY=10737418240
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
All endpoints except `/healthz` then require `Authorization: Bearer ...`. Job reads and mutations are scoped to the authenticated principal. Retrieval also injects the authenticated principal, limits backend search to owned document IDs, and rechecks every returned candidate against PostgreSQL, so a backend that ignores `document_ids` cannot leak another principal's evidence. The current YAML/env entrypoint supports one token; embedded applications can pass a `tokens: { principal => token }` map to `HttpAccessPolicy` for multiple callers.
|
|
593
|
+
|
|
594
|
+
### Real storage and isolation verification
|
|
595
|
+
|
|
596
|
+
The MinIO integration spec is opt-in because it performs real object writes and deletes. Start MinIO, create or allow creation of the configured bucket, load the PostgreSQL test environment, and run:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
SMARTRAG_MINIO_E2E=1 \
|
|
600
|
+
SMARTRAG_MINIO_ENDPOINT=http://127.0.0.1:19000 \
|
|
601
|
+
SMARTRAG_MINIO_BUCKET=smart-rag-e2e \
|
|
602
|
+
SMARTRAG_MINIO_ACCESS_KEY=smart-rag-e2e \
|
|
603
|
+
SMARTRAG_MINIO_SECRET_KEY=smart-rag-e2e-secret \
|
|
604
|
+
bundle exec rspec spec/integration/minio_content_store_spec.rb
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
This verifies cross-instance upload/materialization, asynchronous worker consumption of an `s3://` staging URI, reference attachment/detachment, real object deletion, and failed-job GC protection. PostgreSQL-backed tenant and idempotency coverage can be run independently with:
|
|
608
|
+
|
|
609
|
+
```bash
|
|
610
|
+
bundle exec rspec \
|
|
611
|
+
spec/integration/media_tenant_isolation_spec.rb \
|
|
612
|
+
spec/integration/media_p3_spec.rb
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Those specs cover Bearer-token HTTP retrieval isolation, invalid-token `401`, same-payload deduplication, different-payload `409`, and concurrent inserts racing on the same principal/key.
|