콘텐츠로 이동

핵심 개념

flower에는 고유한 용어가 한 묶음 있다: 실행, 단계, 세션, 코디네이터, 워커, 사전 확인, 목표 가드, 연속성, 핸드오프. 이 페이지에서 한 번에 다 설명한다. 다 읽고 나면 다른 페이지를 읽으면서 추측할 필요가 없다. 5분이면 읽는다.

여기서는 개념만 다루고 API 시그니처는 주지 않는다 —— 시그니처는 Python API, 한 줄 정의와 중영 대조는 용어집, 커맨드라인 스위치는 커맨드라인 레퍼런스를 보라.

한 번의 실행은 어떤 모양인가

세 계층, 큰 것부터 작은 것까지:

용어 무엇인가 어디에 기록되나
실행 run 하나의 Runtime이 시작해서 끝날 때까지의 전 과정. 기본 경로를 탈 때, 한 번의 실행은 당신이 flower를 한 번 치는 것이다 runs/manifest.json
단계 step 실행 내부의 실행 가능한 단위 하나: 컨텍스트 딕셔너리를 받아, agent를 하나 돌리고, 결과를 딕셔너리에 되쓴다. 화면의 == 가로줄 한 줄이 곧 단계 경계다 위와 같음, 단계마다 한 줄
세션 session 모델 쪽의 컨텍스트 한 가닥. 자기 session_id를 갖고, resume 할 수 있고 fork 할 수 있다 runs/sessions.db

이들의 중첩 관계는 일대일이 아니다:

실행 ── 당신이 친 이번 flower
 ├── 단계 요구 확인 ──── 세션 A
 ├── 단계 목표 설정 ──── 세션 B
 └── 단계 작업 ───────── 세션 C ──[컨텍스트 거의 참]──> 세션 C'
      └── 작업·판정#1 ─ 세션 D
  • 한 단계가 세션 여러 가닥을 태워버릴 수 있다. 컨텍스트가 거의 찰 때 compact 하지 않고, 핸드오프 문서를 한 부 쓴 뒤 새 세션을 열어 넘겨받게 한다 —— 이것을 핸드오프라 하고, 같은 실행 내부에서 일어난다.
  • 새로운 실행이 예전 세션에 이어붙을 수 있다. 같은 디렉터리에서 flower를 한 번 더 치면, 각 단계가 지난번 그 세션으로 되이어진다 —— 이것을 연속성이라 하고, 프로세스를 가로질러 일어난다. runs/lineage.json이 "어떤 단계 이름이 어떤 session_id에 대응하는지"를 기억해 준다.
  • 판정하는 그 라운드는 언제나 새 세션이다. 이어지지도 않고 계보에도 들어가지 않는다 —— "다 됐는지"를 판정하는 사람이 방금 그 워커여서는 안 된다.

순서대로 꿰어놓은 한 묶음의 단계를 워크플로우라 한다. flower를 맨몸으로 돌리면 프레임워크에 딸린 3단계 워크플로우를 쓴다: 요구 확인 → 목표 설정 → 작업.

분업: 코디네이터는 손대지 않는다

이것이 프레임워크 전체가 그 위에 세워진 한 줄이다.

코디네이터메인 스레드 위의 그 agent다. 작업을 쪼개고, 일을 시키고, 보고를 읽고, 결정을 내린다 —— 하지만 WriteEdit을 손에 넣지 못한다. Bashls, git status 같은 일회성 명령으로 한 번 들여다보는 정도만 된다 (hook이 관문을 지키며, 프롬프트로 제약하지 않는다. 게다가 이런 결과는 영속화되는 세션 기록에 들어가지 않는다). 도구 목록은 Agent, TodoWrite, Read에 그 제한된 Bash가 전부다.

진짜로 일하는 것은 워커 —— Agent 도구가 파견하는 subagent다.

왜 이렇게 나누는가. subagent는 자기 자신의 transcript 한 가닥을 갖는다: 파일을 몇 개 읽었는지, 테스트를 몇 번 돌렸는지, 시행착오로 얼마나 돌아갔는지가 전부 거기에 기록된다. 메인 스레드는 최종 보고만 받는다. 그리고 메인 스레드는 실행 전체를 관통하는 유일한 컨텍스트이므로, 가장 아껴야 할 가닥이다.

실측(HT001, 10.4 시간짜리 실행 한 번):

메인 스레드 subagent 가라앉은 비율
모델 턴 수 70 3.0K 97.7%
본문 문자 수 200.1K 3.6M 94.8%
도구 호출 32 1,893 ——

한 번 파견할 때마다 평균 82번의 도구 호출을 메인 스레드는 아예 보지 못한다. 이것이 컨텍스트를 아끼는 첫 번째 층이자, 가장 많이 아끼는 층이다. 완전한 논증은 컨텍스트 경제학에 있다.

오해하기 쉬운 두 가지:

  • 코디네이터는 더 똑똑한 agent가 아니다. 워커와 기본적으로 같은 모델 등급을 쓴다. 아끼는 것은 컨텍스트지 모델이 아니다.
  • 응답 형식이 제약되어 있다. 워커의 응답은 정확히 네 단락 —— 결론 / 근거 / 산출물 / 미검증, 30줄을 넘지 않으며, 파일 내용, 명령 출력, 로그, diff 원문을 붙이는 것이 금지된다. 긴 것은 워크벤치artifacts/에 쓰고, 응답에는 경로만 준다.

flower에는 총 다섯 개의 역할이 있고, 모두 같은 방식이다: 주입된 규칙 텍스트 한 뭉치 + 도구 한 세트 + hook 한 세트.

역할 무엇을 하나 손에 든 것
코디네이터 coordinator 일 쪼개기, 사람 파견, 의사결정 Agent TodoWrite Read + 제한된 Bash
워커 worker 코드 작성, 테스트 실행, 자료 조사 Read Write Edit Bash Glob Grep WebFetch WebSearch
확인자 clarify 손대기 전에 질문만, 명확해질 때까지 질문 도구 + 읽기 전용 도구, 쓰기 도구는 하나도 없음
판정자 judge 목표를 설정하거나, "이번 라운드가 끝났는지"를 판정 질문 도구 + Read Glob Grep(명령을 돌리게 하려면 명시적으로 열어야 함)
오라클 oracle 실행 도중에 "지금 어디까지 왔나"에 답변 Read Glob Grep. 그 말은 해당 실행의 컨텍스트에 들어가지 않는다

팩토리 함수의 파라미터와 기본값은 Python API를 보라.

롱 호라이즌은 네 군데에서 망가진다

한 번의 롱 호라이즌 실행은 몇 시간에서 며칠에 걸치고, 여러 세션에 걸치고, 프로세스 재시작에 걸친다. 무너지는 방식은 몇 가지뿐이고, 각각에 하나의 메커니즘이 대응한다:

당신이 두려워하는 것 메커니즘 무엇을 하나 상세
만들어진 게 당신이 원한 게 아님 사전 확인 손대기 전에 요구를 먼저 명확히 물어서 요구 확인서 한 부로 동결한다. 이후 모든 단계가 그것을 읽고, 다시 추측하지 않는다 사전 확인
다 됐다고 하는데 실은 안 됨 목표 가드 매 라운드 끝에, 작업에 참여하지 않은 판정자가 독립적으로 한 번 판정한다. 달성 못 했으면 돌려보내 계속 작업시킨다 목표 가드
몇 시간 돌다 죽어서 처음부터 다시 연속성 같은 디렉터리에서 다시 돌리면 지난번 진행에 자동으로 이어진다 —— 프로세스가 죽든 머신이 재시작하든 마찬가지 연속성
컨텍스트가 차서 요약 한 토막으로 눌림 핸드오프 거의 찰 때 현재 세션이 사람이 읽고 고칠 수 있는 핸드오프 문서를 쓰게 하고, 새 세션을 열어 넘겨받게 한다 핸드오프

따로 기억할 만한 두 줄:

판정의 결론은 세 가지지 두 가지가 아니다. 달성, 미달, 이 환경에서는 검증 불가. 뒤의 두 가지는 서로 다른 결론이다 —— "여기서는 검증할 수 없다"는 절대 통과로 판정하지 않고, 멈춰서 사람에게 묻는다. 그리고 판정자가 판정하는 것은 산출물이지 소스가 아니다: HT002에서 한 번 걸려 넘어졌다. Makefile의 macOS 분기만 보고 통과로 판정했는데, 실제 납품된 것은 Linux ELF였다.

핸드오프는 compact가 아니다. compact는 모델이 스스로 어두운 데서 앞의 대화를 요약 한 토막으로 만드는 것이다: 읽을 수 없고, 고칠 수 없고, 무엇을 잃었는지 당신은 모른다. 핸드오프 문서는 구조화되어 있고 디스크에 떨어져 있어서, 열어서 한 줄 고친 뒤 계속 돌리게 할 수 있다. flower는 기본적으로 네이티브 auto-compact를 끄고, 핸드오프로 대신한다.

이 표에는 없지만 매 실행마다 돌아가는 두 층이 더 있다:

  • 스필 spill —— 도구 결과가 4000자를 넘으면 .flower/spill/에 쓰고, 컨텍스트에는 경로 한 줄만 남긴다. 그 자리에서 바로 잘라낸다. 다 찬 뒤에 되돌아가 compact 하는 게 아니다.
  • 워크벤치 workbench —— .flower/ 아래의 scripts/, artifacts/, notes/ 세 디렉터리, 그리고 INDEX.md 인덱스 한 부가 system prompt에 주입된다. 그래서 agent는 매 라운드 자기 손에 무엇이 있는지 안다. HT001에서는 스크립트 61개가 쌓였고 331번 실행되었으며, 그중 92%가 한 번 넘게 실행되었다.

flower가 하지 않는 것

첫째, 기성 워크플로우를 제공하지 않는다. 프레임워크는 메커니즘만 담당한다: 한 단계를 어떻게 돌리는지, 컨텍스트를 어떻게 아끼는지, 네트워크가 끊기면 어떻게 잇는지, 같은 저장소를 병렬로 고칠 때 어떻게 안 부딪히는지, 사람에게 물어야 할 때 어떻게 멈추는지. 워크플로우는 당신이 쓴다. flower를 맨몸으로 돌릴 때의 그 3단계는 flower/workflow/starter.py에서 오며, 어떤 도메인 가정도 담지 않을 만큼 범용적이다 —— 시작을 돕기 위한 것이지, 프레임워크의 능력 경계가 아니다. 직접 쓰려면 워크플로우 설계를 보라.

둘째, 호스트 머신 설정을 상속하지 않는다. flower는 setting_sources=[]로 돈다: 호스트의 ~/.claude/를 읽지 않고, 프로젝트의 .claude/도 읽지 않는다. 이것이 이식 가능이다 —— 머신을 바꿔도 동작이 같다. 도메인 능력은 저장소를 따라다니는 plugin이 가져오지, 이 머신에 우연히 뭐가 깔려 있느냐에 기대지 않는다.

셋째, 자격증명은 반드시 직접 가져와야 한다. 이것이 둘째 항목의 대가다. flower는 고정된 우선순위로 자격증명을 찾는다(프로세스 환경변수 → $FLOWER_ENV → 현재 디렉터리 .env~/.config/flower/.env → 소스 저장소 루트 .env). 마지막으로 ~/.claude/settings.jsonenv 블록에서 그 9개 자격증명 키를 빌려 폴백으로 쓴다 —— "토큰을 어디서 찾을지"라는 그 한 가지만 빌린다. settings.json 안의 다른 무엇도 agent 동작에 영향을 주지 않는다. 전체 순서와 각 변수의 의미는 설정 레퍼런스를 보라.

넷째, 시스템 프롬프트를 교체하지 않는다. 도메인 지시는 Claude Code 네이티브 시스템 프롬프트 뒤에 덧붙이는 것이지, 그것을 갈아치우는 것이 아니다. 그래서 전문화가 범용 능력을 잃는 대가로 오지 않는다.


여기까지 읽었다면 빠른 시작의 그 출력들은 이제 다 읽힐 것이다. 이 메커니즘들을 각각 어떻게 조절하는지, 언제 쓰면 안 되는지 알고 싶다면 컨텍스트 경제학부터 아래로 읽어라. 명령만 베끼고 싶다면 커맨드라인 레퍼런스로 가라.