world-model-mcp 자체 저장소에서 엔드 투 엔드로 실행해 본 5가지 프리미티브 (Primitives)
요약
world-model-mcp 저장소를 활용하여 AI 코딩 에이전트의 컨텍스트 상실 및 환각 문제를 해결하기 위한 5가지 프리미티브를 엔드 투 엔드로 검증했습니다. 특히 PreToolUse 단계에서 학습된 제약 조건을 통해 잘못된 코드 수정(예: console.log 사용)을 사전에 차단하는 메커니즘을 실제 데이터베이스 출력값으로 입증했습니다.
핵심 포인트
- AI 코딩 에이전트가 압축 과정에서 컨텍스트를 잃거나 동일한 실수를 반복하고 환각을 일으키는 문제를 지적함
- PostToolUse 훅을 통해 코드 수정 사항을 기록하고 규칙을 추론하여 제약 조건을 생성함
- PreToolUse 단계에서 임계값을 초과한 규칙 위반 시 도구 실행을 사전에 거부(deny)하는 제어 메커니즘 구현
- 실제 SQLite 데이터베이스와 멱등성이 보장된 스크립트를 통해 실험 결과의 재현성 확보
저는 12일 동안 v0.6.1부터 v0.7.2까지 world-model-mcp의 네 차례 릴리스를 진행했습니다. 핵심 요지는 "AI 코딩 에이전트(AI coding agents)는 압축(compaction) 과정에서 컨텍스트를 잃어버리고, 동일한 실수를 반복하며, 존재하지 않는 API를 환각(hallucinate)한다"는 것입니다. 이에 대해 더 자세히 쓰기 전에, 누군가가 찍은 스크린샷을 보고 제 말을 믿어야 하는 방식이 아니라, 실제 코드베이스와 실제 출력값을 통해 프리미티브(primitives)를 입증하고 싶었습니다. 사용된 코드베이스는 프로젝트 자체의 저장소(repo)입니다. 저는 python -m world_model_server.cli setup을 실행하였고(소스로부터 598개의 엔티티(entities)가 자동으로 시드되었습니다), 이어서 scripts/demo_seed.py를 실행했습니다. 이 스크립트는 Claude Code가 설치된 상태에서 1~2주간의 개발 과정 동안 실제 PostToolUse / record_correction 훅(hook) 활동이 유기적으로 기록했을 법한 소수의 제약 조건(constraints), 사실(facts), 그리고 압축 감사(compaction audit) 행을 삽입합니다. 아래의 모든 출력 블록은 실제 명령어를 실행한 후 실제 SQLite 데이터베이스에서 가져온 그대로의 내용입니다. 저장소를 클론(clone)하고, python -m world_model_server.cli setup을 실행한 다음, python scripts/demo_seed.py를 실행함으로써 여기의 모든 출력을 재현할 수 있습니다. 이 스크립트는 멱등성(idempotent)을 가지며 --dry-run 및 --reset을 지원합니다. 설치: pip install world-model-mcp. 소스: github.com/SaravananJaichandar/world-model-mcp.
- PreToolUse 경계에서 수정을 거부하는 학습된 제약 조건 (A learned constraint denying an edit at the PreToolUse boundary)
개발자가 에이전트를 수정할 때(예:console.log를logger.debug로 다시 작성), PostToolUse 훅(hook)은 차이점(diff)을 기록하고 규칙을 추론합니다. 해당 규칙의 위반 횟수가 임계값(hard-threshold)을 넘어서면(severity=error, count ≥ 3), 도구가 실행되기 전 PreToolUse 단계에서 다음 시도가 거부됩니다. 그래프에 저장된 제약 조건은 다음과 같습니다: { "rule_name": "no-console-log", "severity": "error", "violation_count": 5, "description": "TypeScript 소스에서 console.log() 대신 logger.debug()를 사용하십시오."
운영 로그는 pino를 통해 라우팅됩니다. console.log()는 포맷팅을 우회하여 다운스트림 파서(downstream parsers)를 망가뜨립니다.`, "file_pattern": "*.ts", "examples": [ {"incorrect": "console.log", "correct": "logger.debug"} ] } console.log를 포함한 수정 사항이 PreToolUse 훅에 도달했을 때의 실제 JSON 응답: { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Hard constraint violation: no-console-log (TypeScript 소스에서 console.log() 대신 logger.debug()를 사용하십시오. 운영 로그는 pino를 통해 라우팅됩니다. console.log()는 포맷팅을 우회하여 다운스트림 파서(downstream parsers)를 망가뜨립니다.). 이전에 5회 위반되었습니다." }, "violations": [ { "rule": "no-console-log", "severity": "error", "violation_count": 5, "is_hard": true, "is_defer": false } ] } CLAUDE.md 또는 AGENTS.md에 있는 규칙은 권고 사항이며 모델은 이를 제안으로 취급합니다. 위반 횟수와 수정 단계에서의 강제 경계(enforcement boundary)가 있는 규칙은 구속력을 가집니다. 두 가지 모두 개발자가 에이전트를 교정한다는 동일한 출처를 갖지만, 그 효과는 매우 다릅니다. 2. 기록된 버그 수정 사항이 있는 파일에 대한 수정을 플래그(flag)하는 회귀 경고(regression warning)는 get_related_bugs가 결정 추적(decision traces)과 이전 버그 수정 사실을 탐색합니다. 기록된 수정 사항이 있는 파일에서 validate_change가 실행되면, related-bugs 쿼리가 이전 수정을 드러내고 제안된 변경 사항에 플래그를 지정합니다. 이 프로젝트에는 content-hash 백필(backfill)에 대한 world_model_server/knowledge_graph.py:120-135 파일의 버그 수정 사항이 있습니다(마이그레이션 로직은 컬럼이 생성될 때뿐만 아니라 모든 initialize()에서 실행되어야 합니다). 저는 백필 루프를 제거하는 리팩토링을 제안했고 related-bugs 체크를 실행했습니다: { "risk_score": 0.6, "bugs": [ { "bug_id": "12457e2a-5638-46ec-a9df-02fe13b9c104", "description": "버그 수정: 마이그레이션 이후의 삽입(inserts)을 처리하기 위해 NULL content_hash 백필은 모든 initialize()에서 실행되어야 합니다.
이전 코드에서는 컬럼이 생성될 때만 백필(backfill)을 수행했기 때문에, merge_from 행들이 해싱(hashed)되지 않은 상태로 남아 중복 제거(dedup) 기능이 깨지는 문제가 있었습니다.", "fixed_at": "2026-05-10T10:17:51.737046", "critical_regions": [ {"file": "world_model_server/knowledge_graph.py", "lines": "120-135"} ] ], "warnings": [ "12457e2a-5638-46ec-a9df-02fe13b9c104에 대한 수정 사항 유지 (Lines 120-135): 버그 수정: 마이그레이션 이후의 삽입(inserts)을 처리하기 위해 NULL content_hash 백필은 모든 initialize()에서 실행되어야 합니다. 이전 코드에서는 컬럼이 생성될 때만 백필을 수행했기 때문에, merge_from 행들이 해싱되지 않은 상태로 남아 중복 제거(dedup) 기능이 깨졌습니다." ] } 위험 점수는 제안된 변경 사항이 수정 사항을 재구현하지 않은 채로 임계 영역(critical region)을 건드렸기 때문에 0.6입니다. 경고 텍스트는 에이전트(또는 사람)가 해당 영역이 단순히 중요하다는 것뿐만 아니라 왜 중요한지를 알 수 있도록 원래의 버그 설명을 직접 인용합니다. 3. 신뢰도(confidence) + 소스 수(source-count) 가중치로 해결된 모순. 시간적 계층(temporal layer)은 각 사실(fact)에 신뢰도 점수(confidence score), 소스 수(source_count), 그리고 유효 시간(valid_at timestamp)을 할당합니다. 동일한 엔티티(entity)에 대한 두 사실이 서로 다를 때, find_contradictions는 양측의 메타데이터와 함께 이를 드러내며, resolve_contradiction은 사용자가 설정한 전략을 사용하여 승자를 선택합니다. 동일한 엔티티(http_transport_port)를 가리키는 두 사실의 예시: { "fact_a_id": "e4b2ff84-8c23-4de5-aa9e-8bbb045a4ed5", "fact_b_id": "7fe854f9-d64a-4304-b43a-7d1b126c6ebb", "fact_a_text": "HTTP 전송 리슨 포트 기본값은 8080입니다", "fact_b_text": "HTTP 전송 리슨 포트 기본값은 8765입니다", "similarity_score": 0.929, "both_valid": true, "reason": "동일한 엔티티, 유사한 텍스트", "confidence_a": 0.7, "confidence_b": 0.95, "source_count_a": 1, "source_count_b": 3 } resolve_contradiction(strategy="auto")는 신호 차이(signal gap)가 가장 큰 전략을 선택합니다.
여기서는 소스 개수가 3:1로 다르므로, keep_most_sources를 선택합니다: { "strategy": "keep_most_sources", "winner_id": "7fe854f9-d64a-4304-b43a-7d1b126c6ebb", "loser_id": "e4b2ff84-8c23-4de5-aa9e-8bbb045a4ed5", "resolved_at": "2026-05-21T10:24:16.287368" } 패배한 항목(loser)은 제자리에서 업데이트됩니다: { "id": "e4b2ff84-8c23-4de5-aa9e-8bbb045a4ed5", "fact_text": "HTTP 전송 리슨 포트(listen port) 기본값은 8080입니다", "status": "superseded", "invalid_at": "2026-05-21T10:24:16.285184", "confidence": 0.7 } "현재 무엇이 사실인가요?"라고 묻는 쿼리는 대체된(superseded) 사실을 조용히 건너뜁니다. "2026-05-18에는 무엇이 사실이었나요?"라고 묻는 쿼리는 여전히 해당 사실을 볼 수 있습니다. 이것이 바로 시간 계층(temporal layer)이 제공하는 가치입니다.
- PostCompact 주입 번들(injection bundle) v0.7.0은 에이전트의 컨텍스트가 압축(compacted)된 후, 상위 제약 조건(constraints)과 최근의 정전(canonical) 사실들을 다시 주입하는 PostCompact 훅(hook)을 추가했습니다. 이 번들은 크기가 작으며(설정 가능, 기본값 약 10개의 제약 조건 + 10개의 사실), 우선순위가 지정됩니다.
get_injection_context(event_type="PostCompact", max_constraints=5, max_facts=5)에 의해 반환되는 실제 번들은 다음과 같습니다:
활성 제약 조건 (위반 횟수 기준 상위 항목)
- no-console-log: TypeScript 소스에서
console.log()대신logger.debug()를 사용하세요. 프로덕션 로그는 pino를 통해 라우팅됩니다;console.log는 포맷팅을 우회하며 다운스트림 파서(downstream parsers)를 망가뜨립니다. (5회 위반) - check-twine-before-tag: 태깅하기 전에
python3 -m twine check dist/*를 실행하세요. 태그가 푸시되기 전에 PyPI 메타데이터 오류를 잡아내어 철회(retraction)를 방지합니다. (5회 위반) - tag-before-upload:
twine upload를 하기 전에 항상git tag+git push --tags를 실행하세요. PyPI는 영구적입니다; 태그가 없는 업로드는 wheel 파일을 특정 git 참조(ref)에 고정하지 못하게 합니다. (2회 위반)
최근 정전(canonical) 사실
git tag전에twine upload를 절대 실행하지 마세요. 항상 태그를 달고, 푸시한 다음, PyPI에 업로드하여 게시된 wheel이 실제 git 참조와 매핑되도록 하세요.
Cursor hooks.json은 다음과 같은 스키마를 사용합니다: version: 1 (정수), preToolUse / preCompact / beforeSubmitPrompt 이벤트 이름, failClosed (fail_open 아님), 초 단위의 timeout.
PostCompact 및 UserPromptSubmit 훅 (hooks)은 압축 (compaction) 후 제약 조건 (constraints)과 최근 사실 (recent facts)을 에이전트 컨텍스트 (agent context)에 다시 결합 (splice)하기 위해 additionalContext를 방출합니다. HTTP 전송 (transport)은 Dockerfile.http에서 기본적으로 8765 포트를 사용합니다. docs/deployment/mcp-tunnel.md와 docker-compose.yml을 함께 업데이트하지 않고는 이를 변경하지 마십시오. BetaAbstractMemoryTool 서브클래스 (subclass)는 world_model_server/memory_backend.py에 위치하며, Anthropic SDK Managed Agents의 메모리 경로에서 요구됩니다. 해당 번들 (bundle)이 압축 이벤트 이후 additionalContext로서 에이전트의 작업 컨텍스트 (working context)에 결합됩니다. 동일한 쿼리 (query)가 UserPromptSubmit 시에도 실행되며, 사용자가 방금 질문한 내용에 편향 (biased)됩니다. 압축 감사 로그 (compaction audit log)는 발생한 일을 기록하며, CLI를 통해 쿼리할 수 있습니다: $ world-model audit-compactions --limit 5 1 compaction audit rows 2026-05-21T10:38:01.606771 session=demo-session-1 pre=84320 post=22150 facts_injected=10 constraints_injected=3 event=PostCompact pre=84320, post=22150 — 압축을 통해 약 62k 토큰의 컨텍스트가 삭제되었습니다. 주입 (injection)을 통해 10개의 사실과 3개의 제약 조건이 다시 삽입되었습니다. 감사 행 (audit row)은 나중에 사람이 "에이전트가 무엇을 보았고 무엇을 잃었는가"에 대해 답할 수 있도록 존재합니다.
- 헤드리스 에이전트 (headless agent)를 일시 중지하는 결정 유예 (defer decision). v0.7.0에서는 거부 (deny)와 경고 (warn) 사이에 결정 유예 (defer) 강제 계층 (enforcement tier)을 추가했습니다. violation_count ≥ 5인 경고 수준 (warning-severity) 위반 사항은 클라이언트가 지원을 광고할 때 permissionDecision: "defer"를 반환하므로, 헤드리스 에이전트가 조용히 통과하거나 강하게 차단되는 대신 일시 중지됩니다. 지원을 광고하지 않는 클라이언트는 자동으로 ask로 폴백 (fall back)됩니다. 저는 violation_count=5, severity=warning인 check-twine-before-tag 제약 조건을 가지고 있습니다. Bash 도구 입력이 이에 일치할 때, 훅은 다음과 같이 반환합니다: { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "defer", "permissionDecisionReason": "Recurring warning-level violations (check-twine-before-tag)." } }
Headless agents should pause for confirmation."
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기