jaqmd 0.1.1__tar.gz
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.
- jaqmd-0.1.1/.github/workflows/ci.yml +34 -0
- jaqmd-0.1.1/.github/workflows/release.yml +40 -0
- jaqmd-0.1.1/.gitignore +44 -0
- jaqmd-0.1.1/AGENTS.md +321 -0
- jaqmd-0.1.1/CHANGELOG.md +44 -0
- jaqmd-0.1.1/LICENSE +21 -0
- jaqmd-0.1.1/PKG-INFO +26 -0
- jaqmd-0.1.1/PLANS.md +181 -0
- jaqmd-0.1.1/README.md +318 -0
- jaqmd-0.1.1/cliff.toml +94 -0
- jaqmd-0.1.1/justfile +62 -0
- jaqmd-0.1.1/pyproject.toml +61 -0
- jaqmd-0.1.1/src/jaqmd/__init__.py +1 -0
- jaqmd-0.1.1/src/jaqmd/chunk.py +100 -0
- jaqmd-0.1.1/src/jaqmd/cli.py +905 -0
- jaqmd-0.1.1/src/jaqmd/config.py +207 -0
- jaqmd-0.1.1/src/jaqmd/embed.py +98 -0
- jaqmd-0.1.1/src/jaqmd/format.py +100 -0
- jaqmd-0.1.1/src/jaqmd/mcp/__init__.py +0 -0
- jaqmd-0.1.1/src/jaqmd/mcp/server.py +260 -0
- jaqmd-0.1.1/src/jaqmd/modelcache.py +16 -0
- jaqmd-0.1.1/src/jaqmd/paths.py +24 -0
- jaqmd-0.1.1/src/jaqmd/progress.py +118 -0
- jaqmd-0.1.1/src/jaqmd/qe.py +219 -0
- jaqmd-0.1.1/src/jaqmd/rerank.py +152 -0
- jaqmd-0.1.1/src/jaqmd/scan.py +44 -0
- jaqmd-0.1.1/src/jaqmd/schema.sql +141 -0
- jaqmd-0.1.1/src/jaqmd/search/__init__.py +0 -0
- jaqmd-0.1.1/src/jaqmd/search/mosearch.py +74 -0
- jaqmd-0.1.1/src/jaqmd/search/query.py +333 -0
- jaqmd-0.1.1/src/jaqmd/search/snippet.py +83 -0
- jaqmd-0.1.1/src/jaqmd/search/trisearch.py +83 -0
- jaqmd-0.1.1/src/jaqmd/search/vsearch.py +122 -0
- jaqmd-0.1.1/src/jaqmd/store.py +287 -0
- jaqmd-0.1.1/src/jaqmd/tokenize/__init__.py +0 -0
- jaqmd-0.1.1/src/jaqmd/tokenize/morph.py +124 -0
- jaqmd-0.1.1/src/jaqmd/tokenize/trigram.py +37 -0
- jaqmd-0.1.1/tests/__init__.py +0 -0
- jaqmd-0.1.1/tests/conftest.py +77 -0
- jaqmd-0.1.1/tests/test_chunk.py +134 -0
- jaqmd-0.1.1/tests/test_cli.py +361 -0
- jaqmd-0.1.1/tests/test_embed.py +147 -0
- jaqmd-0.1.1/tests/test_mcp.py +230 -0
- jaqmd-0.1.1/tests/test_morph_tokenize.py +93 -0
- jaqmd-0.1.1/tests/test_mosearch.py +150 -0
- jaqmd-0.1.1/tests/test_progress.py +99 -0
- jaqmd-0.1.1/tests/test_qe.py +136 -0
- jaqmd-0.1.1/tests/test_query.py +500 -0
- jaqmd-0.1.1/tests/test_rerank.py +185 -0
- jaqmd-0.1.1/tests/test_search_snippet.py +102 -0
- jaqmd-0.1.1/tests/test_store.py +249 -0
- jaqmd-0.1.1/tests/test_trigram_tokenize.py +66 -0
- jaqmd-0.1.1/tests/test_trisearch.py +138 -0
- jaqmd-0.1.1/tests/test_vsearch.py +286 -0
- jaqmd-0.1.1/uv.lock +1260 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
# 同一ブランチ/PRの古い実行をキャンセルして無駄を省く
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
check:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
uses: astral-sh/setup-uv@v7
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: true
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
run: uv python install 3.14
|
|
26
|
+
|
|
27
|
+
- name: Install just
|
|
28
|
+
uses: extractions/setup-just@v4
|
|
29
|
+
|
|
30
|
+
- name: Sync dependencies
|
|
31
|
+
run: uv sync --locked
|
|
32
|
+
|
|
33
|
+
- name: Run checks (lint + fmt-check + test)
|
|
34
|
+
run: just check
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write # GitHub Release 作成
|
|
10
|
+
id-token: write # PyPI Trusted Publishing (OIDC)
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0 # git-cliff がリリースノート生成に全履歴を要する
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v7
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
run: uv python install 3.14
|
|
27
|
+
|
|
28
|
+
- name: Build
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
32
|
+
run: uv publish --trusted-publishing always
|
|
33
|
+
|
|
34
|
+
- name: Generate release notes
|
|
35
|
+
run: uvx git-cliff --latest --strip all -o RELEASE_NOTES.md
|
|
36
|
+
|
|
37
|
+
- name: Create GitHub Release
|
|
38
|
+
env:
|
|
39
|
+
GH_TOKEN: ${{ github.token }}
|
|
40
|
+
run: gh release create "${GITHUB_REF_NAME}" dist/* --title "${GITHUB_REF_NAME}" --notes-file RELEASE_NOTES.md
|
jaqmd-0.1.1/.gitignore
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
*.egg
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
.eggs/
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# uv
|
|
19
|
+
.python-version
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# IDE
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
29
|
+
*.swp
|
|
30
|
+
*.swo
|
|
31
|
+
|
|
32
|
+
# macOS
|
|
33
|
+
.DS_Store
|
|
34
|
+
|
|
35
|
+
# SudachiDict cache
|
|
36
|
+
sudachidict*
|
|
37
|
+
|
|
38
|
+
# fastembed model cache
|
|
39
|
+
.fastembed_cache/
|
|
40
|
+
|
|
41
|
+
# jaqmd index files
|
|
42
|
+
*.db
|
|
43
|
+
*.db-shm
|
|
44
|
+
*.db-wal
|
jaqmd-0.1.1/AGENTS.md
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
このファイルは、jaqmd の開発に携わる AI エージェント(Claude Code など)および開発者向けの実装方針をまとめたものです。
|
|
4
|
+
|
|
5
|
+
## プロジェクト概要
|
|
6
|
+
|
|
7
|
+
jaqmd は日本語ドキュメントに特化したローカル検索エンジンです。tobi/qmd からインスピレーションを受けつつ、日本語テキストの検索品質に振り切っています。
|
|
8
|
+
|
|
9
|
+
**核心となる差別化点:**
|
|
10
|
+
- trigram / 形態素解析 / ベクトルの3つの日本語向け検索方式を提供
|
|
11
|
+
- Qwen等を一切使わず、ruri(名古屋大学)を採用
|
|
12
|
+
- PyTorch非依存の薄い構成
|
|
13
|
+
|
|
14
|
+
## 基本原則
|
|
15
|
+
|
|
16
|
+
### 依存関係を薄く保つ
|
|
17
|
+
|
|
18
|
+
- **PyTorch を引き込まない。** ベクトル推論は fastembed(ONNX Runtime)経由で行う
|
|
19
|
+
- 形態素解析・ベクトル検索は**オプション依存**とし、最小構成では trigram 検索のみで動作させる
|
|
20
|
+
- 新しい依存を追加する前に、本当に必要か検討する
|
|
21
|
+
|
|
22
|
+
### モデルの利用方針
|
|
23
|
+
|
|
24
|
+
- Qwen 系モデルを embedding・rerank・query expansion のいずれにも使用しない
|
|
25
|
+
- embedding は `cl-nagoya/ruri-v3-310m`、rerank は `cl-nagoya/ruri-v3-reranker-310m` を使う
|
|
26
|
+
- query expansion は独自開発する(`szdr/jaqmd-qe-gemma-4-e2b-it`、Gemma 4 E2B の LoRA fine-tune。GGUF 形式で llama-cpp-python 経由)
|
|
27
|
+
|
|
28
|
+
### 段階的なインデックス構築を尊重する
|
|
29
|
+
|
|
30
|
+
- `update`(trigram)は常時、`morph`(形態素)と `embed`(ベクトル)はオプション
|
|
31
|
+
- どのインデックスが構築済みかは `index_meta` テーブルで管理する
|
|
32
|
+
- 検索コマンドは事前要件を満たさない場合、具体的な次のアクションを提示してエラーにする
|
|
33
|
+
- 例: 「形態素インデックスがありません。`jaqmd morph` を実行してください」
|
|
34
|
+
|
|
35
|
+
## 技術スタック
|
|
36
|
+
|
|
37
|
+
| 領域 | 採用技術 |
|
|
38
|
+
|------|----------|
|
|
39
|
+
| 言語 | Python 3.10+ |
|
|
40
|
+
| ストレージ | SQLite + sqlite-vec + FTS5 |
|
|
41
|
+
| ベクトル推論 | fastembed(ONNX Runtime) |
|
|
42
|
+
| 形態素解析 | SudachiPy(sudachidict-core) |
|
|
43
|
+
| Embedding | ruri-v3-310m(ONNX) |
|
|
44
|
+
| Reranker | ruri-v3-reranker-310m(ONNX) |
|
|
45
|
+
| CLI | Typer または Click |
|
|
46
|
+
| MCP | Python MCP SDK |
|
|
47
|
+
| パッケージ管理 | uv |
|
|
48
|
+
|
|
49
|
+
## コマンド体系
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
インデックス構築:
|
|
53
|
+
jaqmd collection add <path> --name <name>
|
|
54
|
+
jaqmd update [--pull] # trigram FTS(常時)
|
|
55
|
+
jaqmd morph # 形態素 FTS(オプション)
|
|
56
|
+
jaqmd embed [-f] # ベクトル(オプション)
|
|
57
|
+
|
|
58
|
+
検索:
|
|
59
|
+
jaqmd search <query> # trigram BM25
|
|
60
|
+
jaqmd mosearch <query> # 形態素 BM25
|
|
61
|
+
jaqmd vsearch <query> # ベクトル
|
|
62
|
+
jaqmd query <query> # ハイブリッド + rerank
|
|
63
|
+
|
|
64
|
+
管理:
|
|
65
|
+
jaqmd get / multi-get / collection / ls / status / cleanup / mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## ストレージ設計
|
|
69
|
+
|
|
70
|
+
データベースは `~/.cache/jaqmd/index.sqlite` に保存する(`XDG_CACHE_HOME` を尊重)。
|
|
71
|
+
|
|
72
|
+
### スキーマ
|
|
73
|
+
|
|
74
|
+
```sql
|
|
75
|
+
-- コンテンツ実体(content-addressable、重複排除)
|
|
76
|
+
CREATE TABLE content (
|
|
77
|
+
hash TEXT PRIMARY KEY, -- SHA-256
|
|
78
|
+
body TEXT NOT NULL
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
-- ドキュメント(ファイルパス → ハッシュのマッピング)
|
|
82
|
+
CREATE TABLE documents (
|
|
83
|
+
id INTEGER PRIMARY KEY,
|
|
84
|
+
collection TEXT NOT NULL,
|
|
85
|
+
path TEXT NOT NULL, -- コレクション相対パス
|
|
86
|
+
hash TEXT NOT NULL REFERENCES content(hash),
|
|
87
|
+
docid TEXT UNIQUE NOT NULL, -- 6文字ハッシュ(例: abc123。"#" は付かない)
|
|
88
|
+
title TEXT,
|
|
89
|
+
mtime INTEGER,
|
|
90
|
+
active INTEGER DEFAULT 1, -- 論理削除フラグ
|
|
91
|
+
indexed_at INTEGER DEFAULT (unixepoch())
|
|
92
|
+
);
|
|
93
|
+
CREATE INDEX idx_documents_collection ON documents(collection);
|
|
94
|
+
CREATE INDEX idx_documents_hash ON documents(hash);
|
|
95
|
+
|
|
96
|
+
-- コレクション定義
|
|
97
|
+
CREATE TABLE collections (
|
|
98
|
+
id INTEGER PRIMARY KEY,
|
|
99
|
+
name TEXT UNIQUE NOT NULL,
|
|
100
|
+
path TEXT NOT NULL,
|
|
101
|
+
glob_mask TEXT NOT NULL DEFAULT '**/*.md',
|
|
102
|
+
created_at INTEGER DEFAULT (unixepoch())
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
-- パスコンテキスト
|
|
106
|
+
CREATE TABLE path_contexts (
|
|
107
|
+
path TEXT PRIMARY KEY,
|
|
108
|
+
context TEXT NOT NULL
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
-- DBメタ情報(インデックス構築状態・モデル情報)
|
|
112
|
+
CREATE TABLE index_meta (
|
|
113
|
+
key TEXT PRIMARY KEY,
|
|
114
|
+
value TEXT NOT NULL
|
|
115
|
+
);
|
|
116
|
+
-- 例: schema_version, morph_indexed, morph_tokenizer,
|
|
117
|
+
-- vec_indexed, embed_model, embed_dim
|
|
118
|
+
|
|
119
|
+
-- FTS1: trigram(trigram tokenizer、辞書不要・デフォルト)
|
|
120
|
+
CREATE VIRTUAL TABLE docs_fts_trigram USING fts5(
|
|
121
|
+
docid UNINDEXED,
|
|
122
|
+
filepath UNINDEXED, -- collection/path(絞り込み用)
|
|
123
|
+
title,
|
|
124
|
+
body,
|
|
125
|
+
tokenize = 'trigram'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
-- FTS2: 形態素(unicode61、Python側で分かち書き済みを投入)
|
|
129
|
+
CREATE VIRTUAL TABLE docs_fts_morph USING fts5(
|
|
130
|
+
docid UNINDEXED,
|
|
131
|
+
filepath UNINDEXED,
|
|
132
|
+
title,
|
|
133
|
+
body,
|
|
134
|
+
tokenize = 'unicode61'
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
-- チャンク本体(ベクトル検索用)
|
|
138
|
+
CREATE TABLE chunk_vectors (
|
|
139
|
+
id INTEGER PRIMARY KEY,
|
|
140
|
+
doc_id INTEGER NOT NULL REFERENCES documents(id),
|
|
141
|
+
docid TEXT NOT NULL,
|
|
142
|
+
chunk_seq INTEGER NOT NULL, -- チャンク番号(0始まり)
|
|
143
|
+
chunk_pos INTEGER NOT NULL, -- ドキュメント内の文字オフセット
|
|
144
|
+
chunk_text TEXT NOT NULL,
|
|
145
|
+
embed_model TEXT NOT NULL,
|
|
146
|
+
UNIQUE(docid, chunk_seq)
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
-- sqlite-vec ベクトルインデックス(ruri-v3-310m は 768次元)
|
|
150
|
+
CREATE VIRTUAL TABLE vectors_vec USING vec0(
|
|
151
|
+
chunk_id INTEGER PRIMARY KEY,
|
|
152
|
+
embedding float[768]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
-- Query Expansion キャッシュ
|
|
156
|
+
CREATE TABLE qe_cache (
|
|
157
|
+
query_hash TEXT PRIMARY KEY,
|
|
158
|
+
query_raw TEXT NOT NULL,
|
|
159
|
+
lex_query TEXT,
|
|
160
|
+
vec_query TEXT,
|
|
161
|
+
hyde_text TEXT,
|
|
162
|
+
model_id TEXT NOT NULL,
|
|
163
|
+
created_at INTEGER DEFAULT (unixepoch()),
|
|
164
|
+
ttl INTEGER DEFAULT 86400
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### トリガーによる FTS 同期
|
|
169
|
+
|
|
170
|
+
trigram FTS は documents の更新に追従してトリガーで自動同期する。
|
|
171
|
+
|
|
172
|
+
```sql
|
|
173
|
+
CREATE TRIGGER documents_ai AFTER INSERT ON documents
|
|
174
|
+
WHEN NEW.active = 1 BEGIN
|
|
175
|
+
INSERT INTO docs_fts_trigram(docid, filepath, title, body)
|
|
176
|
+
SELECT NEW.docid,
|
|
177
|
+
NEW.collection || '/' || NEW.path,
|
|
178
|
+
NEW.title,
|
|
179
|
+
c.body
|
|
180
|
+
FROM content c WHERE c.hash = NEW.hash;
|
|
181
|
+
END;
|
|
182
|
+
|
|
183
|
+
CREATE TRIGGER documents_soft_delete AFTER UPDATE ON documents
|
|
184
|
+
WHEN OLD.active = 1 AND NEW.active = 0 BEGIN
|
|
185
|
+
DELETE FROM docs_fts_trigram WHERE docid = OLD.docid;
|
|
186
|
+
DELETE FROM docs_fts_morph WHERE docid = OLD.docid;
|
|
187
|
+
END;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**形態素 FTS(docs_fts_morph)はトリガーで同期しない。** SQLite トリガー内では SudachiPy を呼べないため、`jaqmd morph` 実行時に Python 側で分かち書きしてから INSERT する。
|
|
191
|
+
|
|
192
|
+
## 検索パイプライン
|
|
193
|
+
|
|
194
|
+
### search(trigram)
|
|
195
|
+
|
|
196
|
+
trigram tokenizer に対してクエリを変換し BM25 で検索する。raw text をそのまま投入する。
|
|
197
|
+
|
|
198
|
+
日本語のトークン化に関する注意:
|
|
199
|
+
- クエリを trigram(3文字)に展開して OR 結合する
|
|
200
|
+
- 例: "東京都庁" → `"東京都" OR "京都庁"`
|
|
201
|
+
- ひらがな・カタカナは低情報語が多く、ノイズになりやすい
|
|
202
|
+
- 3文字未満のトークンは trigram を生成できないためスキップする
|
|
203
|
+
|
|
204
|
+
### mosearch(形態素)
|
|
205
|
+
|
|
206
|
+
SudachiPy で分かち書きしたクエリを unicode61 FTS に対して BM25 検索する。インデックス側もクエリ側も同じ正規化・分かち書きを通すこと(投入時と検索時で処理を揃えないとマッチしない)。
|
|
207
|
+
|
|
208
|
+
### vsearch(ベクトル)
|
|
209
|
+
|
|
210
|
+
クエリを ruri-v3 で embedding 化し、vectors_vec を KNN 検索する。
|
|
211
|
+
|
|
212
|
+
ruri-v3 のプレフィックス仕様を厳守すること:
|
|
213
|
+
- クエリ: `検索クエリ: ` を付与
|
|
214
|
+
- 文書: `検索文書: ` を付与
|
|
215
|
+
|
|
216
|
+
これを忘れると検索品質が大きく劣化する。
|
|
217
|
+
|
|
218
|
+
### query(ハイブリッド)
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
1. Query Expansion で lex: / vec: / hyde: に展開(qe_cache 活用)
|
|
222
|
+
2. 利用可能なインデックスで検索:
|
|
223
|
+
- search(常に)
|
|
224
|
+
- mosearch(morph_indexed なら)
|
|
225
|
+
- vsearch(vec_indexed なら)
|
|
226
|
+
3. RRF(Reciprocal Rank Fusion, k=60)で融合
|
|
227
|
+
4. ruri-reranker で上位候補をリランク
|
|
228
|
+
5. 結果を返却
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
利用可能なインデックスは `index_meta` を見て判定する。morph/embed 未実行でも query は動作する。
|
|
232
|
+
|
|
233
|
+
## fastembed の使い方
|
|
234
|
+
|
|
235
|
+
embedding・reranker ともにカスタムモデル登録で ruri-v3 を読み込む。
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from fastembed import TextEmbedding
|
|
239
|
+
from fastembed.common.model_description import PoolingType, ModelSource
|
|
240
|
+
|
|
241
|
+
TextEmbedding.add_custom_model(
|
|
242
|
+
model="sirasagi62/ruri-v3-310m-ONNX",
|
|
243
|
+
pooling=PoolingType.MEAN,
|
|
244
|
+
normalization=True,
|
|
245
|
+
sources=ModelSource(hf="sirasagi62/ruri-v3-310m-ONNX"),
|
|
246
|
+
dim=768,
|
|
247
|
+
model_file="onnx/model.onnx",
|
|
248
|
+
)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
注意点:
|
|
252
|
+
- prefix(`検索クエリ:` / `検索文書:`)の付与は fastembed では自動化されないので、embed に渡す前に自前で付ける
|
|
253
|
+
- ruri-v3-reranker の ONNX 版が公式に存在しない場合は optimum でエクスポートする(`jaqmd-qe` または別スクリプトで管理)
|
|
254
|
+
|
|
255
|
+
## チャンク戦略
|
|
256
|
+
|
|
257
|
+
- 単位: トークン数ベース(ruri-v3 のトークナイザ基準)
|
|
258
|
+
- デフォルト: 800トークン、15%オーバーラップ(qmd を踏襲)
|
|
259
|
+
- 文境界(。!?)を優先的に分割点とする
|
|
260
|
+
- ruri-v3 は 8192 トークンまで対応するため、長めのチャンクも選択肢
|
|
261
|
+
|
|
262
|
+
## 対応ドキュメント形式(MVP)
|
|
263
|
+
|
|
264
|
+
- **MVP**: Markdown(`.md`)、プレーンテキスト(`.txt`)
|
|
265
|
+
- **将来**: PDF(日本語技術文書が多いため需要は高いが、依存が増えるため後回し)
|
|
266
|
+
|
|
267
|
+
## 開発上の禁止事項
|
|
268
|
+
|
|
269
|
+
- **DB を手書き DDL で直接書き換えない。** スキーマ変更は冪等な `schema.sql`(`CREATE ... IF NOT EXISTS` + トリガーは `DROP TRIGGER IF EXISTS` + `CREATE TRIGGER` で再生成)への追記経由で行い、接続時の初期化で自動反映する
|
|
270
|
+
- **`jaqmd update` / `morph` / `embed` を自動実行しない。** ユーザーに実行コマンドを提示する
|
|
271
|
+
- **PyTorch を依存に追加しない。**
|
|
272
|
+
- **形態素 FTS をトリガーで同期しようとしない**(SudachiPy が呼べない)
|
|
273
|
+
|
|
274
|
+
## ディレクトリ構成(想定)
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
jaqmd/
|
|
278
|
+
├── pyproject.toml
|
|
279
|
+
├── README.md
|
|
280
|
+
├── AGENTS.md
|
|
281
|
+
├── src/jaqmd/
|
|
282
|
+
│ ├── __init__.py
|
|
283
|
+
│ ├── cli.py # CLI エントリポイント
|
|
284
|
+
│ ├── store.py # SQLite ストレージ層
|
|
285
|
+
│ ├── schema.sql # スキーマ定義
|
|
286
|
+
│ ├── tokenize/
|
|
287
|
+
│ │ ├── trigram.py # trigram トークナイズ
|
|
288
|
+
│ │ └── morph.py # SudachiPy ラッパー
|
|
289
|
+
│ ├── search/
|
|
290
|
+
│ │ ├── trisearch.py
|
|
291
|
+
│ │ ├── mosearch.py
|
|
292
|
+
│ │ ├── vsearch.py
|
|
293
|
+
│ │ └── query.py # ハイブリッド + RRF + rerank
|
|
294
|
+
│ ├── embed.py # fastembed ラッパー
|
|
295
|
+
│ ├── rerank.py # ruri-reranker ラッパー
|
|
296
|
+
│ ├── chunk.py # チャンク分割
|
|
297
|
+
│ └── mcp/
|
|
298
|
+
│ └── server.py # MCP サーバー
|
|
299
|
+
└── tests/
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## テスト方針
|
|
303
|
+
|
|
304
|
+
- 日本語の表記ゆれ(サーバー/サーバ、第1条/第一条)のマッチを検証する
|
|
305
|
+
- trigram と形態素で検索結果が期待通り変わることを確認する
|
|
306
|
+
- ruri-v3 のプレフィックス付与漏れを検出するテストを置く
|
|
307
|
+
- インデックス未構築時のエラーメッセージが適切であることを確認する
|
|
308
|
+
|
|
309
|
+
## パッケージ管理メモ
|
|
310
|
+
|
|
311
|
+
```toml
|
|
312
|
+
[project.optional-dependencies]
|
|
313
|
+
morph = ["sudachipy>=0.6", "sudachidict-core"]
|
|
314
|
+
vector = ["fastembed>=0.8"]
|
|
315
|
+
all = ["sudachipy>=0.6", "sudachidict-core", "fastembed>=0.8"]
|
|
316
|
+
|
|
317
|
+
[project.scripts]
|
|
318
|
+
jaqmd = "jaqmd.cli:main"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
最小構成(コア)の依存は sqlite-vec のみに抑える。
|
jaqmd-0.1.1/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
## [0.1.1] - 2026-07-13
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- Implement morph and mosearch functionalities with tokenization support
|
|
6
|
+
- Enhance search results with original text and snippet generation
|
|
7
|
+
- Add vector search functionality and snippet extraction improvements
|
|
8
|
+
- Implement hybrid search functionality with RRF fusion and query enhancements
|
|
9
|
+
- Add min-max scaling for RRF scores in query results
|
|
10
|
+
- Implement reranker functionality with optional disabling
|
|
11
|
+
- Add progress reporting functionality
|
|
12
|
+
- Add collection filtering to update and morph commands, and implement get_collection function
|
|
13
|
+
- Add progress reporting and quiet option to update, morph, and embed commands
|
|
14
|
+
- Update model references and enhance embedding functionality
|
|
15
|
+
- Add --force option to morph command for full index rebuild
|
|
16
|
+
- Add query expansion feature with llama-cpp-python integration
|
|
17
|
+
- Add query expansion result formatting and callback support
|
|
18
|
+
- Suppress stderr output during LLM loading
|
|
19
|
+
- Add reranker model option and update rerank functionality
|
|
20
|
+
- Visualize model loading progress during first-run query
|
|
21
|
+
- MCP サーバー対応(tobi/qmd 準拠)
|
|
22
|
+
- Env / 設定ファイルによるコマンド既定値の上書きに対応
|
|
23
|
+
- Use ruff, just
|
|
24
|
+
- GitHub Actions による CI ワークフローを追加
|
|
25
|
+
- リリースフローを構築
|
|
26
|
+
|
|
27
|
+
### 🐛 Bug Fixes
|
|
28
|
+
|
|
29
|
+
- Ensure idempotent schema application and enhance database initialization
|
|
30
|
+
- 長文入力時のSudachi分割エラーを回避
|
|
31
|
+
- Setup-uv のバージョン指定を存在する v7 タグに修正
|
|
32
|
+
- MCP get の docid 指定で "#" 誤付与を解消
|
|
33
|
+
|
|
34
|
+
### 💼 Other
|
|
35
|
+
|
|
36
|
+
- Trigram
|
|
37
|
+
|
|
38
|
+
### 📚 Documentation
|
|
39
|
+
|
|
40
|
+
- PLANS.md の完了表記を実装状況に合わせて更新
|
|
41
|
+
|
|
42
|
+
### ⚙️ Miscellaneous Tasks
|
|
43
|
+
|
|
44
|
+
- Format code and update dependencies
|
jaqmd-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 szdr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jaqmd-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jaqmd
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: 日本語ドキュメントに特化したローカル検索エンジン
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.14
|
|
7
|
+
Requires-Dist: rich>=13.0
|
|
8
|
+
Requires-Dist: sqlite-vec>=0.1
|
|
9
|
+
Requires-Dist: typer>=0.12
|
|
10
|
+
Provides-Extra: all
|
|
11
|
+
Requires-Dist: fastembed>=0.8; extra == 'all'
|
|
12
|
+
Requires-Dist: huggingface-hub>=0.20; extra == 'all'
|
|
13
|
+
Requires-Dist: llama-cpp-python>=0.3; extra == 'all'
|
|
14
|
+
Requires-Dist: mcp>=1.2; extra == 'all'
|
|
15
|
+
Requires-Dist: sudachidict-core; extra == 'all'
|
|
16
|
+
Requires-Dist: sudachipy>=0.6; extra == 'all'
|
|
17
|
+
Provides-Extra: mcp
|
|
18
|
+
Requires-Dist: mcp>=1.2; extra == 'mcp'
|
|
19
|
+
Provides-Extra: morph
|
|
20
|
+
Requires-Dist: sudachidict-core; extra == 'morph'
|
|
21
|
+
Requires-Dist: sudachipy>=0.6; extra == 'morph'
|
|
22
|
+
Provides-Extra: qe
|
|
23
|
+
Requires-Dist: huggingface-hub>=0.20; extra == 'qe'
|
|
24
|
+
Requires-Dist: llama-cpp-python>=0.3; extra == 'qe'
|
|
25
|
+
Provides-Extra: vector
|
|
26
|
+
Requires-Dist: fastembed>=0.8; extra == 'vector'
|