Yeonguk Lee
FlowCraft · design note · 2026-07-10FlowCraft · 설계 노트 · 2026-07-10

Why my code-generating agents
never write code.
코드 생성 에이전트인데,
코드를 직접 못 쓰게 했어요.

FlowCraft turns a plain-language prompt into a working Flutter app. Six LLM agents are involved, and none of them is allowed to write Flutter code. This note is about why that rule exists and what it took to make it hold.

FlowCraft는 말로 설명한 앱을 실제로 도는 Flutter 앱으로 만들어 줘요. LLM 에이전트 6개가 움직이는데, 그중 누구도 Flutter 코드를 직접 쓸 수 없어요. 왜 이런 규칙을 만들었고, 그 규칙을 어떻게 지키도록 설계했는지 이야기해볼게요.

01

The problem: code is the wrong medium문제: 주고받는 대상이 코드라서

When six agents build an app, the obvious design is to let them write the Flutter code directly. I did the opposite on purpose. Raw code generation drifts between calls: the same prompt yields slightly different code each time, a one-line revision means regenerating whole files, and a "make this button blue" request can break something unrelated on the far side of the app. None of that is a prompt you can fix. It comes from treating code as the thing the agents pass around.

에이전트 6개로 앱을 만든다고 하면, 보통은 에이전트가 Flutter 코드를 직접 쓰게 해요. 저는 일부러 반대로 갔어요. 코드를 그대로 생성하면 모델을 호출할 때마다 출력이 흔들리거든요. 같은 프롬프트인데도 매번 조금씩 다른 코드가 나오고, 한 줄만 고치려는데 파일을 통째로 다시 만들고, "버튼을 파랗게 바꿔줘" 한마디가 앱 반대편의 엉뚱한 걸 건드려요. 이건 프롬프트를 잘 쓴다고 해결되는 문제가 아니에요. 에이전트끼리 코드 자체를 주고받기 때문에 생기는 문제예요.

Raw code is an output you can't diff semantically, can't validate cheaply, and can't regenerate deterministically. So rather than push the agents to write better code, I took code away from them entirely.

코드는 의미 단위로 비교하기도, 적은 비용으로 검증하기도, 똑같이 다시 만들어내기도 어려워요. 그래서 에이전트가 코드를 더 잘 쓰게 만드는 대신, 아예 코드를 손에서 뺐어요.

02

The rule: agents emit state, a generator emits code규칙: 에이전트는 상태를, 생성기는 코드를

So I designed it around one rule. Agents (Clarifier, WidgetExtender, Structure, Design, Repair, Critic) only produce a typed AppState: a JSON model of screens, widgets, navigation, and state. A deterministic generator compiles that model to main.dart. Same state in, same code out, every time. A revision is a patch to the state, not a rewrite of the code.

그래서 규칙 하나를 중심에 두고 설계했어요. 에이전트(Clarifier, WidgetExtender, Structure, Design, Repair, Critic)는 타입이 정해진 AppState만 만들어요. 화면, 위젯, 내비게이션, 상태를 담은 JSON 모델이에요. 그리고 결정적 생성기가 그 모델을 main.dart로 바꿔요. 같은 상태를 넣으면 언제나 같은 코드가 나와요. 수정은 코드를 다시 쓰는 게 아니라 상태에 패치를 적용하는 일이 됐어요.

Generation runs inside a closed loop. A static validator checks the state for structural errors (undeclared variables, illegal widget nesting, reserved identifiers, and so on). Failures go to a Repair agent that patches the state: two attempts max, with oscillation and wall-clock guards so it can't loop forever. If repair still fails, the system falls back to the prior valid state when one exists. A Critic then scores how faithful the result is to the original intent. I didn't build this loop first, though. The generator came first, and the verify-repair loop came months later. I kept it in shadow mode (logging what it would have repaired without touching the output) until I trusted it enough to switch it to enforce, where every generation now runs through it.

생성은 닫힌 루프 안에서 돌아요. 정적 검증기가 구조적 오류(선언 안 된 변수, 위젯 중첩 규칙 위반, 예약어 충돌 같은 것들)를 검사해요. 실패하면 Repair 에이전트가 상태를 고치는데, 최대 2번까지만이고, 같은 수정을 반복하거나 시간을 끌면 가드가 중단시켜요. 그래도 안 되면 이전에 성공한 상태가 있을 때 거기로 돌아가요. 마지막으로 Critic이 결과가 원래 의도에 얼마나 충실한지 점수를 매겨요. 근데 이 루프를 먼저 만든 건 아니에요. 생성기가 먼저였고, 검증-수리 루프는 몇 달 뒤에 붙였어요. 한동안 shadow 모드로 뒀어요. 실제 출력은 안 건드리고, 고쳤을 법한 것만 로그로 남기면서요. 믿을 만해지고 나서야 enforce로 바꿨고, 지금은 모든 생성이 이 루프를 거쳐요.

03

What I claim, and what I don't주장하는 것과, 하지 않는 것

What I claim: the pipeline's shape works. All 53 automated tests pass, covering the validator's error codes, the closed-loop repair contract, and oscillation handling. Revisions stay stable because code is compiled from state, never edited. Preview runs in the browser via DartPad.

주장하는 것: 이 구조가 동작한다는 거예요. 검증기의 에러 코드, Repair 단계의 계약, 진동 처리까지 자동 테스트 53개가 전부 통과해요. 코드는 상태에서 컴파일될 뿐 직접 수정되지 않으니까, 수정을 거듭해도 흔들리지 않아요. 미리보기는 브라우저에서 DartPad로 돌아가요.

What I don't claim: that the agents themselves are smart. The tests measure the pipeline's guarantees, not model quality. A bad generation still happens; the point of the design is that a bad generation gets caught, repaired, or rolled back instead of shipped.

주장하지 않는 것: 에이전트가 똑똑하다는 거요. 테스트가 검증하는 건 파이프라인의 보장이지 모델의 품질이 아니에요. 잘못된 생성 결과는 지금도 나와요. 이 설계의 핵심은 그런 결과가 사용자에게 전달되기 전에 걸리고, 고쳐지고, 안 되면 되돌려진다는 거예요.

The repo is public: github.com/Poiurity/FlowCraft. The pipeline diagram and a live preview screenshot are on the project card.

저장소는 공개돼 있어요: github.com/Poiurity/FlowCraft. 파이프라인 다이어그램과 미리보기 화면은 프로젝트 카드에 있어요.

poiurity.com · iamlyg9667@gmail.com · github.com/Poiurity