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.

Informações da origem

Repositório
grahama1970/agent-stack-public
Última atividade na origem
24 de setembro de 2026 às 15:51
Idioma detectado do SKILL.md
inglês
Estrelas
0
Forks
0

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
18 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub