이 글은
제가 개발하고 있는 Redmine AI Helper 플러그인에서는 코드의 대부분을 AI에게 작성하게 하고 있습니다.
AI에게 코드를 맡기는 데 있어 가장 중요한 것은 '하네스(Harness)'라고 생각합니다. 하지만 하네스는 만들고 끝나는 것이 아니라, 매일 개선해 나가야 합니다. 그리고 이것이 꽤나 힘든 일입니다.
그래서 하네스의 개선 자체도 AI에게 맡겨보았습니다. 이 글에서는 그 메커니즘을 소개합니다. 특히, AI의 지적 사항을 기록시키고, 그것을 AI 스스로 규칙/절차로 발전시키는 시스템을 중심으로 소개하겠습니다.
하네스란 무엇인가
하네스는 AI 코딩 에이전트(Agent)를 둘러싼 '틀'을 의미합니다. 모델 자체라기보다는, 모델에게 전달하는 규칙/절차나 참고 자료, 결과물의 검토 방법 등을 말합니다.
제 경험상 하네스가 잘 갖춰져 있다면 어느 정도 수준의 모델로도 상당한 개발이 가능합니다. 반대로 하네스가 부실하면 아무리 똑똑한 모델이라도 엉뚱한 코드를 작성하게 됩니다.
일반적으로 하네스라고 하면 에이전트 루프(Agent Loop), 도구(Tool), 가드레일(Guardrail) 등, 에이전트를 구동하는 전체적인 메커니즘을 지칭하는 경우가 많습니다. 이 글에서는 그중 개발자가 프로젝트별로 정비하고 매일 발전시켜 나가는 부분을 다음 세 가지로 나누어 생각합니다.
규칙/절차의 정비: 해야 할 일과 하지 말아야 할 일
참조 데이터의 정비: 개발에 필요한 참고 정보를 모으는 것
품질 게이트(Quality Gate)의 정비(결과물 검토): AI에게 검토를 맡기기: 리뷰 지시 / 도구로 검토하기: 자동 테스트, 정적 분석 도구 등
開発者が整備するハーネスの3要素
개발자가 정비하는 하네스의 3가지 요소
품질 게이트는 자동 테스트나 정적 분석 도구, AI에 의한 리뷰 등을 통해 한 번 시스템을 구축해 놓으면 어느 정도 돌아갑니다. 힘든 부분은 규칙/절차와 참조 데이터입니다.
규칙/절차는 개발이 진행될수록
실제 ADR은 이런 식입니다. 채팅에 웹 검색 기능을 추가했을 때, AI가 작성한 것입니다.
# ADR-052: Web search through a dedicated agent, with sources carried on the agent instances
## Context
RubyLLM 2.1 provides `Chat#with_provider_tools(:web_search)` ... The chat should be able
...
웹 검색을 '전용 에이전트에게만 맡긴다'고 결정한 이유가 적혀 있습니다. 다른 에이전트에게 검색 도구를 할당하면, 티켓 요약 등에서 의도치 않게 검색 요청이 발생하여 Redmine 내부 정보가 검색 엔진으로 전송될 우려가 있기 때문입니다. 다음에 또 다른 AI가 이 주변을 건드리면서 '리더 에이전트에게 검색 도구를 붙이면 간단하잖아'라고 생각해도, ADR을 읽으면 주저하게 만듭니다.
LLM Wiki
LLM Wiki는 Andrej Karpathy가 제안한 패턴으로, LLM이 스스로 유지 및 업데이트하는 프로젝트 위키입니다. 매번 소스 코드나 문서를 처음부터 다시 읽어보는 것이 아니라, LLM이 지식을 정리한 위키를 키워나가고 그것을 참조한다는 생각입니다. Spec Kit에는 이를 구현할 수 있는 커뮤니티 확장이 있어서 그것을 사용하고 있습니다. 확장 상세 내용은 이전에 기사를 작성했으니, 그쪽을 참고해 주세요.
wiki/
아래는 다음과 같은 구성이 되어 있습니다.
wiki/
├── INDEX.md # 페이지 목록 (종류별)
├── SCHEMA.md # 위키 규칙
...
각 페이지의 기술에는 반드시 정보 출처 ID(S043 등)를 붙이는 규칙이 되어 있어서, 어떤 사양서나 ADR에서 얻은 지식인지 추적할 수 있게 합니다.
---
title: Prompt Caching & Split System Prompt
type: decision
...
위키 업데이트 자동화하기
위키에 내용을 가져오는 것은 /speckit-wiki-ingest라는 스킬로 진행합니다. 이것을 사람이 매번 호출하는 것은 번거로우므로, Spec Kit의 훅(hook) 기능으로 계획 작성 후와 구현 완료 후에 자동으로 실행되도록 했습니다.
after_plan:
...
command: speckit.wiki.ingest
...
이렇게 하면 기능을 하나 만들 때마다 그 기능의 설계 판단이 위키에 추가됩니다.
참조시키기
그냥 적어주는 것만으로는 의미가 없으므로, 개발할 때 참조하도록 AGENTS.md에 지시를 넣었습니다.
## Specification & Design Reference
- For existing feature specifications and architectural decisions, consult `wiki/INDEX.md`
규칙/절차 정비
AI에게 일을 시키고 나면, 지적이 발생하는 타이밍이 2번 있습니다.
- 사양서를 작성하게 했을 때 (인간으로부터의 사양서에 대한 지적)
- 코드를 작성하게 했을 때 (AI에 의한 리뷰에서의 지적)
코드 리뷰도 AI에게 시키고 있으므로, 인간이 직접 지적하는 것은 주로 사양서 단계입니다.
이 지적을 하나씩 AI가 기록하게 하고, 유사한 지적이 쌓이면 AI 스스로 규칙/절차 개선을 제안하도록 했습니다.
사양서 지적 피드백
AI가 작성한 사양서에 인간이 수정을 지시하면, 수정 후에 그 내용을 docs/spec-feedback/에 기록하게 합니다. 기록에는 record-spec-feedback라는 스킬을 사용합니다.
기록하는 내용은 다음과 같습니다.
- 무엇을 지적받았는지
- AI가 원래 무엇을 작성했었는지
- 어떻게 수정했는지
왜 AI는 그렇게 작성했을까 (근본 원인) 다음부터 같은 실수를 하지 않기 위한 구체적인 규칙/절차
단순히 '틀렸다'로 끝내지 않고, '왜 틀렸는지'를 AI 스스로 분석하게 하는 것이 핵심입니다. 근본 원인은 카테고리별로 나누어져 있는데, 예를 들면 다음과 같은 것들이 있습니다.
⚠️ [IMG:N] 형식 토큰은 이미지 placeholder 입니다. 번역하지 말고 원래 위치에 그대로 유지하세요.
| 카테고리 | 의미 |
|---|
missing-context | AI가 볼 수 있어야 할 정보가 없었다 |
unconfirmed-assumption | 모호한 점을 질문 없이 추측으로 채웠다 |
intent-misread | 요청은 명확했으나, AI가 다른 의미로 해석했다 |
scope-creep | 요청받지 않은 요구사항을 추가했다 |
scope-gap | 요청받은 것을 빠뜨렸다 |
convention-unknown | 기존 사양이나 규약을 찾아보면 알았는데 따르지 않았다 |
실제 기록 사례를 하나 소개합니다. Discord 리액션 삭제 시 에러 처리 건에 대해, AI가 '공식 문서 기반'이라고 썼지만 실제로는 공식 문서를 확인하지 않았던 경우입니다.
# SF-009: Discord reaction error claimed to come from the official docs without checking them
**Category**: unconfirmed-assumption
**Related**: SF-008
...
'Slack과 비슷한 에러가 있으니 Discord에도 있을 것'이라고 유추하여 작성했다는 분석입니다. 상당히 정확한 자기 분석입니다(작성한 것은 AI이지만).
AI에게 리뷰도 맡기기
코드 리뷰 역시 AI의 담당입니다. 구현이 완료되면 Spec Kit의 훅(hook)으로 관점별 AI 리뷰가 자동으로 실행되고, PR을 만들면 GitHub Copilot도 리뷰합니다.
実装完了後に自動で実行される処理
구현 완료 후 자동 실행되는 처리
리뷰 지적에 대한 피드백
AI 리뷰의 지적을 받아 코드를 수정하면 docs/code-review-feedback/ 에 기록하게 합니다. 스킬은 record-code-review-feedback 입니다. 기록된 34건 중, 31건이 로컬에서의 AI 리뷰에서 비롯되었습니다.
여기서 근본 원인 카테고리는 convention-violation, error-handling, security, edge-case, test-gap, doc-comment 등입니다.
PR 리뷰 대응 자체도 handle-pr-review 라는 스킬로 처리합니다. 이 스킬은 다음과 같은 흐름으로 작동합니다.
- PR의 리뷰 코멘트를 가져옵니다.
- 각 지적이 타당한지 코드를 읽고 판단하여 목록표로 보고합니다.
사람의 승인을 기다립니다 - 타당한 지적만 수정합니다.
record-code-review-feedback 으로 기록합니다. - 각 리뷰 스레드에 이유를 적어 회신하고 해결 처리합니다.
handle-pr-reviewの流れ
handle-pr-review의 흐름
리뷰 지적을 '명령'이 아니라 '주장'으로 취급하게 하는 것이 핵심입니다. AI 리뷰는 엉뚱한 경우도 있으므로, 맹신하지 않고 코드를 읽어 검증하게 합니다. 즉, AI가 리뷰하고, 다른 AI가 그 지적을 검증하며, 수정하고 기록까지 합니다.
지적이 쌓이면 AI가 규칙/절차 개선을 제안한다
이 부분이 가장 하고 싶었던 것입니다.
기록을 만들 때, AI는 기존 기록을 확인하고 같은 패턴의 기록이 있으면 Related 에 작성합니다. 그리고 유사한 지적이 2회 이상 발생하면, 그 규칙/절차를 어디로 승격시켜야 할지 AI 스스로 사용자에게 제안합니다. 스킬은 다음과 같이 작성했습니다.
### Step 7: Propose Promotion When It Recurs
If the new entry has one or more **Related** entries (the pattern has occurred
at least twice), propose to the user where the prevention rule should be promoted
...
승격할 파일을 임의로 수정하는 것은 금지하고, 반드시 제안하여 승인을 기다리게 합니다.
실제로 승격된 사례를 소개합니다. AI는 에러 처리에서 rescue StandardError 처럼 무엇이든 다 잡아버리는 코드를 작성하기 쉬워서, 다음과 같이 여러 번 지적받았습니다.
- CRF-014: 웹 검색으로 도중에 끊긴 답변을 성공 취급하고,
rescue
플러그인에서 발생하는 오류를 숨기던 것 - CRF-022: 진행 상황 표시에서 어댑터로부터 오는 StandardError를 모두 rescue하고
했었다 - SF-010: 애초에 사양서 단계에서 'StandardError를 전부 rescue한다'고 적어 놓은
CRF-022를 기록한 타이밍에 AI가 'CRF-014, SF-010과 같은 패턴이므로 AGENTS.md로 승격시키는 것이 어떨까요?'라고 제안해 왔습니다. 이를 승인하자 다음 규칙이 AGENTS.md에 추가되었습니다.
- Error handling: **NEVER implement fallback error handling**. Let errors surface immediately. No silent continues.
+- A `rescue` that turns failures into a result value (a "failed" flag, an error response) lists the error classes the external call can raise (API, network, parse) — never `StandardError` — and wraps only that call. Specs and contracts that prescribe such a rescue name the classes too. A value returned across an adapter or plugin boundary is checked with `is_a?` where it is first used, and a violation raises. A log line for a failed operation on a created resource names that resource's id (CRF-014, CRF-022, SF-010)
요약하자면 '실패를 결과 값으로 바꾸는 rescue에서는 StandardError를 사용하지 않고, 외부 호출이 발생시킬 수 있는 예외 클래스를 나열하여 그 호출만 감싸라'라는 규칙입니다.
규칙/절차의 끝에는 원래 기록의 번호가 붙어 있으므로 '왜 이 규칙/절차가 존재하는지' 나중에 추적할 수 있습니다.
승격된 규칙/절차는 기록 측의 Promoted to 필드도 업데이트됩니다. README 목록표를 보면 어떤 지적이 규칙/절차로 승격되었는지 한눈에 알 수 있습니다.
| Entry | Title | Category | Source | Promoted to |
|-------|-------|----------|--------|-------------|
| [CRF-021](...) | Discord "Unknown Emoji" error ignored as if it meant a missing reaction | error-handling | Local AI review | Not yet |
...
기록 참조시키기
물론 기록을 참조시키지 않으면 의미가 없습니다. constitution에서 다음과 같이 의무화하고 있습니다.
- 사양서를 작성하기 전에
docs/spec-feedback/README.md
목록과 관련된 기록 읽기 - 코드를 작성하기 전에
docs/code-review-feedback/README.md
목록과 관련된 기록 읽기
승격된 규칙/절차는 AGENTS.md에 들어가므로 항상 읽힙니다. 승격되지 않은 기록도 관련 부분을 건드릴 때에는 읽히게 되는 이중 구조입니다.
기록하지 않는 것
모든 것을 기록하면 노이즈가 되기 때문에, 다음은 기록하지 않도록 합니다.
- 오타 수정이나 의미 변화가 없는 표현 수정
- 채택되지 않은 리뷰 지적
또한, 한 번의 지적이 여러 논점을 포함하는 경우 근본 원인별로 1건으로 기록하게 하고 있습니다.
사용해 보니 어땠나
ADR이나 LLM Wiki는 더 오래전부터 운영했지만, 지적을 기록하고 규칙/절차의 승격을 제안시키는 시스템은 2026년 10월 9일에 도입되었습니다. 그로부터 일주일 정도 만에 사양서 지적이 14건, 리뷰 지적이 34건 기록되었고, 이 중 13건의 기록이 AGENTS.md의 규칙/절차로 승격되는 결과를 가져왔습니다. 생각했던 것보다 빠른 속도로 쌓이고 있습니다.
가장 효과적인 부분은 사양서(仕様書)를 수정해야 하는 상황 자체가 줄었다는 것입니다. 이전에는 비슷한 이유로 사양서 수정을 여러 번 지시했지만, 지적이 규칙/절차로 격상된 이후로는 AI가 처음부터 그에 맞는 사양서를 작성해 주게 되었습니다.
ADR(Architecture Decision Record) 역시 오래 운영하면서 효과를 체감하고 있습니다. 설계를 변경하거나 확장할 때, AI가 먼저 wiki/INDEX.md을 경유하여 관련 ADR을 찾아내고, 과거에 무엇을 검토했고 어떻게 결정했는지를 바탕으로 제안해 줍니다. 이전에는 같은 논점을 여러 번 재검토하거나, 과거에 기각했던 안건을 다시 꺼내는 경우가 많았는데, ADR이 축적된 이후로는 그 빈도가 상당히 줄었습니다. 아키텍처상의 판단을 과거 사례를 참조하여 올바르게 할 수 있게 된 것이 가장 큰 변화입니다.
좋았던 점은 다음과 같습니다.
규칙/절차가 구체화된다****규칙/절차의 출처가 파악 가능하다
사람이 규칙/절차를 작성하면 '에러 처리에 주의할 것'과 같은 모호한 것이 되기 쉽습니다. 하지만 AI에게 근본 원인 분석을 시키면, 'StandardError가 아니라 외부 호출에서 던지는 예외 클래스를 나열하는' 것처럼 검증 가능한 규칙/절차가 됩니다.
또한, 각 규칙/절차에 기록 번호가 붙어 있어 왜 그 규칙/절차가 존재하는지 나중에 확인할 수 있습니다.
반면 아쉬운 점도 있습니다.
규칙/절차가 늘어나면 컨텍스트를 압박하고, AI가 규칙/절차를 건너뛰는 원인이 될 수도 있습니다. 결국 규칙/절차의 통폐합을 고려할 필요가 있을 것 같습니다.
요약
AI 코딩에서는 하네스(Harness)가 중요하지만, 그 개선 작업을 사람이 모두 하는 것은 힘듭니다. 그래서 Redmine AI Helper 개발에서는 다음과 같은 과정을 AI에게 자동화시키고 있습니다.
- 참조 데이터 정비: ADR과 LLM Wiki를 작성하게 하여 개발 시 참조하도록 함
- 규칙/절차 정비: 사양서 지적 사항과 AI 리뷰의 지적 사항을 각각 1건씩 기록하고, 유사한 지적이 쌓이면 AI가 규칙/절차 개선을 제안하도록 함
작업은 모두 AI가 하고, 사람은 검토와 승인만 하는 것이 핵심입니다.
이번에 소개된 스킬과 기록들은 모두 리포지토리에서 공개하고 있으니, 관심 있는 분들은 한번 살펴보시기 바랍니다.
토론