[OMEGA-311] feat: provenance-aware memory (remember-claim / query-claims) - #272
[OMEGA-311] feat: provenance-aware memory (remember-claim / query-claims)#272amiroussama wants to merge 1 commit into
Conversation
Adds a provenance schema for stored memories so claims carry structured attribution — claim text, source, source_type, confidence, created_at, atoms_json, and supersession links — instead of ad-hoc metadata. - src/memory_schema.py: schema builders/validators + a provenance-scoped store over the chroma collection (remember_claim_llm / query_claims_text), confidence defaults, and supersession filtering. - src/memory.metta: `remember-claim` / `query-claims` MeTTa tools. - src/rag.py: knowledge-prior chunks now carry the full provenance schema via build_metadata (keeps the legacy breadcrumb/type/time keys for back-compat). - Autotests/test_memory_schema.py (pure-Python; chroma-backed cases self-skip without chromadb) + run_mandatory. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
paul-v-snet
left a comment
There was a problem hiding this comment.
This PR has several critical issues that need to be addressed before further review can be performed.
In addition, I have concerns about its compatibility with #284, which introduces the Context Frames mechanism and also changes the way ChromaDB is handled.
@alyona-snet, please consider whether it would be better to close this PR.
| mock/test_transition_metta_to_remember_mock.py | ||
| mock/test_transition_pin_to_remember_mock.py | ||
| mock_websocket/test_wschat_unit.py | ||
| test_memory_schema.py |
There was a problem hiding this comment.
Unit-tests location: Autotests/unit/, so please move this.
| (= (remember-claim $claim) | ||
| (progn (py-call (memory_schema.remember_claim_llm $claim (embed $claim))) | ||
| REMEMBER-CLAIM-SUCCESS)) | ||
|
|
||
| (= (query-claims $str) | ||
| (py-call (memory_schema.query_claims_text (embed $str) (maxRecallItems)))) |
There was a problem hiding this comment.
Both expressions were not added to getStaticSkills and LLM_COMMANDS, so the agent will never see and use them.
| # NOTE: the action-protocol round-trip test for the remember-claim / query-claims tools lives | ||
| # with the action_protocol module (not part of this memory-provenance change). |
There was a problem hiding this comment.
You mentioned:
The
memory.mettatool wiring runs in the MeTTa/Docker runtime and is covered bybuild / commonCI.
But this is not the case - the repository does not contain tests covering the MeTTa tools added in this PR, and your request does not include adding such tests.
| def remember_claim(claim, embedding, source_type, source=None, confidence=None, | ||
| session_id="", turn_id=None, atoms=None, supersedes=None): | ||
| """Write a structured, provenance-tagged claim to the shared memories collection. | ||
|
|
||
| ``embedding`` is computed MeTTa-side (``(embed $claim)``), mirroring ``remember``. | ||
| Returns the stable claim id. | ||
| """ | ||
| meta = build_metadata( | ||
| claim, source or f"{source_type}:adhoc", source_type, confidence=confidence, | ||
| session_id=session_id, turn_id=turn_id, atoms=atoms, supersedes=supersedes, | ||
| ) | ||
| err = validate_metadata(meta) | ||
| if err: | ||
| raise ValueError(f"invalid claim metadata: {err}") | ||
| cid = claim_id(meta) | ||
| coll = _collection() | ||
| coll.upsert(ids=[cid], embeddings=[embedding], documents=[claim], metadatas=[meta]) | ||
| # If this claim supersedes an earlier record, mark that record superseded so it is | ||
| # excluded from default recall. Best-effort: ignore if the id is absent. | ||
| if supersedes: | ||
| try: | ||
| coll.update(ids=[supersedes], metadatas=[{"superseded": True}]) | ||
| except Exception as exc: # pragma: no cover - defensive | ||
| print(f"[memory_schema] WARNING could not mark superseded id={supersedes}: {exc}", flush=True) | ||
| print(f"[memory_schema] REMEMBER_CLAIM id={cid} source_type={source_type} confidence={meta['confidence']}", flush=True) | ||
| return cid |
There was a problem hiding this comment.
remember-claim accepts only $claim and calls only remember_claim_llm(claim, embedding), which never passes supersedes/session_id/turn_id. As a result, this mechanism does not work, and the agent will never be able to supersede an existing claim.
What use cases did you have in mind for this mechanism?
| def claim_id(meta): | ||
| """Deterministic, stable id so re-remembering an identical claim is idempotent.""" | ||
| digest = hashlib.sha1((meta.get("claim") or "").encode("utf-8")).hexdigest()[:10] | ||
| turn = meta.get("turn_id", "") | ||
| src = re.sub(r"[^A-Za-z0-9_.-]", "_", str(meta.get("source") or "src")) | ||
| return f"claim_{src}_{turn}_{digest}" |
There was a problem hiding this comment.
Same reason as R191-R216:
The agent does not manage sessions or pass their IDs, so in practice two different claims from different sessions will be treated as the same claim and overwrite each other. In this case, idempotency creates a potential data leakage risk.
This mechanism should be designed more carefully
| def query_claims(embedding, n=5, filters=None): | ||
| """Similarity query returning provenance metadata. | ||
|
|
||
| Returns a list of ``{document, metadata, distance}``. By default only | ||
| provenance-bearing, non-superseded records are returned (see :func:`build_where`), | ||
| so legacy memories / hash sentinels and superseded claims are excluded. Pass | ||
| ``filters={"any_source": True}`` / ``{"include_superseded": True}`` to widen. | ||
| """ | ||
| where = build_where(filters) | ||
| kwargs = {"query_embeddings": [embedding], "n_results": int(n)} | ||
| if where is not None: | ||
| kwargs["where"] = where | ||
| res = _collection().query(**kwargs) | ||
| docs = (res.get("documents") or [[]])[0] | ||
| metas = (res.get("metadatas") or [[]])[0] | ||
| dists = (res.get("distances") or [[]])[0] | ||
| out = [] | ||
| for i, doc in enumerate(docs): | ||
| out.append({ | ||
| "document": doc, | ||
| "metadata": metas[i] if i < len(metas) else {}, | ||
| "distance": dists[i] if i < len(dists) else None, | ||
| }) | ||
| return out |
There was a problem hiding this comment.
With maxRecallItems = 20 (default) and MAX_CHUNK_CHARS = 6000 (src/rag.py), the maximum possible size of the response is ~120,000 characters.
At the same time, maxFeedback = 50,000 (loop.metta), so the response will be implicitly truncated, potentially displacing the results of other skills executed during the same cycle.
| "claim": claim, | ||
| "source": source, | ||
| "source_type": source_type, | ||
| "confidence": float(confidence), |
There was a problem hiding this comment.
The petta_lib_chromadb project dependency already implements and uses the confidence metadata key. These changes overwrite its existing value, which may alter the behavior expected by the dependency and lead to unintended side effects.
| print(f"[memory_schema] WARNING could not mark superseded id={supersedes}: {exc}", flush=True) | ||
| print(f"[memory_schema] REMEMBER_CLAIM id={cid} source_type={source_type} confidence={meta['confidence']}", flush=True) |
There was a problem hiding this comment.
The current version of the project uses the logger for logging (src/logger.py), while this request still uses print.
|
It seems that Context Frames can solve this issues, so I am closing the PR as non-actual. |
Description
Fixes #271. Adds a provenance schema for stored memories so claims carry structured attribution instead of ad-hoc metadata.
src/memory_schema.py— schema builders/validators (build_metadata,validate, claim-id) + a provenance-scoped store over the chroma collection (remember_claim_llm/query_claims_text), confidence defaults, min-confidence and supersession-aware filtering.src/memory.metta—remember-claim(agent-emitted ⇒llmsource, default confidence 0.55) andquery-claims(recall with inline provenance) tools.src/rag.py— knowledge-prior chunks now carry the full provenance schema viabuild_metadata, keeping the legacybreadcrumb/type/timekeys for back-compat. (Upstream's exception logging ininit_knowledge/_get_stored_hashis left unchanged.)Standalone: depends only on the existing
rag._get_collection.How Has This Been Tested?
Autotests/test_memory_schema.py(pure-Python; chroma-backed cases use an in-memorychromadb.EphemeralClient()and self-skip when chromadb is absent), registered inrun_mandatory: schema shape +atoms_json, confidence defaults/overrides, validation, stable claim-id, where-clause scoping + supersession, filter parity, and round-trip provenance (remember → query, min-confidence exclusion, supersession exclusion, source-type filter). Run:cd Autotests && python3 test_memory_schema.py→ passes (chroma cases skipped on hosts without chromadb; exercised in the Docker CI). Thememory.mettatool wiring runs in the MeTTa/Docker runtime and is covered bybuild / commonCI.Checklist