AGENTS.md의 지시를 LLM이 가끔 무시한다


AGENTS.md로 공통 지시를 전달한다.

AGENTS.md는 저장소에 두는 AI 에이전트용 지시 파일이다. Codex는 AGENTS.md를 자동으로 읽지만, Claude Code는 CLAUDE.md만 자동으로 읽는다. 나는 공통 규칙을 AGENTS.md에 모아두고, CLAUDE.md에서 AGENTS.md를 읽고 따르라고 지시해 Codex와 Claude Code가 같은 규칙을 따르게 했다.

이 파일에는 테스트 작성 원칙, 각 작업마다 커밋하기, React 컨벤션 같은 프로젝트 규칙을 정리해두고 있다.

그런데 항상 따르지는 않는다.

대체로는 지시를 잘 따른다. 하지만 상황에 따라 흔들린다.

특히 다음과 같은 조건이 겹치면 지시를 건너뛰는 경우가 생긴다.

  • 작업 수가 많아질 때
  • 실행이 길어질 때
  • 중간에 추가 지시가 들어올 때
  • 컨텍스트가 길어질 때

이런 상황에서는 일부 절차를 생략하거나, 아예 규칙 자체를 무시하기도 한다.

예를 들어 AGENTS.md에 Given-When-Then 주석을 강제하는 규칙이 있지만, 실제 테스트 작성 시 누락된 파일이 있다.

tasks.md의 "작업마다 커밋" 규칙도 비슷하다. 초반에는 잘 지키다가도, 작업이 쌓이면 여러 작업을 한 번에 처리하고 커밋을 하나로 묶는다.

이러면 작업을 되돌리거나 직접 수정하는 일이 생긴다.

내 잘못도 있다

물론 에이전트만 탓할 건 아니다. 스펙 범위를 넓게 잡아서 작업이 길어졌고, 구현 도중 개입해 추가 작업을 지시하기도 했다. 이 문제는 이전 글에서 다뤘고, 설계 확정 시점에 범위를 고정하는 방식으로 대응하고 있다. (내 경우는 사실 애초에 고려할 게 많은 작업이긴 했다. 스펙을 메인 스펙과 서브 스펙을 나누어서 처리하는 방안을 도입할 필요가 있음)

하지만 그래도 불안은 남는다. 에이전트가 지시를 이해하지 못하는 경우를 모두 잡아낼 수는 없으니까.

왜 이런 일이 발생하는가

LLM은 규칙을 '이해해서 지키는' 시스템이 아니라, 주어진 컨텍스트를 바탕으로 다음 텍스트를 확률적으로 생성하는 모델이다.

이 구조에서는 모든 지시가 동일하게 유지되지 않는다. 상황에 따라 일부는 빠지거나 약해진다.

  • 지시가 많으면 일부가 빠진다.
  • 대화가 길어지면 앞쪽 규칙부터 흐려진다.
  • 컨텍스트 요약 과정에서 규칙이 유실된다.

결국 AGENTS.md의 규칙도 항상 지켜지지는 않는다.

어떻게 대응할 것인가?

이 문제를 줄이기 위해 몇 가지 방법을 생각해봤다. 다른 글들은 실제로 적용한 뒤에 썼지만, 이번에는 적용 전에 계획부터 정리한 것이다.

규칙을 실행 흐름에 포함시킨다.

가장 효과 대비 비용이 낮은 방법이다.

기존에는 "작업마다 커밋" 규칙이 AGENTS.md에 있었다. 그런데 /opsx:apply의 task 루프는 "코드 수정 → 완료 표시 → 다음 작업"이다. 커밋 단계가 루프에 없으니까, 에이전트가 AGENTS.md의 규칙을 떠올리지 못하면 커밋 없이 다음 작업으로 넘어간다.

커밋 단계를 루프 안에 직접 넣으면 이 문제가 줄어든다.

For each pending task:
- Make the code changes required
- Mark task complete
- Stage and commit          ← 추가
- Continue to next task

"AGENTS.md의 규칙을 기억해라"가 아니라 "루프의 다음 단계를 실행해라"가 된다. 여전히 확률적이지만, 먼 곳의 원칙을 기억하는 것보다 눈앞의 단계를 실행하는 게 쉽다.

Skill로 필요한 시점에 규칙을 주입한다

테스트 작성 원칙은 AGENTS.md에 있다. 이 규칙은 테스트를 쓸 때만 필요한데, 항상 컨텍스트에 떠 있다. 앞 섹션에서 다뤘듯이 지시가 많으면 각 지시에 돌아가는 주의가 줄어든다.

이걸 /write-test라는 별도 skill로 분리하려고 한다. 테스트를 쓸 때만 호출되고, 호출 시점에 신선한 컨텍스트로 로드된다. opsx:apply의 task 루프에도 "테스트 작업이면 /write-test를 호출하라"고 넣을 것이다.

AGENTS.md가 짧아지면 남은 규칙을 더 잘 따르고, 테스트 규칙은 필요한 순간에 skill로 바로 읽히니까 무시될 가능성이 줄어들 것이다.

다만 skill 호출 자체가 확률적이다. 에이전트가 "이건 테스트 작업이다"라고 판단하고 skill을 호출해야 하는데, 이 판단을 안 할 수도 있다. 적용해보고 실제로 얼마나 따르는지 확인해야 한다.

검증 가능한 규칙은 hook으로 안전망을 둘 수 있다

AGENTS.md의 규칙 중 일부는 기계적으로 검증할 수 있다. commitlint가 커밋 메시지 형식을 잡듯이, hook으로 결과물을 검사해서 통과 못하면 막는 방식이다. AGENTS.md를 없애는 게 아니라, 지켜지지 않는 규칙에 안전망을 추가하는 구조다.

다만 이건 Claude Code 한정이다. Codex에는 hook이 없다. Codex에서 자주 깨지는 규칙은 해당 skill에 중복해서 넣는 수밖에 없다. 에이전트 상관없이 작동하는 건 git hook과 CI뿐이다.

정리하면:

  • 공통 규칙은 AGENTS.md에 둔다
  • 자주 깨지는 규칙은 해당 skill에 중복한다
  • 반드시 지켜야 하는 건 git hook이나 CI로 올린다

지금 당장 전부 추가하지는 않는다. skill 루프 개선과 /write-test 분리를 먼저 적용하고, 부족하면 hook을 하나씩 추가할 생각이다.

정리

AGENTS.md는 여전히 쓴다. 에이전트가 프로젝트에 처음 진입할 때 맥락을 잡아주는 데는 여전히 쓸모가 있다. 대체로 잘 따르지만, 항상 지켜진다고 믿고 넘어가지는 않으려 한다.

지금 하려는 건 두 가지다.

  • 규칙을 실행 흐름 안에 넣는다. AGENTS.md에 원칙으로 적어두는 것보다, skill 프롬프트의 루프에 단계로 넣는 게 따를 확률이 높다.
  • 특정 시점에만 필요한 규칙은 skill로 분리한다. 항상 컨텍스트에 띄워두는 것보다, 필요할 때 주입하는 게 낫다.

그리고 이 글을 쓰면서, 앞에서 말한 "가끔 안 따른다"가 언제 발생했는지 생각해보니, 내가 개입한 순간이었다(또 내 잘못). 작업 중간에 "이거 이름 바꿔" 같은 지시를 하면 LLM은 그 지시를 처리하지만, 처리한 뒤 원래 루프로 돌아오지 않고 흐름이 끊긴다. 개입을 막을 수는 없으니, 개입 후 /opsx:apply를 다시 호출해서 루프를 재개하면 된다. tasks.md가 상태를 들고 있으니 미완료 항목부터 다시 시작하면 된다.

다음 할 일

이미 완료한 OpenSpec 아티팩트를 테스트 삼아, 같은 스펙을 Claude Code와 Codex에 각각 수행시킨다. 결과를 비교해서 어떤 지시가 잘 지켜지고 어떤 지시가 깨지는지 파악하고, 그걸 바탕으로 워크플로우를 개선한다.