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.

Quellinformationen

Repository
grahama1970/agent-stack-public
Letzte Quellaktivität
24. September 2026 um 15:51
Erkannte Sprache von SKILL.md
Englisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
18 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen