Apply Q1–Q3 decisions: GraphDB/SPARQL, reuse Qdrant, self-host embeddings via TEI
- Q1 → Ontotext GraphDB (SPARQL). docs/08 fully rewritten with RDF schema, example SPARQL queries (transitive deps via property paths, time-windowed topic counts, cross-pillar joins via nexa:vectorId). - Q2 → reuse qdrant_scientific with nexa_* collection prefix; docs/09 step 2 now points there explicitly. - Q3 → no OpenAI embeddings. Self-host on the docker host. Use TEI (HuggingFace text-embeddings-inference) — single Rust binary, ~500 MB image, OpenAI-compatible — instead of Ollama, since we only need embeddings. - docs/09 Phase-3.4 add-on simplified to a single Ontotext compose snippet (Neo4j option dropped) plus repo creation curl. - docs/11 Q3 marked resolved; new Q15 picks the model (bge-m3 vs nomic-embed) and adds the open question of whether SAIA already proxies an embedding model that would let us skip TEI entirely. - docs/03 + CLAUDE.md updated with the new decisions so future runs don't re-litigate.
This commit is contained in:
+67
-25
@@ -20,19 +20,20 @@ Surveyed from Homepage / Dozzle / Proxmox / Zoraxy:
|
||||
| Backrest | LXC 103 | (internal) |
|
||||
| AdGuard DNS | LXC 102 | (internal) |
|
||||
| Zoraxy reverse proxy | LXC 108 → `192.168.1.4:8000` | TLS for *.nuclide.systems |
|
||||
| `qdrant_scientific` (existing) | docker LXC 104 | reuse — see [11](./11-open-questions.md) |
|
||||
| `qdrant_scientific` (existing) | docker LXC 104 | **reused** — Nexa uses `nexa_*` collections in this instance |
|
||||
|
||||
The deployment task is **not** "spin up the stack" — most of the stack is already up. It is **wire Nexa across these services + add the small bits that are missing**.
|
||||
|
||||
## What's missing for Nexa
|
||||
|
||||
1. **Qdrant collection** for Nexa (`nexa_knowledge`) — either on the existing `qdrant_scientific` or a dedicated container.
|
||||
2. **n8n workflows** (`./nexa-core/n8n-workflows/`) imported into the running n8n.
|
||||
3. **Nextcloud lists & calendars** for Work / Personal / Shopping / Wishes (auto-discovered via `#nexa:config`).
|
||||
4. **Memos webhook → n8n** wired through the Memos config.
|
||||
5. **LiteLLM virtual key** for the `nexa` user with embedding + chat models.
|
||||
6. **A Zoraxy host entry** is *not* needed — Memos / n8n / LiteLLM are already proxied.
|
||||
7. **(Phase 3.4)** decision on graph DB (Neo4j vs. Ontotext GraphDB) — see [11](./11-open-questions.md).
|
||||
1. **Qdrant collection** for Nexa (`nexa_knowledge`) inside the existing `qdrant_scientific` instance — vector dim follows Q15 (1024 for `bge-m3`, 768 for `nomic-embed-text`).
|
||||
2. **TEI** (HF text-embeddings-inference) on the docker host for self-hosted embeddings (LiteLLM key is **not** authorised for OpenAI embeddings — see [11/Q3+Q15](./11-open-questions.md)). Lighter than Ollama: single Rust binary, ~500 MB image, no LLM runtime.
|
||||
3. **n8n workflows** (`./nexa-core/n8n-workflows/`) imported into the running n8n.
|
||||
4. **Nextcloud lists & calendars** for Work / Personal / Shopping / Wishes (auto-discovered via `#nexa:config`).
|
||||
5. **Memos webhook → n8n** wired through the Memos config.
|
||||
6. **LiteLLM virtual key** for the `nexa` user with chat-only access (no embeddings — handled by Ollama).
|
||||
7. **A Zoraxy host entry** is *not* needed — Memos / n8n / LiteLLM are already proxied.
|
||||
8. **(Phase 3.4) Ontotext GraphDB** for the SPARQL pillar — see add-on at the bottom of this doc.
|
||||
|
||||
---
|
||||
|
||||
@@ -60,14 +61,43 @@ curl -X PUT "$QDRANT_HOST/collections/nexa_knowledge" \
|
||||
-d @nexa-core/config/qdrant_schema.json
|
||||
```
|
||||
|
||||
> If reusing `qdrant_scientific`, this just adds another collection; no new container needed.
|
||||
> Targets the existing `qdrant_scientific` instance — just an extra collection, no new container.
|
||||
> The schema file's `vectors.size` must match the embedding model picked in [Q15](./11-open-questions.md): **1024** for `bge-m3`, **768** for `nomic-embed-text-v1.5`.
|
||||
|
||||
## Step 3 — LiteLLM virtual key
|
||||
## Step 3 — Self-hosted embeddings (TEI)
|
||||
|
||||
Use HuggingFace **text-embeddings-inference** — single Rust binary, ~500 MB image, OpenAI-compatible API, loads exactly one model. Lighter than Ollama because there's no LLM runtime, no GGUF loader, no model registry.
|
||||
|
||||
```bash
|
||||
# on the docker host (LXC 104)
|
||||
docker run -d --name nexa-embed \
|
||||
--restart unless-stopped \
|
||||
-p 127.0.0.1:8080:80 \
|
||||
-v tei-data:/data \
|
||||
ghcr.io/huggingface/text-embeddings-inference:cpu-1.5 \
|
||||
--model-id BAAI/bge-m3
|
||||
```
|
||||
|
||||
Memory budget: ~1.1 GB resident (bge-m3 is ~1 GB + ~100 MB overhead). First start downloads the model into the named volume; subsequent restarts are instant.
|
||||
|
||||
Register it inside LiteLLM (admin UI → Models) with the OpenAI-compatible adapter:
|
||||
|
||||
- model name: `nexa-embed`
|
||||
- provider: `openai`
|
||||
- model: `bge-m3`
|
||||
- api_base: `http://nexa-embed:80/v1`
|
||||
- api_key: any non-empty string (TEI ignores it)
|
||||
|
||||
Now n8n only ever talks to LiteLLM and the model is swappable without touching workflows.
|
||||
|
||||
## Step 4 — LiteLLM virtual key
|
||||
|
||||
In the LiteLLM admin UI (`ai.nuclide.systems`):
|
||||
|
||||
1. Create user `nexa`.
|
||||
2. Issue a virtual key with access to one chat model (e.g. `gpt-4o-mini` / a local Sonnet equivalent) and one embedding model (`text-embedding-3-small`).
|
||||
2. Issue a virtual key with access to:
|
||||
- one chat model (already-available model from your SAIA gateway).
|
||||
- the `nexa-embed` model from Step 3.
|
||||
3. Paste the key into `SAIA_API_KEY` in `.env`.
|
||||
|
||||
## Step 4 — n8n workflows
|
||||
@@ -158,31 +188,43 @@ For deeper detail: [10 — Operations](./10-operations.md).
|
||||
|
||||
---
|
||||
|
||||
## Phase add-on: graph DB (Phase 3.4)
|
||||
## Phase add-on: Ontotext GraphDB (Phase 3.4)
|
||||
|
||||
Defer until 3.1–3.3 ship. When needed, two minimal options — pick one:
|
||||
|
||||
### Option A — Neo4j (lighter, popular for graph-RAG)
|
||||
Defer until 3.1–3.3 ship.
|
||||
|
||||
```yaml
|
||||
# nexa-core/docker-compose.graph.yml
|
||||
services:
|
||||
neo4j:
|
||||
image: neo4j:5-community
|
||||
container_name: nexa-neo4j
|
||||
ports: ["7687:7687", "7474:7474"]
|
||||
graphdb:
|
||||
image: ontotext/graphdb:10.7.0
|
||||
container_name: nexa-graphdb
|
||||
ports: ["127.0.0.1:7200:7200"]
|
||||
environment:
|
||||
NEO4J_AUTH: "neo4j/${NEO4J_PASSWORD}"
|
||||
NEO4J_server_memory_heap_max__size: 2G
|
||||
volumes: ["./data/neo4j:/data"]
|
||||
GDB_JAVA_OPTS: "-Xmx4g -Xms1g"
|
||||
volumes:
|
||||
- ./data/graphdb:/opt/graphdb/home
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
### Option B — Ontotext GraphDB (SPARQL, RDF native)
|
||||
After first start, create the repository (one-time):
|
||||
|
||||
Keep the snippet from the original `DEPLOYMENT.md` Schritt 1.3 for `graphdb`. Heavier (~4 GB heap), only justified if you want SHACL / OWL reasoning.
|
||||
```bash
|
||||
curl -X POST http://localhost:7200/rest/repositories \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"id": "nexa_knowledge",
|
||||
"title": "Nexa Knowledge Graph",
|
||||
"type": "graphdb",
|
||||
"params": {
|
||||
"ruleset": {"value": "rdfsplus-optimized"},
|
||||
"baseURL": {"value": "https://nuclide.systems/nexa/"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
Add a Zoraxy host entry `graph.nuclide.systems` → `192.168.1.40:7474` (Neo4j) or `:7200` (GraphDB) **only if** browser access is desired; otherwise n8n talks to it on the docker network.
|
||||
Optional Zoraxy entry `graph.nuclide.systems` → `192.168.1.40:7200` if you want the SPARQL Workbench in a browser; otherwise n8n talks to it on the docker network at `http://nexa-graphdb:7200`.
|
||||
|
||||
For schema and example queries: [08-graphrag-architecture](./08-graphrag-architecture.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user