pax-beehive/paxm · 언어 Go 97% · 기본 저장소 SQLite(FTS5/BM25) · 인터페이스 CLI · MCP · 스킬 · 라이프사이클 훅 · 라이선스 Apache-2.0 · 분석 시점 v0.2.4 · shallow clone 소스 직접 분석)
코딩 에이전트를 써 본 사람은 다 안다. 어제 "우리는 배포를 GitHub Actions로만 한다, 노트북에서 직접 배포 금지"라고 알려줬는데, 오늘 새 세션을 열면 에이전트는 그걸 까맣게 잊는다. 아키텍처 결정, 팀 컨벤션, "이 함수는 건드리지 마라" 같은 운영 제약을 매 세션 처음부터 다시 설명해야 한다. PAXM은 이 반복을 끊는다.
한 세션에서 paxm remember로 결정을 남겨두면, 나중 세션에서 paxm recall로 되살린다. 게다가 패시브(passive) 통합을 켜면, 에이전트가 답하기 전에 관련 맥락을 알아서 불러와 넣어주고, 대화가 끝난 후에 완료된 턴을 조용히 저장한다. 프로바이더가 느리거나 죽어도 코딩 세션 자체는 멈추지 않는다.
메모리 없는 에이전트는 영화 속 매일 기억이 초기화되는 주인공 같다. 실력은 좋은데, 어제 합의한 걸 오늘 또 처음부터 설명해야 한다.
PAXM은 그 사이에 공용 인수인계 노트를 끼워 넣는다. Codex가 적어 둔 결정을 Claude Code가 펼쳐 읽고, OpenCode가 이어 쓴다. 노트는 내 서랍(로컬 SQLite)에 있고, 원하면 팀 공용 캐비닛(Zep·Mem0 등)으로 옮길 수 있다.
중요한 설계 철학이 하나 더 있다: 사용자가 통제권을 쥔다. 자격증명, 훅 신뢰(hook trust), 라우팅, 데이터 위치, 비활성화·삭제·롤백이 전부 사용자 소유다. 에이전트 툴(recall/remember)은 훅을 설치하거나 자격증명을 바꾸거나 라우팅을 조작할 수 없다 — 에이전트가 몰래 내 메모리 설정을 장악하지 못하도록 권한을 최소화했다.
AI 메모리 도구는 이미 여럿 있다(Mem0, Zep, MemGPT 계열 등). 그런데 PAXM이 트렌딩에 오른 건 접근각이 다르기 때문이다. 네 가지 결이 겹친다.
대부분의 메모리 시스템은 시작부터 임베딩 모델 + 벡터 DB + API 키를 요구한다. PAXM의 기본값은 로컬 SQLite 하나다. 임베딩도, 외부 LLM 호출도, 계정도 없이 바로 "기억하고 되살리는" 완결된 루프가 돈다. 검색은 SQLite에 내장된 FTS5 전문검색 + BM25 랭킹으로 처리한다. 벡터가 아니라 단어 기반이라 결정론적이고 빠르며, 오프라인에서도 완전히 동작한다.
로컬에서 시작했다가 팀 규모가 커지면 전용 메모리 시스템으로 옮기고 싶어진다. PAXM은 이때 에이전트를 다시 배선하지 않는다. 프로파일(profile) 설정만 바꾸면 된다. 기본 내장 프로바이더만 해도 SQLite · Zep · Mem0 · Mem0 Cloud · MemOS · MemOS Cloud · OpenViking, 여기에 커스텀 JSON-RPC로 사내 비공개 메모리 시스템까지 붙일 수 있다. 여러 프로바이더를 동시에 켜고 라우팅할 수도 있다.
Codex에서 붙잡은 결정을 Claude Code·OpenCode·Pi·아무 MCP 클라이언트에서 되살린다. 도구가 달라도 기억은 하나다. "어제 Codex랑 정한 걸 오늘 Claude Code가 모른다"는 단절을 없앤다. 이건 특정 벤더 툴에 락인되지 않으려는 개발자에게 특히 매력적이다.
메모리 기능을 켜면 흔히 생기는 걱정이 "메모리 서버가 느리면 코딩도 같이 느려지지 않나?"이다. PAXM은 패시브 쓰기를 로컬 durable 큐에 먼저 커밋하고, 프로바이더 전달은 백그라운드에서 재시도한다. 패시브 recall도 전체 800ms·프로바이더당 250ms 예산을 걸어 느린 프로바이더가 있어도 건강한 부분 결과만 받고 넘어간다. 지연·실패가 세션을 막지 않는다.
README는 초기 LoCoMo 평가에서 SQLite 턴 메모리가 30문제 중 13개, Mem0 기본값이 11개를 맞혔다고 밝히면서도, "이건 공식 점수가 아니라 탐색적 결과이며, 한 번의 대화만 다뤘다"고 스스로 못박는다. 성능을 과장하지 않고 한계를 먼저 적는 태도가, 이 프로젝트의 엔지니어링 신뢰도를 오히려 높인다.
| 구분 | 전형적 메모리 서비스 | PAXM |
|---|---|---|
| 시작 조건 | 계정 + API 키 + 임베딩 모델 | 없음 — 로컬 SQLite 바로 시작 |
| 검색 방식 | 벡터 임베딩(외부 모델 호출) | FTS5 + BM25 (단어 기반, LLM 불필요) |
| 백엔드 | 그 회사 저장소 고정 | 7종 내장 + 커스텀 JSON-RPC 교체 |
| 에이전트 범위 | 대개 특정 툴 | Codex·Claude·OpenCode·Pi·MCP 공통 |
| 느린 백엔드 | 세션이 같이 느려짐 | durable 큐 + 타임아웃 예산으로 격리 |
| 데이터 소유 | 클라우드 | 로컬 우선, 자격증명·위치 사용자 소유 |
PAXM의 코드를 열면 가장 먼저 놀라는 건 의존성이 이상하리만치 적다는 점이다. go.mod의 직접 의존성은 단 7개. CLI 프레임워크도, MCP SDK도 외부 라이브러리를 쓰지 않고 손으로 짰다. 이건 "코딩 에이전트에 설치되는 도구"라서 가볍고, 크로스 컴파일이 쉽고, 공급망 위험이 작아야 한다는 판단으로 읽힌다.
// module github.com/pax-beehive/paxm — go 1.25.0
charm.land/huh/v2 v2.0.0 // 대화형 setup TUI 폼
charm.land/lipgloss/v2 v2.0.1 // 터미널 스타일링
github.com/charmbracelet/x/term v0.2.2
github.com/getzep/zep-go/v3 v3.23.0 // Zep provider 공식 SDK
golang.org/x/sys v0.44.0
gopkg.in/yaml.v3 v3.0.1 // config.yaml 파싱
modernc.org/sqlite v1.53.0 // 순수 Go SQLite (cgo 불필요)
| 영역 | 선택 | 왜 이렇게 했나 |
|---|---|---|
| 언어 | Go 1.25.0 | 단일 정적 바이너리 → macOS·Linux·Windows(amd64·arm64) 한 번에 배포 |
| SQLite 드라이버 | modernc.org/sqlite | 순수 Go 구현. cgo·C 컴파일러 없이 크로스빌드(가장 흔한 mattn/go-sqlite3가 아님) |
| CLI | 표준 flag + 자작 디스패처 | cobra 안 씀. 의존성·바이너리 크기 최소화 |
| MCP 서버 | 자작 JSON-RPC 2.0 | MCP SDK 안 씀. stdio로 프로토콜 직접 구현(internal/mcp/server.go) |
| 대화형 setup | Charm 스택(huh·lipgloss·bubbletea) | 사용자 ID·프로바이더·통합을 고르는 TUI 폼 |
| 설정 | yaml.v3 | config.yaml — 프로바이더·프로파일·에이전트·훅·텔레메트리 |
| 외부 SDK | zep-go/v3 (Zep만) | 나머지 프로바이더는 표준 net/http로 직접 호출 |
modernc.org/sqlite는 C로 된 SQLite를 Go로 트랜스파일한 구현이다. 보통 SQLite를 쓰면 C 컴파일러가 필요해 크로스 컴파일이 까다로운데, 이건 순수 Go라 GOOS=windows go build 한 줄로 다른 OS 바이너리를 뽑을 수 있다. "설치가 쉬운 CLI 도구"에는 결정적 이점이다.SQLite 헬스체크는 설정된 DB 옆에 WAL/SHM 파일을 만들 수 있어야 한다. DB는 읽히는데 부모 폴더에 쓰기가 막힌 샌드박스에서는 SQLite error 14가 뜬다. 샌드박스 평가 때는 쓰기 가능한 격리 경로를 SQLite 경로로 지정해야 한다(같은 설정이 실제 에이전트 프로세스에선 정상일 수 있음).
PAXM은 스스로를 "메모리 서비스가 아니라 메모리 어댑터"라고 규정한다. 전체 데이터 흐름은 한 줄로 요약된다:
internal/memory/types.go)어떤 백엔드든 PAXM에 붙으려면 단 4개 메서드만 구현하면 된다. 나머지 고급 기능(배치 쓰기·삭제·만료 정리 등)은 옵셔널 인터페이스(capability)로 분리해, 지원하는 프로바이더만 골라 구현한다. Go의 "인터페이스 타입 어서션" 관용구를 교과서처럼 쓴 예다.
// 모든 프로바이더가 반드시 구현하는 최소 계약
type Provider interface {
Name() string
Search(ctx context.Context, query SearchQuery) ([]MemoryHit, error)
Put(ctx context.Context, item MemoryItem) (MemoryRef, error)
Health(ctx context.Context) error
}
// 지원하는 프로바이더만 추가로 구현하는 옵셔널 capability
type BatchProvider interface { PutBatch(...) ([]MemoryRef, error) }
type TurnBoundaryProvider interface { PreserveTurnBoundaries() bool }
type CleanupExpiredProvider interface { CleanupExpired(...) (int, error) }
// 그 외 CloseProvider / DeleteProvider / EvalScopeCleaner ...
저장 단위인 MemoryItem·MemoryHit은 Origin(누가 만들었나 — user·agent·session·turn)과 Scope(가시성 경계)를 분리한다. 메모리 계층은 stm(단기, 작업 메모리)과 ltm(장기, 영속 사실) 2단계다.
internal/memory/router.go)
여러 프로바이더를 동시에 켰을 때의 오케스트레이터다. 핵심은 1-슬롯 벌크헤드(bulkhead). 프로바이더마다 크기 1짜리 채널(searchSlot·putSlot chan struct{})을 두어, 어떤 프로바이더가 슬롯을 잡고 늦으면 그 프로바이더로 가는 후속 요청은 빠르게 실패(fail-fast)한다. 한 곳이 막혀도 전체가 멈추지 않는다.
runSearch/runPut이 각 프로바이더에 context.WithTimeout을 건다.required 프로바이더의 에러만 전체 실패로 전파. best-effort는 실패해도 나머지 건강한 결과를 유지.정규화 relevance / (프로바이더 순위)²로 상위 프로바이더에 가중, 여기에 recency boost와 텍스트 기반 중복 제거를 얹는다.에이전트가 PAXM에 닿는 길은 두 가지다. 둘 다 같은 런타임·같은 프로바이더 라우터를 쓰므로 필터링·프로파일·랭킹·타임아웃이 일관된다.
| 경로 | 진입점 | 쓰임새 |
|---|---|---|
| 액티브(Active) | CLI · MCP · 스킬 | 의도적 recall, 명시적 쓰기, 검사(inspection) |
| 패시브(Passive) | 에이전트 라이프사이클 훅 | 프롬프트 직전 자동 recall + 턴 종료 시 자동 캡처 |
패시브 훅은 internal/capture/handler.go가 처리한다. 훅 종류는 user_input(recall+write), tool_use·tool_failure, turn_end. 첫 user_input이면 느슨한 passive_initial 프로파일로 초기 recall을 하고, 이후는 passive 프로파일(결과 2개, 최소 관련도 0.75)로 좁혀 잡는다.
internal/capturequeue/queue.go)패시브 쓰기는 프로바이더로 바로 보내지 않는다. 먼저 로컬 SQLite durable 큐에 커밋하고, 전달은 백그라운드에서 lease·재시도로 처리한다. 훅은 큐에 durable append만 하고 즉시 반환하므로, 프로바이더 지연이 세션을 막지 않는다. 큐는 3개 테이블로 상태를 관리한다.
// capturequeue — 3개 테이블 상태 머신 (modernc sqlite 백엔드)
capture_events // 원시 훅 이벤트 (session_key, sequence …)
capture_episodes // 세션별 묶음 (checksum, 누락 sequence 추적, complete)
capture_deliveries // 프로바이더별 전달 (state, lease_until, next_attempt_at)
// 크래시 복구: 시작 시 state='delivering' → 'retry'로 리셋
RecoverDelivering() // 중단된 전송을 재시도 가능 상태로 되돌림
// RetryMin 기본 1s · MaxAttempts 기본 10 · 초과 시 'dead'(dead-letter)
패시브 recall로 불러온 맥락을 그대로 다시 저장하면, 같은 기억이 무한히 자기 복제되며 부풀어 오른다. PAXM은 패시브 쓰기 전에 recall provenance를 벗겨내(strip) 이 메아리를 차단한다. 또 정확 일치 LTM 통합으로 중복 누적을 제한한다. 메모리 시스템을 오래 굴려 본 사람만 아는 함정을 미리 막아 둔 설계다.
진입점 cmd/paxm/main.go는 14줄뿐이다 — cli.MainWithDependencies(...)만 호출한다. 실제 로직은 전부 internal/ 아래 22개 서브패키지에 모여 있다. Go의 internal/ 규칙(외부 임포트 차단)으로 공개 API 표면을 좁게 유지하는 전형적 구성이다.
이 레포를 처음 읽는다면 memory/types.go(계약) → memory/router.go(오케스트레이션) → adapters/sqlite(기본 구현) → adapters/jsonrpc(확장 방식) 순서를 추천한다. "인터페이스를 먼저, 그다음 그 인터페이스를 구현한 가장 단순한 백엔드, 마지막에 외부 확장 규격"으로 내려가면 설계 의도가 자연스럽게 보인다. mcp/server.go와 capturequeue/queue.go는 각각 "MCP를 손으로 짜면 이렇게 된다"·"durable 큐를 SQLite로 짜면 이렇게 된다"의 독립 교재로 읽어도 좋다.
"모든 백엔드가 구현할 4개 메서드"와 "지원하는 백엔드만 구현할 옵셔널 인터페이스"를 나눈 설계는, 플러그형 시스템을 확장 가능하게 만드는 정석이다. 새 capability가 생겨도 기존 프로바이더 코드를 건드리지 않는다. Go의 인터페이스 타입 어서션(if p, ok := prov.(BatchProvider); ok)을 실무 규모로 어떻게 쓰는지 배울 수 있다.
분산 시스템 신뢰성의 핵심 패턴이 router.go 한 파일에 응축돼 있다. 1-슬롯 채널로 느린 의존성을 fail-fast시키고, context.WithTimeout으로 각 호출에 예산을 매기고, required/best-effort로 실패 전파를 제어한다. "외부 서비스에 의존하는데 그게 느려질 때 내 시스템은 어떻게 버티는가"의 실전 답안이다.
벡터 DB가 유행이지만, PAXM은 SQLite FTS5 + BM25만으로 쓸 만한 메모리 검색을 만든다. 배울 점: FTS5 가상 테이블과 동기화 트리거, BM25는 "낮을수록 좋음"이라 rawScore = -rank로 부호를 뒤집는 정규화, 그리고 3단계 폴백 플랜(정확 구절 → 모든 단어 → 부분 일치)으로 "빈손 결과"를 줄이는 기법. camelCase·snake_case·CJK 분해와 8KiB 초과 히트의 질의-관련 발췌(excerpt)까지 결정론적으로 처리한다.
// adapters/sqlite/provider.go — FTS5 가상 테이블 + 동기화 트리거
CREATE VIRTUAL TABLE memory_fts USING fts5(
text, source,
content='memories', content_rowid='rowid',
tokenize='unicode61'
);
// AFTER INSERT/DELETE/UPDATE 트리거로 memories ↔ memory_fts 동기화
// PRAGMA: journal_mode=WAL · busy_timeout=5000 · SetMaxOpenConns(1)
// retrieval/search.go — BM25는 작을수록 좋아 부호를 뒤집는다
ORDER BY bm25(memory_fts) ASC, m.created_at DESC LIMIT ?
const RawScoreKind = "sqlite_fts_bm25_negated" // rawScore = -rank
"쓰기를 잃지 않으면서 백엔드 지연을 흡수"하려면 durable 큐가 필요하다. capturequeue/queue.go(1133줄)는 이벤트를 세션별 episode로 묶고, lease·재시도·dead-letter 상태 머신으로 전달하며, 시작 시 RecoverDelivering()으로 중단된 전송을 되살린다. SQLite로 신뢰성 있는 작업 큐를 짜는 완결된 예제다.
언어에 상관없이 메모리 백엔드를 붙이는 방법이 adapters/jsonrpc에 있다. PAXM은 요청 1건마다 프로바이더 프로세스를 새로 띄우고, stdin으로 요청 하나를 쓰고, stdin을 닫고, stdout에서 응답 하나를 읽은 뒤 프로세스를 종료한다. 상태는 프로세스 밖에 영속화한다. "표준 입출력만으로 만드는 언어 중립 플러그인 규격"의 교과서다.
internal/mcp/server.go(602줄)는 MCP SDK 없이 JSON-RPC 2.0을 직접 구현한다. protocolVersion "2025-11-25", 개행 구분 JSON 읽기/쓰기, 툴 4개(paxm_recall·paxm_remember·paxm_history·paxm_config_doctor)의 inputSchema를 손으로 구성한다. MCP 프로토콜 내부가 어떻게 생겼는지 뜯어보기에 이상적이다.
CLI 명령을 operator / tool / internal 3분류로 나눠, 에이전트가 부르는 recall·remember·mcp(tool 계열)는 훅 설치·자격증명 변경·라우팅 조작을 할 수 없다. setup·config·uninstall 같은 위험한 조작은 operator 계열에만 있고 사람이 실행한다. MCP 서버도 setup·자격증명·훅 설치를 툴로 노출하지 않는다 — 에이전트가 몰래 자기 메모리 환경을 장악하지 못하게 한 경계다.
릴리즈는 macOS · Linux · Windows의 amd64·arm64를 모두 커버한다. 아카이브에 SHA256SUMS가 포함되고, 설치 스크립트가 교체 전 아카이브를 검증한다. 임베딩·GPU·별도 서버가 필요 없다 — 기본 SQLite는 로컬 파일 하나면 된다.
# 설치 (최신 릴리즈 바이너리 검증 후 설치)
curl -fsSL https://github.com/pax-beehive/paxm/releases/latest/download/install.sh | bash
paxm setup # 사용자 ID·프로바이더·통합 대화형 선택
paxm config doctor # 설정·프로바이더 헬스체크
# 첫 루프 검증
paxm remember --profile stm --text "PAXM_FIRST_RECALL_OK"
paxm recall --query "PAXM_FIRST_RECALL_OK"
| 필요 | 버전 / 비고 |
|---|---|
| Go | 1.25.0 (go.mod 기준) |
| C 컴파일러 | 불필요 — 순수 Go SQLite라 cgo 없음 |
| 디스크 | SQLite DB + WAL/SHM 쓰기 가능한 경로 |
go test ./...
go vet ./...
go build -o /tmp/paxm ./cmd/paxm
| 작업 | 어댑터 지연 |
|---|---|
| 128 KiB SQLite 쓰기 | 1.84 ms |
| 2 MiB SQLite 쓰기 | 14.31 ms |
| 10개 / 1.25 MiB 배치 | 12.36 ms |
| 10만 개 짧은 메모리에서 recall | 0.54 ms |
| 1만 개 × 32 KiB 메모리에서 recall | 0.61 ms |
위 벤치마크는 PAXM 어댑터 자체의 지연이지, 에이전트의 종단 응답 시간이 아니다. 실제 체감 속도는 여기에 모델 추론·네트워크가 더해진다. 또 벤치마크는 런타임 생성 임시 데이터셋으로 측정하며, 벤치마크 코퍼스를 레포에 커밋하지 않는다(재현 명령은 docs/benchmarks.md).
바이너리를 설치하고 paxm setup 후, paxm remember --profile ltm --text "우리는 배포를 GitHub Actions로만 한다"로 결정을 남긴다. 그다음 paxm recall --query "배포는 어떻게 하지?"로 되살아나는지 확인하라. 계정·키·임베딩 없이 완결되는 루프를 눈으로 보는 게 목표다.
claude plugin marketplace add pax-beehive/paxm → 플러그인 설치 → paxm setup --integration claude-plugin. 새 세션을 열어, 이전 세션의 결정이 프롬프트 직전에 자동으로 recall되는지 관찰하라. paxm history --days 1로 캡처된 턴도 확인.
internal/adapters/sqlite/retrieval/search.go를 열어 exact_phrase → strict_all_terms → relaxed_partial 3단계가 어떻게 "가장 먼저 결과가 차는 단계"만 반환하는지 읽어라. FTS5로 top-K가 안 차면 LIKE 스캔을 보강해 RRF로 융합하는 지점, 그리고 analyzer.go의 camelCase/CJK 분해를 함께 본다.
examples/jsonrpc-provider/의 참조 구현을 베껴, paxm.health/search/put/capabilities에 응답하는 나만의 프로바이더(아무 언어)를 만든다. paxm eval provider jsonrpc --command ./provider로 블랙박스 적합성을 돌려 통과시켜라. "프로세스-per-요청" 계약(stdin 한 요청 → close → stdout 한 응답 → 종료)을 지키는 게 핵심.
패시브 쓰기가 capture_deliveries에 쌓이는 중에 프로세스를 강제 종료한 뒤 재시작해, RecoverDelivering()이 state='delivering'을 'retry'로 되돌려 전달이 재개되는지 확인하라. MaxAttempts(기본 10)를 넘겨 'dead'로 떨어지는 경로, 그리고 recall provenance 스트리핑으로 "메모리 메아리"가 안 생기는지도 함께 검증한다.
| 주차 | 주제 | 할 것 |
|---|---|---|
| 1주차 | 인터페이스 설계 | memory/types.go 정독 → "최소 계약 + 옵셔널 capability" 패턴 이해 → 직접 더미 프로바이더를 Provider만 구현해 등록해보기 |
| 2주차 | 신뢰성(벌크헤드·타임아웃) | router.go의 1-슬롯 채널·context.WithTimeout·required/best-effort 재현 → config의 800ms/250ms 타임아웃 예산이 어디서 적용되는지 추적 |
| 3주차 | 검색·저장(SQLite/FTS5) | FTS5 가상 테이블·트리거·BM25 학습 → retrieval/의 3단계 플랜·RRF·excerpt 코드 정독 → 임베딩 없는 검색의 한계·강점 정리 |
| 4주차 | 확장(MCP·JSON-RPC·큐) | mcp/server.go로 MCP 프로토콜 뜯기 → adapters/jsonrpc로 언어 중립 플러그인 규격 → capturequeue로 durable 큐 상태 머신 재현 |
이 저장소는 "외부 서비스에 의존하는 신뢰성 있는 어댑터 계층을 어떻게 짜는가"의 종합 교재다. AI 메모리라는 주제를 걷어내고 봐도, 인터페이스 분리 · 장애 격리 · durable 큐 · 언어 중립 플러그인 · 손으로 짠 프로토콜 서버라는 백엔드 엔지니어링 정석이 한 레포에 다 들어 있다. Go로 견고한 CLI/서버를 짜고 싶은 사람에게 특히 값지다.
| 용어 | 뜻 |
|---|---|
| 메모리 어댑터 | 에이전트와 실제 저장소 사이의 얇은 중간층. 에이전트엔 고정 인터페이스, 뒤엔 교체 가능한 백엔드 |
| provider(프로바이더) | 실제 메모리 백엔드. SQLite·Zep·Mem0·MemOS·OpenViking·커스텀 JSON-RPC 등 |
| 액티브 / 패시브 | 액티브=CLI·MCP·스킬로 의도적 recall/write. 패시브=라이프사이클 훅으로 자동 recall/capture |
| stm / ltm | 메모리 계층. stm=단기(작업 메모리), ltm=장기(영속 사실) |
| Origin / Scope | 메모리의 출처(user·agent·session·turn)와 가시성 경계를 분리해 저장 |
| FTS5 / BM25 | SQLite 내장 전문검색 엔진과 랭킹 함수. 임베딩 없이 단어 기반 검색 |
| RRF | Reciprocal Rank Fusion(k=60) — 여러 후보 목록의 순위를 합쳐 하나로 융합 |
| 벌크헤드(bulkhead) | 1-슬롯 채널로 느린 프로바이더를 fail-fast 격리해 전체 멈춤을 방지하는 패턴 |
| required / best-effort | required 실패만 전체 실패로 전파, best-effort는 실패해도 건강한 결과 유지 |
| durable 큐 | 쓰기를 로컬에 먼저 커밋하고 백그라운드 재시도로 전달하는 큐(크래시에도 유실 없음) |
| dead-letter | MaxAttempts(기본 10) 초과로 전달 실패한 항목이 떨어지는 최종 상태('dead') |
| process-per-request | JSON-RPC 프로바이더를 요청 1건마다 새 프로세스로 띄우고 응답 후 종료하는 계약 |
| capability 협상 | paxm.capabilities로 배치·삭제 지원 여부를 물어, 미지원(-32601)이면 개별 put으로 폴백 |
| 메모리 메아리(echo) | recall한 맥락을 그대로 재저장해 같은 기억이 무한 복제되는 현상. provenance 스트리핑으로 차단 |
| MCP | Model Context Protocol — 외부 도구·서버를 표준 방식으로 에이전트에 꽂는 개방 규격 |
internal/memory/types.go(Provider 계약) · internal/memory/router.go(벌크헤드·RRF) · internal/adapters/sqlite/retrieval/(FTS5·BM25·3단계) · internal/mcp/server.go(자작 MCP) · internal/capturequeue/queue.go(durable 큐)docs/architecture.md · docs/provider-adapter-contract.md · docs/jsonrpc-provider-protocol.md · docs/benchmarks.md · evals/locomo/README.mdplugins/paxm-claude(Claude Code) · plugins/paxm-memory(Codex) · skills/paxm/SKILL.md