
생성형 AI를 애플리케이션에 적용할 때 흔히 프롬프트 작성부터 시작합니다.
좋은 프롬프트를 입력하면 모델이 필요한 답을 만들어 줄 것이라는 접근입니다.
간단한 요약, 분류, 문장 생성 정도라면 이 방식으로도 충분합니다. 그러나 코드 수정, 장애 분석, 데이터 조회, 배포, 검증처럼 여러 단계가 필요한 작업은 단일 모델 호출만으로 안정적으로 처리하기 어렵습니다.
복잡한 문제를 해결하려면 모델이 필요한 정보를 추가로 찾고, 도구를 실행하고, 결과를 확인한 뒤 다음 행동을 결정할 수 있어야 합니다.
이 과정에서 워크플로우와 에이전트, 세션과 턴, 도구와 작업 큐, 가드레일과 훅 같은 개념이 등장합니다.
이 글에서는 AI 에이전트가 내부적으로 어떤 구조로 동작하는지 살펴보고, 이를 실제 제품 개발에 적용할 때 무엇을 고려해야 하는지 정리합니다.
LLM이 답변을 생성할 때 활용할 수 있는 정보는 크게 두 가지입니다.
모델이 학습하지 않은 사내 정책, 최근 장애 기록, 고객 데이터, 현재 소스 코드 상태는 별도로 전달하지 않는 한 알 수 없습니다.
예를 들어 다음과 같은 요청을 모델에 전달한다고 가정해 보겠습니다.
최근 발생한 결제 실패의 원인을 분석해 주세요.
이 요청만으로는 정확한 분석이 어렵습니다.
최소한 다음 정보가 추가로 필요합니다.
결국 복잡한 문제는 한 번의 프롬프트로 끝나는 것이 아니라, 필요한 정보를 단계적으로 수집하고 검증하는 과정이 필요합니다.
복잡한 업무 상황을 하나의 프롬프트에 완전하게 기술하기는 어렵습니다.
요구사항, 예외 조건, 운영 정책, 권한 규칙, 기존 설계 의도, 과거 장애 사례를 모두 한 번에 작성하려 하면 프롬프트는 지나치게 길어집니다.
반대로 설명을 줄이면 중요한 조건이 누락됩니다.
따라서 실제 문제 해결에서는 사람이 모든 정보를 처음부터 완성된 형태로 제공하기보다, 모델이 작업을 진행하면서 필요한 정보를 추가로 요청하거나 직접 조회하도록 구성하는 편이 효율적입니다.
초기 요청
→ 필요한 정보 식별
→ 로그 또는 코드 조회
→ 결과 분석
→ 추가 정보 요청
→ 최종 판단
LLM은 동일한 입력을 받더라도 항상 같은 결과를 반환하지 않습니다.
샘플링, 디코딩 설정, 컨텍스트 상태에 따라 표현뿐 아니라 문제 해결 과정도 달라질 수 있습니다. 특히 모델이 직접 도구와 경로를 선택하는 에이전트 구조에서는 실행 과정 자체가 매번 달라질 수 있습니다.
따라서 다음과 같은 요구가 있는 업무에는 별도의 통제 장치가 필요합니다.
프롬프트에 “사실만 답하라”, “모르면 모른다고 답하라”는 지시를 넣더라도 할루시네이션을 완전히 제거할 수는 없습니다.
오류를 줄이기 위해서는 프롬프트 문구뿐 아니라 실행 구조에서 통제해야 합니다.
예를 들면 다음과 같습니다.
핵심은 모델을 무조건 신뢰하는 것이 아니라, 모델이 실수해도 시스템 전체가 잘못되지 않도록 구조를 설계하는 것입니다.
워크플로우와 에이전트를 구분하는 가장 명확한 기준은 제어 흐름을 누가 결정하느냐입니다.
코드가 흐름을 결정하면 워크플로우이고, 모델이 흐름을 결정하면 에이전트입니다.
워크플로우에서는 개발자가 실행 순서와 분기를 코드로 정의합니다.
입력 수신
→ 분류
→ 관련 문서 검색
→ 답변 생성
→ 결과 검증
→ 응답 반환
모델은 각 단계에서 정해진 역할만 수행하며, 다음 단계로 이동할지는 애플리케이션 코드가 결정합니다.
대표적인 워크플로우 패턴은 다음과 같습니다.
하나의 모델 출력을 다음 모델 호출의 입력으로 연결합니다.
문서 요약
→ 핵심 키워드 추출
→ 보고서 작성
입력을 분류하고 미리 정의된 처리 경로로 보냅니다.
return switch (requestType) {
case PAYMENT -> paymentHandler.handle(request);
case REFUND -> refundHandler.handle(request);
case DELIVERY -> deliveryHandler.handle(request);
};
서로 독립적인 작업을 동시에 실행한 뒤 코드가 결과를 취합합니다.
보안 검토 ─┐
성능 검토 ─┼→ 결과 취합
코드 품질 ─┘
워크플로우는 예측 가능성과 재현성이 높습니다.
실행 경로가 코드에 고정되어 있으므로 테스트와 운영 통제가 비교적 쉽습니다.
에이전트에서는 모델이 현재 상태를 바탕으로 다음 행동을 결정합니다.
모델은 실행 중에 다음과 같은 판단을 할 수 있습니다.
일반적인 흐름은 다음과 같습니다.
요청 수신
→ 모델 판단
→ 도구 선택
→ 도구 실행
→ 결과 관찰
→ 다음 행동 판단
→ 목표 달성 시 종료
에이전트는 고정된 워크플로우보다 유연합니다.
반면 다음 문제가 발생할 수 있습니다.
따라서 모든 업무를 에이전트로 만들기보다, 불확실한 판단이 필요한 구간에만 모델의 제어권을 주는 방식이 현실적입니다.
에이전트의 동작을 이해하려면 세션, 턴, 이터레이션을 구분해야 합니다.
| 개념 | 의미 |
|---|---|
| 세션 | 하나의 문제를 해결하기 위해 유지되는 대화와 상태의 범위 |
| 턴 | 하나의 요청이 시작되어 최종 응답이 반환될 때까지의 범위 |
| 이터레이션 | 하나의 턴 안에서 모델 호출과 도구 실행이 반복되는 단위 |
턴은 요청이 발생하면서 시작되고 최종 응답을 출력하면서 종료됩니다.
요청은 반드시 사용자가 직접 입력하는 프롬프트일 필요는 없습니다.
다음과 같은 이벤트도 새로운 요청이 될 수 있습니다.
요청 발생
→ 턴 시작
→ 작업 수행
→ 최종 응답
→ 턴 종료
하나의 턴 안에서 모델은 여러 번 호출될 수 있습니다.
예를 들어 “회원가입 오류를 분석하고 수정해 주세요”라는 요청은 다음과 같이 진행될 수 있습니다.
모델 판단
→ 프로젝트 파일 검색
→ 검색 결과 수령
→ 관련 파일 읽기
→ 코드 분석
→ 코드 수정
→ 테스트 실행
→ 실패 원인 분석
→ 재수정
→ 최종 응답
이 과정에서 발생하는 각각의 모델 판단과 도구 실행 주기가 이터레이션입니다.
모델 판단
→ 도구 호출
→ 도구 실행
→ 결과 수령
→ 모델 재판단
에이전트가 긴 작업을 수행할 수 있는 이유는 하나의 답변을 오래 생성해서가 아니라, 턴 내부에서 여러 이터레이션을 반복하기 때문입니다.
세션은 여러 턴을 수용하는 작업 공간입니다.
사용자는 하나의 세션에서 요구사항을 추가하고, 모델은 이전 대화와 작업 결과를 참고하면서 문제를 점진적으로 해결합니다.
세션
├─ 턴 1: 요구사항 분석
├─ 턴 2: 설계 제안
├─ 턴 3: 코드 수정
└─ 턴 4: 테스트 및 검증
세션은 사람이 완성된 프롬프트를 처음부터 작성하는 대신, 모델과 협력해 문제 해결에 필요한 컨텍스트를 점진적으로 작성하는 과정으로 볼 수 있습니다.
그러나 세션이 길어진다고 항상 좋은 것은 아닙니다.
세션이 길어질수록 다음 정보가 함께 쌓입니다.
예를 들어 다음과 같은 대화가 모두 컨텍스트에 남을 수 있습니다.
JPA를 사용해 주세요.
→ MyBatis로 변경해 주세요.
→ 기존 JPA 구조는 유지해 주세요.
→ JPA 관련 코드는 모두 제거해 주세요.
사람도 이런 이력을 한꺼번에 전달받으면 현재 기준을 판단하기 어렵습니다. 모델도 마찬가지입니다.
컨텍스트 길이가 늘어나면 단순히 토큰 수만 증가하는 것이 아니라, 중요하지 않은 정보와 모순된 정보가 함께 증가합니다.
이를 컨텍스트 희석 또는 컨텍스트 오염으로 볼 수 있습니다.
긴 세션을 계속 유지하면 다음과 같은 문제가 생길 수 있습니다.
일부에서는 컨텍스트 사용량을 시계에 비유해 4시와 7시를 경험적인 경계로 설명하기도 합니다.
이는 모든 모델에 적용되는 공식 법칙은 아닙니다. 모델 종류, 작업 복잡도, 프롬프트 구성에 따라 달라지는 경험적 가이드입니다.
중요한 것은 정확한 비율이 아니라 다음 원칙입니다.
하나의 세션에서 모든 요구사항을 끝없이 처리하지 말고, 작업의 목적과 범위가 달라지면 새로운 세션으로 분리해야 합니다.
다음과 같은 경우에는 새로운 세션을 여는 편이 좋습니다.
새 세션을 열 때는 기존 대화를 전부 복사하기보다 현재 유효한 결정만 정리해 전달해야 합니다.
현재 확정 사항
1. Spring Boot 4와 Java 21을 사용한다.
2. 결제 모듈 외 다른 모듈은 수정하지 않는다.
3. 공개 API 스펙은 유지한다.
4. 기존 JPA 방식은 폐기하고 MyBatis를 사용한다.
5. 수정 후 통합 테스트를 실행한다.
컨텍스트는 많이 주는 것이 목적이 아니라, 필요한 정보를 정확히 주는 것이 목적입니다.
서브에이전트를 여러 명의 AI 직원처럼 표현하기도 하지만, 구현 관점에서는 별도의 세션에 가깝습니다.
메인 모델이 도구를 통해 새로운 세션을 생성하고, 해당 세션에 일부 작업을 위임하는 구조입니다.
메인 세션
├─ 코드 분석 서브세션
├─ 보안 검토 서브세션
└─ 테스트 검토 서브세션
기존 요구사항과 설계 배경을 알아야 하는 작업이라면 메인 세션의 컨텍스트 일부를 전달할 수 있습니다.
현재까지 확정된 설계 결정과 변경 이력을 참고하여
결제 모듈의 트랜잭션 경계를 검토해 주세요.
이 방식은 기존 맥락을 유지할 수 있지만, 메인 세션에 잘못된 가정이나 불필요한 정보가 많다면 그대로 영향을 받습니다.
객관적인 검토가 필요하다면 빈 컨텍스트에서 시작하는 편이 낫습니다.
이 코드만 기준으로 보안, 동시성, 트랜잭션 문제를 검토해 주세요.
기존 대화나 설계 의도는 참고하지 마세요.
동일한 모델을 사용하더라도 컨텍스트가 다르면 다른 결과를 낼 수 있습니다.
따라서 서브에이전트를 사용하는 핵심 목적은 모델 수를 늘리는 것이 아니라, 문제와 컨텍스트를 분리하는 데 있습니다.
일반적인 세션은 하나의 턴이 끝나면 다음 요청을 기다립니다.
반면 Goal이 설정된 세션에서는 턴이 끝날 때마다 모델이 목표 달성 여부를 평가할 수 있습니다.
작업 수행
→ 턴 종료
→ 목표 달성 여부 평가
→ 미달성
→ 후속 요청 생성
→ 다음 턴 수행
예를 들어 목표를 다음과 같이 설정할 수 있습니다.
모든 테스트가 통과하고,
정적 분석에서 심각한 오류가 없으며,
기존 공개 API가 유지될 때까지 작업을 계속한다.
이 경우 모델은 코드 수정만 하고 종료하지 않고, 테스트와 검증 결과를 확인한 뒤 목표가 충족되지 않으면 새로운 작업을 생성할 수 있습니다.
다만 목표가 지나치게 추상적이면 반복이 끝나지 않을 수 있습니다.
나쁜 목표는 다음과 같습니다.
코드를 완벽하게 개선한다.
완료 조건을 판단할 수 없기 때문입니다.
목표는 검증 가능한 조건으로 작성해야 합니다.
1. 전체 단위 테스트가 통과해야 한다.
2. 결제 모듈 외 파일은 변경하지 않는다.
3. 응답 시간은 기존 대비 10% 이상 악화되지 않아야 한다.
4. 공개 API 스펙은 변경하지 않는다.
AI 에이전트의 도구를 단순히 파일 읽기나 검색 기능으로만 보면 부족합니다.
모델에게 어떤 기능을 도구로 제공하느냐에 따라 모델이 제어할 수 있는 범위가 달라집니다.
같은 기능이라도 코드에 고정하면 애플리케이션이 제어합니다.
if (currentTaskCompleted()) {
executeNextTask();
}
반면 다음 작업을 생성하는 기능을 도구로 제공하면 모델이 실행 여부를 결정합니다.
create_next_request(
task = "통합 테스트 실패 원인을 분석한다"
)
전자는 워크플로우이며, 후자는 에이전트적 제어입니다.
모델에게 다음 기능을 도구로 제공할 수 있습니다.
도구가 늘어날수록 모델의 자율성도 커지지만, 실패했을 때의 영향 범위도 넓어집니다.
따라서 에이전트 설계의 핵심 질문은 다음과 같습니다.
모델에게 어디까지 판단권과 실행권을 넘길 것인가?
에이전트가 여러 작업을 처리한다고 해서 모델이 실제 프로그램의 반복문을 직접 작성하는 것은 아닙니다.
내부 시스템에서는 모델이 생성한 요청이나 도구 결과를 큐에 넣고, 에이전트 런타임이 이를 순차적으로 처리합니다.
공통 작업 큐
├─ USER_REQUEST
├─ TOOL_RESULT
├─ SUB_AGENT_RESULT
├─ SCHEDULED_TASK
└─ GOAL_RETRY
초기에는 다음과 같이 큐가 기능별로 분리될 수 있습니다.
그러나 큐가 많아질수록 상태 관리가 복잡해집니다.
따라서 공통 큐 하나를 사용하고 메시지 타입으로 이벤트를 구분하는 구조도 고려할 수 있습니다.
public sealed interface AgentEvent {
record UserRequest(String content) implements AgentEvent {}
record ToolResult(String toolId, Object result) implements AgentEvent {}
record SubAgentResult(String sessionId, String result) implements AgentEvent {}
record ScheduledTask(String taskId) implements AgentEvent {}
}
중요한 것은 큐의 개수가 아니라 다음 사항입니다.
AI 코딩 에이전트는 도구를 제공하는 방식에 따라 크게 두 방향으로 나눌 수 있습니다.
파일 검색, 읽기, 수정, 패치, 테스트 실행 등 목적별 도구를 제공합니다.
search_files
read_file
edit_file
run_tests
inspect_git_diff
장점은 다음과 같습니다.
단점도 있습니다.
Shell이나 터미널처럼 강력한 도구 몇 개만 제공하고 모델이 명령을 조합하도록 합니다.
shell("grep -R \"PaymentService\" ./src")
shell("./gradlew test")
shell("git diff --stat")
장점은 다음과 같습니다.
단점도 분명합니다.
전용 도구와 범용 도구 중 하나가 항상 우월한 것은 아닙니다. 모델의 성능, 실행 환경, 보안 수준, 작업 복잡도에 따라 선택해야 합니다.
도구에는 일반적으로 이름, 설명, 입력 스키마 정도만 제공됩니다.
도구 설명이 지나치게 길어지면 모델이 도구를 언제 선택해야 하는지 판단하기 어려워질 수 있습니다.
반대로 설명이 짧으면 복잡한 사용 방법과 예외 조건을 전달하기 어렵습니다.
이를 보완하기 위해 실행 기능과 사용 지침을 분리할 수 있습니다.
실행 기능
→ Shell 도구
사용 방법과 절차
→ Skill 문서
예를 들면 다음과 같습니다.
도구
- shell
스킬
- spring-deployment.md
- kubernetes-rollback.md
- database-migration.md
- incident-analysis.md
스킬 문서에는 다음 내용을 포함할 수 있습니다.
이 구조는 실행 도구를 단순하게 유지하면서도 구체적인 작업 절차를 제공할 수 있다는 장점이 있습니다.
다만 동일한 기능을 도구 설명과 스킬 문서에 중복해서 작성하면 모델의 선택이 오히려 불안정해질 수 있습니다. 역할을 분명히 나눠야 합니다.
도구 실행은 대부분 비동기적으로 처리할 수 있습니다.
특히 다음 작업은 오래 걸릴 가능성이 높습니다.
이 작업들을 에이전트의 메인 루프에서 동기적으로 기다리면 전체 세션이 멈출 수 있습니다.
도구를 에이전트 본체와 같은 프로세스에서 실행하는 방식입니다.
장점:
단점:
도구를 독립된 프로세스로 실행하는 방식입니다.
장점:
단점:
CLI와 외부 프로그램을 실행하는 도구라면 별도 프로세스가 안전한 경우가 많습니다.
도구 실행이 오래 걸릴 때는 두 가지 방식이 있습니다.
도구가 완료될 때까지 현재 턴을 유지합니다.
도구 실행
→ 상태 대기
→ 완료 이벤트
→ 모델 재호출
장점:
단점:
도구 실행을 시작한 뒤 현재 턴을 종료합니다.
스케줄러가 작업 상태를 주기적으로 확인하고, 완료되면 새로운 요청을 생성합니다.
작업 시작
→ 현재 턴 종료
→ 스케줄러가 상태 확인
→ 작업 완료
→ 세션 재개
장점:
단점:
도구는 단순히 실행 결과만 반환하지 않을 수 있습니다.
다음과 같은 부수 효과를 발생시킬 수 있습니다.
예를 들어 여러 단계로 진행되는 데이터 마이그레이션 도구는 각 단계가 끝날 때마다 상태를 전달할 수 있습니다.
스키마 검증 완료
→ 데이터 복사 30% 진행
→ 데이터 복사 100% 완료
→ 무결성 검증 시작
→ 검증 실패 3건 발견
모델이 긴 작업에서 목표를 잊지 않도록 다음과 같은 리마인더를 삽입할 수도 있습니다.
현재 목표:
1. 원본 데이터는 변경하지 않는다.
2. 실패한 레코드는 별도 파일로 저장한다.
3. 전체 검증이 끝나기 전에는 운영 테이블을 교체하지 않는다.
에이전트의 메인 루프에 보안, 검증, 로깅, 리라이팅 기능을 직접 추가하기 시작하면 코드가 빠르게 복잡해집니다.
이를 방지하기 위해 주요 이벤트 사이에 훅을 배치할 수 있습니다.
요청 수신
→ 요청 훅
→ 모델 호출 전 훅
→ 모델 응답
→ 도구 호출 훅
→ 도구 실행
→ 도구 결과 훅
→ 응답 완료 훅
사용자나 시스템의 요청을 수신한 직후 실행합니다.
활용 예:
모델에 컨텍스트를 전달하기 직전에 실행합니다.
활용 예:
모델의 응답을 스트리밍으로 수신하는 동안 실행합니다.
활용 예:
모델이 도구를 실행하려 할 때 호출합니다.
활용 예:
예를 들어 다음 명령은 자동 실행을 막아야 합니다.
rm -rf /
DROP DATABASE production;
kubectl delete namespace production
도구 실행 결과를 모델에 전달하기 전에 호출합니다.
활용 예:
모델이 최종 응답을 생성했을 때 실행합니다.
활용 예:
훅을 사용하면 핵심 턴 루프는 단순하게 유지하면서 정책과 통제 기능을 독립적으로 확장할 수 있습니다.
모델 학습에서는 사람이 작성한 정답만 사용되는 것이 아닙니다.
모델이 다양한 문제 해결 궤적을 생성하고, 테스트나 평가를 통해 성공한 결과만 선별해 다시 학습에 활용할 수 있습니다.
문제 제시
→ 여러 해결 과정 생성
→ 테스트 또는 채점
→ 성공한 궤적 선별
→ 학습 데이터로 활용
이 방식이 확대되면 모델은 사람이 일반적으로 선택하는 해법과 다른 방식으로 문제를 풀 가능성이 있습니다.
코드가 테스트를 통과하더라도 다음과 같은 문제가 생길 수 있습니다.
인간이 이해하기 어려운 코드가 곧바로 더 좋은 코드를 의미하지는 않습니다.
제품 코드에는 정답 여부 외에도 다음 조건이 필요합니다.
따라서 AI가 생성한 코드는 테스트 통과만으로 승인해서는 안 됩니다.
AI는 새로운 프로그램을 빠르게 생성하는 데 강합니다.
그러나 실제 제품 개발의 대부분은 새로 만드는 일이 아니라 기존 코드를 지속적으로 변경하는 일입니다.
실제 환경에서는 다음과 같은 변경이 반복됩니다.
코드 구조가 뒤엉켜 있으면 작은 수정 요청도 여러 모듈에 영향을 줍니다.
에이전트는 관련 파일을 넓게 수정하고, 예상하지 못한 회귀 오류를 만들 수 있습니다.
이 문제를 줄이는 가장 현실적인 방법은 변경 범위를 구조적으로 격리하는 것입니다.
예를 들어 의료 시스템을 다음과 같이 분리할 수 있습니다.
patient/
registration/
consultation/
billing/
insurance/
claim/
settlement/
common/
security/
audit/
AI에게 전체 프로젝트를 맡기는 대신 수정 범위를 제한합니다.
billing/payment 모듈만 수정해 주세요.
제약 조건:
1. 다른 모듈의 구현 파일은 변경하지 않습니다.
2. 공개 인터페이스는 유지합니다.
3. 데이터베이스 스키마는 변경하지 않습니다.
4. 수정 후 billing 모듈 테스트를 실행합니다.
5. 변경된 파일 목록과 이유를 보고합니다.
폴더 경계만 만든다고 좋은 구조가 되는 것은 아닙니다.
실제 책임과 변경 이유를 기준으로 나눠야 합니다.
파일을 많이 나누는 것이 목적은 아닙니다.
중요한 것은 특정 요구사항이 들어왔을 때 변경 범위가 제한되는 구조입니다.
나쁜 구조는 다음과 같습니다.
common/
CommonService.java
CommonUtil.java
CommonManager.java
서로 다른 도메인의 로직이 공통 클래스에 몰려 있으면 작은 수정도 전체 시스템에 영향을 줍니다.
반면 책임이 분리된 구조에서는 결제 수정이 회원이나 배송 코드까지 번지는 일을 줄일 수 있습니다.
payment/
application/
domain/
infrastructure/
member/
application/
domain/
infrastructure/
AI 코딩 환경에서는 변경 범위가 명확할수록 다음 효과가 있습니다.
에이전트를 여러 개 실행하면 한 번의 결과 품질은 높아질 수 있습니다.
예를 들어 여러 에이전트가 각각 구현, 테스트, 보안 검토를 맡고 최종 결과를 취합하는 방식입니다.
그러나 구조가 좋지 않은 프로젝트에서 에이전트 수만 늘리면 다음 문제가 발생합니다.
나쁜 구조 + 많은 에이전트
≠ 안정적인 제품
명확한 모듈 경계 + 제한된 작업 범위
= 지속적으로 수정 가능한 제품
멀티 에이전트는 작업 경계가 명확할 때 효과적입니다.
적합한 예:
부적합한 예:
작업 분해가 불분명한 멀티 에이전트는 협업 시스템이라기보다 비용이 많이 드는 반복 실행 시스템이 되기 쉽습니다.
AI 에이전트를 실제 개발 환경에 적용할 때는 다음 원칙이 중요합니다.
결제 승인, 권한 검증, 개인정보 처리처럼 반드시 같은 절차를 따라야 하는 영역은 워크플로우로 구현합니다.
모델에게 전체 흐름을 맡기지 않습니다.
문서 검색, 장애 원인 후보 생성, 코드 위치 탐색처럼 유연한 판단이 필요한 부분에 모델을 사용합니다.
모델의 판단 결과는 코드와 정책으로 검증합니다.
모델이 사용할 수 있는 도구와 경로를 제한합니다.
허용:
- 프로젝트 디렉터리 읽기
- 특정 모듈 수정
- 테스트 실행
금지:
- 운영 데이터베이스 접근
- 운영 배포
- 프로젝트 외부 파일 수정
- 인증 정보 출력
모델에 “전체적으로 개선해 주세요”라고 요청하기보다 수정 범위와 금지 사항을 지정합니다.
수정 대상:
- payment/application
- payment/domain
변경 금지:
- public API
- database schema
- authentication module
검증:
- payment 단위 테스트
- 전체 컴파일
- git diff 확인
구현한 모델에게 같은 컨텍스트로 자체 검토를 시키면 기존 판단을 그대로 정당화할 가능성이 있습니다.
검토 작업은 별도의 세션에서 수행하는 편이 낫습니다.
구현 세션
→ 코드 작성
독립 검토 세션
→ 코드와 테스트 결과만 전달
→ 보안·동시성·트랜잭션 검토
“문제를 완벽하게 해결한다”는 목표는 종료할 수 없습니다.
다음처럼 측정 가능한 조건을 사용해야 합니다.
1. 전체 테스트가 통과한다.
2. 정적 분석에서 Critical 오류가 없다.
3. 변경 파일이 지정된 모듈 안에만 존재한다.
4. API 스펙이 변경되지 않았다.
5. 실패 시 롤백 절차가 존재한다.
대화가 길어졌다고 해서 그 세션을 계속 유지할 이유는 없습니다.
현재 유효한 결정을 요약한 뒤 새로운 세션으로 전환하는 것이 품질과 비용 면에서 나을 수 있습니다.
LLM 단일 호출은 간단한 생성 작업에는 효과적이지만, 복잡한 문제 해결과 실제 시스템 조작에는 한계가 있습니다.
에이전트는 모델이 도구를 사용하고 결과를 관찰하며 다음 행동을 결정할 수 있도록 확장한 구조입니다.
그러나 자율성을 높이는 것만으로 안정적인 시스템이 만들어지는 것은 아닙니다.
실제 제품에서는 다음 요소가 함께 필요합니다.
AI는 코드를 빠르게 생성할 수 있습니다.
그러나 변경을 반복해도 무너지지 않는 제품을 만들려면 구조, 경계, 검증, 운영까지 함께 설계해야 합니다.
결국 중요한 것은 모델에게 얼마나 많은 일을 맡기느냐가 아닙니다.
모델에게 어떤 정보를 제공하고, 어디까지 제어권을 주며, 실패했을 때 시스템이 어떻게 안전하게 복구되도록 만들 것인지가 핵심입니다.
이번 내용을 정리하면서 AI 에이전트는 단순히 LLM에 도구를 연결한 기능이 아니라, 모델에게 어느 범위까지 판단과 실행을 맡길 것인지 설계하는 시스템이라는 점을 다시 확인할 수 있었습니다.
처음에는 워크플로우와 에이전트의 차이를 단순히 자동화 수준의 차이로 생각했습니다.
그러나 실제 차이는 제어 흐름의 위치에 있었습니다. 실행 순서와 분기를 코드가 결정하면 워크플로우이고, 모델이 현재 상황을 판단해 도구와 다음 행동을 선택하면 에이전트입니다.
에이전트의 자율성이 높아질수록 유연성은 커지지만, 비용과 비결정성, 보안, 재현성 문제도 함께 커진다는 점이 중요했습니다.
세션, 턴, 이터레이션의 구분도 에이전트의 동작을 이해하는 데 도움이 되었습니다.
겉으로는 하나의 요청을 처리하는 것처럼 보이지만, 내부에서는 모델 판단과 도구 실행, 결과 확인이 여러 차례 반복됩니다.
긴 세션을 계속 유지한다고 항상 더 좋은 결과가 나오는 것도 아니었습니다.
이전 지시와 폐기된 설계가 컨텍스트에 남으면 오히려 판단을 흐릴 수 있으므로, 현재 유효한 정보만 정리하고 작업 단위에 따라 세션을 분리하는 전략이 필요합니다.
가장 인상적이었던 부분은 도구가 단순한 실행 기능이 아니라 모델에게 넘겨주는 제어권의 범위라는 점입니다.
파일 읽기나 터미널 실행뿐 아니라 새로운 요청 생성, 서브에이전트 호출, 목표 평가, 스케줄링까지 도구로 제공하면 모델이 작업 흐름 자체를 제어할 수 있습니다.
따라서 도구를 많이 제공하는 것이 무조건 좋은 설계는 아니며, 권한과 실패 영향도를 고려해 필요한 기능만 제한적으로 열어야 합니다.
또한 AI가 코드를 빠르게 생성한다고 해서 아키텍처의 중요성이 줄어드는 것은 아니었습니다.
오히려 수정 범위가 명확하지 않은 코드에서는 작은 요청 하나에도 여러 파일이 함께 변경되고, 반복 수정 과정에서 기존 기능이 쉽게 깨질 수 있습니다.
모듈 경계, 공개 인터페이스, 테스트 범위가 명확해야 AI에게도 제한된 컨텍스트와 작업 범위를 전달할 수 있습니다.
멀티 에이전트 역시 만능 해결책은 아니라고 생각합니다.
작업을 명확하게 분리할 수 있을 때는 효과적이지만, 모호한 목표를 여러 에이전트에게 맡기면 같은 분석과 수정을 반복하면서 토큰과 시간만 소비할 수 있습니다.
에이전트 수를 늘리기 전에 문제를 분해하고, 완료 조건과 검증 방법을 먼저 정의해야 합니다.
결국 AI 에이전트를 활용할 때 중요한 것은 모델이 얼마나 많은 일을 할 수 있는지가 아닙니다. 어떤 작업을 코드가 통제하고, 어떤 판단을 모델에 맡길지, 실패했을 때 어디까지 영향을 허용할지를 설계하는 것이 핵심입니다.
AI는 구현 속도를 높여 주지만, 안정적으로 변경하고 운영할 수 있는 제품을 만드는 책임까지 대신해 주지는 않습니다.