Recipes

pi, Claude, Codex 에이전트를 위한 실행별 학습 메모리 시딩

README 링크에서 발견 · today

Research & KnowledgeSoftware & DevOps

에이전트는 각 실행을 git 기반 메모리 폴더에서 시작하며, 주입된다

What they did

저자는 멀티 하네스 에이전트 서버를 위한 학습 루프의 전반부를 구축했다. 즉, 스위치, 폴더, 그리고 모든 실행이 시작되는 시드(seed)다. 구체적으로: (1) 에이전트 정의에 `learning: boolean` 필드를 추가했으며, true가 아니면 꺼져 있고, 각 리비전에 실려 편집 시 생략되어도 보존되며, 워크플로 정의에서는 거부되고, 서빙되는 문서에는 `learning/1`로 표시된다. (2) 서버 킬 스위치 `--learning on|off`(기본값 on) 또는 `AGENTRUN_SERVE_LEARNING`으로, `capabilities.features.learning`으로 보고된다. (3) Claude Code의 auto-memory 형식으로 `DATA/homes/<agent id>/learning/`에 에이전트별 폴더를 두었다 — `MEMORY.md`는 노트당 한 줄짜리 인덱스이고, 노트마다 `<slug>.md` 하나에 `name`, `description`, `type`과 선택적 `status`/`superseded_by`를 담는다 — `main`이 체크아웃된 평범한 git 저장소로, 에이전트의 첫 학습 실행에서 생성되고, 기존의 안전한 git 러너와 에이전트별 큐를 통해 서버만 기록한다. (4) 실행 시작 시 시드 단계가 `runs/<id>/learning.json`에 `main`의 커밋을 고정하고, git plumbing으로 해당 커밋의 `MEMORY.md`를 읽어 처음 200줄(25 KB 이내)을 `# Learned` 아래 실행 지침에 넣으며, 인덱스 사본과 활성 노트를 `work/.learning/`에 복사한다. (5) 호출 지점은 `app.ts`의 `withLearned` 하나로, pi의 시스템 프롬프트와 claude-agent 및 codex의 지침에 공급한다. (6) 에이전트 홈, 학습 폴더, `learning/.git`에서 `mkdir`과 `lstat`을 통한 심볼릭 링크 거부, 그리고 `MEMORY.md` 쓰기에 `O_NOFOLLOW`를 적용했다. (7) 실행 상태는 `seeded` 또는 `dropped`/`seed_failed`로 가시화되며, 실행을 절대 차단하지 않는다. (8) `packages/learn`에 형식 헬퍼(`parseNoteFile`, `activeNotes`, `buildIndex`, `loadedIndex`)를 두었다. 학습된 스킬, 기존 `learnings/INDEX.md` 레이아웃, 읽을 수 없는 저장소에서의 500, `files.ts` 변경은 제거했다. Node 22와 24에서 TAP 스위트(serve 154/154, recovery 18/18, learn 153/153), 세 하네스 시드 테스트, 가짜 Anthropic 엔드포인트를 대상으로 한 실제 어댑터 claude-agent 케이스, 그리고 관련 테스트를 빨갛게 만드는 것이 각각 확인된 여덟 개의 수동 적용 뮤턴트로 검증했다.

How it went

Node 22.23.2와 24.21.0에서 모든 스위트가 통과했습니다: serve 154/154, recovery 18/18, learn 153/153이며, 실제 어댑터 claude-agent 케이스는 건너뛰지 않고 실제로 실행되었습니다. 손으로 적용한 8개의 뮤턴트는 각각 의도대로 red가 되었으며, working-tree 읽기, superseded-note 복사, learning이 꺼진 상태에서의 seeding, seed 실패 시 throw, per-try 스위치 경쟁, 남은 pin, `lstat` 대신 `stat`, 누락된 `O_NOFOLLOW`를 커버했습니다. 앞선 Node 22 실행 두 번은 느린 공유 디스크에서 타임아웃되었고, 통과한 실행은 다른 디스크의 scratch 디렉터리를 사용했습니다.

Worth knowing

이 PR은 읽기 절반만 담고 있습니다. 아직 폴더에 쓰는 것은 없으므로, 실행 종료 시점의 노트 제안 턴이 들어오기 전까지는 `main`에 사람이나 테스트가 커밋한 내용이 그대로 남습니다. 심볼릭 링크 처리는 부분적입니다. 실제 `.git` 내부의 링크나, 마지막 검사 이후 git 호출 전 사이의 교체는 잡아내지 못합니다. 실행 중의 `work/.learning/` 사본은 다시 읽히지 않으므로, 에이전트가 Claude Code처럼 실행 도중 자신의 메모리를 편집할 수 없습니다. 시드는 `MEMORY.md`의 200줄 또는 25 KB로 제한됩니다.

Wiring

순서: 정의 검증(`validate.ts` `DEFINITION_FIELDS`) → 리비전 빌드(`agents.ts` `revisionOf`, agentic 전용, harness와 place 옆에 있는 `commit()`의 per-try 빌드에 실려 `definitionOf`에 의해 반영됨) → 에이전트 스토어(`pi-durable-agent/src/agent-store.ts` `AgenticBody`와 `NewRevision`, `bodyOf`에서 검사됨) → 서버 시작(`server.ts` `ServeOptions.learning`, `capabilities(…, learning)`, `features.learning`) → 실행(`runs.ts` `RunsOptions.learning`, 로드 시 다시 읽히는 `RunMeta.learning`, 절대 throw하지 않는 `seedFor`, learning이 켜져 있을 때만 시딩하는 `start()`, `view`, `summary`, `summaryOfRow`의 `learning`) → harness 분기 전 `app.ts`의 한 번의 `withLearned` 호출이 `systemOf`와 `promptOf`에 공급 → OpenAPI(`openapi.ts` `DefinitionInput.learning`, `Run.learning`, `RunSummary.learning`, `Features.learning`, 모두 `learning/1`). 폴더는 `learning.ts` 자체에 의해 빌드되므로 `files.ts`와 `skills.ts`는 건드리지 않는다. 핀은 실행 디렉터리가 동기화되기 전에 제자리로 이름이 변경되므로, 드롭된 실행이 오래된 인덱스를 실어 나르는 일은 없다. 포맷 헬퍼는 `packages/learn`에 있으며 다음 슬라이스의 검사 및 병합과 공유된다.

Try it yourself

# Per-run learned-memory seed

## 1. Agentic definitions: `learning` field

Add `learning: boolean` to agentic definitions, default `false`.

- Stored on the revision.
- Preserved when an edit omits it (merge with prior revision value).
- Refused on workflow definitions (validation error).

## 2. Server-level switch

`--learning on|off` (default `on`), plus env var `RUNCHR_LEARNING=on|off`. Surfaced in the capabilities response.

## 3. Per-agent learning folder

`DATA/homes/<agent id>/learning/` in Claude Code auto-memory format:

- `MEMORY.md` index, one line per note: `- [name](file.md) — description`
- One `<slug>.md` per note with frontmatter: `name`, `description`, `type` (`user|feedback|project|reference`), optional `status: superseded|retired`, optional `superseded_by`.
- Plain git repo with `main` checked out.
- Created on the agent's first learning run.
- Written only by the server, through the existing safe git runner and per-agent queue.

## 4. Run start

- Pin `main`'s commit in `runs/<id>/learning.json`.
- Read `MEMORY.md` at that commit via git plumbing.
- Inject first 200 lines (within 25 KB) into the run's instructions under a `# Learned` heading, with one line telling the agent the notes live in `.learning/`.
- Copy the index and only active notes into `work/.learning/`.

## 5. Single call site

Route through one `withLearned` call site that feeds pi's system prompt and claude-agent's and codex's instructions.

## 6. Symlink refusal

Refuse symlinks at the agent home, the learning folder, and `learning/.git` using `mkdir` plus `lstat`. Open `MEMORY.md` with `O_NOFOLLOW`.

## 7. Run state

Record `seeded` or `dropped`/`seed_failed`. Never let a learning fault block a run. If the copy fails after the pin is written, remove the pin too.

## Tests

- Three-harness seeding
- Uncommitted working-tree edits not reaching the run
- Superseded notes not copied
- Learning-off and server-switch-off
- Unreadable folder and unreadable `main`
- Per-try switch race
- Symlinks planted at each path

Source

English

Tools in this stack