17 KiB
pgvecterAPI
MCP サーバーのブリッジ用 API
開発メモ
必要な環境変数
ADMIN_API_KEY: 管理者APIの認証キー(未設定だと/admin系は 503 +ADMIN_API_KEY is not set)DATABASE_URL: PostgreSQL接続文字列(未指定だと APIキーはメモリ保存、/kb/*と/collectionsは 503 +DATABASE_URL is not set)PORT: HTTPポート(省略時8080)EMBEDDING_PROVIDER:llamacpporopenai(省略時はLLAMA_CPP_URLがあればllamacpp、なければopenai)LLAMA_CPP_URL: llama.cpp embed API の URL(省略時http://127.0.0.1:8092)EMBEDDING_DIM: 埋め込み次元(省略時1024)OPENAI_API_KEY: OpenAI embeddings を使う場合のみ必須EMBEDDING_MODEL: OpenAI embeddings モデル名(省略時text-embedding-3-small。運用は.envに明示固定を推奨)
設定不足時のレスポンス
ADMIN_API_KEY未設定 →/admin系は503+ADMIN_API_KEY is not setDATABASE_URL未設定 →/kb/*//collections//admin/collectionsは503+DATABASE_URL is not set
起動
ADMIN_API_KEY=your-admin-key \
DATABASE_URL=postgres://... \
EMBEDDING_PROVIDER=llamacpp \
LLAMA_CPP_URL=http://127.0.0.1:8092 \
EMBEDDING_DIM=1024 \
go run .
OpenAI embeddings を使う場合:
ADMIN_API_KEY=your-admin-key \
DATABASE_URL=postgres://... \
EMBEDDING_PROVIDER=openai \
OPENAI_API_KEY=sk-... \
EMBEDDING_MODEL=text-embedding-3-small \
go run .
llama.cpp (embedding) 起動例 (Mac)
/Users/sunamurahideyuki/LLM/llama.cpp/build/bin/llama-server \
-m /Users/sunamurahideyuki/LLM/llama.cpp/models/bge-m3-Q4_K_M.gguf \
--embeddings -t 6 -c 2048 --host 127.0.0.1 --port 8092
管理UI
http://localhost:8080/admin
ブラウザでも X-ADMIN-API-KEY を使う(admin_key クエリは localhost のみ・非推奨)。
基本操作
APIキー発行
curl -s -X POST http://localhost:8080/admin/api-keys \
-H "X-ADMIN-API-KEY: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"label":"dev","permissions_users":["user_123"]}'
upsert (embedding 自動生成 / auto_chunk)
python3 - <<'PY' | \
curl -s -X POST http://localhost:8080/kb/upsert \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d @-
import json, uuid
long_text = "\n\n".join(["段落テキスト " * 200 for _ in range(6)])
print(json.dumps({
"id": str(uuid.uuid4()),
"content": long_text,
"collection": "dev-diary",
"auto_chunk": True,
"metadata": {"source":"manual"}
}))
PY
metadata.permissions は受け付けない(400 + 監査ログ記録)。
filter 内の metadata.permissions.* も受け付けない(400 + 監査ログ記録)。
auto_chunk: true の場合、chunk_size は既定 800、chunk_overlap は既定 100。
search (query_embedding 自動生成)
python3 - <<'PY' | \
curl -s -X POST http://localhost:8080/kb/search \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d @-
import json
print(json.dumps({
"query": "hello world",
"collection": "dev-diary",
"top_k": 5
}))
PY
search (collections 複数指定)
python3 - <<'PY' | \
curl -s -X POST http://localhost:8080/kb/search \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d @-
import json
print(json.dumps({
"query": "hello world",
"collections": ["dev-diary", "default"],
"top_k": 5
}))
PY
collections 一覧
curl -s http://localhost:8080/collections \
-H "X-API-KEY: pgv_..."
VPS運用メモ
llama-embed.service (VPS)
- サービスが有効であることを確認
sudo systemctl status llama-embed.service
- ヘルスチェック
curl -s http://127.0.0.1:8092/health
- 埋め込みチェック (1024次元)
curl -s http://127.0.0.1:8092/embedding \
-H 'Content-Type: application/json' \
-d '{"content":"次元チェック"}' | jq '.[0].embedding[0] | length'
DBセットアップ (VPS)
sudo -u postgres psql
-- 新規DB/ユーザー
CREATE USER pgvecter_api WITH PASSWORD 'YOUR_STRONG_PASSWORD';
CREATE DATABASE pgvecter_api OWNER pgvecter_api;
\c pgvecter_api
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- テーブル
CREATE TABLE IF NOT EXISTS kb_doc_chunks (
id uuid PRIMARY KEY,
doc_id uuid NOT NULL,
chunk_index integer NOT NULL,
collection text NOT NULL DEFAULT 'default',
content text NOT NULL,
metadata jsonb NOT NULL DEFAULT '{}',
embedding vector(1024) NOT NULL, -- EMBEDDING_DIM と一致させる
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS kb_doc_chunks_embedding_hnsw
ON kb_doc_chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX IF NOT EXISTS kb_doc_chunks_collection_idx
ON kb_doc_chunks (collection);
-- 権限
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO pgvecter_api;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO pgvecter_api;
既存DBにコレクションを追加する場合:
ALTER TABLE kb_doc_chunks
ADD COLUMN IF NOT EXISTS collection text NOT NULL DEFAULT 'default';
CREATE INDEX IF NOT EXISTS kb_doc_chunks_collection_idx
ON kb_doc_chunks (collection);
コレクション制約を強化する場合:
UPDATE kb_doc_chunks
SET collection = 'default'
WHERE collection IS NULL OR collection = '';
ALTER TABLE kb_doc_chunks
ALTER COLUMN collection SET NOT NULL;
ALTER TABLE kb_doc_chunks
ADD CONSTRAINT kb_doc_chunks_collection_ck
CHECK (collection ~ '^[a-z0-9_]{1,50}$');
起動時に EMBEDDING_DIM と kb_doc_chunks.embedding の次元を照合し、不一致なら起動失敗。
APIキー運用フロー
- 管理者が
/adminから APIキーを発行 - 発行時に
permissions.usersを設定 - 発行後のキーは 一度しか表示されないので安全に保管
- 失効が必要になったら管理UIで即時失効
systemd (pgvecter API) 例
[Unit]
Description=pgvecter API
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/pgvecterAPI
Environment=ADMIN_API_KEY=your-admin-key
Environment=DATABASE_URL=postgres://pgvecter_api:YOUR_STRONG_PASSWORD@127.0.0.1:5432/pgvecter_api?sslmode=disable
Environment=EMBEDDING_PROVIDER=llamacpp
Environment=LLAMA_CPP_URL=http://127.0.0.1:8092
Environment=EMBEDDING_DIM=1024
Environment=PORT=8080
ExecStart=/usr/local/bin/go run .
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
nginx リバプロ例
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
HTTPS (Let’s Encrypt) 設定例
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
systemd (バイナリ実行) 例
[Unit]
Description=pgvecter API
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/pgvecterAPI
EnvironmentFile=/opt/pgvecterAPI/.env
ExecStart=/opt/pgvecterAPI/pgvecter-api
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
メモリ/CPU制限のベストプラクティス
- systemd のリソース制限
[Service]
MemoryMax=512M
CPUQuota=200%
TasksMax=200
- nginx のタイムアウト設定例
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
デプロイ手順 (VPS)
- リポジトリ配置
sudo mkdir -p /opt/pgvecterAPI
sudo chown $USER:$USER /opt/pgvecterAPI
git clone <repo-url> /opt/pgvecterAPI
cd /opt/pgvecterAPI
2.ビルド (Go)
go build -o pgvecter-api .
3..env 設定
cat > /opt/pgvecterAPI/.env <<'ENV'
ADMIN_API_KEY=your-admin-key
DATABASE_URL=postgres://pgvecter_api:YOUR_STRONG_PASSWORD@127.0.0.1:5432/pgvecter_api?sslmode=disable
EMBEDDING_PROVIDER=llamacpp
LLAMA_CPP_URL=http://127.0.0.1:8092
EMBEDDING_DIM=1024
PORT=8080
ENV
4.systemd 反映
sudo cp /opt/pgvecterAPI/pgvecter-api.service /etc/systemd/system/pgvecter-api.service
sudo systemctl daemon-reload
sudo systemctl enable --now pgvecter-api.service
5.nginx 反映
sudo ln -s /etc/nginx/sites-available/pgvecter-api.conf /etc/nginx/sites-enabled/pgvecter-api.conf
sudo nginx -t && sudo systemctl reload nginx
運用時のチェックリスト
systemctl status pgvecter-api.serviceがactive (running)curl -s http://127.0.0.1:8080/healthがokcurl -s http://127.0.0.1:8092/healthがok- DB接続が正常 (
SELECT 1;が通る) - 管理UIでAPIキー発行/失効ができる
/kb/upsertが成功する/kb/searchで結果が返る
ローカルテスト結果(例)
/healthOK/kb/upsert正常系 OK/kb/search正常系 OKmetadata.permissionsは 400 で拒否filter内metadata.permissions.*は 400 で拒否/admin/audit/permissionsで監査ログ取得 OK
ログ/監視の最低限
- systemd/journal でログ確認
journalctl -u pgvecter-api.service -f
- ヘルスチェック
curl -s http://127.0.0.1:8080/health
- 埋め込み API ヘルス
curl -s http://127.0.0.1:8092/health
他サービスからの利用
チャットbot等の別リポジトリから参照する場合は、以下の2点を明確にする。
X-API-KEYを必ず送るpermissions.usersはサーバ側で強制注入される前提(metadata.permissionsは受け付けない)
エンドポイント一覧(他サービス向け)
GET /health: ヘルスチェック(認証不要)POST /db/query: SELECT-only の読み取りクエリ(X-API-KEY必須)POST /kb/upsert: 1ドキュメントを upsert(X-API-KEY必須、idは UUID)POST /kb/search: ベクトル検索(X-API-KEY必須)POST /kb/delete: ドキュメント削除(X-API-KEY必須、idまたはfilter。doc_id単体は不可)GET /collections: コレクション一覧(X-API-KEY必須)GET /admin/collections: 管理者向けコレクション一覧(X-ADMIN-API-KEY必須)GET /admin/api-keys: 管理APIキー一覧(X-ADMIN-API-KEY必須)POST /admin/api-keys: 管理APIキー作成(X-ADMIN-API-KEY必須)PATCH /admin/api-keys/{id}: 管理APIキー更新(X-ADMIN-API-KEY必須)POST /admin/api-keys/{id}/revoke: 管理APIキー失効(X-ADMIN-API-KEY必須)GET /admin/audit/permissions: 改ざん監査ログ一覧(X-ADMIN-API-KEY必須)
改ざん監査ログ
metadata.permissions を送ったリクエストは 400 で拒否し、監査ログに記録される。
filter 内の metadata.permissions.* も同様に 400 で拒否し、監査ログに記録される。
取得例:
curl -s "http://localhost:8080/admin/audit/permissions?limit=100" \
-H "X-ADMIN-API-KEY: your-admin-key"
期待レスポンス例:
{
"items": [
{
"id": "uuid",
"api_key_id": "key_id",
"permissions_users": ["user_123"],
"requested_at": "2026-02-08T11:00:00Z",
"endpoint": "POST /kb/search",
"remote_addr": "127.0.0.1:12345",
"user_agent": "curl/8.0.1",
"provided_permissions": {"users":["tampered_user"]},
"provided_metadata": {"permissions":{"users":["tampered_user"]},"source":"manual"}
}
]
}
フィルタ:
since: RFC3339(requested_atの下限)until: RFC3339(requested_atの上限)api_key_id: APIキーIDで絞り込みlimit: 取得件数(既定 100、最大 1000)
改ざん検知の例(search):
python3 - <<'PY' | \
curl -s -X POST http://localhost:8080/kb/search \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d @-
import json
print(json.dumps({
"query": "hello",
"collection": "default",
"filter": {"exists": {"metadata.permissions.users": True}}
}))
PY
期待レスポンス:
- HTTP 400
code:invalid_requesterror:metadata.permissions filter is not allowed
改ざん検知の例(delete):
python3 - <<'PY' | \
curl -s -X POST http://localhost:8080/kb/delete \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d @-
import json
print(json.dumps({
"filter": {"exists": {"metadata.permissions.users": True}}
}))
PY
期待レスポンス:
- HTTP 400
code:invalid_requesterror:metadata.permissions filter is not allowed
MCP メモ
dev_log_upsertではcontent_hash未指定時に MCP 側で自動計算されるvisibility未指定時の既定値はprivatecontent_hashの計算式:title + "\n" + bodyを trim & LF 正規化して SHA-256 を計算し、sha256:<hex>で保存dev_log_upsertはts未指定時に現在時刻を自動補完するdev_log_upsertはauthor未指定時にPGVECTER_DEFAULT_AUTHORまたはUSERを使用するdev_log_searchはmetadata.visibility=privateをデフォルトで付与するdev_log_upsertはmetadata.idにもidを保存する(IDでの検索用)trade_event_upsertはmetadata.idにもidを保存する(IDでの検索用)doc_upsertはmetadata.idにもidを保存する(IDでの検索用)security_upsertはmetadata.idにもidを保存する(IDでの検索用)*_deleteはdry_run: trueで件数のみ確認できる*_deleteで期間指定する場合はfilterが必須diary_upsertはdev_diary/chitchat_diaryのみ許可diary_upsertはsource/author/topic/ts/tags/visibility/content_hashを自動補完diary_upsertのcontent_hashは本文 (content) を trim & LF 正規化して SHA-256 を計算し、sha256:<hex>で保存*_search_by_idはmetadata.idをeqで検索するショートカット
MCP 運用ルール(開発日記)
Cursor などの指示ファイルに以下の運用ルールを設定しておくと、会話だけで日記が回せる。
ルール概要
- ツール:
diary_upsert - collection:
dev_diary - content: 1〜4文で要約(実作業が分かる具体性)
- tags: 2〜5個(候補から選ぶ)
- topic: 1語(候補から選ぶ、なければ
general) - source:
codex - author/ts: 未指定なら自動補完
タグ候補
pgvecter, mcp, api, deploy, ops, security, db, migration, embedding, search, bugfix, refactor, docs, test
topic 候補
api, mcp, deploy, ops, security, db, docs, test, general
例: /kb/search
curl -s -X POST http://localhost:8080/kb/search \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d '{"query":"hello world","top_k":5}'
例: /kb/delete (id 指定)
curl -s -X POST http://localhost:8080/kb/delete \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d '{"id":"00000000-0000-0000-0000-000000000001"}'
例: /kb/delete (metadata 条件 + 期間)
curl -s -X POST http://localhost:8080/kb/delete \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d '{
"collection":"dev_log",
"filter": {"and":[
{"contains": {"metadata.tags": "error"}},
{"lt": {"metadata.ts": "2025-12-01T00:00:00+09:00"}}
]}
}'
例: /kb/delete (dry_run で件数のみ確認)
curl -s -X POST http://localhost:8080/kb/delete \
-H "X-API-KEY: pgv_..." \
-H "Content-Type: application/json" \
-d '{
"collection":"dev_log",
"filter": {"contains": {"metadata.tags": "error"}},
"dry_run": true
}'
削除リクエストは kb_delete_audit に監査ログとして記録されます。
例: MCP *_search_by_id
{"id":"00000000-0000-0000-0000-000000000001"}
{"collection":"dev_diary","id":"00000000-0000-0000-0000-000000000001"}