karaage0703/xs-workspace-rag
ワークスペース全体をベクトル検索+構造化ファクト管理する任意スキル。ユーザーがRAGのセットアップ・検索・ファクト操作を明示的に依頼した場合に使う。「RAGをセットアップして」「RAGで探して」「ファクト登録」で使用。
npx skills add https://github.com/karaage0703/ai-assistant-workspace --skill xs-workspace-rag
ワークスペース内のドキュメントをベクトル検索+構造化ファクト管理するスキル。port 7890 で常駐、検索とファクト CRUD が 1 サーバーに集約されている。
/facts 系 API で ADD/UPDATE/DELETE。/search にも相乗りで返る?forgetting=on のときだけ MemoryBank 式 decay を適用(デフォルト OFF)。NO_DECAY 系(AGENTS.md/CLAUDE.md/MEMORY.md)以外は全フォルダ対象。新しい記事を上に出したい時のみ ONこのスキルは、ユーザーがRAGの導入または利用を明示した場合だけ使う。通常の会話や開発作業では、サーバーの確認・起動・検索を自動実行しない。
[SKILL_DIR]/
├── SKILL.md
└── scripts/
├── workspace_rag.py # CLI(インデックス・検索)
├── workspace_rag_server.py # 常駐HTTPサーバー
├── start_server.sh # サーバー起動スクリプト
└── pyproject.toml
cd [SKILL_DIR]/scripts
uv sync
# 大規模インデックス向けのFaissは任意
uv sync --extra faiss
cd [SKILL_DIR]/scripts
# 初回インデックス(全ファイル処理)
uv run python workspace_rag.py index -w [WORKSPACE]
# 差分インデックス(変更ファイルのみ更新。同じコマンドを再実行するだけ)
uv run python workspace_rag.py index -w [WORKSPACE]
# 強制再インデックス(全ファイル再処理)
uv run python workspace_rag.py index -w [WORKSPACE] -f
# ファイルサイズ上限を変更(デフォルト100KB、0=無制限)
uv run python workspace_rag.py index -w [WORKSPACE] --max-file-size 200000
所要時間の目安:
AIツール(Claude Code / Codex CLI)のセッションでは、長時間処理でタイムアウトする可能性がある。
その場合は setsid + バックグラウンド実行 を使う。nohup 単独では親tool shellのprocess group cleanupに巻き込まれる環境がある。
以下はLinux / WSLの例。setsid がないmacOSでは、後述のlaunchdまたはpm2などservice managerで常駐させる。
cd [SKILL_DIR]/scripts
# バックグラウンドでインデックス作成(PID・ログ・終了コードを保存)
RAG_STATE_DIR="$(mktemp -d)"
TRIGGER_CHANNEL="" # xangiの場合だけチャンネルIDを設定
setsid bash -lc '
state_dir="$1"
trigger_channel="$2"
echo $$ > "$state_dir/pid"
uv run python workspace_rag.py index -w [WORKSPACE] > "$state_dir/index.log" 2>&1
rc=$?
echo "$rc" > "$state_dir/exit"
if [ -n "$trigger_channel" ] && command -v xangi-cmd >/dev/null 2>&1; then
xangi-cmd trigger --channel "$trigger_channel" \
--message "RAGインデックス処理が終了しました。保存済みの終了状態とログを確認してください" \
--source workspace-rag-index
fi
exit "$rc"
' bash "$RAG_STATE_DIR" "$TRIGGER_CHANNEL" >/dev/null 2>&1 &
# 起動報告前に、親とは別SID/PGIDで生存していることを確認
sleep 2
ps -o pid,ppid,sid,pgid,stat,etime,cmd -p "$(cat "$RAG_STATE_DIR/pid")"
echo "State: $RAG_STATE_DIR"
# 進捗確認
tail -f "$RAG_STATE_DIR/index.log"
# 完了確認
cat "$RAG_STATE_DIR/exit"
tail "$RAG_STATE_DIR/index.log"
ポイント:
setsid bash -lc '...' & で親tool shellから分離するps で別SID/PGIDと生存を確認するTRIGGER_CHANNEL を設定し、成功・失敗の両方で結果確認のturnを起動する常駐HTTPサーバーが起動中なら、curlで高速検索できる。ベクトル検索はFaiss IndexFlatIPの完全検索を使い、/healthのvector_backendで実際のbackendを確認できる。
重要: 日本語クエリは URL エンコードが必要。 curl -G --data-urlencode を使うこと(直書きは Bad request 400 になる)。
# 基本検索(ハイブリッド: ベクトル+FTS5)
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=検索クエリ"
# ベクトル検索のみ(意味的に近い文書を検索)
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=検索クエリ" --data-urlencode "mode=vector"
# キーワード検索のみ(FTS5 trigram、英語/コードに強い)
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=Python import" --data-urlencode "mode=keyword"
# R²AGフォーマット付き
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=検索クエリ" --data-urlencode "r2ag=1"
# 結果数・最低スコア指定
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=検索クエリ" --data-urlencode "k=10" --data-urlencode "s=0.5"
# ヘルスチェック
curl -s "http://127.0.0.1:7890/health"
# インデックス更新(HTTP 202を返し、バックグラウンドで処理)
curl -s -X POST "http://127.0.0.1:7890/reindex"
# 完了確認
curl -s "http://127.0.0.1:7890/health"
POST /reindexの202は受付完了であり、再索引完了ではない。必要な場合はreindex_in_progress=falseかつlast_reindex_error=nullを確認する。再索引中も既存キャッシュで検索できる。
query encoderを短時間で取得できない場合はHTTP待ち行列を伸ばさずkeyword検索へ縮退し、レスポンスへdegraded: trueとdegraded_reason: "query_encoder_busy"を付ける。RAG停止とは区別する。
RAG_VECTOR_BACKEND=numpyを設定するとベクトルbackendをNumPyへ切り替えられる。Faissのimportまたはindex構築に失敗した場合も自動でNumPyへ縮退する。
検索モード:
忘却曲線オプション (?forgetting=on) — デフォルト OFF:
# 全フォルダのチャンクに MemoryBank 式 decay を適用
# (新しい記事/参照されてる記事を上に出したい時のみ使用)
curl -s -G "http://127.0.0.1:7890/search" --data-urlencode "q=検索クエリ" --data-urlencode "forgetting=on"
final = combined * path_weight * freshness * decay、 decay = 2^(-t/S), S = 30*(1+access_count*0.5)(30日半減期、参照で延長)AGENTS.md/CLAUDE.md/MEMORY.md は decay=1.0(常に参照されてほしいルール系)access_count が更新されて忘れにくくなる構造化された事実を ID 付きで保存・更新・削除する API。毎回「検索 → 判断 → 操作」の3ステップで進めること(自動 UPDATE は廃止)。
# (1) 検索: 既存ファクトに似たものがあるか
curl -s -G "http://127.0.0.1:7890/facts/similar" \
--data-urlencode "q=ファクトの要点" --data-urlencode "k=3"
# (2-A) ADD: 新規追加(POST /facts)
curl -s -X POST "http://127.0.0.1:7890/facts" \
-H "Content-Type: application/json" \
-d '{"facts": [{"text": "好きな言語はPython"}]}'
# (2-B) UPDATE: 既存IDを上書き(同じ事実の最新化のみ)
curl -s -X PUT "http://127.0.0.1:7890/facts/<ID>" \
-H "Content-Type: application/json" \
-d '{"text": "新しい内容"}'
# (2-C) DELETE: 期限切れ・無関係になったファクトの削除
curl -s -X DELETE "http://127.0.0.1:7890/facts/<ID>"
# 一覧
curl -s "http://127.0.0.1:7890/facts"
判断基準:
重要: 別トピックを既存IDに UPDATE しない(old_values 履歴が混乱する)。違うトピックは ADD。
ファクトは「後から検索で一発で取り出したい、比較的変わりにくい事実」を短く持つ。目安は1〜3行、200字程度まで。
ファクトへ入れる例:
別の場所へ保存する例:
memory/YYYYMMDD.md または MEMORY.mdnotes/同じファクトの最新化だけUPDATEし、別トピックはADDする。長文になりそうなら、核となる事実と詳細ファイルへの参照だけを残す。
/search のレスポンスにもファクトが相乗りで返る(フィールド facts)ので、検索だけでファクトも引ける。
cd [SKILL_DIR]/scripts
# 基本検索
uv run python workspace_rag.py search -w [WORKSPACE] -q "検索クエリ"
# R²AGフォーマット出力(関連度ラベル付き、LLMへの入力に最適)
uv run python workspace_rag.py search -w [WORKSPACE] -q "検索クエリ" --r2ag
# 結果数を指定(デフォルト5件)
uv run python workspace_rag.py search -w [WORKSPACE] -q "検索クエリ" -k 10
# 最低スコア閾値を指定(デフォルト0.3)
uv run python workspace_rag.py search -w [WORKSPACE] -q "検索クエリ" -s 0.5
# JSON出力
uv run python workspace_rag.py search -w [WORKSPACE] -q "検索クエリ" --json
検索結果を回答に使う時は、必要に応じて以下を報告する。
報告フォーマット例:
ワークスペースRAGで「検索クエリ」を検索(10件ヒット)
| # | ファイル | 関連度 |
|---|---------|--------|
| 1 | notes/20250723_topic.md | 0.92 (高) |
| 2 | memory/20250722.md | 0.88 (高) |
| 3 | ... | 0.45 (低) |
その後、検索結果をもとに:
チャットの短い返答では、表形式にこだわらず「RAG: 5件ヒット、主に notes/foo.md と memory/yyyymmdd.md を参照」のように短くまとめてもよい。
人名、施設名、製品名、イベント名、過去記事を探す場合、依頼文全体を1本の長い
クエリに詰め込まない。FTS5の空白区切りは実質ANDになり、文書にない周辺語が
1つ混ざるだけでキーワード点が0になる。
k 件の file_path と本文を確認する施設名 / 人名 施設名 / イベント名RAGが結果を返しているのに採用しなかった場合は「RAGでヒットしなかった」と表現せず、
「結果に出ていたが読み落とした」と区別して報告する。
論文「R²AG: Incorporating Retrieval Information into RAG」(EMNLP 2024)のアイデアを簡易実装。
通常のRAG:
文書1: ...
文書2: ...
質問に答えて
R²AG簡易版(関連度スコア付き):
文書1 [関連度: 0.92 (高)]: ... ← 「これは重要」
文書2 [関連度: 0.45 (低)]: ... ← 「これは参考程度」
質問に答えて
関連度スコアをプロンプトに含めることで、LLMが文書の重要度を判断しやすくなる。
bash [SKILL_DIR]/scripts/start_server.sh
# ワークスペースルートに移動してから実行
cd [WORKSPACE]
# 起動
pm2 start "cd skills/xs-workspace-rag/scripts && uv run python workspace_rag_server.py -w $(pwd) -p 7890" --name workspace-rag
# 状態確認
pm2 status workspace-rag
# ログ確認
pm2 logs workspace-rag
# 再起動
pm2 restart workspace-rag
# OS再起動時の自動復帰
pm2 save
pm2 startup
ポート: 7890(WORKSPACE_RAG_PORT 環境変数で変更可)
メモリ使用量: 約800MB(モデル400MB + 埋め込みキャッシュ300MB + オーバヘッド100MB)
scripts/workspace_rag.py の PATH_WEIGHTS でディレクトリごとの検索スコア重みを設定できる。重要なディレクトリのスコアを上げることで、検索結果の精度が向上する。
scripts/workspace_rag.py の DEFAULT_EXCLUDE_PATTERNS / DEFAULT_INCLUDE_EXTENSIONS を直接編集する。
デフォルトは100KB。--max-file-size オプションまたは DEFAULT_MAX_FILE_SIZE 定数で変更可能。
intfloat/multilingual-e5-small(384次元)[WORKSPACE]/.workspace_rag/index_<hash>.db.md, .txt, .py, .js, .ts, .json, .yaml, .toml, .csv 等.git/, node_modules/, __pycache__/, .venv/, 画像・バイナリ等base_score * path_weight。forgetting=on のときだけbase_score * path_weight * freshness_score * decay
「Index not found」エラー:
→ index コマンドを先に実行する
OOM(メモリ不足)でインデックスが途中で停止:
→ バッチ処理+DB再接続でOOMを回避する設計だが、それでも落ちる場合は対象ディレクトリを絞って段階的にインデックスする
検索結果が的外れ:
→ クエリを具体的にする、-s で最低スコア閾値を上げる(0.5〜0.7)
日本語クエリで Bad request 400:
→ URL 直書きは NG。curl -G --data-urlencode "q=..." を使う
「AIエージェントについて書いたファイルを探して」
「コンテキストエンジニアリングに関するメモを検索して」
「去年のイベント登壇資料を見つけて」
「RAGで○○を調べて」
以下のときに「検索 → 判断 → ADD/UPDATE/DELETE」を実行:
別トピックを既存 ID に UPDATE で上書き禁止(同じ事実の最新化のみ UPDATE、別トピックは ADD)。
Take karaage0703/xs-workspace-rag from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.