Repository navigation
Plan Protocol
🌐 English | 中文
The plan protocol is Philont's discipline for changing the world — anything that deploys, registers, sends, or delivers an artifact. Its job is to stop a model from winging a multi-step task. (Its sibling, for changing what you know, is deep_explore.)
The loop is: draft a plan → execute it step by step → revise to route around any step that fails → close only with a verified outcome. Every stage is mechanism-enforced.
It exists in two implementations, and knowing which one you're in explains most of the behaviour: §1–6 are the protocol the model walks under a gate — general, always available. §7 is a deterministic state machine that takes the pen entirely when the task ships with a written guide, and it is where the two-weak-model review lives.
agent-memory/src/task_mode.ts
At the start of a turn, the model self-assesses task complexity by answering four questions: does the task have ≥2 independently verifiable outputs? are there inter-step dependencies? is there an external-world write? did the user supply a multi-step guide? Any "yes" leans slow.
-
fast→ no plan constraint (chat, single lookups, simple follow-ups). -
slow→ armsplan_protocol_gate.
Critically, the mode is derived from plan state, not an independent switch. While a plan is draft or executing, the model cannot flip back to fast to escape the protocol — it must first plan_close. (Open-ended exploration/judgment requests are routed to deep_explore instead, which is exempt from the plan gate and carries its own stricter per-claim protocol.)
server/src/plan_gate.ts
A "lazy gate": it fires at "the contract moment before writing", not before exploration. Concretely:
-
Read-class tools and read-only shell (
ls,cat,grep,findwithout-delete/-exec, …) pass through freely — the model can explore the data first. - The first write/execute action (a POST, a
writeFile, apip install, …) is blocked until a plan exists and has enteredexecuting. -
deep_exploreandplan_*tools are always exempt (stacking two protocols would deadlock the model).
The read-only shell allowlist is strict: redirections (>), command chaining (;, &&, ||), command substitution (`, $(…)), and | tee/sponge/dd all disqualify a command, so nothing write-capable slips through as "read-only."
agent-memory/src/plan_tools.ts
The model decomposes the task into steps, each bound to a concrete deliverable with a verification check. Unverifiable deliverables ("research X", "investigate Y") are rejected at draft time — every step must answer "what does done look like, and how do I verify it?" This bakes self-review into planning rather than leaving it to a separate (skippable) review step. Each deliverable id must be referenced by a step's covers field, so close-time status has an evidence basis.
Marking the first step doing moves the plan to executing (which opens the gate for all tools). Each step records evidence as it completes. The plan is now a live contract the rest of the turn executes against.
When execution hits a wall, the model calls plan_revise to replace the failing steps and route around the root cause; the plan returns to draft and re-enters executing on the next plan_update_step. Revision history is tracked.
This is wired to failure detection: when the turn loop sees repeated same-root-cause failures (see Honesty Gates → in-turn reflection), it injects an auto-revise hint pointing the model at plan_revise — so a stuck plan is nudged to change approach instead of grinding.
This is the honesty teeth of the protocol. plan_close requires a deliverable_status map and validates it:
- C1 — the status keys must exactly match the plan's declared deliverable ids (no missing, no extra).
-
C2 — each value must be a valid status:
done/partial/skipped/failed/not-attempted. -
C3 — to close
success, every deliverable must bedoneorskipped. If any ispartial/failed/not-attempted, the model is first asked to correct honestly; if it insists on "success" anyway, the close is auto-converted to afailure— a partial run can never masquerade as a win. -
C4 — even with all-done statuses, if any step's
evidencecontains infrastructure failure signals (ENOENT,EACCES,ECONNREFUSED,fetch failed,timed out, …),successis rejected — you can't paste a raw tool error into evidence and still claim success.
Placeholder plans (auto-created skeletons) cannot close as success — they carry no real completion evidence. A failure close is not a dead end: it feeds the self-learning loop, distilling a failure playbook for next time.
server/src/plan_execute_loop.ts· flagPHILONT_PLAN_LOOP(default on) · design doc
Everything above is a protocol the model walks: the gate makes states hard to skip, but the model still holds the pen. When a task arrives with a written guide (a URL in your message, routed to plan), Philont switches to something stricter — a deterministic state machine that owns the entire turn:
DRAFT → VERIFY → REVISE (bounded ring) → EXECUTE (tool evidence per step) → computed CLOSE.
The model fills in each state's content; the transitions are code. It cannot skip VERIFY and it cannot declare itself finished — completion is computed from tool evidence. (Production, before this existed: a weak model would not call plan_revise and announced "注册完成" at the gate wall. As long as the workflow was policy, it escaped.)
Scheduled and autonomous turns deliberately do not enter this loop — it owns the whole turn and would bypass the recurring-task safety nets (cross-turn reflection, credential/heartbeat teaching, failure throttles). Heartbeats stay on the protocol path above.
VERIFY runs two independent reviewers over the draft:
- a deterministic coverage check — every mandatory requirement extracted from the guide must be matched by a deliverable or a step;
- an aux model — the
AUX_LLM_*endpoint, a second, different model, which sees only the guide and the deliverable list and reports what it reads as uncovered. LeaveAUX_LLM_*unset and this reviewer simply does not exist; VERIFY degrades to the deterministic check alone.
Two weak models reviewing each other is not automatically worth more than one model trying twice. The first version wasn't. The REVISE prompt never showed the model its own previous plan, so every round redrafted from scratch and silently discarded whatever the last round got right: production oscillated — deliverables 3→9→5 and 7→6→5, unresolved gaps 2→5 — and because plan = draft overwrote unconditionally, when a later round came back worse, the worse plan is the one that executed. Three independent attempts, not one improving one.
Both models are stateless across rounds, and that is the actual gap. Memory is exactly what a mechanism is good at and a model is not, so the mechanism now carries it:
| What the mechanism remembers | What it does with it |
|---|---|
| the previous plan + the deliverables no longer contested | REVISE is handed both and told to keep the accepted ones verbatim, closing only the remaining gaps — ground can be added, never lost |
| which round scored best | that plan executes, not whichever draft happened to come last |
| every gap already raised | a complaint the reviewer raises again after a revision is marked [RAISED AGAIN — your last revision did not close this]; identity is by meaning-overlap, since the reviewer rewords freely (a missed pairing merely fails to escalate — it never blocks) |
| reviewer-vs-checker disagreement | kept as CONTESTED instead of silently resolved in the checker's favour; raised a second time, it is put to the author as a question: name the deliverable that covers it, or add one |
The aux reviewer is deliberately never given this memory. It keeps receiving a fresh snapshot — guide plus deliverables, never the author's reasoning — and tests pin that. Its independence is the entire reason its verdict carries information; anchoring it on its own earlier judgements would turn the second opinion into a rerun of the first.
The division of labour: judgement from the models, memory from the mechanism. That is what lets two passes be worth more than two attempts. Production after the ratchet landed: deliverables 5→9→12, gaps 8→4→1.
Two further teeth, because a converged plan still has to survive contact with a weak model:
-
Mechanical adoption — a mandatory guide item still uncovered after the last round is added by the mechanism as a deliverable + a fulfilling step, rather than asked for again. Filters keep three things out of that list, each of which caused a real production failure: prose that merely describes an action (adopted → a step ordering "DO IT NOW — http POST"), another runtime's
$VARintegration snippet, and behavioural rules (adopted as deliverables → false ❌ while the real posting requirement sat unadopted). - Contested items stay out of the owner-facing gap report. The deterministic check does read them as covered, and you should not be handed a disputed item as though it were a confirmed omission. They shape the revision, not the report.
An earlier increment had the aux model compile a service's guide into an authoritative contract (endpoints, auth, required fields) that downstream guards enforced. It is off by default now — PHILONT_SPEC_COMPILE=1 re-enables it — and the reasoning is the same one that justifies every other mechanism on this page:
Compiling 17k characters of prose into a contract asks the weak model to do, in one shot, the very comprehension we already assume it cannot be trusted with — and then grants the result authority over every downstream guard. A wrong call gets corrected by the service; a wrong contract has nothing to correct it, because the contract is the corrector.
Production agreed. Every fresh compile landed at the 0.30 confidence floor with paths that were never base-resolved (→ 404s), and rejected_by_spec_request_guard became the single largest source of tool failures in the system — every case investigated was a false block, including the endpoint that issues the credential and a plain documentation file on the same host.
What was kept is everything that needs no comprehension:
- the deterministic regex anchor over the guide (hosts + endpoints) and the host allowlist guard it feeds — a wrong host is the one mistake the service cannot report back, surfacing as an opaque
fetch failedthat an agent will misread for days; -
endpoint / auth / body mistakes → left to the service, which answers
404/401/400free and exact; - guide coverage → the review loop above, which converges where a one-shot compile cannot;
- the replayable service skill, now emitted from the run's verified calls plus the regex anchor instead of from the compiled spec. The artefact worth keeping must not depend on the step most likely to fail — a scheduled heartbeat replayed it at 29/29 tools.
Also on this path: an id-provenance guard (PHILONT_ID_PROVENANCE, on by default, scoped to this loop's own tool runner) refuses a write whose identifier appears in nothing the agent actually read this turn — the task text and every successful tool result — because fabricating a plausible token at a knowledge gap is the weak model's signature failure and the one class a mechanism catches that a same-model critic does not.
If the work succeeds, what changed?
- The world (deployed, registered, sent, delivered) → plan: decompose into action steps, verify by completion, always close with an outcome, guard against winging it.
- What you know (a proof, a decision, a diagnosis) → deep_explore: decompose into claims with a truth status, verify each, stay open-ended, guard against self-deception.
They compose: think a hard question through in deep_explore first, then execute the consequences under a plan.
English | 中文
计划协议是 Philont 用于改变世界的纪律 —— 任何部署、注册、发送、交付产物的动作。它的职责是阻止模型拍脑袋硬干一个多步任务。(用于改变你的认知的姊妹协议是 deep_explore。)
回路是:起草计划 → 逐步执行 → 失败时修订以绕行 → 仅以可验证结果关闭。 每个阶段都由机制强制。
它有两套实现,而「你现在在哪一套里」解释了大部分行为差异:§1–6 是模型在门约束下自己走的协议 —— 通用、始终可用;§7 是一台在任务自带书面 guide 时把笔完全接管过去的确定性状态机,两个弱模型互评就住在那里。
agent-memory/src/task_mode.ts
turn 开始时,模型通过回答四个问题自评复杂度:任务是否有 ≥2 个独立可验证产出?步骤间是否有依赖?是否有对外部世界的写操作?用户是否给了多步 guide?任一「是」就倾向 slow。
-
fast→ 无 plan 约束(闲聊、单次查询、简单追问)。 -
slow→ 武装plan_protocol_gate。
关键在于:模式派生于 plan 状态,而非独立开关。当 plan 处于 draft 或 executing 时,模型无法切回 fast 逃避协议 —— 必须先 plan_close。(开放式的探索/审议请求改走 deep_explore,它豁免 plan 门,并自带更严的逐 claim 协议。)
server/src/plan_gate.ts
一道**「惰性门」**:它在*「写之前的契约时刻」*触发,而非在探索之前。具体:
-
读类工具与只读 shell(
ls、cat、grep、不带-delete/-exec的find…)自由通过 —— 模型可先探索数据。 -
第一个 写/执行 动作(POST、
writeFile、pip install…)在计划存在并进入executing前被挡。 -
deep_explore与plan_*工具永远豁免(两套协议叠加会把模型锁死)。
只读 shell 白名单很严格:重定向(>)、命令串联(;、&&、||)、命令替换(`、$(…))、| tee/sponge/dd 都会使命令失格,因此任何可写命令都无法伪装成「只读」混过去。
agent-memory/src/plan_tools.ts
模型把任务分解为步骤,每步绑定一个带验证检验的具体交付物。不可验证的交付物(「研究 X」「调查 Y」)在起草时被拒 —— 每步必须回答*「完成长什么样,怎么验证?」*。这把自审烤进规划本身,而不是留给一个(可跳过的)独立 review 步骤。每个交付物 id 必须被某步的 covers 字段引用,这样关闭时的状态才有证据基础。
把第一步标为 doing 会让 plan 进入 executing(从而为所有工具开门)。每步完成时记录证据。此时 plan 成为本 turn 后续执行所对照的活契约。
执行撞墙时,模型调 plan_revise 来替换失败步骤、绕开根因;plan 回到 draft,下次 plan_update_step 再进入 executing。修订历史会被记录。
它与失败检测联动:当 turn 循环发现反复同根因失败(见 诚实门 → turn 内反思),会注入 auto-revise 提示,把模型指向 plan_revise —— 让卡住的 plan 改换思路而非死磨。
这是协议的诚实之牙。plan_close 要求一个 deliverable_status 映射并校验它:
- C1 —— 状态键必须与 plan 声明的交付物 id 完全一致(不缺、不多)。
-
C2 —— 每个值必须是合法状态:
done/partial/skipped/failed/not-attempted。 -
C3 —— 要以
success关闭,每个交付物必须是done或skipped。若有partial/failed/not-attempted,先要求模型诚实修正;若它仍坚持「成功」,关闭被自动转为failure—— 半成品永远无法冒充胜利。 -
C4 —— 即便全部 done,只要任一步骤
evidence含基础设施失败信号(ENOENT、EACCES、ECONNREFUSED、fetch failed、timed out…),success被拒 —— 你不能把原始工具报错粘进证据还声称成功。
占位 plan(自动创建的骨架)不能以成功关闭 —— 它不携带真实完成证据。failure 关闭并非死胡同:它喂给自学习回路,为下次蒸馏一份失败 playbook。
server/src/plan_execute_loop.ts· flagPHILONT_PLAN_LOOP(默认开)· 设计文档
以上都是模型自己走的协议:门让它难以跳步,但笔仍在模型手里。当任务自带一份书面 guide(消息里给了 URL,并被路由为 plan)时,Philont 切换到更硬的东西 —— 一台接管整个 turn 的确定性状态机:
DRAFT → VERIFY → REVISE(有界环)→ EXECUTE(每步带工具证据)→ 计算得出的 CLOSE。
模型只填每个状态的内容;状态转移是代码。它跳不过 VERIFY,也宣布不了自己完成 —— 完成度由工具证据算出来。(生产上、在此之前:弱模型死活不调 plan_revise,在门前直接宣布*「注册完成」*。只要工作流是「政策」,它就能溜掉。)
定时与自主 turn 刻意不进这条回路:它接管整个 turn,会绕过复发任务的安全网(跨轮反思、凭证/心跳教学、失败节流)。心跳仍走上面的协议路径。
VERIFY 对草稿跑两个独立评审:
- 一道确定性覆盖检查 —— 从 guide 抽出的每条强制要求,都必须被某个交付物或步骤匹配上;
- 一个 aux 模型 —— 即
AUX_LLM_*端点,一个第二个、不同的模型,它只看到 guide 和交付物清单,报告它认为没被覆盖的部分。不配AUX_LLM_*就没有这个评审员,VERIFY 退化为只有确定性检查。
两个弱模型互评并不自动比一个模型试两次更值钱。第一版就不值钱:REVISE 提示词从没把「模型自己上一版计划」给它看,所以每轮都从零重画,悄悄丢掉上一轮做对的东西 —— 生产上是震荡的:交付物 3→9→5、7→6→5,未解决 gap 2→5;而且 plan = draft 无条件覆写,于是当后一轮更差时,执行的正是那个更差的计划。那是三次独立尝试,不是一次持续改进。
两个模型跨轮都无状态,这才是真正的缺口。而记忆恰恰是机制擅长、模型不擅长的东西,所以现在由机制来背:
| 机制记住什么 | 它用来做什么 |
|---|---|
| 上一版计划 + 已不再被质疑的交付物 | REVISE 同时拿到这两样,并被告知把已认可项原样保留,只去堵剩下的 gap —— 阵地只增不失 |
| 哪一轮得分最好 | 执行的是那一版,而不是恰好最后画出来的那版 |
| 已经提过的每条 gap | 评审在一次修订后再次提起的抱怨,会被标上 [RAISED AGAIN —— 你上次修订没有关掉这条];识别按语义重叠而非字符串相等(评审换措辞很随意;配不上顶多是「没能升级」,绝不会阻塞) |
| 评审员与检查器的分歧 | 保留为 CONTESTED,而不是默默判检查器赢;当它被第二次提出,就以提问形式抛回作者:指出哪个交付物覆盖了它,或者补一个 |
aux 评审员刻意永不获得这份记忆。它每轮拿到的都是干净快照 —— guide + 交付物,从不给它作者的推理过程,并有测试钉住这一点。它的独立性正是它的判断有信息量的全部理由;拿它自己过去的判断去锚它,第二意见就退化成第一意见的复读。
分工:判断力归模型,记忆归机制。 这才是「两遍 > 两次尝试」成立的原因。棘轮上线后的生产曲线:交付物 5→9→12,gap 8→4→1。
还有两颗牙,因为一个收敛的计划仍要经受弱模型的接触:
-
机制性采纳 —— 最后一轮仍未被覆盖的强制 guide 条目,由机制直接加成交付物 + 一个兑现步骤,而不是再问模型一次。三类内容被过滤在外,每一类都对应一次真实的生产事故:只是描述动作的散文(被采纳后变成「DO IT NOW —— http POST」的步骤)、别的运行时的
$VAR集成片段、以及行为约束类规则(被当交付物采纳 → 假 ❌,而真正的发帖要求反而没人认领)。 - CONTESTED 条目不进给主人看的 gap 报告。确定性检查确实读它为已覆盖,不该把一条有争议的条目当作已确认的遗漏交到你手上。它们塑造修订,不进报告。
更早的一个增量,是让 aux 模型把服务 guide 编译成一份权威契约(端点、鉴权、必填字段),再由下游守卫强制执行。它现在默认关(PHILONT_SPEC_COMPILE=1 可重新打开),而理由,和本页其它机制成立的理由是同一个:
把 17k 字散文一次性编译成契约,等于要求弱模型当场完成「我们本就假设它做不到」的那种理解 —— 然后把结果授予凌驾所有下游守卫的权威。错的调用会被服务纠正;错的契约没有东西能纠正它,因为契约本身就是那个纠正者。
生产给出了同样的答案:每次全新编译都落在 0.30 置信度地板上,且路径从未做 base 解析(→ 404);rejected_by_spec_request_guard 成了全系统最大的单一工具失败来源 —— 而查过的每一例都是误挡,包括那个签发凭证的端点,以及同主机上一个普通文档文件。
被保留下来的,是所有不需要理解力的部分:
- guide 上的确定性正则锚(host + endpoint)与它喂养的 host 白名单守卫 —— host 写错是服务唯一无法回报的错误,它只会表现为一句不透明的
fetch failed,agent 能误读好几天; -
端点 / 鉴权 / body 的错误 → 交给服务回答,
404/401/400免费且精确; - guide 覆盖度 → 交给上面的评审回路,它能收敛,而一次性编译不能;
- 可 replay 的 service skill,现在从本次运行的已验证调用 + 正则锚产出,而不是从编译出的 spec 产出。值得保留的产物,绝不能依赖最容易失败的那一步 —— 一次定时心跳以 29/29 工具成功 replay 了它。
同属这条路径的还有一颗 id 溯源守卫(PHILONT_ID_PROVENANCE,默认开,仅作用于本回路自己的工具运行器):当一个写请求里的标识符在「本轮真正读到过的东西」(任务原文 + 每个成功的工具结果)里根本找不到时,拒绝它 —— 在知识缺口上编一个看起来合理的 token,是弱模型的招牌失败,也正是「机制抓得住、而同模型评审抓不住」的那一类。
如果这活成功了,改变了什么?
- 世界(部署、注册、发送、交付)→ plan:分解为动作步骤,以完成验证,总以结果关闭,防拍脑袋。
- 认知(证明、决策、诊断)→ deep_explore:分解为带真值状态的claim,逐一验证,保持开放,防自欺。
二者可组合:先在 deep_explore 里把难题想清楚,再用 plan 执行其后果。
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 中文.