fsi-anomaly / research /user_journal.py
FerrellSyntheticIntelligence's picture
backup all: 100 files (batch)
76b78ee verified
Raw
History Blame Contribute Delete
5.1 kB
"""Per-user journalist's notebook (deterministic suit memory).
The tiny model cannot rederive "who the user is and what they care about" from
weights. So the suit keeps a small, auditable USER JOURNAL: focus topics, active
investigation threads, remembered corrections, and a tone preset. The journal is
injected as a short context prefix on each analysis turn, and is updated with a
hashed-simple key + rationalized text / questions. This is memory in the suit,
not in the brain; when the user returns days later, the model "still knows them"
like a good journalist knows their subject.
Usage:
from research.user_journal import UserJournal
j = UserJournal() # loads ./data/user_journal.json (creates default)
ctx = j.context() # compact prompt-prefix string
j.note_thread(text) # remember this turn as an active thread
j.note_fact(fact) # pin a fact/correction the user cares about
"""
import json
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
# Editorial tone presets -> the single line we hand the model.
TONES = {
"spock": "Tone: strictly logical, evidence-first, concise; state what is "
"unsupported instead of guessing.", # default
"journalist": "Tone: probe the question; separate asserted fact from "
"speculation; ask what source the user already trusts.",
"coach": "Tone: explain briefly and support the user's own reasoning, "
"correcting only where evidence demands.",
"concise": "Tone: compact and direct, no filler.",
}
DEFAULT_TONE = "spock"
MAX_THREADS = 12
MAX_FACTS = 12
_default = {
"handle": "guest",
"tone": DEFAULT_TONE,
"focus": [],
"threads": [],
"facts": [],
"notes": "",
"corrections": [],
"last_seen": "",
}
class UserJournal:
def __init__(self, path=None):
self.path = Path(path) if path else ROOT / "data" / "user_journal.json"
self.data = dict(_default)
if self.path.exists():
try:
import json
self.data.update(json.loads(self.path.read_text()))
except Exception:
pass
def save(self):
import json, time
self.data["last_seen"] = time.strftime("%Y-%m-%d %H:%M")
self.path.parent.mkdir(parents=True, exist_ok=True)
self.path.write_text(json.dumps(self.data, indent=2, ensure_ascii=False))
# ---- reads ----
def context(self):
d = self.data
lines = ["\nJOURNAL (about the user, journal's private notes):"]
lines.append("handle: " + str(d.get("handle", "guest")))
if d.get("tone"):
lines.append(TONES.get(d["tone"], TONES[DEFAULT_TONE]))
if d.get("threads"):
lines.append("active threads: " + "; ".join(t if isinstance(t, str) else t.get("title","") for t in d["threads"][-MAX_THREADS:]))
if d.get("facts"):
lines.append("notes: " + " | ".join(str(f)[:140] for f in d["facts"][-MAX_FACTS:]))
if d.get("corrections"):
lines.append("remembered corrections: " + " | ".join(c[:140] for c in d["corrections"][-6:]))
lines.append("These are private notes. Use them to be relevant to THIS user, "
"but do not state them back verbatim.\n")
return "\n".join(lines)
# ---- writes (suit heuristics) ----
def set_handle(self, name):
self.data["handle"] = (name or "guest").strip()
def set_tone(self, preset):
if preset in TONES:
self.data["tone"] = preset
def note_thread(self, text):
title = " ".join((text or "").split()[:12])
if not title:
return
threads = [t for t in self.data.setdefault("threads", []) if not (isinstance(t,str) and t==title)]
threads.append(title)
self.data["threads"] = threads[-MAX_THREADS:]
def note_fact(self, fact):
fact = (fact or "").strip()
if not fact:
return
self.data.setdefault("facts", []).append(fact)
self.data["facts"] = self.data["facts"][-MAX_FACTS:]
def note_focus(self, terms):
for t in (terms or []):
t = str(t).strip()
if t and t not in self.data.setdefault("focus", []):
self.data["focus"].append(t)
self.data["focus"] = self.data["focus"][-16:]
def remember_correction(self, text):
# A safety-corpus: if the user explicitly corrects us, keep it short.
low = text.lower()
if any(k in low for k in ("you're wrong", "that's wrong", "no, ", "correction", "actually ")):
self.data.setdefault("corrections", []).append(text[:160])
def snapshot(self):
return dict(self.data)
if __name__ == "__main__":
import sys
j = UserJournal()
print(j.context())
print("---")
j.note_thread("wanted to verify the 2022 repaint permit narrative")
j.note_fact("user cares about timeline provenance across agencies")
print("after write snapshot keys:", sorted(j.snapshot().keys()))