Files
n1kt4r eca8a11e23 feat(pegas-memory): Tier-1 self-improvement tools (commit + staleness)
- memory_commit: closes the 'commit hole' — writes verified answers from
  closed gaps back into atoms (mandatory dedup); leaves gap open if no
  verified answer exists. Reads verified status from PostgreSQL (source of
  truth), not the Qdrant projection.
- memory_stale: staleness guard with two signals — content-staleness
  (re-fetch + LLM verdict, needs proxy for foreign sites) and age-staleness
  (proxy-free fallback by pm_document.fetched_at, max_age_days).
- db: populate pm_document.fetched_at on upsert (freshness signal).
- docs: README/AGENTS/ARCHITECTURE/EXAMPLES updated (18 tools).
- self_improve.py: new module (227 lines).
2026-10-15 22:06:30 +05:00

297 lines
8.8 KiB
Markdown

# Pegas Memory — Usage Examples
Concrete examples of how the agent should interact with the knowledge base.
All calls go through the MCP tools `mcp__pegas-memory__*`.
> **On the values.** Tool names, argument names, response **shapes** and behaviour are exact —
> verified against the running service on 2026-10-15. Concrete numbers (atom/doc/gap counts,
> ids, similarity scores, `doc_id`) are a **snapshot** taken at that moment and change as the
> base grows; every example that captures real output is marked *actual*, and every example that
> mutates data shows an *illustrative* id (marked accordingly). The **shape is stable, the values
> are not** — always call the tool instead of quoting a remembered number.
---
## Example 1 — "What do you know?" (the most common mistake)
**User:** "What's in your knowledge base?"
### ✗ Wrong (a real observed failure)
```
memory_search("knowledge base") → [] (semantic search finds nothing useful)
read("/home/neo/workspace") → ERROR (it is a directory, not a file)
web_search("memory") → ❌ searching the INTERNET for your OWN base
web_search("knowledge base") → ❌
web_search("atoms") → ❌
web_search("logical_rule") → ❌
```
That is 2 wrong calls + 4 wasted web searches.
### ✓ Correct
```
memory_stats()
```
**Result** (actual, 2026-10-15):
```json
{"atoms": 89, "documents": 6, "open_gaps": 3}
```
**Answer:** "My knowledge base holds 89 atoms across 6 documents; 3 open research gaps."
> Counts grow as the base grows — always call the tool, never quote a remembered number.
---
## Example 2 — "What do you know about X?"
**User:** "What do you know about Memgraph?"
### ✓ Correct
```
memory_search("Memgraph", top_k = 5)
```
**Result** (actual, abridged):
```json
{"query": "Memgraph",
"hits": [
{"id": 171, "score": 0.854, "type": "text",
"claim": "Memgraph Lab provides graph visualization and execution of ad hoc queries for data understanding.",
"status": "candidate", "confidence": 0.9, "doc_id": 13},
{"id": 168, "score": 0.843, "type": "fact",
"claim": "Memgraph documentation is available at memgraph.com/docs.",
"status": "candidate", "confidence": 0.95, "doc_id": 13},
{"id": 128, "score": 0.835, "type": "definition",
"claim": "Memgraph serves as the graph engine for GraphRAG pipelines, AI memory systems...",
"status": "candidate", "confidence": 0.9, "doc_id": 10}
]}
```
**Answer:** "The base holds several Memgraph atoms (Lab visualization, docs location, role as a
GraphRAG engine). All are still `candidate` — cross-confirm before treating them as verified."
> IDs, scores and `doc_id` change with every ingest. The *shape* is stable; the values are not.
If nothing is found → say "not in the knowledge base" and offer to research it.
---
## Example 3 — Filling a knowledge gap (full loop)
**User:** "Research how HNSW indexing works in vector databases."
```
1. memory_search("HNSW") → [] # not known yet
2. memory_add_gap("How does HNSW work?",
reason = "low_coverage", priority = 0.8)
3. web_search(["How does HNSW indexing work?"]) # built-in search
web_fetch(chosen_url) → full page text
memory_ingest(url) → distills atoms, saves them
4. memory_verify() → cross-confirm the new atoms
5. memory_search("HNSW") → now returns verified hits
```
**Note:** step 1 (CHECK) is mandatory — it prevents re-researching what is already known.
---
## Example 4 — Storing one fact
**User:** "Remember that Memgraph keeps the whole graph in RAM."
### ✓ Correct — one atomic claim + type
```
memory_store(
type = "fact",
claim = "Memgraph keeps the entire graph in RAM",
content = "Memgraph stores the graph in memory, which is why it is faster than a disk-based graph DB on analytical workloads.",
confidence = 0.9
)
```
**Result** (illustrative — the id is assigned by the database):
```json
{"atom_id": 172, "created": true}
```
If the same claim already exists, the deduplicator returns the existing id instead:
```json
{"atom_id": 168, "created": false, "duplicate_of": 168}
```
### ✗ Wrong
```
memory_store(type = "text", content = "<entire article pasted here>")
```
Raw articles are not atoms. Use `memory_ingest` to distill a document, or write one claim.
---
## Example 5 — Ingesting a document
**User:** "Add this article to the base: https://example.com/graph-dbs"
```
memory_ingest(url = "https://example.com/graph-dbs")
```
**Result** (illustrative shape — values depend on the source):
```json
{"doc_id": 14, "chunks": 5, "atoms_created": 14,
"atoms_skipped": 3, "duplicates": 2}
```
- `duplicates` — near-duplicates filtered by the semantic deduplicator (cosine ≥ 0.90).
- `atoms_skipped` — low-confidence atoms dropped by the distillation threshold.
- If the site is geo-blocked and `PROXY_URL` is set, the fetch goes through the proxy;
otherwise a research-level snippet fallback is used.
---
## Example 6 — Verifying a fact
**User:** "Is it true that Memgraph is faster than Neo4j?"
```
memory_verify(atom_id = 113, self_critique = true)
```
**Result** (illustrative — depends on the atom and the current base):
```json
{"atom_id": 8, "verdict": "duplicate", "corroborations": 1,
"new_status": "stale", "new_confidence": 1.0,
"reason": "Repeats an already known fact about Qdrant from another source."}
```
Verdicts: `new` · `duplicate` (→ stale) · `corroborates` (→ verified) · `contradicts` (→ contested).
---
## Example 7 — Code analysis
**User:** "What functions does this module call, and who depends on it?"
```
memory_code_graph(language = "python", source = "<file contents>",
repo = "pegas-memory", path = "db.py")
```
**Result:**
```json
{"symbols_stored": 7, "dependencies_stored": 27}
```
Then:
```
memory_search_code("upsert")
```
**Result** (actual, abridged):
```json
{"query": "upsert",
"symbols": [
{"id": 11, "repo": "pegas-memory",
"path": "/home/neo/workspace/pegas-memory/pegas_memory/memgraph_store.py",
"symbol_name": "upsert_link", "kind": "function", "line_start": 26},
{"id": 83, "repo": "pegas-memory", "path": "memgraph_store.py",
"symbol_name": "upsert_link", "kind": "function"}
]}
```
Inference (edges `imports → depends_on`, transitive):
```
memory_infer(if = "imports", then = "depends_on")
memory_infer(if = ["depends_on", "depends_on"], then = "depends_on")
```
---
## Example 8 — Inspecting the graph
```
memory_query_graph(kind = "depends_on", top_k = 10)
```
**Result** (actual, abridged):
```json
{"kind": "depends_on",
"edges": [
{"src": "code_symbols", "dst": "hashlib", "kind": "depends_on", "inferred": true},
{"src": "code_symbols", "dst": "tree_sitter","kind": "depends_on", "inferred": true},
{"src": "db", "dst": "psycopg", "kind": "depends_on", "inferred": true}
],
"nodes": 89, "code_symbols": 162}
```
> `"inferred": true` marks an edge produced by logical inference, not extracted from source.
> Empty-ish targets like `"."` come from relative imports (`from . import x`) — harmless.
---
## Example 9 — Self-improvement (commit + staleness)
Commit a verified answer from a closed gap into atoms (closes the «commit hole»):
```
memory_commit(gap_ids = [3], dedup = true)
```
**Result** (actual, abridged):
```json
{"committed": 1, "resolved": 1, "skipped_empty": 0, "skipped_duplicate": 0,
"atoms": [218]}
```
Mark stale atoms — content-staleness (re-fetch + LLM, needs proxy for foreign sites)
or age-staleness (proxy-free, by `fetched_at`):
```
memory_stale(atom_id = 126)
memory_stale(all = true, limit = 20, sample = true,
min_confidence = 0.5, age_only = true, max_age_days = 90)
```
**Result** (abridged):
```json
{"checked": 0, "stale": 1, "supported": 0, "skipped_no_source": 0,
"atom_ids": [126], "reasons": {"126": "content_age 10d"}}
```
> `age_only = true` skips the network re-fetch and flags atoms whose source
> `fetched_at` is older than `max_age_days` (default 90). For foreign sites
> (memgraph.com, cloudflare/geo-blocked) set `PROXY_URL` or the re-fetch
> returns `ConnectTimeout` and falls back to age-staleness.
---
## Anti-patterns (do not do this)
| Anti-pattern | Why it is wrong | Do instead |
|---|---|---|
| `web_search` to learn what your base contains | it searches the internet, not memory | `memory_stats` / `memory_search` |
| `read` on a directory | `read` expects a file | `bash ls` or the fs-search tool |
| `memory_store` of a whole article | violates the atomic-atom rule | `memory_ingest` |
| Skipping `memory_search` before research | re-researches known facts | check first |
| Reporting unverified atoms as facts | they are `candidate` | run `memory_verify` |
| Estimating counts | `memory_stats` gives exact numbers | call it |