Codemode in Pi: 도구 호출 오케스트레이션을 위한 JavaScript 샌드박스
개요 – Codemode를 사용하면 LLM이 하네스 내부에서 짧은 JavaScript 스크립트를 실행하여 도구 호출을 오케스트레이션할 수 있습니다
Codemode는 Pi의 하네스(harness) 측에서 실행되는 샌드박스형 JavaScript 환경(WASM 런타임의 QuickJS)으로, bash 도구가 실행되는 실행 샌드박스와는 별개입니다. LLM은 작은 스크립트를 작성함으로써 여러 도구 호출을 구성하고, 중간 상태를 저장하며, 모든 중간 출력을 모델의 토큰 창으로 보내지 않고도 MCP API를 호출할 수 있습니다. 이는 턴 수를 줄이고, 컨텍스트 오버플로를 방지하며, 복잡한 워크플로우(예: 이미지 생성, 분류, 하위 에이전트 루프)를 실현 가능하게 합니다.
Codemode의 작동 방식 – 두뇌와 손 사이의 언어 수준 브리지
- 두뇌 vs 손 – 두뇌(신뢰할 수 있는 하네스)와 손(bash 도구가 실행되는 Unix 샌드박스)은 서로 다른 신뢰 수준을 가진 격리된 파일 시스템입니다. Codemode는 두뇌에서 실행되어 LLM에 하네스를 위한 프로그래밍 가능한 인터페이스를 제공합니다.
- JavaScript 함수로서의 도구 호출 – 샌드박스 내부에서 LLM은
tools.bash,models.generateImages,models.classify등을 일반적인 비동기 함수로 호출할 수 있습니다. 샌드박스는 원시 텍스트가 아닌 구조화된 객체(JSON)를 반환하므로 모델은 깔끔한 데이터를 수신합니다. - 상태 지속성 –
store(key, value)호출은 세션 기록에 데이터를 기록하며, 이후 호출에서load(key)를 통해 이를 검색할 수 있습니다. 이를 통해 대량의 페이로드를 다시 보내지 않고도 다단계 추론이 가능합니다. - 동시성 – 환경이 JavaScript이므로 LLM은
Promise.all을 시작하여 최대 4개의 동시 도구 실행(Pi의 내장 제한)을 수행한 다음 결과를 처리할 수 있습니다.
이점 – LLM 기반 에이전트에게 Codemode가 중요한 이유
- 컨텍스트 사용량 감소 – 전체 중간 도구 로그가 아닌 최종 스크립트 출력만 모델에 주입됩니다.
- 구성 가능한 워크플로우 – LLM은 루프, 조건문, 병렬 호출을 작성하여 임시 도구 호출을 적절한 프로그램으로 전환할 수 있습니다.
- 직접적인 MCP 액세스 – 일반 도구로 노출되지 않는 MCP API(예: 이미지 생성, 분류기)를 스크립트에서 호출할 수 있어 별도의 맞춤형 확장이 필요하지 않습니다.
- 결정론적 데이터 교환 –
models.*및 MCP 호출에서 제공하는 구조화된 JSON은 취약한 텍스트 파싱 문제를 방지합니다. - 향상된 하위 에이전트 오케스트레이션 – 스크립트는 외부 CLI 래퍼에 의존하지 않고도 하위 에이전트를 생성하고 조정할 수 있습니다.
예시: Codemode를 통한 이미지 생성
const [painter] = await models.getAvailableOfType("image");
const result = await models.generateImages(painter, {
input: [{ type: "text", text: "A cute little puppy sitting on a grassy lawn, soft natural light, photorealistic" }]
});
if (result.stopReason !== "stop") return result.errorMessage;
for (const block of result.output) {
if (block.type === "image") image(block); // sends image back to LLM
else text(block.text);
}
이 스크립트는 내부 이미지 모델을 호출하고, 이미지 바이트가 포함된 객체를 수신한 다음, 단일 image() 호출을 통해 LLM으로 전달합니다.
예시: GitHub 이슈의 대량 감정 분류
const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
const r = await tools.bash({
command: "gh issue list --state open --limit 100 --json number,title,body,comments"
});
const issues = JSON.parse(r.output);
const results = await Promise.all(issues.map(async issue => {
const res = await models.classify(jev, {
state: {
title: issue.title,
body: (issue.body || "").slice(0,4000),
comments: issue.comments.slice(-5).map(c => c.body.slice(0,800))
},
questions: { /* sentiment, frustration, kind */ }
});
return { n: issue.number, title: issue.title, ...res.answers };
}));
store("sentiment_results", results);
return results.filter(r=>!r.error).sort((a,b)=>b.frustration.score-a.frustration.score).slice(0,12)
.map(r=>`#${r.n} ${r.frustration.score.toFixed(2)} [${r.kind.choice}] ${r.title}`);
이 스크립트는 100개의 이슈를 가져와 병렬로 분류하고, 전체 결과를 저장한 다음 간결한 순위를 반환합니다.
MCP와의 상호작용 – 점진적 발견 및 제한 사항
- 점진적 도구 발견 – Codemode 내부에서 LLM은
tools.mcp__sentry__find_organizations({})를 호출한 다음 반환된 조직을 반복하여 프로젝트를 쿼리할 수 있습니다. 하네스는 structuredContent 필드를 제공하여 추가 호출을 결정론적으로 만듭니다. - 현재의 문제점 – 많은 MCP 서버가 여전히 JSON 대신 자유 형식의 텍스트를 반환하여 구조화된 데이터를 기대하는 Codemode의 작동을 방해합니다. 일관되지 않은 배치 크기와 대용량 바이너리 지원 부족도 오류를 유발합니다.
- 이중 이스케이프 문제 – MCP 서버 자체가 Codemode를 실행할 때(예: Cloudflare의 MCP), LLM은 JavaScript 내부에 JavaScript를 중첩하게 되어 이중 JSON 이스케이프가 발생하고 디버깅이 더 어려워집니다.
커뮤니티 피드백 – 사용자들의 생각
"이 글과 Cloudflare 게시물 사이에서, 말만 많고 간단한 시스템 수준의 그림은 없습니다. … Codemode는 기본적으로 LLM을 위한 동형성(homoiconicity)과 액터 의미론을 재발견하는 것입니다." – @ssivark
"저처럼 기사와 댓글을 읽고도 'codemode'가 무엇인지 여전히 궁금하다면: 하네스에 작은 js 샌드박스를 제공하여 도구 호출을 구성하고, 데이터를 컨텍스트로 읽기 전에 조작할 수 있게 하는 것입니다." – @ohgodhelpplease
"왜 거의 모든 codemode 구현이 Javascript를 선택하는지 모르겠습니다. 저는 bash를 codemode 언어로 사용하는 에이전트를 프로토타이핑했는데, 똑같이 잘 작동하며 별도의 학습이 필요하지 않았습니다." – @ylxdzsw
"Codemode는 턴 수를 약 10배 정도 줄여줍니다." – @lionkor
"Pi의 약속은 기본적으로 bash만 있으면 되고 다른 도구는 필요 없다는 것이었습니다. 최소 침습적 접근 방식은 도구 호출을 가상 bash 명령으로 주입하는 것이었을 겁니다. codemode는 필요 없었습니다." – @dnlgrmm
이러한 댓글들은 턴 감소에 대한 열광과 언어 선택, 복잡성, 그리고 순수 bash 워크플로우라는 원래 비전에 대한 우려를 모두 보여줍니다.
제한 사항 및 미해결 질문
- 내구성 – 장기 실행 스크립트는 체크포인트가 필요할 수 있습니다. 현재 구현에는 내장된 스냅샷 기능이 부족합니다.
- 언어 선택 – JavaScript는 편리하지만 유일한 옵션은 아닙니다. 일부 사용자는 결정론적 실행을 위해 Bash나 Starlark를 사용하여 성공했다고 보고합니다.
- 바이너리 처리 – MCP가 아직 바이너리 블롭을 스트리밍하지 않기 때문에 대용량 이미지나 파일은 여전히 우회 방법(사전 서명된 URL)이 필요합니다.
- MCP 발견의 확장성 – 여러 MCP 서버에 걸쳐 도구 검색을 분산시키는 표준화된 방법이 없으며, 창발적 동작이 불안정해질 수 있습니다.
향후 방향 – Codemode를 LLM을 위한 일급 프로그래밍 계층으로 만들기
- 결정론적 구성 언어 – Starlark나 Lisp 스타일 언어가 JavaScript를 대체하여 참조 투명성을 보장할 수 있습니다.
- 워크플로우 엔진 – 내구성 있는 워크플로우 엔진(예: Temporal)을 통합하면 안정적인 스냅샷과 재시도를 제공할 수 있습니다.
- 더 나은 MCP 계약 –
outputSchema와 일관된 JSON 응답을 강제하면 Codemode와 외부 서비스 간의 가교가 원활해질 것입니다. - 통합 하네스 설계 – LLM을 언어 런타임 참여자(토큰 생성기가 아닌)로 취급하면 동형성 운영 체제의 비전과 유사한 새로운 클래스의 "code-mode 인식" 하네스가 탄생할 수 있습니다.
Codemode는 만병통치약은 아니지만, 모델에 하네스 내부의 가볍고 샌드박스화된 프로그래밍 표면을 제공함으로써 더 풍부하고 오버헤드가 낮은 LLM 기반 자동화를 위한 실용적인 경로를 제공합니다.