Repository navigation
Memory System
🌐 English | 中文
Philont's memory is 5 layers, all SQLite-backed and cross-session. It is part of what lets a small model behave consistently over time: the model doesn't have to hold the relationship in its context window, because the runtime carries it.
Schema:
agent-memory/src/schema.ts(currentSCHEMA_VERSION = 31). Default DB path:~/.philont/memory/memory.sqlite(override withMEMORY_DB_PATH); the parent dir is created0700, WAL mode, with a siblingbackups/.
| Layer | Table(s) | What it stores |
|---|---|---|
| 0 · Raw timeline |
memory_raw_sessions, memory_raw_messages (+ _fts) |
Append-only conversation log: role (user/assistant/tool/system) + content + timestamp, with an FTS5 trigram index for full-text recall. |
| 0.5 · Action log | memory_actions |
Tool-call trace: tool_name, params, result, success, linked skill. The raw material reflection learns from. |
| 1 · Notes |
memory_notes (+ _fts) |
Free-text notes / fallbacks: content, importance, access timestamps, forgotten_at. FTS5-searchable. |
| 2 · Facts | memory_facts |
Structured KV facts with a namespace, confidence, time semantics (fact_kind state|event, occurred_at, valid_from/until) and a per-namespace decay constant. |
| 3 · Skills | memory_skills |
Reusable action templates: trigger keywords, action template, use/success/failure counts, maturity grade, consecutive failures, positive|negative kind. |
agent-memory/src/decay.ts
Facts don't just accumulate — they decay so stale knowledge loses weight automatically. The effective score is:
score = confidence × exp(−age_days / tau)
tau is per-namespace: user.* → ∞ (never decay), project.* → 60 days, context.*/session.* → 1 day, default → 365 days. last_accessed_at is refreshed on every access (LRU-ish), and a fact can be pinned (decay_tau_days = −1) to never decay. This is how "the user moved to Berlin last year" quietly outranks "the user is in a café right now" once the café visit is days old.
agent-memory/src/skill_maturity.ts
A learned skill carries a maturity grade that gates whether it's trusted and callable — knowledge evolves rather than piling up unchecked. The state machine:
| Grade | Meaning |
|---|---|
playbook |
Lesson/experience only — not callable via use_skill. |
draft |
Written after first success; callable but flagged "verify before use." |
confirmed |
≥2 successes, 0 failures — injected normally. |
stable |
≥5 successes and failure ratio < 0.1 — highest trust. |
deprecated |
≥3 consecutive failures, or failure ratio > 0.3 with ≥5 successes — no longer callable; kept as a counter-example. |
Promotion happens only on success and never skips a tier (draft → confirmed → stable); a failure demotes one tier (or jumps straight to deprecated when the failure threshold is hit). Skills are produced by the self-learning loop.
⚠️ What "success" counts as — and what it used to count as. Until recently,use_skillfetching a skill's body ran through the outcome recorder, so a skill retrieved twice climbed toconfirmed: "confirmed" meant "fetched twice", not "worked twice" — a fabricated efficacy signal that nothing in the store could distinguish from a real one. Retrieval is now decoupled:recordUsagebumpsuse_count/last_used_atfor ranking only, and touches neithersuccess_countnor this state machine. Efficacy is credited solely by the reflector's attribution, which looks at whether the actions after the retrieval actually succeeded.The climb itself is being demoted as a promotion path: under verify-then-condense, a skill is meant to be born validated — crystallized from a judge-verified success — leaving this ladder to do only what it does reliably: demotion on observed failure, plus disuse decay. That step is gated on the learning judge proving trustworthy first, so today the ladder still stands as described.
A callable recipe (a skill with a machine-checkable verification) additionally carries a revision_history: when skill self-repair rewrites a demoted recipe, SkillStore.reviseRecipe snapshots the prior version before overwriting, so a skill is versioned, not destructively replaced — and "did the rewrite actually help" becomes measurable per recipe.
agent-memory/src/tools.ts
store_fact · get_fact · list_facts · search_notes · search_skills · use_skill · recall_sessions · create_calendar_event · list_upcoming · schedule_reminder · cancel_schedule.
(Note the casing split: memory tools are snake_case; the filesystem/shell tools in agent-tools are camelCase — readFile, writeFile, glob. The honesty gate and permission classifier match the real names exactly.)
The model's working context is not held across WebSocket turns. Instead, each turn re-recalls from the raw global timeline — so the agent's identity is continuous while history is brought back on demand:
-
TimelineRetriever(agent-memory/src/timeline.ts) assembles two sections under a token budget: a recency section (newest N messages) plus a keyword-recall section (FTS5 hits on the user's current message, de-duplicated against recency). -
buildMemoryPrefix()(server/src/chat-handler.ts) prepends a condensed index of facts, skills, and notes, plus host environment and any active plan. - A recall trigger (
agent-memory/src/recall_trigger.ts) detects when the user references a past conversation ("remember when we…", "上次讨论过…") and forces the model to callrecall_sessionsfirst, so it answers from the real record rather than guessing.
This design means context cost stays bounded regardless of how long the relationship is — the model pays only for what it recalls this turn, not for the entire history.
English | 中文
Philont 的记忆是 5 层,全部基于 SQLite 且跨会话。它是小模型能长期保持一致表现的一部分原因:模型不必把关系装进上下文窗口,因为运行时替它携带。
Schema:
agent-memory/src/schema.ts(当前SCHEMA_VERSION = 31)。默认 DB 路径:~/.philont/memory/memory.sqlite(可用MEMORY_DB_PATH覆盖);父目录以0700创建,WAL 模式,并有同级backups/。
| 层 | 表 | 存什么 |
|---|---|---|
| 0 · 原始时间线 |
memory_raw_sessions、memory_raw_messages(+ _fts) |
仅追加的对话日志:role(user/assistant/tool/system)+ 内容 + 时间戳,带 FTS5 trigram 索引用于全文召回。 |
| 0.5 · 动作日志 | memory_actions |
工具调用轨迹:tool_name、参数、结果、success、关联 skill。反思学习的原料。 |
| 1 · 笔记 |
memory_notes(+ _fts) |
自由文本笔记/兜底:内容、重要性、访问时间戳、forgotten_at。可 FTS5 搜索。 |
| 2 · 事实 | memory_facts |
结构化 KV 事实,带 namespace、confidence、时间语义(fact_kind state|event、occurred_at、valid_from/until)以及按 namespace 的衰减常数。 |
| 3 · 技能 | memory_skills |
可复用动作模板:触发关键词、动作模板、使用/成功/失败计数、成熟度等级、连续失败数、positive|negative 的 kind。 |
agent-memory/src/decay.ts
事实不只是累积 —— 它们会衰减,让陈旧知识自动失权。有效分数为:
score = confidence × exp(−age_days / tau)
tau 按 namespace:user.* → ∞(永不衰减),project.* → 60 天,context.*/session.* → 1 天,默认 → 365 天。last_accessed_at 每次访问刷新(类 LRU),且事实可被钉住(decay_tau_days = −1)永不衰减。这就是为什么一旦「用户刚才在咖啡馆」过了几天,「用户去年搬到柏林」会悄悄盖过它。
agent-memory/src/skill_maturity.ts
学到的 skill 带一个成熟度等级,决定它是否被信任、可否调用 —— 知识是演化而非无序堆积。状态机:
| 等级 | 含义 |
|---|---|
playbook |
仅教训/经验 —— 不能通过 use_skill 调用。 |
draft |
首次成功后写下;可调用但标注「用前先验证」。 |
confirmed |
≥2 次成功、0 失败 —— 正常注入。 |
stable |
≥5 次成功且失败率 < 0.1 —— 最高信任。 |
deprecated |
≥3 次连续失败,或 ≥5 次成功且失败率 > 0.3 —— 不再可调用;作为反例保留。 |
晋升只在成功时发生且不跳级(draft → confirmed → stable);失败降一级(或在触及失败阈值时直接跳到 deprecated)。技能由自学习回路产生。
⚠️ 「成功」算什么 —— 以及它过去算什么。 直到不久前,use_skill「把技能正文取出来」这个动作也会走结果记录器,于是一个被取过两次的技能就爬到了confirmed:「confirmed」的真实含义是「被取过两次」,而不是「管用过两次」 —— 一个伪造的有效性信号,而库里没有任何东西能把它和真信号区分开。现在取用已解耦:recordUsage只更新use_count/last_used_at供排序,既不碰success_count也不碰这台状态机。有效性只由 reflector 的归因记账 —— 它看的是「取用之后的动作到底成没成」。这架梯子本身作为「晋升通道」正在被降格:在先验证再凝结的框架下,技能应当生下来就是被验证的 —— 从裁判确认的成功里结晶出来 —— 而梯子只保留它做得可靠的那部分:观察到失败时降级 + 闲置衰减。这一步卡在「学习裁判先被证明可信」之后,所以今天梯子仍如上所述工作。
一个 callable recipe(带机器可校验 verification 的技能)额外携带一个 revision_history:当技能自修复改写一个被降级的 recipe 时,SkillStore.reviseRecipe 会在覆盖前把旧版本存快照,所以技能是有版本的,不是破坏性替换——"这次改写到底有没有用"因此可以按 recipe 度量。
agent-memory/src/tools.ts
store_fact · get_fact · list_facts · search_notes · search_skills · use_skill · recall_sessions · create_calendar_event · list_upcoming · schedule_reminder · cancel_schedule。
(注意大小写分裂:记忆工具是 snake_case;agent-tools 里的文件/shell 工具是 camelCase —— readFile、writeFile、glob。诚实门与权限分类器都按真名精确匹配。)
模型的工作上下文不跨 WebSocket turn 保留。相反,每 turn 从原始全局时间线重新召回 —— 因此 agent 身份连续,而历史按需取回:
-
TimelineRetriever(agent-memory/src/timeline.ts)在 token 预算下组装两段:最近段(最新 N 条)加关键词召回段(对用户当前消息的 FTS5 命中,与最近段去重)。 -
buildMemoryPrefix()(server/src/chat-handler.ts)在前面拼上 facts/skills/notes 的浓缩索引,以及主机环境和任何活跃 plan。 - 一个召回触发器(
agent-memory/src/recall_trigger.ts)检测用户提及过往对话(「记得我们…」「上次讨论过…」)并强制模型先调recall_sessions,从而依据真实记录作答而非猜测。
这一设计意味着无论关系多长,上下文成本都有界 —— 模型只为本 turn 召回的内容付费,而非整段历史。
Philont-Agent · MIT · being-agent · Developer preview — docs track the code and may evolve. / 文档随代码演进。
Start here · 入门
- Home / 首页
- Who Is Philont For? / 谁适合使用
- Selfhood Engineering / 自我工程
- Compass / 指南针
- Architecture / 架构
- Why Smaller Models / 为什么选择较小模型
- Design Centers / 设计中心
- Evaluation Status / 评测状态
Core mechanisms · 核心机制
- Plan Protocol / 计划协议
- Honesty Gates / 诚实门
- Deep Reasoning / 深度推理
- Memory System / 记忆系统
- Autonomy & Self-Learning / 自主与自学习
Operate · 运维与扩展
🌐 Each page is bilingual — English first, then 中文.