138 lines
4.6 KiB
Markdown
138 lines
4.6 KiB
Markdown
---
|
|
name: save-q-memory
|
|
description: "Manual save to Qdrant memories collection with enforced 10-type schema. Load this skill EVERY time the user says 'save to memory' or 'save to qdrant' — it is the ONLY path for manual Qdrant writes."
|
|
version: 1.0.0
|
|
author: Hermes Agent
|
|
license: MIT
|
|
metadata:
|
|
hermes:
|
|
tags: [qdrant, memories, save, schema, manual]
|
|
related_skills: [qdrant-collection-management]
|
|
---
|
|
|
|
# Save Q Memory — Manual Write Enforcer
|
|
|
|
This skill is the ONLY path for manual Qdrant writes. Load it every time the user says "save to memory" or "save to qdrant." No write bypasses this skill.
|
|
|
|
## Trigger
|
|
|
|
User says: "save to memory", "save to qdrant", "save this to memories", or any variant directing a manual Qdrant write.
|
|
|
|
## Prerequisites
|
|
|
|
- Qdrant at `http://10.0.0.22:6333`
|
|
- Ollama at `http://localhost:11434` with `snowflake-arctic-embed2:latest`
|
|
- Collection: `memories` (1024-dim, Cosine)
|
|
|
|
## Mandatory Save Procedure
|
|
|
|
### Step 1: Classify the Content
|
|
|
|
Before embedding, classify EVERY item:
|
|
|
|
**`type`** — pick ONE of 10:
|
|
- `fact` — timeless/slow-changing world truth
|
|
- `infrastructure` — hosts, IPs, services, configs, topology
|
|
- `entity` — person, org, project, tool identity
|
|
- `preference` — user's stable likes/dislikes, style, defaults
|
|
- `procedure` — how-to: steps, commands, runbooks, fixes
|
|
- `decision` — chosen approach + rationale (the why)
|
|
- `observation` — consolidated pattern from many episodes (2nd-order)
|
|
- `reference` — external KB/docs, URLs, citations
|
|
- `event` — something that happened at a time; state change
|
|
- `experience` — raw conversation turn / interaction log
|
|
|
|
**`mem_class`** — pick ONE:
|
|
- `semantic` — for fact, infrastructure, entity, preference, decision, observation, reference
|
|
- `procedural` — for procedure
|
|
- `episodic` — for event, experience
|
|
|
|
**`entities`** — list of people, servers, IPs, tools, projects, models mentioned. Empty array `[]` if none.
|
|
|
|
**`tags`** — 2-3 lowercase category tags for filtering. Examples: `["infrastructure", "dgx"]`, `["preference", "output_style"]`.
|
|
|
|
**`importance`** — 0.0-1.0:
|
|
- 0.9+: critical (court docs, credentials, core infrastructure)
|
|
- 0.7-0.8: important (decisions, preferences, procedures)
|
|
- 0.5-0.6: useful (facts, entities, references)
|
|
- 0.3-0.4: contextual (events, observations)
|
|
- 0.1-0.2: noise (raw experiences, heartbeats)
|
|
|
|
**`confidence`**:
|
|
- 1.0 — user explicitly stated this
|
|
- 0.7 — agent inferred this from context
|
|
- 0.5 — single episode, low certainty
|
|
|
|
**`ttl_hint`**:
|
|
- `"slow"` — semantic or procedural types (long-term)
|
|
- `"medium"` — references, infrastructure
|
|
- `"fast"` — episodic types (event, experience)
|
|
|
|
### Step 2: Embed via Ollama
|
|
|
|
```bash
|
|
curl -s http://localhost:11434/api/embeddings \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"model": "snowflake-arctic-embed2:latest", "prompt": "<text>"}'
|
|
```
|
|
|
|
### Step 3: Build the Point
|
|
|
|
```python
|
|
import json, hashlib, subprocess
|
|
from datetime import datetime, timezone
|
|
|
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
point_id = int(hashlib.sha256(text.encode()).hexdigest()[:16], 16) % (2**63)
|
|
|
|
point = {
|
|
"id": point_id,
|
|
"vector": vector, # from Step 2
|
|
"payload": {
|
|
"text": text,
|
|
"type": "<classified type>",
|
|
"mem_class": "<semantic|episodic|procedural>",
|
|
"entities": ["entity1", "entity2"],
|
|
"tags": ["tag1", "tag2"],
|
|
"importance": 0.7,
|
|
"quality_score": None, # ALWAYS null — cron computes this
|
|
"confidence": 0.7,
|
|
"created_at": now,
|
|
"updated_at": now,
|
|
"source": "agent", # "agent" for manual saves
|
|
"ttl_hint": "slow"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 4: Upsert to Qdrant
|
|
|
|
```bash
|
|
curl -s -X PUT http://10.0.0.22:6333/collections/memories/points \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"points": [<point>]}'
|
|
```
|
|
|
|
Use `subprocess.run(["curl", ...])` — NOT Python `requests` (sandbox can't reach 10.0.0.22 via requests).
|
|
|
|
### Step 5: Report
|
|
|
|
Confirm to user: what was saved, type, mem_class, point ID.
|
|
|
|
## Multi-Item Saves
|
|
|
|
If the user asks to save multiple items, classify and embed each one separately. Batch upsert all points in a single curl call (up to 100 points).
|
|
|
|
## What NOT to Do
|
|
|
|
- Do NOT save without classifying type + mem_class
|
|
- Do NOT set quality_score to anything other than null
|
|
- Do NOT use Python `requests` library — use `subprocess.run(["curl", ...])`
|
|
- Do NOT use string point IDs — must be unsigned integers
|
|
- Do NOT save to any collection other than `memories` unless explicitly directed
|
|
|
|
## Reference
|
|
|
|
Full schema documentation: `/home/n8n/workspace/general/qdrant.md`
|
|
Collection management (dedup, migration, registry): `qdrant-collection-management` skill
|