모델한테 YAML 쓰게 하지 마라: LLM은 필드 변경 의도만, 파일 수정은 결정적 코드로
Don't Let the Model Write the YAML: Deterministic, Minimal-Diff GitOps Remediation from LLM-Proposed Field Changes
TL;DR Highlight
LLM은 바꿀 필드만 말하고 코드가 바이트를 고쳤더니 415회 전부 정확
Who Should Read
LLM 에이전트가 제안한 수정을 Kubernetes manifest에 자동 반영하는 GitOps 파이프라인을 만드는 플랫폼·DevOps 개발자
Core Mechanics
- diff 생성은 조용히 틀린다
- 엄격하게 적용하면 Sonnet 5 diff가 거의 안 붙는다(2.7%).
- GNU patch --fuzz는 96%를 적용하지만 14~20%는 엉뚱한 곳에 에러 없이 들어간다.
- 전체 파일 재작성은 모델 따라 갈린다
- 작은 모델(gemini-3.7-flash)은 파일 전체를 reflow(줄바꿈·들여쓰기를 다시 정리)해 망가뜨린다.
- 프런티어 모델(claude-sonnet-5)은 대체로 맞지만 같은 요청이 어떤 seed에서는 필드를 빠뜨린다.
- 모델은 '무엇을'만, 코드가 '어떻게'를
- 에이전트는 kind, name, field_path, new_value, reason만 구조화해서 낸다.
- 코드가 YAML 파서의 위치 정보로 대상 값의 글자 범위를 찾아 그 부분만 바꾼다.
- 파일을 다시 직렬화하지 않아 diff가 최소다
- 범위 밖 바이트는 그대로라서 주석·따옴표·키 순서가 유지된다.
- 결과는 모델이 뭐든 같고, 생성 비용은 파일 크기와 무관하다.
- 애매하면 거부한다(fail-closed)
- 대상이 없거나 중복이거나 스칼라가 아니면 추측하지 않고 거절한다.
- 알려진 구멍은 YAML alias로, anchor 줄을 대신 고친다.
Evidence
- span-edit은 415회 전부 정답
- 83개 task × 5 seed 모두 correct 1.000, flaky 0/83이다.
- 두 모델에서 결과가 동일하다(모델을 호출하지 않음).
- Sonnet 5 전체 파일 재작성은 6/83이 불안정
- 정답률 0.976이지만 6개 task가 seed에 따라 맞기도 틀리기도 한다.
- 405 vs 415 정답, 즉 415회 중 10회 차이다.
- diff 적용기별 오적용률 (Sonnet 5 B2 diff 415개)
- strict 정답 0.027, offset-tolerant 0.675(오적용 0).
- GNU patch --fuzz=3은 적용 0.964, 오적용 0.140이고 공백 무시(-l)를 켜면 0.202다.
- flash 전체 재작성은 정답 2.4%
- 97.6%가 무관한 줄까지 바꾸고 평균 diff는 921.3줄이다.
- 출력 토큰은 편집당 span-edit 약 50~150, B1 약 9,300(Sonnet 5 근사치)이다.
How to Apply
- 에이전트 출력을 FieldChange JSON으로 제한하기
- 지금 수정 파일/diff를 쓰게 하는 프롬프트를 kind, name, field_path, new_value, reason JSON 출력으로 바꿔 보자.
- field_path의 컨테이너와 env는 인덱스가 아닌 이름으로 지정하게 하라.
- 적용 단계를 파서 위치 기반 치환으로 바꾸기
- PyYAML의 yaml.compose_all로 노드를 얻고 start_mark.index와 end_mark.index 구간만 문자열 치환하라.
- 파일을 dump해서 다시 쓰지 마라.
- 모호한 대상은 거부하게 만들기
- 리소스·필드가 없거나 (kind, name)이 중복되거나 대상이 스칼라가 아니면 수정하지 말고 None을 반환하라.
- alias 필드는 아직 구멍이라 명시적으로 거부하라.
- 병합은 사람만 하게 막기
- preview로 diff를 보여주고 PR만 열게 하라.
- merge 엔드포인트 호출 코드가 없는지 CI 테스트로 강제하라.
Try It
# 파이썬 3 터미널에서 실행 (논문 방법의 간이 버전: 위치 기반 치환)
# 설치: pip install pyyaml
import yaml
raw = """spec:
replicas: 3 # keep >= HPA min
template:
spec: {}
"""
def find_span(raw, path):
node = yaml.compose(raw)
for key in path:
if isinstance(node, yaml.MappingNode):
node = next((v for k, v in node.value if k.value == key), None)
else:
return None
if node is None:
return None
if not isinstance(node, yaml.ScalarNode):
return None
return node.start_mark.index, node.end_mark.index
span = find_span(raw, ["spec", "replicas"])
if span is None:
print("refuse")
else:
s, e = span
print(raw[:s] + "5" + raw[e:])
# 이어서 해 볼 것: 없는 필드, 맵 대상, 이름 있는 리스트(containers)를 넣어 거부 동작과 주석 보존을 확인해 보자.Terminology
관련 논문
같은 시뮬레이션, 다른 근거: 도구 출력을 파일로 넘기면 LLM 에이전트 정확도가 어디서 깎이나
긴 툴 출력을 파일로 넘기자 GPT-5.1은 정확도 11.3%p 하락, 정식 출력 지정하니 회복
가짜 뉴스가 진짜 폭락을: LLM 트레이딩 에이전트 대상 정보 주입 공격
가짜 헤드라인 한 줄로 LLM 트레이딩 에이전트 62%를 조종했다(프롬프트 주입은 14%)
Harness Engineering 101: LLM은 뇌, harness는 몸
에이전트는 JSON 배열을 관리하는 루프이고, 16개 챕터로 직접 만들어 본다
Harness²: 에이전트 harness를 스스로 고쳐 쓰는 재귀 최적화 프레임워크
모델은 고정하고 harness를 재귀로 고쳤더니 벤치마크 점수가 최대 11.8%p 올랐다
해고된 Savannah 경찰관, ICE 요원 남편에게 Flock 계정 공유
경찰관이 Flock 계정을 남편(ICE 요원)에게 공유했고, 감사에서 983건 검색이 드러나 해고됐다
tmux와 Claude Code: 세션, send-keys, 그리고 한계선
tmux로 Claude Code 여러 개를 돌리면 편하지만 에이전트 4개쯤에서 한계가 온다
Hybrid Reasoning Agents: 잠재 공간에서 생각하고 텍스트로 행동하는 LLM 에이전트
잠재 공간 추론과 텍스트 도구 호출을 섞었더니 성능 10.4%↑, 토큰 최대 54.7%↓
Original Abstract (Expand)
LLM agents increasingly diagnose incidents and propose remediations. In a GitOps workflow, applying a fix means editing a version-controlled config file, and the obvious implementation, having the model author the edited file or a diff, is what practitioners reach for first. Evaluating that choice on real Kubernetes manifests, we find no text-generation strategy is safe for unattended automation. Unified diffs are unsafe: under strict patching almost none apply, but that is an artifact, since a tolerant tool (GNU patch) applies 96%, yet silently misapplies about 1 in 7 (14-20%) with no error signal. Full-file rewrite is capability-dependent: a small model corrupts the file, while a frontier model is usually correct but non-deterministic (it silently drops a field or edits a neighbor on some runs) and must regenerate the whole file, costing O(file size) per edit. We present an alternative that separates the semantic decision (which resource, field, and value) from the syntactic act of editing the file. The agent emits only a structured field-change intent; a deterministic pipeline indexes manifests by (kind, name), locates the target scalar's exact character span via the YAML parser's node position marks, and replaces only that span in the raw text. Because the file is never re-serialized, the diff is minimal by construction, formatting and comments are preserved, and the edit is correct and deterministic independent of the model, at O(1) generation cost. The contribution is the pairing of an LLM-proposed intent with a deterministic, fail-closed application contract for GitOps. We implement it in KubeAstra (Apache-2.0) and release the benchmark. Our claim is scoped to faithful application of a known change; whether the change is right is left to human PR review.