마지막 업데이트 2026-09-21
지금의 agent-trace는 잘못한 턴을 찾아 사람에게 보고까지만 한다. 이 계획은 그 결과를 오답노트(lessons)로 쌓고, Claude Code · Codex CLI · Hermes · OpenClaw · OpenCode 다섯 에이전트가 세션 시작·프롬프트·툴 호출 직전에 자동으로 꺼내 읽게 하고, 팀원 누구나 한 줄로 설치할 수 있는 패키지로 만드는 일이다.
apply --yes를 눌러 CLAUDE.md에 한 줄 넣는 방식이라 실제 반영은 0건이고, 에이전트가 git push를 치려는 그 순간에 "지난주 같은 상황에서 이렇게 실수했다"는 말을 아무도 해주지 않는다. 계획의 핵심은 ① lessons 테이블(후보 → 승인 → 은퇴)과 ② 150ms 안에 답하는 agent-trace recall, ③ 에이전트마다 다른 전달 어댑터(Claude Code·Codex는 훅, Hermes는 pre_llm_call, OpenCode·OpenClaw는 플러그인, 공통 폴백은 MCP), ④ OpenClaw·OpenCode 파서, ⑤ uv tool install + agent-trace init 한 줄 설치와 lessons 파일의 git 공유다. 총 4개 플랜, 약 17 작업일.
Plan 1~3으로 수집·판정·제안까지는 동작한다. 숫자는 2026-09-21 로컬 SQLite 스냅숏.
그림 1. 파이프라인은 오른쪽 끝까지 흐르지만, 에이전트로 돌아오는 화살표는 점선(기대)이다.
| # | 갭 | 지금 | 왜 문제인가 |
|---|---|---|---|
| G1 | 전달 | apply가 CLAUDE.md/AGENTS.md 끝에 한 줄 append. 사람이 --yes를 눌러야 하고, 상황(어느 저장소·어느 명령)과 무관하게 항상 읽힌다. | 목표는 "작업 수정 직전에 관련 교훈을 읽는 것"인데, 그 시점에 개입하는 경로가 없다. 정적 파일은 길어질수록 무시되고, 실제 반영은 0건. |
| G2 | 수집 범위 | 파서 3개(Claude Code·Codex·Hermes). | 팀은 OpenClaw·OpenCode도 쓴다. 이 둘의 실수는 오답노트에 들어오지 않는다. 로컬에 OpenCode 13세션·OpenClaw 5세션 기록이 있는데 읽지 않는다. |
| G3 | 배포 | 개발자 본인 머신 전제. uv run --project 절대경로, plist 수동, Phoenix 필수. | 팀원에게 "설치"로 전달할 수 없다. 훅 설정·서비스 등록·초기 백필을 손으로 해야 한다. |
에이전트 다섯이 남긴 기록 → 오답노트 → 다섯 에이전트가 일하는 세 순간에 되돌아간다.
그림 2. 목표(to-be). 초록 화살표가 그림 1의 점선을 실선으로 바꾼다. 세 번째 시점(툴 호출 직전)이 이 계획의 존재 이유다.
lessons approve 할 때까지 전달되지 않는다. 자동으로 에이전트 컨텍스트에 들어가는 것은 승인분만.pre_llm_call만 컨텍스트 주입 가능, OpenCode·OpenClaw는 플러그인. MCP는 다섯 모두의 공통 폴백.지금의 verdicts.proposal은 문장일 뿐이다. 교훈이 되려면 언제 꺼낼지(trigger)와 상태(승인 여부)가 붙어야 한다.
그림 3. 기존 테이블은 건드리지 않는다. lessons는 verdicts로부터 결정적으로 다시 만들 수 있어야 하고(id = 정규화 텍스트 sha1), 승인·은퇴 상태만 사람이 바꾼다.
# lessons 한 행 (예 — 12.2 실검증에서 나온 판정을 교훈으로 바꾼 것)
id = 316a5efb
text = 사용자가 준 파일 경로는 타이핑하지 말고 그대로 복사해 쓴다. jq 필터에 or를 쓸 때는 괄호로 우선순위를 명시한다.
category = tool_misuse severity = 1
tool = codex scope_cwd = /Users/chulsu/git-projects/ppi
trigger_event = pre_tool trigger_tool = Bash trigger_pattern = \bjq\b|cat\s+/
status = approved count = 1 hits = 0
# recall이 에이전트에게 실제로 건네는 텍스트 (최대 1,500자, 상위 3건)
[agent-trace 오답노트 · ppi · Bash]
- (316a5efb) 사용자가 준 파일 경로는 타이핑하지 말고 그대로 복사해 쓴다. jq or 필터는 괄호로 우선순위 명시. (근거 1턴, 2026-09-14)
- (…) …
| 명령 | 하는 일 | 파일 쓰기 |
|---|---|---|
lessons harvest [--since-days 30] [--min-severity 2] | verdicts(+human_marks)에서 후보 교훈 생성·갱신. 기존 propose는 이 명령의 표 출력 별칭으로 남긴다. | DB만 |
lessons list [--status …] [--cwd …] | 오답노트 보기(상태·히트·근거 턴). | 없음 |
lessons approve <id> [--scope project|global] [--trigger pre_tool:Bash:'regex'] [--text …] | 후보 → 승인. 상황(trigger)과 문구를 사람이 다듬는다. | DB만 |
lessons add --text … --trigger … | judge 없이 사람이 직접 오답노트에 쓴다(가장 빠른 경로). | DB만 |
lessons retire <id> | 은퇴. 전달 중단, 기록은 보존. | DB만 |
recall --agent … --event … --cwd … [--tool-name … --input-json …] [--format hook|hermes|text|json] | 지금 상황에 맞는 승인 교훈 상위 K건을 에이전트 형식으로 출력하고 lesson_hits에 기록. | DB만 |
apply <id> --yes (기존) | 정적 파일에 한 줄 append. 훅을 못 쓰는 환경의 폴백으로 유지. | 사용자 파일(승인 필수) |
가장 가치 있는 시점은 ③ 툴 호출 직전이다. 에이전트가 git push --force를 실행하려는 바로 그때 훅이 끼어들어 교훈을 건넨다.
그림 4. 훅은 "조언자"다. 툴을 막지 않고, 늦으면 조용히 물러난다. 이 두 규칙이 없으면 팀원이 훅을 끈다.
| 에이전트 (로컬 버전) | ① 세션 시작 | ② 프롬프트 | ③ 툴 직전 | 설치 위치 | 확인 근거 |
|---|---|---|---|---|---|
| Claude Code 2.1.278 | SessionStart | UserPromptSubmit | PreToolUse matcher Bash|Edit|Write | ~/.claude/settings.json hooks (JSON 병합) | 공식 훅 레퍼런스. additionalContext 10,000자 상한, resume 시 stale |
| Codex CLI 0.155.1 | SessionStartcodex exec에서는 미실행 | UserPromptSubmit | PreToolUse | ~/.codex/hooks.json (Claude와 같은 스키마, 이미 herdr·orca 훅이 있어 병합 필수) | 로컬 hooks.json·config.toml의 hooks = true 확인. exec 신뢰 이슈 #46210 |
| Hermes 0.16.0 | on_session_start 주입 불가·기록만 | pre_llm_call{"context": …} | pre_tool_call 차단만 가능, 주입 불가 | ~/.hermes/config.yaml hooks: (현재 {}) | 공식 훅 문서: 컨텍스트 주입은 pre_llm_call만. 이슈 #44582(pre_tool_call 미호출) 주의 |
| OpenCode 1.17.9 | event(session.created) | chat.message parts에 텍스트 추가 — 스파이크 필요 | tool.execute.before args 변경·차단 | ~/.config/opencode/plugins/agent-trace.js (이미 herdr 플러그인 있음) | Plugin API. experimental.chat.system.transform은 변경이 버려짐(이슈 #17100) → 쓰지 않음 |
| OpenClaw 2026.5.3 | before_prompt_buildprependSystemContext | agent_turn_prepareappendContext | before_agent_run 차단만 | 플러그인(~/.openclaw/extensions/, openclaw.json plugins.allow) | 공식 플러그인 훅 문서. 툴 단위 훅은 없음 → ①②로 대체 |
| 공통 폴백 | MCP 서버 agent-trace mcp — 툴 recall_lessons(cwd, intent)·record_lesson(text). 다섯 모두 MCP 지원. 에이전트가 스스로 호출해야 하므로 지침 파일에 "수정 전 recall_lessons 호출" 한 줄이 필요 | 각 에이전트 MCP 설정 | OpenCode는 이미 codegraph MCP를 로컬 config에 등록해 쓰고 있음 | ||
chat.message 주입이 실제로 LLM에 닿는지 스파이크로 먼저 확인한다(Plan 5 T4). 확인 전에는 MCP 폴백으로 간다.status=approved ∧ (tool ∈ {any, 요청 에이전트}) ∧ (scope_cwd가 NULL이거나 요청 cwd의 조상 경로) ∧ trigger_event 일치 ∧ (trigger_tool·trigger_pattern이 있으면 tool_name·tool_input에 매칭).severity × count 내림차순, 같은 세션에 이미 전달한 교훈은 후순위(같은 세션 반복 주입 방지 — lesson_hits 조회).lessons(status, tool, trigger_event) 인덱스, 정규식은 approve 시 컴파일 검증. recall 경로는 anthropic·opentelemetry·phoenix를 import하지 않는다(현재 cli.py가 상단에서 전부 import하므로 지연 import로 재배치 — 측정 후 결정).둘 다 로컬에 기록이 이미 있고 형식을 확인했다. 기존 파서 3개와 같은 Parser 계약(파일/DB → Session + Turn[])으로 추가한다.
| 에이전트 | 기록 위치 (로컬 확인) | 형식 | 턴 경계 | 서브에이전트 | watch 방식 |
|---|---|---|---|---|---|
| OpenCode | ~/.local/share/opencode/opencode.db (SQLite · drizzle · 13세션 · 199메시지) | 테이블 session / message / part. message.data·part.data가 JSON. assistant 메시지에 tokens·modelID·providerID·time.created/completed·finish, part에 type:"tool"·tool·callID·state.input/output/status | user 메시지 → 다음 user 메시지 전까지. parentID로 연결 | session.parent_id | Hermes와 같은 폴링(WAL 모드, 읽기 전용 ?mode=ro) |
| OpenClaw | ~/.openclaw/agents/<agentId>/sessions/*.jsonl + *.trajectory.jsonl + sessions.json (5세션) | 세션 jsonl은 id/parentId 트리(model_change·custom·message…). trajectory는 openclaw-trajectory schema v1: session.started · prompt.submitted · context.compiled · model.completed · session.ended · trace.artifacts(로컬 665회) | prompt.submitted ~ 다음 prompt.submitted. trajectory의 runId가 턴 id 후보 | 세션 키 agent:<id>:<…>로 에이전트 구분. 하위 세션은 스파이크로 확인 | Claude Code와 같은 watchdog 파일 이벤트 |
opencode가 열어 두고 있으므로 immutable이 아닌 mode=ro로 열고, 잠금 충돌 시 재시도한다(Hermes state.db와 같은 패턴). OpenClaw은 *.jsonl.reset.<시각> 보관 파일이 함께 있어 glob에서 제외해야 중복 세션이 생기지 않는다. 두 파서 모두 실기록 3~5개를 익명화한 픽스처로 테스트를 고정한다(기존 파서와 동일).--tool 어휘에 openclaw·opencode를 추가하고, Tool enum·suggest_target(OpenCode는 AGENTS.md, OpenClaw은 워크스페이스 AGENTS.md)·resume_command(opencode --session <id>, openclaw agent --session <key> — 정확한 플래그는 구현 시 --help로 확인)를 함께 확장한다.
요청한 두 실수 유형은 지금 룰로도 일부 잡히지만 오탐·미탐이 알려져 있다(스펙 12.7).
| 실수 유형 | 지금 잡는 룰 | 알려진 문제 | 보강 |
|---|---|---|---|
| 멈춤 (오래 걸림·중단) | slow_turn(도구별 p95), aborted | judge 5건 중 slow_turn 3건이 실은 툴 wall time 또는 AskUserQuestion 대기였다(오탐 2/3). Claude Code aborted는 항상 False. | latency 분해: latency_ms = agent_ms + tool_wall_ms + user_wait_ms로 turns에 컬럼 3개 추가(마이그레이션 7). slow_turn은 agent_ms 기준. 새 룰 stall: 턴 안에서 연속 이벤트 간격이 5분 초과이면서 툴 실행 중도, 사용자 대기도 아닌 구간. evidence에 임계값 출처(p95/60s floor/300s 기본)를 명시(M1). |
| 툴 오작동 (오류 반복·헛돌기) | tool_loop(동일 호출 ≥3), tool_error_ratio(≥0.4) | 같은 명령을 인자만 조금 바꿔 반복하는 경우는 tool_loop에 안 걸린다. 오류 문자열이 같은데 입력만 다른 반복도 미탐. | tool_loop에 정규화 키(경로·숫자를 마스킹한 명령) 추가, 새 룰 same_error_repeat: 동일 오류 시그니처(앞 80자) ≥3. 둘 다 evidence에 명령 헤드를 남겨 교훈의 trigger_pattern 추정에 재사용. |
룰 추가는 기존 evaluate_turn에 함수 하나씩 붙이는 일이고, 각 룰은 양성/음성 픽스처 1쌍으로 고정한다. aborted의 Claude Code 미감지는 트랜스크립트에 abort 이벤트가 구분되어 있지 않은 문제라 이 계획에서도 해결하지 않고 한계로 남긴다.
설치는 두 줄, 나머지는 init이 한다. Phoenix는 선택이다.
그림 5. 개인 데이터(턴·프롬프트)는 머신을 떠나지 않는다. 팀에 가는 것은 사람이 승인하고 다듬은 교훈 문장과 trigger뿐이다.
| 항목 | 결정 | 이유 |
|---|---|---|
| 배포 형태 | uv tool install git+https://github.com/Dobraindev/agent-trace (1차), pipx 동등. Homebrew tap은 후순위 | 이미 pyproject에 [project.scripts]가 있어 wheel만 만들면 된다. 팀은 uv를 쓴다 |
| 의존성 분리 | anthropic·arize-phoenix-client·opentelemetry-*를 extras([judge], [phoenix])로 이동. 기본 설치 = 파서 + SQLite + recall + 훅 | recall 기동 시간과 설치 크기. Plan 3 리뷰에서도 anthropic 필수 의존을 지적(플랜 레벨) |
| Phoenix | 기본 제외(--no-export가 기본), init --with-phoenix로 켬 | 팀원 대다수는 뷰어보다 오답노트만 필요. Docker 필수 조건을 없앤다 |
| 설정 병합 | JSON은 키 병합 + "_agent_trace": true 마커, YAML은 # agent-trace:begin/end 블록, 쓰기 전 .bak-YYYYMMDD 백업, init 재실행 멱등, uninstall로 마커 블록만 제거 | 로컬만 봐도 ~/.codex/hooks.json과 OpenCode plugins에 herdr·orca 훅이 이미 있다. 덮어쓰면 다른 도구가 깨진다 |
| 서비스 | init --service가 사용자 홈 기준 plist를 생성·load(Linux는 systemd user unit). uv tool 바이너리 경로를 자동 기입 | README의 plist가 절대경로 하드코딩(G3) |
| 산출물 경로 | reports/·proposals/를 ~/.local/share/agent-trace/ 아래로 이동(M8) | 어느 디렉터리에서 실행해도 한곳에 모임 |
가치가 큰 순서다. Plan 4만 끝나도 가장 많이 쓰는 두 에이전트(Claude Code·Codex)가 작업 직전 교훈을 받는다.
그림 6. 각 마일스톤은 "테스트 통과"가 아니라 "실제 에이전트에서 실측"이다. Plan 3에서 실검증이 스펙과 다른 API 계약 버그를 잡아냈다.
| Task | 내용 | 완료 기준 |
|---|---|---|
| T1 | 마이그레이션 7: lessons·lesson_hits + turns의 latency 분해 컬럼. lessons harvest/list/approve/add/retire. propose는 harvest의 표 출력으로 재배선 | fresh/기존 DB 마이그레이션 테스트. harvest가 결정적 id로 재실행 멱등. approve 없이는 recall 결과 0건 |
| T2 | recall 엔진: 매칭·랭킹·상한·hits 기록. --format hook|hermes|text|json. 무거운 import 지연 로딩 | 매칭 규칙 각 1쌍 테스트. time agent-trace recall … p95 < 150ms(로컬 100회). 실패 시 종료코드 0·빈 출력 |
| T3 | Claude Code·Codex 훅 어댑터: 셸 래퍼 스크립트 생성, settings.json/hooks.json 병합기(마커·백업·멱등), PreToolUse matcher Bash|Edit|Write|MultiEdit | 병합기 테스트(기존 훅 보존·재실행 무변경·제거). 실제 두 에이전트에서 3시점 훅 호출 로그 확인 |
| T4 | 룰 보강: latency 분해 백필, slow_turn을 agent_ms 기준으로, 새 룰 stall·same_error_repeat, tool_loop 정규화 키, evidence에 임계값 출처(M1) | 룰별 양성/음성 픽스처. 실 DB 재flag 후 slow_turn 건수 변화와 judge ok 재분류 비율 기록 |
| T5 | 실검증: 이 저장소(agent-trace)와 ppi에서 훅 켠 채 3일 사용 → hits·주입 텍스트·체감 지연 기록. 스펙 §13 + README | M1: git push/rm 류 Bash 직전 주입 로그 ≥1건, 승인 교훈 ≥5개, 훅으로 인한 에이전트 차단·오류 0건 |
| Task | 내용 | 완료 기준 |
|---|---|---|
| T1 | OpenCode 파서(SQLite ro, message/part JSON → Turn, parent_id 서브세션) + 폴링 watch | 익명화 픽스처 3세션 테스트. 로컬 13세션 backfill 성공·턴 수 기록 |
| T2 | OpenClaw 파서(sessions jsonl 트리 + trajectory 보조, reset 파일 제외) + watchdog | 픽스처 3세션. 로컬 5세션 backfill. 하위 세션 유무 스파이크 결과를 스펙에 기록 |
| T3 | Hermes 어댑터: config.yaml hooks.pre_llm_call 블록 병합, {"context": …} 출력 | 실 Hermes 세션에서 주입 확인(hits 1건) |
| T4 | OpenCode 스파이크(반나절): chat.message로 parts 추가 시 LLM 입력에 실제로 닿는지 → 되면 플러그인 구현, 안 되면 tool.execute.before + MCP 폴백으로 확정 | 스파이크 결과(닿음/안 닿음)를 근거 로그와 함께 스펙에 기록. 어느 쪽이든 hit 1건 |
| T5 | OpenClaw 플러그인: before_prompt_build의 prependSystemContext로 recall 결과 주입, openclaw.json plugins.allow 병합 | 실 OpenClaw 턴에서 주입 확인 |
| T6 | MCP 서버 agent-trace mcp(stdio): recall_lessons·record_lesson. init에서 선택 등록 | Claude Code·OpenCode에서 MCP 툴 호출 1회씩 실측 |
| T7 | 문서·실검증(M2) | 5개 도구 backfill --tool all 성공, 어댑터 5개 각 hit ≥1 |
| Task | 내용 | 완료 기준 |
|---|---|---|
| T1 | extras 분리([judge]·[phoenix]), --no-export 기본값 전환, 산출물 경로 이동(M8), uv tool install 동작 | 깨끗한 venv에서 기본 설치 후 recall·backfill 동작, judge는 extras 없으면 친절한 오류 |
| T2 | init(감지 → 병합 → 서비스 → 백필), doctor(훅 호출 dry-run·DB·버전·지연), uninstall | init 2회 실행 시 파일 무변경. doctor가 의도적으로 깨뜨린 훅을 잡아냄 |
| T3 | lessons export/import, 세션 시작 훅의 .agent-trace/lessons.jsonl 자동 import, 충돌 규칙(id 동일 → approved_at 최신) | 두 DB 간 왕복 테스트. ppi 저장소에 첫 lessons.jsonl 커밋 |
| T4 | 팀 온보딩 README(설치 2줄·doctor·끄는 법·개인정보 경계), 팀원 1명 신규 설치(M3) | 10분 내 완료, doctor 전부 pass, 첫 hit 확인 |
lesson_hits × flags로 계산해 lessons stats로 출력.| 리스크 | 영향 | 대응 |
|---|---|---|
| 훅 지연이 모든 툴 호출을 느리게 한다 | 팀원이 훅을 끈다 → 계획 무효 | PreToolUse matcher를 Bash|Edit|Write|MultiEdit로 한정, 150ms 예산·타임아웃 시 빈 출력, 지연 import, doctor가 p95 측정 |
| 교훈이 컨텍스트를 오염(노이즈·과다) | 에이전트가 무시하거나 엉뚱한 규칙을 따름 | approved만, K=3·1,500자, 프로젝트 스코프 우선, 같은 세션 반복 주입 억제, 사람이 approve 때 문구 다듬기 |
| 기존 훅 설정과 충돌(herdr·orca가 이미 사용 중) | 다른 도구 훅이 사라짐 | 병합기 + 마커 + 백업 + 멱등 테스트. 덮어쓰기 경로 없음 |
| 에이전트 포맷 변경(OpenCode drizzle 스키마, OpenClaw schema v1, Codex 훅 신뢰 정책) | 파서·어댑터 파손 | 도구 버전별 픽스처 테스트(기존 방식), 파서가 미지 필드는 무시·미지 버전은 경고 후 계속, doctor가 버전 표기 |
Codex exec에서 SessionStart 미실행(이슈 #46210) | 헤드리스 실행에 세션 교훈 누락 | UserPromptSubmit·PreToolUse로 이중화. SessionStart는 보너스로 취급 |
| OpenCode 컨텍스트 주입이 LLM에 닿지 않을 수 있음(이슈 #17100 계열) | OpenCode는 툴 직전 주입 불가 | Plan 5 T4 스파이크 선행. 안 되면 MCP + AGENTS.md 한 줄("수정 전 recall_lessons 호출")로 확정 |
| 팀 공유 파일에 개인정보·비밀이 섞임 | 저장소에 프롬프트 내용 유출 | export는 id·text·trigger·category·severity만. 근거 턴·프롬프트는 export 대상 아님. approve가 사람 검토 단계. AGENT_TRACE_JUDGE_REDACT 기본 on 검토(결정 D5) |
| 훅이 에이전트를 막는 사고(exit code·JSON 오류) | 팀원 작업 중단 | 래퍼 스크립트는 항상 exit 0, 출력은 스키마 검증 후만 emit, 실패는 ~/.local/share/agent-trace/hook.log로 |
| # | 질문 | 권장 |
|---|---|---|
| D1 | 교훈 기본 스코프 — 프로젝트(cwd) vs 전역 | 프로젝트. harvest가 verdict의 cwd를 그대로 붙이고, 사람이 approve 때 --scope global로 승격 |
| D2 | PreToolUse에서 심각(severity 3) 교훈은 차단(deny)까지 할지 | 주입만. 차단은 오탐 한 번에 신뢰를 잃는다. Plan 7 이후 데이터 보고 재검토 |
| D3 | 팀 공유 채널 — 저장소 파일 vs 중앙 서버 | 저장소 파일(.agent-trace/lessons.jsonl). 서버·인증은 범위 밖 유지(스펙 3절) |
| D4 | Phoenix를 팀 배포에 포함할지 | 선택(기본 제외). 뷰어가 필요한 사람만 init --with-phoenix |
| D5 | judge 리댁션 기본값을 on으로 바꿀지 | on. 팀 배포 이후 judge 프롬프트에 남의 저장소 내용이 섞일 가능성이 커진다 |
| D6 | OpenClaw 우선순위(로컬 사용량 5세션) | Plan 5 안에서 OpenCode 다음. 팀 사용량이 더 낮으면 Plan 5에서 빼도 된다 |
| D7 | 플랜 문서를 agent-trace 저장소 docs/superpowers/plans/에도 마크다운으로 둘지 | 둔다(Plan 1~3과 같은 위치). 이 HTML은 팀 설명용, 마크다운은 구현 지시서 |
~/.codex/hooks.json에 herdr·orca 훅이 이미 있음, OpenCode plugins 디렉터리 존재, SQLite 스냅숏 수치.additionalContext 10,000자 상한, Codex 훅 이벤트와 exec 신뢰 이슈, Hermes pre_llm_call만 주입 가능, OpenCode Plugin API 훅 목록과 experimental 훅 폐기 이슈, OpenClaw before_prompt_build 반환 필드.chat.message 주입이 LLM에 닿는지, OpenClaw 하위 세션 구조, resume 명령의 정확한 플래그. 전부 해당 플랜의 스파이크·실검증 Task에서 확정한다.