Skip to main content

embedding

Dual-backend embedding service for semantic search. Text-only (MiniLM, 384d) and multimodal (Qwen3-VL-Embedding-2B via vLLM, 2048d). Persistent Docker containers with automatic routing by modality.

소스 정보

저장소
grahama1970/agent-stack-public
최근 소스 활동
2026년 9월 24일 15:51
감지된 SKILL.md 언어
영어
스타
0
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
18 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
embedding
description
Dual-backend embedding service for semantic search. Text-only (MiniLM, 384d) and multimodal (Qwen3-VL-Embedding-2B via vLLM, 2048d). Persistent Docker containers with automatic routing by modality.
internal
true
allowed-tools
Bash, WebFetch
triggers
["embed this","embed text","start embedding service","get embeddings","generate vectors","semantic search vectors","multimodal embedding","embed image"]
metadata
{"short-description":"Dual-backend embedding service (text + multimodal)"}
provides
["embedding"]
composes
["memory","edge-verifier","scillm","task-monitor","agentic-evals"]
taxonomy
["search","similarity"]
disciplines
["memory-knowledge","model-ops"]
> STOP. READ THIS ENTIRE SKILL.MD BEFORE CALLING ANY ENDPOINT. # Embedding Skill Dual-backend embedding service for semantic search across any database. ## Architecture ``` ┌──────────────────────────────────────────────────────────────┐ │ embed.py (routing layer) │ │ Routes by modality: text → :8602, multimodal → :8603 │ └────────────┬──────────────────────────────┬──────────────────┘ │ │ ┌─────────▼──────────┐ ┌──────────▼───────────┐ │ memory-embedding │ │ embry-embedding-mm │ │ MiniLM-L6-v2 │ │ Qwen3-VL-Embed-2B │ │ 384 dims, CPU/GPU │ │ 2048 dims, GPU │ │ sentence-transformers│ │ vLLM │ │ Port 8602 │ │ Port 8603 │ └─────────────────────┘ └──────────────────────┘ │ │ ┌─────────▼──────────────────────────────▼──────────┐ │ ArangoDB: doc.embedding (384d) ← existing │ │ doc.embedding_visual (2048d) ← new │ └───────────────────────────────────────────────────┘ ``` ## Quick Start ```bash # Start both backends docker compose -f .pi/skills/embedding/docker-compose.yml up -d # Start only multimodal backend docker compose -f .pi/skills/embedding/docker-compose.yml up -d memory-embedding-mm # Text embedding (MiniLM, 384d — existing behavior) ./run.sh embed --text "your query here" # Multimodal embedding (Qwen3-VL, 2048d) ./run.sh embed --text "your query here" --multimodal # Check both backends ./run.sh info ``` ## Commands | Command | Description | | -------------------------------------- | ----------------------------------------------- | | `./run.sh serve` | Start FastAPI server (text backend, inside container) | | `./run.sh embed --text "..." ` | Embed via text backend (384d) | | `./run.sh embed --text "..." -m` | Embed via multimodal backend (2048d) | | `./run.sh embed-batch --file f.txt` | Batch embed via text backend | | `./run.sh embed-batch --file f.txt -m` | Batch embed via multimodal backend | | `./run.sh info` | Show status of both backends | ## Backfill: embedding_visual Field The backfill script writes `embedding_visual` (2048d) alongside existing `embedding` (384d). **Never overwrites the `embedding` field.** ```bash # Check coverage uv run --project .pi/skills/embedding python .pi/skills/embedding/backfill_multimodal.py status # Dry run uv run --project .pi/skills/embedding python .pi/skills/embedding/backfill_multimodal.py embed --dry-run # Run backfill (all collections, resumable) uv run --project .pi/skills/embedding python .pi/skills/embedding/backfill_multimodal.py embed # Single collection uv run --project .pi/skills/embedding python .pi/skills/embedding/backfill_multimodal.py embed -c sparta_qra # Verify service + dimensions uv run --project .pi/skills/embedding python .pi/skills/embedding/backfill_multimodal.py verify ``` ## Qdrant Semantic Backfill Contract For migrated `$memory` semantic retrieval, Qdrant owns vectors and ArangoDB owns documents, BM25 views, graph edges, and Qdrant pointer metadata. **Do not write vector arrays back into ArangoDB** during Qdrant backfills. Arango updates should contain metadata only: ```json { "_key": "abc123", "qdrant_collection": "memory_chunks_mm_jina_v4_1024", "qdrant_point_id": "uuid-v5-point-id", "embedding_model": "jina-embeddings-v4", "embedding_version": "v4", "text_hash": "sha1-of-source-text", "semantic_sync_state": "synced" } ``` Backfill implementation requirements: 1. Build a local page of Arango docs/texts, usually 512+ docs. 2. Embed with GPU-safe microbatches (`SEMANTIC_SYNC_BATCH_SIZE`), not one document at a time. 3. Accumulate returned vectors locally into a Qdrant point batch. 4. Bulk upsert the point batch to Qdrant. 5. Bulk update Arango metadata only after Qdrant upsert succeeds. 6. If Arango metadata update fails, delete the just-created Qdrant point IDs before retrying. 7. Compute `text_hash` from the canonical full source text used by coverage checks, not from a truncated embedding payload. 8. Use stale-vector resync when the canonical text contract or selected source fields change. Recommended safe settings for `embry-embedding-mm` when `Qwen3-VL` or Jina v4 OOMs on long text: ```bash export SEMANTIC_MODEL_MAX_CHARS=1024 export SEMANTIC_SYNC_BATCH_SIZE=4 export SEMANTIC_EMBED_TIMEOUT_SEC=60 export QDRANT_TIMEOUT_SEC=60 ``` Example memory-owned Qdrant backfill: ```bash cd ${HOME}/workspace/experiments/memory export MEMORY_ARANGO_PORT=8531 export SEMANTIC_VECTOR_BACKEND=qdrant export QDRANT_URL=http://127.0.0.1:6333 export QDRANT_SEMANTIC_COLLECTION=memory_chunks_mm_jina_v4_1024 export QDRANT_SEMANTIC_VECTOR_NAME=text_mm export SEMANTIC_MODEL_MAX_CHARS=1024 export SEMANTIC_SYNC_BATCH_SIZE=4 uv run --all-extras python scripts/migrate_arango_embeddings_to_qdrant.py \ --collection sparta_url_knowledge \ --batch-size 512 \ --embed-batch-size 4 ``` When reconciling an existing Qdrant collection after text-field or hash-contract changes, run a stale resync instead of assuming existing point counts prove coverage: ```bash uv run --all-extras python scripts/migrate_arango_embeddings_to_qdrant.py \ --collection sparta_url_knowledge \ --batch-size 512 \ --embed-batch-size 16 \ --resync-stale ``` Validation: ```bash uv run --all-extras python scripts/validation/source_embedding_coverage.py --no-manifest ``` Common Qdrant backfill mistakes: - **Wrong:** embed one doc, upsert one Qdrant point, update one Arango doc in a tight loop. - **Right:** embed microbatches, accumulate a local point batch, bulk upsert Qdrant, then bulk update Arango metadata. - **Wrong:** store `embedding`, `embedding_visual`, or `embedding_2` in Arango after Qdrant migration. - **Right:** store only Qdrant pointer metadata in Arango and keep vectors in Qdrant. - **Wrong:** treat Qdrant point count as sufficient if Arango `text_hash` is stale. - **Right:** verify `missing_vectors == 0`, `stale_vectors == 0`, and `arango_embedding_docs == 0`. ## Configuration | Variable | Default | Description | | ------------------------------- | --------------------------- | ------------------------------------ | | `EMBEDDING_MODEL` | `all-MiniLM-L6-v2` | Text backend model | | `EMBEDDING_DEVICE` | `auto` | Text backend device | | `EMBEDDING_PORT` | `8602` | Text backend port | | `EMBEDDING_SERVICE_URL` | `http://127.0.0.1:8602` | Text backend URL | | `EMBEDDING_MULTIMODAL_URL` | `http://127.0.0.1:8603` | Multimodal backend URL (vLLM) | | `EMBEDDING_MULTIMODAL_DIMENSIONS` | `2048` | Multimodal vector dimensions | | `EMBEDDING_CONTAINER` | `memory-embedding` | Text backend container name | | `EMBEDDING_CONTAINER_MM` | `embry-embedding-mm` | Multimodal backend container name | ## Backends ### Text (MiniLM, :8602) — Production - Model: `all-MiniLM-L6-v2` (sentence-transformers) - Dimensions: 384 - Field: `embedding` - Container: `embry-embedding` (existing, custom image) - CPU or GPU, ~200MB VRAM ### Multimodal (Qwen3-VL, :8603) — New - Model: `Qwen/Qwen3-VL-Embedding-2B` - Dimensions: 2048 (MRL: supports 64-2048 at inference time) - Field: `embedding_visual` - Container: `embry-embedding-mm` (vLLM image) - GPU required, ~5-8GB VRAM (bf16) - OpenAI-compatible API: `/v1/embeddings` - Supports: text, image, video, mixed modalities ### Finetunes (Ollama, :8604) — Future - Stubbed in docker-compose.yml - For domain-specific GGUF finetunes when available ## API Endpoints ### Text Backend (:8602) ``` POST /embed {"text": "query"} → {"embedding": [...], "dimensions": 384} POST /embed/batch {"texts": [...]} → {"vectors": [[...]], "count": N} GET /info → model, device, dimensions, status GET /health → {"status": "ok"} ``` ### Multimodal Backend (:8603, vLLM) Text (single pooled vector): ``` POST /v1/embeddings {"model": "Qwen/Qwen3-VL-Embedding-2B", "input": ["query"]} → {"data": [{"embedding": [...], "index": 0}]} ``` Image (per-token vectors, client mean-pools): ``` POST /pooling {"model": "...", "messages": [{"role": "user", "content": [ {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}} ]}]} → {"data": [{"data": [[...], [...], ...]}]} # mean-pool client-side ``` ``` GET /health → health status ``` ## ArangoDB Embedding Contract ### Document Structure Every embeddable document has up to two vector fields on the **same document**: ```json { "_key": "abc123", "text": "...", "embedding": [0.01, -0.03, ...], // 384d, MiniLM text "embedding_visual": [0.005, -0.002, ...] // 2048d, Qwen3-VL multimodal } ``` ### Writing Embeddings Use `/upsert` via the daemon socket. **Omit the field entirely** if you don't have the embedding — never set it to `null`. ```python transport = httpx.HTTPTransport(uds="/run/user/1000/embry/memory.sock") client = httpx.Client(transport=transport, base_url="http://localhost") # Text embedding only (384d) client.post("/upsert", json={"collection": "lessons", "documents": [ {"_key": "abc123", "embedding": [0.01, -0.03, ...]} # 384 floats ]}) # Visual embedding only (2048d) client.post("/upsert", json={"collection": "datalake_chunks", "documents": [ {"_key": "abc123", "embedding_visual": [0.005, ...]} # 2048 floats ]}) ``` ### Finding Docs Missing Embeddings ```python # Docs missing visual embedding resp = client.post("/list", json={ "collection": "datalake_chunks", "limit": 100, "filters": {"embedding_visual": None} }) ``` ### Vector Index Status | Collection | Field | Dim | Index | Sparse | |---|---|---|---|---| | `datalake_chunks` | `embedding` | 384 | `datalake_chunks_vec_text` | Yes | | `datalake_chunks` | `embedding_visual` | 2048 | `datalake_chunks_vec_visual` | Yes | | `lessons` | `embedding` | 384 | `lessons_vec_text` | No | | `sparta_qra` | `embedding` | 384 | `sparta_qra_vec_text` | No | ### Rules 1. **Never set embedding fields to `null`** — omit the field entirely. Explicit null blocks vector index operations. 2. **Always `DESC`** in `SORT APPROX_NEAR_COSINE(...)` — without it, ArangoDB error 1554. 3. **Dimension must match exactly** — 384 for `embedding`, 2048 for `embedding_visual`. Wrong dimensions corrupt the index. 4. **Use `/upsert`** not `/learn` for embedding-only updates — `/learn` is for full documents with text content. 5. **Verify with sanity check**: `uv run python -m graph_memory.maintenance.sanity_recall integrity` ## Common Mistakes ```python # WRONG: No timeout on embedding calls
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기