omp

⌥ oh-my-pi

AI Coding Agent Harness — 55,000줄의 Rust가 만든 에이전트 생태계

AI & Tools
Agent Harness
Open Source

대부분의 AI 코딩 에이전트는 똑똑한 채팅창이다. 메시지를 주고받고, 코드를 생성하고, 파일을 수정한다. 그런데 막상 써보면 자꾸 틀린 위치에 코드를 끼워넣고, 문법 에러를 만들고, “이 파일을 찾을 수 없습니다” 같은 소리를 한다.

왜일까? 모델은 똑똑한데, 도구가 멍청해서. 모델이 아무리 좋은 코드를 생각해내도, 그걸 파일에 쓰는 도구가 허접하면 결과는 항상 중간 이하다.

oh-my-pi는 이 문제를 정면으로 해부한 프로젝트다. can1357이 Mario Zechner의 Pi를 fork해서 만든, 55,000줄의 Rust 코어 위에 32개의 네이티브 툴을 올린 에이전트 하네스다. 17.4k 스타, MIT 라이선스.

문제: 에이전트가 유능하려면, 모델보다 도구가 중요하다

지금까지 AI 코딩 에이전트 시장은 모델 전쟁이었다. “우리 모델이 SWE-bench에서 1등” “우리 모델이 Claude Code를 이겼다.” 그런데 실제로 써보면 느낀다. 모델이 달라져도 똑같은 문제가 반복된다.

  • 파일 수정이 실패한다: whitespace mismatch, line number drift, “파일이 이미 변경되었습니다”
  • 검색이 느리다: 매번 shell에 grep을 fork-exec로 띄운다
  • 디버깅이 안 된다: print 문 찍는 게 최선이다
  • 상태를 기억 못 한다: 세션 끝나면 리셋, 다음에 또 같은 걸 설명해야 한다

이건 모델 문제가 아니라, 하네스 문제였다. oh-my-pi는 이 통찰에서 출발한다.

이 포스트에서 보여줄 것

  • oh-my-pi가 해결하는 3가지 근본 문제와 그 설계
  • hashline — 내용 기반 편집이 왜 게임 체인저인가
  • 32개 네이티브 툴의 실제 구성과 철학
  • Time-traveling stream rules로 에이전트 행동 제어하기
  • Hindsight 메모리 시스템이 세션을 넘어 학습하는 방법
  • Drewgent가 oh-my-pi에서 배울 점

왜 oh-my-pi인가: 하네스 우선 접근법

oh-my-pi의 가장 큰 결정은 “모델은 교체 가능하다”는 전제에서 시작한다는 거다. 40개 이상의 프로바이더를 지원하고, 역할별로 다른 모델을 배정할 수 있으며 (default/smol/slow/plan), Ctrl+P로 실시간 전환도 된다.

대신 툴에 집중했다. 32개 툴이 모두 같은 인터페이스를 공유한다. read 하나로 로컬 파일, 디렉토리, SQLite, PDF, URL, PR, 이슈를 다 읽는다. 코드 인텔리전스부터 브라우저 자동화, 디버거, 서브에이전트까지 — 같은 read/write 문법 위에서 동작한다.

이 설계의 효과: 모델이 배울 게 적다. 툴마다 다른 인자, 다른 포맷, 다른 시맨틱스를 외울 필요 없이, 익숙한 read/write 패턴으로 모든 걸 할 수 있다. 툴 인터페이스가 단순할수록 모델이 툴을 올바르게 호출할 확률이 올라간다.

Rust 코어의 힘

55,000줄의 Rust는 단순한 자랑이 아니다. grep, glob, find, PTY, syntax highlight, BPE token counting, 이미지 디코딩 — 모두 in-process에서 실행된다. 다른 에이전트는 rg/grep/sed 같은 외부 바이너리에 의존하는데, oh-my-pi는 그걸 프로세스 안에서 직접 처리한다. fork-exec 오버헤드가 사라지고, 크로스플랫폼 호환성 문제도 없다.

실제 숫자: oh-my-pi의 grep은 ripgrep 라이브러리를 직접 링크해서 in-process로 실행한다. Mac, Linux, Windows에서 같은 바이너리로 동작한다. WSL 브릿지가 필요 없다.

Slice 1: Hashline — 내용 기반 편집

기존 에이전트의 가장 큰 약점은 파일 편집의 신뢰성이다. Claude Code는 line number 기반 diff를 쓰고, Codex는 str_replace를 쓴다. 둘 다 문제가 있다:

  • line number는 파일이 조금만 바뀌어도 틀어진다
  • str_replace는 중복 문자열이 있으면 모호하다
  • 에러가 나도 모델이 알아채기 어렵다

oh-my-pi의 hashline은 완전히 다른 접근을 취한다. 편집하려는 대상 코드의 내용 해시를 기준으로 앵커를 잡고, 그 앵커를 찾아서 패치를 적용한다.

효과는 극적이다:

모델 지표 변화
Grok Code Fast 1 1차 시도 성공률 6.7% → 68.3% (10.2배)
Gemini 3 Flash str_replace 대비 +5 pp
Grok 4 Fast 출력 토큰 −61%
MiniMax 통과율 2.1배

“모델을 바꾸지 않고도 성능이 10배 올라갔다.” 이게 하네스가 중요한 이유다. 모델 업그레이드는 기다려야 하지만, 하네스 최적화는 지금 당장 할 수 있다.

거기에 더해: stale anchor detection — 모델이 이미 변경된 파일을 기준으로 편집을 시도하면, 앵커가 일치하지 않아서 패치가 거절된다. “일단 쓰고 보니 망가졌네” 하는 상황이 원천 차단된다.

Slice 2: LSP가 모든 write와 함께한다

대부분의 에이전트는 코드를 수정할 때 파일 시스템만 보고, 언어 서버가 가진 정보를 전혀 활용하지 않는다. “rename symbol”을 요청하면 파일 전체에서 문자열 치환을 한다 — barrel 파일, alias import는 다 깨진다.

oh-my-pi는 LSP를 모든 write의 백엔드로 연결했다. rename을 요청하면 workspace/willRenameFiles를 호출해서 re-export, barrel, aliased import까지 전부 업데이트한다. 진단 정보는 lsp/diagnostics로 실시간 확인하고, 코드 액션까지 자동 적용된다.

여기서 중요한 건 “에이전트가 IDE를 흉내내는 게 아니라, IDE의 인프라를 그대로 가져온다”는 점이다. LSP, DAP, tree-sitter, ast-grep — 기존 개발자 도구 생태계와의 호환성을 유지하면서, 그 위에 에이전트 레이어를 얹는다.

Slice 3: 진짜 디버거를 움직인다

“대부분의 에이전트는 아직 print 문을 뿌리고 있다.” oh-my-pi의 README에 있는 문장이다. 현실이다.

oh-my-pi는 DAP (Debug Adapter Protocol)을 완전히 지원한다. C 바이너리가 segfault 나면 lldb-dap을 붙여서 bad pointer를 찾고, Go 서비스가 hang 걸리면 dlv로 goroutine을 탐색한다. Python process가 wedged면 debugpy로 붙어서 스택을 본다.

이게 가능한 이유: oh-my-pi의 debug 툴은 28개의 DAP 오퍼레이션을 지원한다. breakpoint, step, thread, stack, variable — IDE에서 하던 모든 걸 에이전트가 자동화할 수 있다.

Slice 4: Time-Traveling Stream Rules

에이전트 행동 제어의 고전적인 방법: system prompt에 규칙을 잔뜩 넣는다. 문제는 규칙이 많아질수록 프롬프트가 길어지고, 모델이 규칙을 무시하기 시작한다는 거다.

oh-my-pi의 stream rules는 완전히 다른 접근이다. 규칙은 평소에 비활성 상태로 있다가, 모델이 규칙을 위반하는 순간 실시간으로 중단되고 규칙이 주입된다. 스트림이 mid-token에서 abort되고, 규칙이 system reminder로 들어가고, 같은 지점부터 다시 시작된다.

효과: 규칙이 context tax를 내지 않는다. 50개의 규칙이 있어도 메인 프롬프트는 짧게 유지된다. 규칙이 발동된 후에는 compaction을 견디고 유지되므로, “한 번 교정되면 끝”이 아니라 세션 내내 지속된다.

Drewgent에도 비슷한 니즈가 있다 — “절대 secrets를 평문으로 저장하지 마라” 같은 P0 규칙을 system prompt에 박지 않고, 위반 시에만 작동하게 할 수 있다면?

Slice 5: 서브에이전트와 IRC

oh-my-pi의 task 시스템은 독특하다. 여러 서브에이전트를 병렬로 spawn하고, 각각 격리된 worktree에서 실행하며, 결과를 typed JSON으로 반환받는다. “prose를 파싱해서 결과를 추출한다”는 과정이 사라진다. 자식이 반환한 객체를 부모가 직접 참조한다.

더 흥미로운 건 IRC (Inter-Process Chat)다. 서브에이전트끼리 작업 중에 짧은 메시지를 주고받을 수 있다. “내가 이 파일을 수정했어, 너는 그거 참고해” 같은 조정이 가능하다. 협업하는 두 에이전트가 서로 상태를 공유하는 메커니즘이다.

이건 Drewgent의 content pipeline에 있는 4개의 pillar writer (engineer, researcher, philosopher, creative)와 유사한 패턴이다 — 다만 oh-my-pi는 에이전트 간 통신을 first-class citizen으로 설계했다는 점에서 한 단계 더 나갔다.

Slice 6: Hindsight — 에이전트가 스스로 관리하는 메모리

oh-my-pi의 Hindsight는 단순한 vector DB가 아니다. 에이전트가 작업 중에 retain으로 사실을 저장하고, recall로 검색하고, 세션 종료 시 reflect로 압축한다. 프로젝트 스코프가 기본값이라, 레포 A에서 배운 내용이 레포 B로 새지 않는다.

이것도 Drewgent의 knowledge.db + recall/remember 시스템과 비슷한데, 차이는 “에이전트가 스스로 저장 시점을 결정한다”는 점이다. Drewgent는 cron 기반 ingest에 의존하지만, oh-my-pi는 워크플로 중간에 자연스럽게 저장한다.

설계 결정과 실패 모드

oh-my-pi의 모든 결정이 완벽한 건 아니다. 직접 써보면서 느낀 한계들:

결정 장점 단점 / 실패 모드
Rust 코어 + N-API addon 크로스플랫폼, 성능 빌드가 복잡함. 네이티브 확장이 필요할 때 장벽
32개 툴, 단일 인터페이스 모델 학습 비용 최소화 복잡한 오퍼레이션의 표현력 제한
hashline 편집 신뢰성 10배 향상 앵커가 너무 구체적이면 유연성 저하
Stream rules (시간 역행) 프롬프트 길이 불변, 실시간 교정 mid-token abort의 UX가 불안정할 수 있음
서브에이전트 IRC 피어 간 직접 소통 프로토콜이 단순해서 복잡한 협업에는 부족
40+ 프로바이더 모델 선택의 자유 설정 복잡도 증가, 프로바이더별 차이 관리 부담

Drewgent가 배울 점

oh-my-pi를 분석하면서 Drewgent에 적용할 수 있는 인사이트가 몇 개 있었다:

  1. 툴 인터페이스 통일. read가 모든 걸 읽는다 — 파일, URL, DB, PR. Drewgent의 recall/remember/search 툴도 같은 패턴으로 통일할 수 있다.
  2. 하네스가 모델보다 중요하다. 모델 업그레이드는 플랫폼 의존적이지만, 하네스 최적화는 지금 당장 할 수 있다. hashline 같은 기술은 Drewgent의 edit 툴에도 적용 가능하다.
  3. 서브에이전트 간 통신. Drewgent의 content pipeline 4개 writer는 각자 독립적으로 작업하는데, IRC 같은 메커니즘으로 조정하면 시너지가 생길 수 있다.
  4. 실시간 행동 교정. Stream rules 패턴은 Drewgent의 “규칙 위반 시 자동 정지” 시스템에 적용할 수 있다.

마치며

oh-my-pi는 “또 하나의 코딩 에이전트”가 아니다. 에이전트 생태계 전체를 다시 생각하게 만드는 하네스다. 55,000줄의 Rust는 단순한 기술적 과시가 아니라, “이 문제를 진지하게 풀겠다”는 선언이다.

흥미로운 시대다. 모델 전쟁이 식어가면서, 누가 더 좋은 모델을 만드느냐보다 누가 더 좋은 하네스를 만드느냐의 경쟁이 본격적으로 시작됐다. oh-my-pi는 이 경쟁에서 가장 진지한 선수 중 하나다.

직접 써보고 싶다면: curl -fsSL https://omp.sh/install | sh

MIT 라이선스, GitHub에서 공개: can1357/oh-my-pi

— 이 글은 humanerd의 개인적인 경험과 분석을 바탕으로 작성되었다. 오픈소스 프로젝트에 대한 감사를 전한다.