AI 에이전트가 사용할 CLI(명령줄 인터페이스) 제작 방법: JSON 출력, 종료 코드, AGENTS.md
요약
본 글은 AI 에이전트가 호출하는 것을 전제로 하는 CLI(명령줄 인터페이스) 제작 방법을 안내합니다. AI 사용을 위해 도구 출력을 JSON 형식으로 통일하고, 표준 출력에는 리포트만, 로그는 표준 에러로 분리하며, 종료 코드의 의미를 명확히 정의하여 안정성을 높이는 것이 핵심입니다.
핵심 포인트
- AI 전용 CLI 제작은 설정 없이 터미널에서 바로 사용 가능합니다.
- 도구 결과는 AI가 읽기 쉬운 JSON 형식으로 통일해야 합니다.
- 표준 출력(stdout)에는 리포트만, 로그/경고는 표준 에러(stderr)로 분리하세요.
- 종료 코드의 의미를 명확히 정의하여 AI가 상태를 정확히 파악하게 하세요.
에이전트 형태의 AI는 터미널에서 명령을 실행할 수 있습니다. 즉, 우리가 직접 만든 도구(CLI)를 AI에게 사용하게 할 수 있다는 의미입니다.
다만, 사람이 사용할 것을 전제로 만들어진 도구를 그대로 AI에게 사용하게 하면 원활하지 않을 때가 있습니다. 출력 형태가 제각각이라 오독하기 쉽거나, 실패했는지 성공했는지 알기 어렵고, 어떤 도구를 써야 할지 혼란스러울 수 있습니다.
이 글에서는 AI 에이전트가 호출하는 것을 전제로 한 CLI 제작 방법을 소개합니다. 다루는 예시는 업무 효율화 도구들을 모아 놓은 devtools-workspace와 그 공통 라이브러리인 devtools-common입니다. 이들은 특정 회사나 시스템의 이름을 포함하지 않은 범용 버전으로 공개되어 있습니다.
왜 MCP가 아닌 CLI인가
AI에게 도구를 제공하는 방법으로는 MCP 서버도 잘 알려져 있습니다. 하지만 이 도구 세트에서는 의도적으로 'GitHub에서 clone → 설정(setup) → Copilot을 통해 명령 호출'의 형태를 취했습니다. 그 이유는 문서에 다음과 같이 명시되어 있기 때문입니다.
| 선택 | 이유 |
|---|---|
| clone + CLI + 프롬프트 파일 (채택) | 추가적인 설정이나 상주 프로세스가 필요 없습니다. Copilot은 평소처럼 터미널에서 명령을 실행하기만 하면 됩니다. 도구는 Copilot 없이도 단독으로 사용 가능합니다. |
| MCP 서버 (현재 보류) | Copilot이 도구를 자동으로 찾을 수 있다는 장점은 있지만, MCP 활성화 및 설정 파일, 조직의 정책 확인 등이 필요하여 초보자가 첫발을 내딛기 무겁습니다. |
핵심은 '도구는 AI가 없어도 단독으로 사용 가능하다'는 점입니다. AI에 의존할 수 없을 때 사람도 직접 사용할 수 있고, 테스트 역시 일반적인 CLI로 작성할 수 있습니다.
또한, 각 CLI의 출력 형식과 종료 코드를 통일해 놓으면, 필요할 때 MCP의 얇은 레이어(wrapper)를 나중에 추가하는 것도 가능합니다. CLI를 먼저 만들어 두는 것은 후퇴하기 어려운 선택이 아닙니다.
약속 1: 기계가 읽을 수 있는 출력을 준비한다
AI에게 도구 결과를 읽게 할 때는 사람을 위한 보기 좋게 꾸며진 출력은 적합하지 않습니다. 시각적인 모습이 바뀔 때마다 오독이 발생할 수 있기 때문입니다.
그래서 모든 도구에서 공통의 '리포트 형식'을 정했습니다. -f json 옵션을 붙이면 다음과 같은 형태로 결과를 반환합니다.
{
"schema_version": "1.0",
"tool": "fieldcheck",
...
}
여기서 고안한 부분은 세 가지입니다.
- AI는 건수(件数)를 보고 자세히 읽을지 판단할 수 있습니다.
summary로 전체 내용을 한눈에 파악할 수 있습니다. - - '무엇이 다르고 어떻게 수정해야 하는지'까지 작성되어 있어, AI가 사람에게 설명하기 쉽습니다.
expected와actual그리고suggestion이 존재합니다. - - 형식을 비호환적인 방식으로 변경했을 때는 메이저 번호를 올립니다. 사용하는 쪽은 메이저 버전이 예상대로인지 확인할 수 있습니다.
schema_version이 존재합니다.
사람이 읽는 Markdown이나 HTML 형식도 준비되어 있지만, 이 경우 '외관은 예고 없이 바뀔 수 있음'을 명시하고 있습니다. AI나 다른 도구에서 사용할 때는 JSON을 사용하기로 약속했습니다.
약속 2: 표준 출력에는 리포트만 내보낸다
로그나 진행 상황, 경고는 모두 표준 에러(stderr)로 보냅니다. 표준 출력(stdout)에는 리포트만 출력합니다.
doc2md 보고서.pdf -d out/ -f json > report.json
이렇게 해두면 출력을 그대로 파일에 저장해도 로그가 섞여 망가질 일이 없습니다. AI가 명령 결과를 읽을 때도 어디부터가 리포트인지 헷갈리지 않습니다.
약속 3: 종료 코드의 의미를 통일한다
AI는 명령어의 종료 코드를 보고 성공/실패를 판단합니다. 그래서 모든 도구에서 그 의미를 통일했습니다.
| 코드 | 의미 | 사용하는 쪽의 처리 |
|---|---|
0 | 정상 종료. 지적 없음 | 성공 |
1 | 정상적으로 처리는 했으나, 지적이 있음 | 리포트의 findings를 확인한다 |
2 | 사용법/설정 파일/입력 오류 (전제가 무너짐) | 데이터가 아닌, 인자(argument), 설정, 열 이름을 수정한다 |
3 | 예상치 못한 실행 시간 에러 | 결함으로 보고한다 |
특히 중요한 것은 1과 2를 분리했다는 점입니다.
- 1은 '도구는 제대로 작동했고, 데이터에 문제가 발견됨'을 의미합니다.
- 2는 '애초에 도구 사용법 자체가 잘못되었음'을 의미합니다.
이것이 같은 번호라면, AI는 인수의 오류를 '데이터의 문제'라고 오해하고 데이터를 수정하려고 합니다. 번호를 분리해 두면, AI가 무엇을 고쳐야 할지 혼동하지 않습니다.
구현에서는 공통 라이브러리에 종료 코드(ExitCode)를 정의하여 모든 툴에서 재사용하고 있습니다.
class ExitCode(IntEnum):
OK = 0 # 정상 종료・지적 없음
FINDINGS = 1 # 정상적으로 처리했지만 error 중대도의 지적이 있음
...
약속 4: 어떤 툴을 사용할지 AGENTS.md에 작성하기
툴이 늘어날수록, AI는 어떤 것을 사용해야 할지 망설입니다. 그래서 워크스페이스의 AGENTS.md에 '언제・어떤 툴을 사용할지' 목록을 작성하고 있습니다. 발췌하면 다음과 같은 형태입니다.
| 하고 싶은 것 | 명령어 |
|---|---|
| Word / Excel / PowerPoint / PDF / draw.io 내부 내용을 읽고 싶다 | doc2md <file> -d out/ -f json → out/<stem>.md를 읽는다 |
| 표 항목 간의 정합성을 확인하고 싶다 | fieldcheck validate <data> -r rules.yaml -f json |
| 설계서의 신구 차이점을 보고 싶다 | design-diff excel --type api --before 旧.xlsx --after 新.xlsx |
| Office 문자열을 일괄로 치환하고 싶다 | 반드시 먼저 office-replace <dir> --rules rules.csv --dry-run 후, 확인 후에 본 작업 수행 |
핵심은 툴 이름이 아니라 '하고 싶은 것'으로 접근할 수 있게 하는 것입니다. AI는 'Excel 내부 내용을 읽고 싶다'라는 상황에서 사용해야 할 툴에 도달할 수 있습니다.
주의사항도 함께 작성합니다.
- 기존 툴로 충분한 것을 새로운 스크립트로 만들지 않는다.
doc2md의 JSON에loss.*경고가 있다면, 변환 과정에서 내용이 누락되었을 가능성이 있다. 사용자에게 전달한다 - 종료 코드 2는 데이터가 아니라 전제의 오류이다. 데이터가 아니라 전제를 확인한다.
마지막 항목은 약속 3의 종료 코드를 AI에 대한 지시로도 강조하는 것입니다.
약속 5: 위험한 작업은 미리 확인할 수 있게 하기
파일을 수정하는 툴에는 --dry-run 옵션을 준비했습니다.
office-replace samples/input --rules samples/rules.xlsx -r --dry-run
--dry-run에서는 어느 부분이 치환될지에 대한 리포트만 출력하고, 파일은 수정하지 않습니다. 게다가 본 작업에서도 원본 파일을 수정하지 않고 다른 폴더에 출력합니다.
AGENTS.md에도 '반드시 먼저 --dry-run'이라고 작성되어 있어, AI가 갑자기 수정하지 않습니다. 되돌릴 수 없는 작업일수록 확인 절차를 툴과 AI 지시 양쪽에 포함합니다.
약속 6: 기밀을 외부에 유출하지 않기
업무 문서를 다루는 툴이므로, 데이터 취급에도 신경 쓰고 있습니다.
- 툴은 PC 내부에서만 작동하며 네트워크에 연결하지 않는다. 생성 AI의 API도 호출하지 않는다.
- 임시 파일은 작성자만 읽을 수 있는 폴더에 만들고, 처리 후에 반드시 삭제한다.
- 로그에는 본문을 출력하지 않고, 건수나 파일명만 출력한다.
로그에 본문이 나오지 않도록 공통 라이브러리에는 본문 대신 길이만 반환하는 함수를 준비했습니다.
def describe_text(text: str) -> str:
""본문 대신 로그로 낼 요약 (길이만).""
return f"<text {len(text)} chars>"
AI를 사용할 때의 주의사항도 작성합니다. Copilot에는 채팅 내용이나 Copilot이 읽은 파일・명령어 결과가 전송됩니다. 그래서 원본 문서를 직접 열게 하지 않고 변환 결과를 읽게 하거나, 검증은 툴에 맡기고 지적된 행만 설명하게 하는 등의 사용법을 권장하고 있습니다.
자신의 툴에 적용하는 절차
- 출력에 최소한
-f json를 추가한다.summary와findings목록을 반환한다. - 로그를 표준 에러로 이동시킨다. 표준 출력에는 리포트만 출력한다.
- 종료 코드를 결정한다. 적어도 '지적 있음'과 '사용법 오류'는 다른 번호로 한다.
- 수정하는 툴에
--dry-run
붙일-
리포지토리(Repository)에 '하고 싶은 것 → 명령어' 표와 주의사항을 적는 AGENTS.md를
두세요.
AI에게 한 번 사용하게 해보세요. 망설이거나 잘못 읽은 부분을 AGENTS.md나 도구의 출력에 반영합니다.
마지막 단계가 중요합니다. AI가 막히는 부분은 사람이 초보일 때 막히는 부분입니다.
흔한 실수들
사람을 위한 출력만 준비하는 것
테두리나 색깔이 있는 표는 사람에게는 읽기 쉬워도 AI에게는 읽기 어렵습니다. 기계가 읽는 형식(format)을 따로 준비해야 합니다.
로그와 리포트를 같은 출력에 섞는 것
표준출력(Standard Output)에 로그가 섞이면, 결과를 파일로 저장했을 때 손상됩니다. AI도 어디부터가 결과인지 알 수 없게 됩니다.
실패를 모두 같은 종료 코드로 처리하는 것
'데이터에 문제가 있다'와 '사용법이 다르다'를 구분할 수 없다면, AI는 엉뚱한 곳을 고치려고 합니다.
도구의 사용 구분을 적지 않는 것
도구가 많아질수록, AI는 어떤 것을 사용해야 할지 선택하지 못합니다. AGENTS.md에 '하고 싶은 것'에서 끌어낼 수 있는 목록을 두세요.
요약
AI 에이전트에게 CLI는 손과 도구입니다. 도구가 다루기 쉬우면, AI의 작업 품질도 올라갑니다.
- 기계가 읽을 수 있는 출력(JSON)과 표준출력/표준에러의 사용 구분
- 의미가 통하는 종료 코드
- '하고 싶은 것'에서 끌어낼 수 있는 AGENTS.md
- 위험한 작업 전의
--dry-run
하네스(Harness) 기사에서 Tools는 'PC와 사내 시스템 계정'에 해당한다고 썼습니다. 우리가 직접 만드는 CLI는 그 Tools를 늘리는 것입니다. AI에게 사용하게 할 것을 염두에 두고 만들어 두면, 사람에게도 사용하기 쉬운 도구가 됩니다.
Discussion
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기