본문으로 건너뛰기
뒤로가기

[LLM Workbench] btwin Orchestration: 에이전트 작업 흐름을 작게 제어하기

TL;DR — LLM 에이전트의 판단 자체를 결정론적으로 통제하기는 어렵지만, 어떤 맥락을 전달하고 어떤 산출물을 요구할지는 외부에서 관리할 수 있습니다. btwin orchestration은 무거운 agent framework보다 작은 control plane을 두고, thread / protocol / phase / guard / hook으로 작업 흐름을 검증하는 실험입니다.

Table of contents

Open Table of contents

들어가며

LLM에게 여러 작업을 맡기다 보면 어느 순간 “작업을 시작했습니다”라는 응답 뒤에 실제 진행이 멈추는 상황을 만납니다. 이 글은 그 문제를 해결하기 위해 btwin에서 시도한 lightweight orchestration 구조를 정리합니다.

이 글에서 다루는 내용:

사전 지식: Codex, Claude Code 같은 CLI 기반 LLM 도구와 hook / MCP의 기본 개념을 전제로 합니다.


1. 문제: LLM은 계속 실행되는 프로세스가 아닙니다

LLM은 스스로 계속 실행되는 worker process가 아닙니다. 일반적인 사용 흐름에서는 사용자가 입력을 보내거나 외부에서 dispatch를 넣을 때 다음 행동을 시작합니다.

따라서 LLM의 내부 판단을 완전히 결정론적으로 통제하기는 어렵습니다. 대신 외부에서 더 안정적으로 관리할 수 있는 부분이 있습니다.

btwin orchestration은 이 지점에 집중했습니다. 에이전트의 사고를 프레임워크가 강하게 통제하는 방식이 아니라, LLM이 일하기 좋은 작은 작업 레일을 제공하는 방식입니다.


2. 설계 기준: 작은 control plane을 둡니다

btwin orchestration의 기준은 다음과 같았습니다.

전체 구조는 다음과 같습니다.

flowchart TD
    U["User / Foreground Agent"] --> CP["btwin Control Plane"]
    CP --> AR["Dispatched Agent Runtime"]
    AR --> F["Agent Output / Contribution"]
    F --> R["btwin Record / Next Action"]
계층내부 구성
btwin Control PlaneMemory/Context Store, Thread State, Protocol/Phase State, Guard/Gate Rules
Dispatched Agent RuntimeCommon Instructions, Agent Identity/Role Contract, Dynamic Turn Brief

3. 모델: Thread, Protocol, Phase, Guard, Gate

btwin은 협업 흐름을 Thread, Protocol, Phase, Guard, Gate로 나눠 관리합니다.

개념역할
Thread하나의 작업 흐름입니다. 참여 agent, 현재 phase, contribution, 다음 행동 상태를 보관합니다.
Protocolthread가 어떤 절차로 진행될지 정의하는 협업 규칙입니다.
Phase현재 해야 할 작업 단위입니다. 예를 들어 context, discussion, review, decision이 있습니다.
Guard현재 phase에서 이 행동이 허용되는지 확인하는 runtime 안전장치입니다.
Gatephase 결과를 보고 다음 상태를 결정하는 전이 규칙입니다.

개념적으로는 다음과 같습니다.

phase = 지금 어떤 작업을 하는가
guard = 이 행동이 현재 상태에서 허용되는가
gate  = 이번 phase 결과를 보고 어디로 이동할 것인가

현재 구현에서 gate는 별도 독립 객체라기보다 from / on / to 형태의 transition을 gate 개념으로 해석합니다. 중요한 점은 LLM이 “다음 단계로 넘어가도 된다”고 말하는 것과 실제 thread state가 전이되는 것을 분리했다는 점입니다.


4. Hook: 완료 조건을 외부에서 확인합니다

LLM의 작업 품질은 비결정적일 수 있지만, 완료를 인정하기 위한 최소 산출물은 외부에서 확인할 수 있습니다. btwin은 Codex hook을 이용해 turn 종료 시점에 completion contract를 검사합니다.

flowchart LR
    A["Agent Turn"] --> B["Codex Hook"]
    B --> C["btwin Workflow Check"]
Hook역할
SessionStartthread / phase context 복원
UserPromptSubmit현재 phase contract overlay
Stop종료 직전 산출물 검증
Workflow Check내용
required output존재 여부 확인
actor / phase / contribution조건 확인
guard위반 시 Stop block

Stop hook은 provider turn이 끝나려는 시점에 실행되는 마지막 검문소로 사용했습니다. 현재 phase에서 필요한 contribution이 없으면 hook 응답으로 decision: block을 반환해 turn 종료를 막고, 에이전트에게 어떤 산출물을 남겨야 하는지 다시 안내합니다.

phase transition이나 next action 계산은 hook이 직접 처리하지 않습니다. hook은 attempt, check, blocked 같은 workflow event를 남기고, 이후 protocol / delegation layer가 다음 행동을 계산합니다.


5. Instruction Layer: 역할과 작업 지시를 분리합니다

에이전트별 역할 지침도 중요한 설계 포인트였습니다. 단순히 CLI를 한 번 실행한 뒤 첫 turn을 사용해 “너는 reviewer입니다”라고 말하면, 역할 주입 자체가 작업 turn을 소비합니다.

btwin은 Codex가 startup 시점에 현재 작업 경로 기준으로 instruction, config, hook layer를 읽는다는 점을 활용했습니다. target repo 내부에 helper workspace를 만들고, 이 경로를 dispatched agent의 cwd로 사용합니다.

레이어내용
1. Global Layerglobal config, user MCP / hooks
2. Project Layerproject root, repo AGENTS.md, project .codex config
3. btwin Helper Overlay.btwin/helpers/<agent>/ workspace, AGENTS.md, .codex/hooks.json
4. Runtime Bindingthread_id, agent_name, launch developer instructions
5. Dynamic Turn Briefcurrent phase, required action, expected output
flowchart TD
    A["1. Global Layer"] --> B["2. Project Layer"]
    B --> C["3. btwin Helper Overlay"]
    C --> D["4. Runtime Binding"]
    D --> E["5. Dynamic Turn Brief"]

이 구조에서 기존 repo의 AGENTS.md.codex 설정은 덮어쓰지 않습니다. repo-local helper overlay 아래에 별도 config를 두고, agent identity는 launch developer instructions로, phase별 작업 지시는 dispatch message로 전달합니다.

helper overlay는 target repo 내부에 있고, Codex가 해당 프로젝트를 trusted project로 인식할 때 가장 안정적으로 동작합니다. btwin도 helper overlay를 만들기 전에 workspace가 Git repo 내부인지, Codex 설정에서 trusted project인지 확인하도록 설계했습니다.


6. Delegation: 멈춘 작업을 사람이 다시 잡을 수 있게 합니다

orchestration에서 중요한 것은 에이전트가 항상 성공하는 구조를 만드는 것이 아닙니다. 실패하거나 멈췄을 때 사람이 어디서 이어가야 하는지 알 수 있어야 합니다.

btwin은 delegation을 start / status / wait / respond / stop 같은 bounded loop로 관리합니다. helper가 멈추거나 추가 입력이 필요할 때 다음과 같은 정보를 사람이 볼 수 있게 합니다.

이 정보는 단순한 로그보다 중요합니다. 현재 상태를 사람이 다시 해석하지 않아도 다음 행동을 결정할 수 있기 때문입니다.


정리하며

핵심 요약:

참고 자료


이 게시물은 학습한 내용을 바탕으로 초안을 작성한 뒤, LLM의 도움을 받아 내용을 검수하고 다듬어 완성되었습니다.


공유하기:

이전 글
[LLM Workbench] btwin Memory: 세션 밖에 맥락 남기기
다음 글
[LLM Workbench] Hooklusion: 에이전트 상태를 작은 신호로 보기