2026-02-09 18:31:30 +09:00
2026-02-07 19:27:53 +09:00
2026-02-07 21:20:16 +09:00
2026-02-07 19:27:53 +09:00

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: llamacpp or openai(省略時は 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 set
  • DATABASE_URL 未設定 → /kb/* / /collections / /admin/collections503 + 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 は既定 800chunk_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_DIMkb_doc_chunks.embedding の次元を照合し、不一致なら起動失敗。

APIキー運用フロー

  1. 管理者が /admin から APIキーを発行
  2. 発行時に permissions.users を設定
  3. 発行後のキーは 一度しか表示されないので安全に保管
  4. 失効が必要になったら管理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 (Lets 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)

  1. リポジトリ配置
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.serviceactive (running)
  • curl -s http://127.0.0.1:8080/healthok
  • curl -s http://127.0.0.1:8092/healthok
  • DB接続が正常 (SELECT 1; が通る)
  • 管理UIでAPIキー発行/失効ができる
  • /kb/upsert が成功する
  • /kb/search で結果が返る

ローカルテスト結果(例)

  • /health OK
  • /kb/upsert 正常系 OK
  • /kb/search 正常系 OK
  • metadata.permissions は 400 で拒否
  • filtermetadata.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点を明確にする。

  1. X-API-KEY を必ず送る
  2. permissions.users はサーバ側で強制注入される前提(metadata.permissions は受け付けない)

エンドポイント一覧(他サービス向け)

  • GET /health: ヘルスチェック(認証不要)
  • POST /db/query: SELECT-only の読み取りクエリ(X-API-KEY 必須)
  • POST /kb/upsert: 1ドキュメントを upsertX-API-KEY 必須、id は UUID
  • POST /kb/search: ベクトル検索(X-API-KEY 必須)
  • POST /kb/delete: ドキュメント削除(X-API-KEY 必須、id または filterdoc_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: RFC3339requested_at の下限)
  • until: RFC3339requested_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_request
  • error: 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_request
  • error: metadata.permissions filter is not allowed

MCP メモ

  • dev_log_upsert では content_hash 未指定時に MCP 側で自動計算される
  • visibility 未指定時の既定値は private
  • content_hash の計算式: title + "\n" + body を trim & LF 正規化して SHA-256 を計算し、sha256:<hex> で保存
  • dev_log_upsertts 未指定時に現在時刻を自動補完する
  • dev_log_upsertauthor 未指定時に PGVECTER_DEFAULT_AUTHOR または USER を使用する
  • dev_log_searchmetadata.visibility=private をデフォルトで付与する
  • dev_log_upsertmetadata.id にも id を保存するIDでの検索用
  • trade_event_upsertmetadata.id にも id を保存するIDでの検索用
  • doc_upsertmetadata.id にも id を保存するIDでの検索用
  • security_upsertmetadata.id にも id を保存するIDでの検索用
  • *_deletedry_run: true で件数のみ確認できる
  • *_delete で期間指定する場合は filter が必須
  • diary_upsertdev_diary / chitchat_diary のみ許可
  • diary_upsertsource / author / topic / ts / tags / visibility / content_hash を自動補完
  • diary_upsertcontent_hash は本文 (content) を trim & LF 正規化して SHA-256 を計算し、sha256:<hex> で保存
  • *_search_by_idmetadata.ideq で検索するショートカット

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"}
Description
mcp サーバーのブリッチ
Readme 7.5 MiB
Languages
Go 90.7%
Shell 9.3%