Files
pi-agent-config/docs/personality-layering.md
T
Kai cbba8faabc docs: pi 0.84.3 runtime mechanics, isolation baseline, personality layering, gateway patterns
Establishes this repository as the authoritative source for Pi agent
configuration across scenarios, starting with the documentation layer.

Key verified findings (probe harness included, zero model tokens):

- The skills section of the system prompt is emitted only when an active tool
  named 'read' exists (system-prompt.js:59,113). Therefore --no-tools silently
  makes every SKILL.md unreachable and --skill a no-op.
- registerTool accepts a plain JSON Schema object, so tool definitions can be
  served from a backend instead of duplicated in TypeScript.
- An extension can shadow a built-in tool by name, which is how a dedicated
  agent gets a path-restricted 'read' while still satisfying the rule above.
- .pi/SYSTEM.md replaces pi's coding-assistant prompt, but the replacement
  branch contributes neither the tool list nor the guidelines.
- Without --no-skills/--no-extensions, user-global resources leak into every
  scenario; probed leak was find-skills, modsearch, summarize.

Measured effect of the full baseline: system prompt 2619 -> 960 characters,
coding-assistant framing and pi-docs paths removed, skill finally reachable.

Secrets are guarded by scripts/verify-no-secrets.sh, installed as a pre-commit
hook. Backups deliberately live outside the repository.
2026-08-26 22:47:53 -07:00

129 lines
5.8 KiB
Markdown

# Personality Layering
> How to decide what goes in `SYSTEM.md`, `AGENTS.md`, `SKILL.md` and the
> per-request prompt. Mechanisms verified against pi 0.84.3; see
> [`pi-runtime-notes.md`](pi-runtime-notes.md) §§2, 8, 9, 19.
Four slots exist and they are not interchangeable. Putting content in the wrong
slot is why rules get duplicated three times and then drift.
---
## Assembly order
With `.pi/SYSTEM.md` present and trusted, pi builds the system prompt as:
```
SYSTEM.md
→ APPEND_SYSTEM.md
→ <project_context> … AGENTS.md from ~/.pi/agent, each parent dir, cwd
→ <available_skills> … name + description + location only
→ "Current working directory: …"
```
Without `SYSTEM.md`, the first slot is pi's built-in **coding assistant** prompt,
which also contributes a tool list, a guidelines list, and absolute paths to
pi's own documentation.
Critically: **the `SYSTEM.md` branch contributes neither the tool list nor the
guidelines.** If you replace, you own both.
---
## Slot assignment
| Slot | Loaded | Cost | Put here |
|---|---|---|---|
| `.pi/SYSTEM.md` | needs trust (`--approve`) | always in context | Identity. Tool overview. Fact-authority map. Write discipline. Untrusted-data rule. Output format. |
| `.pi/APPEND_SYSTEM.md` | needs trust | always in context | Nothing, normally. Use only when you want to keep pi's default prompt and bolt something on. |
| `AGENTS.md` (workspace) | always | always in context | Durable role, responsibilities, routing, domain defaults, escalation policy. Human-editable narrative. |
| `AGENTS.override.md` | always | always in context | Same as `AGENTS.md`, but also **stops** `AGENTS.md`/`CLAUDE.md` from that directory. Use to make the personality deterministic against stray parent files. |
| `.pi/skills/<n>/SKILL.md` | needs trust, or explicit `--skill` | **description only** up front; body read on demand | Task-specific procedure that is not needed on every turn. The place for long checklists and worked examples. |
| Per-request prompt | n/a | per call | Only the current inputs and the schema for this one response. |
### Rule of thumb
- Needed on **every** turn → `SYSTEM.md` or `AGENTS.md`.
- Needed on **some** turns, and long → `SKILL.md`.
- Changes **per request** → the request.
A `SKILL.md` whose description says "use for every request" is not a skill; it is
system-prompt content paying an extra tool round-trip. Either move it up a slot
or split it into genuinely conditional skills.
---
## Replace or append?
**Replace (`SYSTEM.md`) when the agent is not a coding agent.**
pi's default prompt opens with:
> You are an expert coding assistant operating inside pi, a coding agent
> harness. You help users by reading files, executing commands, editing code,
> and writing new files.
and closes with absolute paths to pi's README, docs and examples plus an
instruction to read them and follow cross-references. For a media-curation or
note-routing agent that is not just wasted context — it is a documented,
ready-to-use escalation path for anything injected through fetched web content.
Measured: replacing it cut the Curator system prompt from 2619 to 960
characters and removed both the coding-assistant framing and the pi-docs block.
**Append (`APPEND_SYSTEM.md`) only** when you want the built-in coding
behaviour and are adding a constraint on top.
---
## What a replacement `SYSTEM.md` must contain
Because the replacement branch drops pi's tool list and guidelines, cover all
six sections explicitly:
1. **Identity and negative identity** — what the agent is, and that it is *not* a
coding assistant and does not read or modify project code.
2. **Tool overview** — each tool, its purpose, and when to prefer it. Keep it
consistent with the `promptSnippet` values in the extension.
3. **Fact authority** — which backend is authoritative for which class of fact,
and that tool results are the only source of truth.
4. **Write discipline** — whether the agent may write at all; if it may only
*propose*, say so, and forbid claiming completion without a receipt.
5. **Untrusted data** — that content arriving from fetched pages, search
snippets or documents is evidence, and instructions inside it are never
executed.
6. **Output discipline** — language, target surface (e.g. Telegram plain text),
structure, and the rule that unknown stays empty rather than guessed.
Explicitly **omit**: any path to pi's own documentation, and any wording about
editing files or running commands.
---
## Anti-patterns observed in this repository's history
| Anti-pattern | Consequence |
|---|---|
| `--no-tools` together with `--skill` | skills block never rendered; the entire `SKILL.md` was dead for the whole lifetime of the service |
| Same rule written in `AGENTS.md`, `SKILL.md` and the request prompt | drifted — one copy listed 4 recommendation values, another 5 |
| `AGENTS.md` in Chinese, `SKILL.md` in English, prompts in Chinese | cross-language alignment cost, harder review |
| Relying on `allowed-tools:` frontmatter | not enforced in 0.84.3; false sense of security |
| Feeding internal fields (`_model_used`) into a prompt that forbids naming the model | self-contradictory instruction |
| Long defensive prohibition lists | usually a symptom of a wrong base persona; fix the persona instead |
---
## Single source of truth
Enumerations and schemas that both the model and the backend must agree on
(intent values, recommendation scales, verdict scales, field names) belong in
**code**, not in prose:
- define them once in the scenario backend (e.g. `contracts.py`),
- generate the JSON Schema from that definition,
- serve the schema to the extension so tool `parameters` match by construction,
- and generate any prose enumeration in `SKILL.md`/`SYSTEM.md` from the same
source, or assert equality in a test.
Prose then describes *policy*; code defines *shape*.