"Agent"는 현재 소프트웨어 분야에서 가장 많이 사용되는 단어 중 하나입니다. 이는 몇 가지 도구를 가진 챗봇일 수도 있고, 작업이 완료될 때까지 반복하는 스크립트일 수도 있으며, 또는 여러 단계에 걸쳐 계획하고 행동하며 자신의 작업을 점검하는 시스템일 수도 있습니다. 이 글에서는 해당 용어를 정의하고, 에이전트가 어떻게 구축되는지 설명하며, 중요한 실패 모드를 나열하고, 마지막으로 에이전트에게 로컬 TDK 스택에 대한 실제 제어권을 부여하는 방법을 보여줍니다.
에이전트란 무엇인가?
**에이전트(agent)**는 도구와 함께 루프(loop) 안에 배치된 언어 모델입니다. 한 번 답변을 하는 대신, 다음과 같은 과정을 거칩니다:
- 목표(goal)와 현재 상태를 읽고,
- 다음 행동을 결정하며,
- 도구를 호출하고 그 결과를 읽으며,
- 목표가 달성되거나 포기할 때까지 반복합니다.
모델은 판단력을 제공하고, 도구는 행동할 수 있는 능력을 제공합니다. 텍스트만 작성하는 모델은 챗봇입니다. 명령을 실행하거나, 파일을 읽거나, API를 호출하여 학습한 것을 사용할 수 있는 모델이 에이전트입니다.
대부분의 에이전트는 세 가지 부분으로 정의됩니다:
- 모델(The model): 행동을 선택합니다.
- 도구(Tools): 에이전트가 호출할 수 있는 함수들입니다. 파일 읽기, 명령 실행, 데이터베이스 쿼리, 서비스 시작 등이 있습니다.
- 루프와 그 상태(The loop and its state): 시도했던 내용과 발생한 일을 기록합니다.
에이전트 대 워크플로우 (Agents versus workflows)
모든 다단계 AI 시스템이 에이전트는 아닙니다. 이 구분이 중요한 이유는 시스템에 얼마나 신뢰를 둘지 결정하기 때문입니다.
- **워크플로우(workflow)**는 작성자가 지정한 경로를 따릅니다. 단계 A가 모델을 호출하고, 단계 B가 출력을 파싱하며, 단계 C가 파일을 작성합니다. 모델이 내용을 채우지만, 순서는 사용자가 제어합니다.
- **에이전트(agent)**는 스스로 경로를 선택합니다. 다음에 어떤 도구를 호출할지, 언제 멈출지를 결정합니다.
단계가 사전에 알려져 있을 때는 워크플로우를 선호하세요. 다음 확인 사항이 마지막 오류에 의존하는 것처럼, 에이전트가 발견한 것에 따라 경로가 달라지는 경우에는 에이전트를 사용하세요.
에이전트는 어떻게 구축되는가 (How agents are built)
대부분의 에이전트 시스템은 소수의 설계 결정 사항을 공유합니다.
도구(tool)는 이름과 모델이 읽을 수 있는 설명, 인자(argument)에 대한 스키마, 그리고 구현체로 구성됩니다. 코드가 아무리 좋아도 명확한 설명만큼 중요합니다. 모델은 도구를 그 설명을 읽고 선택하기 때문에, 모호한 설명은 잘못된 호출을 유발합니다.
좋은 도구 설계:
- 도구당 하나의 명확한 목적,
- 검증(validation)이 적용된 좁은 범위의 인자,
- 모델이 파싱할 수 있는 구조화된 출력,
- 무엇이 잘못되었는지, 그리고 다음에 무엇을 시도해야 하는지 알려주는 오류 메시지.
컨텍스트 제한 (Context is limited)
모델은 자신의 컨텍스트 윈도우에 있는 것만 볼 수 있습니다. 전체 로그 파일과 같은 긴 도구 출력은 앞쪽의 정보를 밀어냅니다(push out). 따라서 경계가 정해지고 관련성 높은 결과를 반환하도록 도구를 설계해야 합니다. 마지막 200개 로그 라인을 반환하는 도구가 전체 파일을 덤프하는 도구보다 에이전트가 사용하기 더 쉽습니다.
루프에 중지 조건 필요 (The loop needs a stop condition)
중지 조건이 없는 에이전트는 무한히 반복하거나 진전 없이 비용을 소모할 수 있습니다. 다음 사항들을 정의해야 합니다:
- 성공 확인(success check) (테스트 통과, 서비스가
200으로 응답),
- 최대 단계 수 또는 시간,
- 막혔을 때 무엇을 할지 (인간에게 보고).
인간의 검토 지점 (Human checkpoints)
데이터 삭제, 메시지 전송, 배포와 같이 되돌리기 어려운 동작의 경우, 사람의 승인을 요구해야 합니다. 에이전트는 빠르지만, 잘못된 파괴적 행동 역시 매우 빠릅니다.
에이전트가 실패하는 지점 (Where agents go wrong)
능력을 감탄하는 것보다 실패 모드를 이해하는 것이 더 유용합니다.
- 잘못된 도구 사용 및 인자 오류: 모델이 가짜 값으로 올바른 도구를 호출하는 경우. 인자를 엄격하게 검증해야 합니다.
- 확신에 찬 오답 보고: 에이전트가 확인 절차 없이 성공했다고 보고하는 경우. 성공 여부 확인을 모델의 의견이 아닌 도구 호출로 만들어야 합니다.
- 루프(Looping): 에이전트가 동일한 실패 행동을 반복하는 경우. 반복되는 호출을 감지하고 중단해야 합니다.
- 범위 이탈 (Scope creep): 에이전트가 아무도 건드리라고 요청하지 않은 부분을 "수정"하는 경우. 작성할 수 있는 내용과 범위를 제한해야 합니다.
- 오래된 상태 정보 (Stale state): 에이전트가 이미 변경된 세상의 모습(view)을 기반으로 행동하는 경우. 행동하기 전에 현재 상태를 다시 읽어야 합니다.
- 로그에 대한 과도한 신뢰: 실행 중인 프로세스가 건강한 서비스라는 의미는 아닙니다. 단순히 무언가가 시작되었다는 것뿐만 아니라 준비 상태(readiness)까지 확인해야 합니다.
에이전트를 위한 안전 기본 설정 (Safe defaults for agents)
- 에이전트가 작업을 완료하는 데 필요한 최소한의 도구만을 제공합니다.
- 기본값은 읽기 전용(read-only) 도구로 설정하고, 파괴적인(destructive) 도구는 명시적으로 사용하도록 합니다.
- 홈 디렉터리가 아닌 프로젝트 디렉터리 내에서 실행합니다.
- 무슨 일이 일어났는지 검토할 수 있도록 모든 도구 호출을 기록(log)합니다.
- 실제 작업에 신뢰하기 전에 임의의 프로젝트에서 루프를 테스트해 봅니다.
에이전트와 로컬 인프라 (Agents and local infrastructure)
에이전트는 인프라 작업을 수행하는 데 유용합니다. 왜냐하면 이러한 작업은 시작 스택(start the stack), 실패한 서비스 확인, 로그 읽기, 설정 수정, 재시작과 같이 루프 형태의 과정이기 때문입니다. 인간도 이 과정을 할 수 있지만 지루하고 번거롭습니다. 에이전트는 올바른 도구를 가지고 있고 언제 성공했는지 알 수 있다면 빠르게 처리할 수 있습니다.
가장 어려운 부분은 에이전트에게 신뢰할 수 있는 도구를 제공하는 것입니다. 로컬 스택에는 많은 움직이는 부분이 있습니다. 만약 에이전트가 Docker를 어떻게 시작해야 하는지, 어떤 포트가 사용 중인지, 로그는 어디에 있는지 추측해야 한다면 자주 실패할 것입니다. 해결책은 스택을 명확한 결과를 가진 도구를 통해 노출하는 것입니다.
TDK CLI가 바로 이 역할을 합니다.
TDK CLI가 에이전트에게 로컬 스택 제어권을 부여하는 방법
TDK CLI는 AI 모델이 아니며 무엇을 구축할지 결정하지 않습니다. 이는 service.json 파일에 스택을 설명하고 Docker와 Tilt로 실행하는 로컬 도구입니다. 이 도구가 에이전트에게 유용한 이유는 모든 작업이 기계가 읽을 수 있는 형태로 되어 있으며, CLI가 Model Context Protocol (MCP) 서버로 작동할 수 있기 때문입니다.
사용하려면 세 가지 구성 요소가 함께 사용되어야 합니다.
1. 기계가 읽을 수 있는 명령어
대부분의 TDK 명령어는 --json 옵션을 받아 사람이 읽을 수 있는 텍스트 대신 버전이 지정된 객체 하나를 출력합니다. 에이전트에게 가장 필요한 명령어들은 다음과 같습니다:
tdk doctor --json: 호스트(local, wsl2, devcontainer, codespaces, webcontainer, native-windows)가 접근 가능한지, 컨테이너 런타임이 도달 가능한지, 그리고 여기서 tdk up이 작동할 수 있는지(canUp)를 보고합니다.
tdk up --json: 스택을 시작하고 모든 리소스가 구축되고, 실행되며, 정상 상태가 될 때마다 ok: true를 출력하거나 무언가 실패하면 errors와 함께 ok: false를 출력합니다. Tilt는 객체가 출력된 후에도 계속 실행되므로, 에이전트는 이를 백그라운드에서 실행해야 합니다.
tdk status --json: 포트, URL, 그리고 준비 상태(data.tilt.readiness의 ready, pending, failures)를 보고합니다.
tdk logs --json --service <이름> --tail <n>: 가장 최근 줄들을 경계가 지정되고 구조화된 항목으로 출력하여 에이전트가 무한한 출력을 읽지 않도록 합니다.
tdk down --json: 스택을 중지하고 결과를 보고합니다.
오류 또한 유형별로 분류되어 있습니다. 예시로는 UNKNOWN_SERVICE, TILT_ALREADY_RUNNING, TILT_NOT_RUNNING 등이 있으며, 각각 종료 코드(exit code)를 가집니다. 에이전트는 영어 텍스트를 파싱하는 대신 이 코드를 기반으로 분기 처리를 할 수 있습니다.
2. TDK MCP 서버
tdk mcp는 stdio를 통해 Model Context Protocol (MCP) 서버를 실행합니다. 별도의 패키지나 호스팅된 엔드포인트가 없으며, CLI가 로컬에서 실행되고 클라이언트가 stdin과 stdout을 통해 이와 통신합니다.
이것은 doctor, up, down, status, logs, 그리고 resource_list의 여섯 가지 도구를 노출합니다. 각 도구는 일치하는 --json 명령어를 실행하므로, MCP는 CLI처럼 작동합니다. 인수는 아무것도 실행되기 전에 유효성 검사를 거칩니다: 이름은 문자(letters), 숫자(digits), ., _, 그리고 -만 사용할 수 있습니다.
에이전트에게 중요한 두 가지 세부 사항이 있습니다:
up은 Tilt가 분리된 상태로 시작하고 준비 완료를 기다리지 않고 반환합니다. 빌드는 단일 도구 호출보다 오래 지속될 수 있기 때문입니다. 에이전트는 data.tilt.readiness.ready가 true가 될 때까지 status를 폴링해야 합니다. 이것이 성공 확인을 제공합니다.
logs는 스트림이 아니라 스냅샷입니다. 에이전트는 최근 줄들을 읽고, 결정하고, 다시 호출합니다.
3. TDK로 구축할 수 있는 MCP 서비스
자체 애플리케이션에 에이전트 인터페이스를 제공하려면, TDK가 스택 내 서비스로서 MCP 서버를 구성(scaffold)할 수 있습니다. 이것은 mcp라는 리소스 유형이며, 이미 MCP SDK가 연결된 Bun 기반의 구성 도구를 사용합니다:
tdk resource support-tools --type mcp
저는 이 명령어 형태를 CLI 소스(cli/src/backend-frameworks/registry.ts, 여기서 mcp를 자체 리소스 유형으로 처리함)에서 추론했습니다. 의존하기 전에 사용 중인 버전의 tdk resource --help를 확인하세요.
생성된 index.ts는 플레이스홀더 도구인 echo가 포함된 MCP 서버를 생성하고, 요청당 새로운 서버로 HTTP를 통해 서비스하며, 따라서 컨테이너가 재시작되어도 엔드포인트가 잃을 세션이 없습니다. echo를
data.host.containerRuntimeReachable과 data.host.canUp을 확인하십시오. 만약 canUp이 false라면, 에이전트는 이 장치에서 스택을 시작할 수 없으며, 아무리 재시도해도 해결되지 않습니다. 예를 들어 WebContainer의 경우 Docker 데몬이 없으므로, 에이전트는 데몬이 있는 장치에서 실행되어야 합니다.
3단계: 에이전트를 MCP 서버에 연결하기
MCP 서버는 .tdk/project.json을 포함하는 디렉토리에서 시작해야 합니다. Codex의 경우, .codex/config.toml에 프로젝트 범위 항목을 추가하십시오:
[mcp_servers.tdk]
command = "tdk"
args = ["mcp"]
...
Claude Code의 경우, TDK agent tooling guide의 Claude Code 섹션을 따르십시오. 원칙은 동일합니다: 명령어는 tdk, 인수는 mcp이며, 작업 디렉토리는 프로젝트로 설정해야 합니다.
일부 클라이언트는 일반적인 형태도 허용하며, 이는 대부분의 MCP 호스트에서 작동합니다:
{ "mcpServers": { "tdk": { "command": "tdk", "args": ["mcp"] } } }
작업 디렉토리를 클라이언트에서 설정해야 합니다. 잘못된 폴더에서 시작된 서버는 프로젝트를 찾을 수 없기 때문입니다.
4단계: 명령어 대신 결과 요청하기
연결되면, 일반 언어로 질문할 수 있습니다. 좋은 요청은 결과를 설명합니다:
- "TDK doctor를 실행하고 시작 전에 무엇을 수정해야 하는지 알려줘."
- "웹 서비스와 그 종속성만 시작한 다음 상태를 확인해 줘."
- "최근 5분 동안의 API 로그를 보여줘."
- "스택을 중지해 줘."
그러면 에이전트는 올바른 순서로 doctor, up, status, logs, 그리고 down을 호출합니다.
5단계: 에이전트가 따라야 할 루프 작성하기
에이전트에게 명시적인 절차를 제공하여 임의로 행동하지 않도록 하십시오. 스택 시작을 위한 작동 예시는 다음과 같습니다:
doctor를 호출합니다. 만약 canUp이 false라면, 멈추고 이유를 보고합니다.
- 필요한 서비스와 함께
up을 호출합니다.
readiness.ready가 true가 되거나 failures가 비어있지 않을 때까지 반복적으로 status를 호출합니다.
- 실패가 있다면, 실패한 서비스를 대상으로 제한된
tail과 함께 logs를 호출합니다.
status에서 얻은 URL과 함께 결과를 보고합니다.
종료 조건은 준비 상태 확인(readiness check)이며, 실패 분기(failure branch)는 명시적입니다. 이 내용을 에이전트의 지침이나 프로젝트의 AGENTS.md에 포함하여 모든 세션에서 따르도록 하세요.
6단계: 깔끔하게 종료하기 (Stop cleanly)
작업이 완료되면, 에이전트가 down을 호출하도록 하세요. 실행 중인 스택은 메모리와 포트를 소모하며, 다음 tdk up 명령이 이전 Tilt 프로세스가 여전히 응답하고 있어 시작을 거부할 수 있습니다.
전체 예시 (A complete example)
여기 웹(web) 프론트엔드, API 백엔드, 그리고 지원 도구(support-tools) MCP 서비스를 갖춘 프로젝트에 대한 간단한 엔드투엔드 흐름입니다.
- 사용자가 직접
tdk doctor를 실행하여 canUp: true가 보고됩니다.
- 프로젝트 디렉터리에서 Codex를 엽니다.
tdk MCP 서버와 연결됩니다.