leave-copilot
Leave Copilot — MCP에서 전용 Agentic 모델까지
실행 가능한 참조 구현: MCP로 의도적으로 어렵게 설계된 휴가/근태 도구를 표준화하고, Google ADK로 이를 조작할 수 있는 Agent를 만들고, 이중 평가로 기반 모델의 부족함을 측정한 뒤, 이 도구들을 원래 잘 다루는 전용 모델을 미세 조정합니다.
이것은 2026 iThome 철인전 30일 시리즈의 부속 코드입니다.
도구가 '의도적으로 어렵게 설계된' 이유
일반적으로 API 설계는 직관적이고 사용하기 쉽게 하는 것을 추구하지만, 이 프로젝트는 그 반대가 필요합니다.
검수 방식은 미세 조정 전후의 성능 차이를 비교하는 것입니다. 도구가 너무 직관적이면 기반 모델이 원래 올바르게 호출할 수 있어 정확도가 처음부터 만점에 가깝고, 미세 조정은 자연히 어떤 개선도 보여주지 못합니다. 미세 조정이 효과가 없어서가 아니라, 개선할 여지가 아예 없기 때문입니다.
따라서 선택 기준은 단 하나입니다: 기반 모델이 거의 반드시 틀리고, 미세 조정으로 가르칠 수 있는 것.
의도적으로 심은 네 가지 난점
# | 난점 | 구현 방식 | 모델의 전형적인 오류 |
① | 호출 간 의존성 | 번호를 추측 불가능한 형식( | 조회를 건너뛰고 |
② | Elicitation 삼태 | 파괴적 작업은 | decline 후 다른 도구로 우회 |
③ | 상태 머신 제약 | 상태는 | draft에서 approved로 직접 점프 |
④ | 매개변수 함정 | 시간은 시간(hour) 단위(반나절 = 4, 0.5 아님), |
|
이 네 가지 난점의 공통 특징: 모두 JSON Schema로 표현할 수 없는 규칙입니다. Schema는 status가 네 문자열 중 하나여야 한다는 것은 관리할 수 있지만, '이 번호가 어디서 왔는지'는 관리할 수 없습니다.
Related MCP server: MCP Leave Management
빠른 시작
환경
패키지 | 버전 | 이유 |
|
| 이 시리즈는 1.x의 FastMCP를 사용하므로 버전 범위를 생략하지 마세요 |
|
| 1.x도 유지보수되지만, 새 프로젝트가 구버전에서 시작할 이유는 없습니다 |
Python |
| 둘의 공통 하한 |
첫 번째 항목은 특히 걸리기 쉽습니다. MCP Python SDK 공식 사이트가 기본적으로 다른 API의 문서(
MCPServer)를 보여주기 때문이며, 여기서 사용하는FastMCP작성 방식과 완전히 다릅니다.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txtMCP Server 실행
python -m mcp_server.server # streamable-http on 127.0.0.1:8090네 가지 난점 검증
python eval/verify_difficulties.py실제로 Server에 연결하여 네 가지 난점을 하나씩 트리거하고 오류 메시지와 반환 의미를 확인합니다:
難點 ① 跨呼叫依賴
✓ 捏造的編號被擋下
✓ 錯誤訊息指出正確取得方式
✓ search_leaves 查得到真實編號
…
難點 ④ 參數陷阱
✓ 餘額欄位以小時命名
✓ 傳姓名而非員工編號被擋下
難點 ② Elicitation 三態
✓ accept → cancelled
✓ decline → aborted
✓ cancel → aborted
✓ accept_but_false → aborted
✓ 撤銷後時數退回餘額
✓ decline 的訊息明確禁止繞道
✓ cancel 的訊息與 decline 語意不同
──────────────────────────────────────────────
19/19 通過테스트 데이터 초기화
update_leave_status, cancel_approved_leave는 실제로 데이터를 변경합니다. 매 평가 라운드 시작 전에 반드시 초기화해야 합니다. 그렇지 않으면 두 번째 라운드의 전제 조건이 첫 번째 라운드와 달라 결과를 비교할 수 없습니다.
python eval/reset.py도구 모음
아홉 개의 도구와 평가 스크립트 전용 관리 엔드포인트 하나가 있습니다.
카테고리 | 도구 |
|
휴가 신청 |
| ✅ |
직원 |
| ✅ |
결재 |
| ✗ |
인수인계 |
| ✗ |
취소 |
| ✗(Elicitation 사용) |
관리 |
| ✗ |
readOnlyHint는 단순한 문서가 아닙니다. 평가 도구가 이를 통해 '읽기 전용 준수성'을 계산합니다: Agent가 읽기 전용 작업에서 쓰기 도구를 사용했는지 여부입니다.
_reset_fixtures는 Agent 쪽에서 반드시tool_filter로 제외해야 합니다. '초기화'라는 이름의 도구는 LLM에게 묘한 매력이 있습니다.
오류 메시지를 이렇게 명확하게 작성하는 이유
도구의 오류 메시지는 그대로 모델에게 돌아가 다음 단계의 근거가 됩니다.
# ✗ 模型只知道錯了,得猜哪裡錯
raise ValueError("Invalid status transition")
# ✓ 模型知道錯在哪、也知道該改成什麼
raise ValueError(
f"狀態不可從 {current} 跳至 {target},下一個合法狀態為 {next_valid}"
)이것은 도구 설계로 모델 능력을 보완하는 전형적인 기법이며, 비용은 몇 글자 더 쓰는 것뿐입니다.
프로젝트 구조
.
├── mcp_server/ # ✅ MCP Server:九個工具 + 四個難點
│ ├── server.py
│ ├── store.py # 模擬資料層
│ └── fixtures.py # 初始資料與 reset
├── eval/ # ✅ 驗證與重置腳本
│ ├── verify_difficulties.py # 19/19
│ ├── verify_agent.py # 架構驗證 10/10
│ └── reset.py
├── agents/leave_copilot/ # ✅ Google ADK Agent(含 elicitation callback)
├── plugins/ # ⏳ 軌跡記錄與生產防禦 Plugin
├── data/ # ⏳ 軌跡萃取與資料擴增
├── training/ # ⏳ SFT 訓練腳本
└── deploy/ # ⏳ 權重合併、量化、vLLM 部署✅ 완료 및 실측됨 ⏳ 구축 중
Port 할당
⚠️ FastMCP와 Google ADK api_server의 기본 port는 모두 8000이므로, 둘 중 하나를 반드시 변경해야 합니다. 이 프로젝트는 MCP Server를 8090으로 옮겼습니다.
서비스 | Port |
MCP Server(streamable-http) | 8090 |
Google ADK api_server | 8000 |
평가 도구 Web UI | 8080 |
vLLM | 8001 |
Ollama | 11434 |
관련 프로젝트
ADEval — Google ADK Agent 평가 도구(Apache-2.0)
Twinkle Eval — 표준 Benchmark 평가(MIT)
라이선스
Apache-2.0
검증된 부분
eval/verify_difficulties.py와 eval/verify_agent.py는 모두 실제로 실행된 것이지, '문서에 그렇게 적혀 있는' 것이 아닙니다.
MCP Server 層(eval/verify_difficulties.py) 19/19
四個難點的錯誤訊息、Elicitation 四條路徑
Google ADK 層(eval/verify_agent.py,A 段架構驗證) 10/10
McpToolset 載入、tool_filter 排除管理端點
accept / decline / cancel / accept-but-false 四條路徑
都確認走到 Client callback,且語意正確回報환경: mcp 1.29.1 + google-adk 2.7.1 + gemini-3.7-flash.
기반 모델의 행동 관찰
verify_agent.py의 B 섹션은 단언하지 않고 기록만 합니다 — 모델이 틀렸다고 해서 테스트가 실패하는 것이 아니며, 그것이 바로 측정하려는 대상입니다. 실제로 실행해 보면 가장 주목할 만한 실패 패턴은 다음과 같습니다:
모델이 도구 호출 대신 텍스트 응답을 사용합니다. 파괴적 작업에 직면했을 때
gemini-3.7-flash는cancel_approved_leave를 호출하여 Server가 Elicitation을 발행하게 하는 대신, 스스로 대화에서 '정말입니까?'라고 묻는 경향이 있습니다. 결과적으로 확인 프로세스가 프로토콜 계층에서 대화 계층으로 떨어지며, 대화 계층의 확인은 강제력이 없습니다.
더 심각한 변형은 환각형 성공입니다 — 모델이 '휴가 신청서를 결재에 제출했습니다'라고 답하지만, 도구 시퀀스에 update_leave_status가 전혀 없고 휴가 신청서 상태도 변하지 않았습니다. 사용자는 완료된 줄 압니다.
이런 실패는 Prompt로 제거할 수 없습니다. 모델의 '안전'에 대한 내장된 성향에서 비롯되기 때문입니다. 이것이 바로 이후에 미세 조정으로 처리할 대상입니다.
Related MCP Connectors
Connect, monitor, and control AI agents — tasks, approvals, schedules, and governance.
Shared task queue for humans and AI agents: leases, handoffs, approvals and signed receipts.
Agentic workflow budget approvals with usage receipts.
AI agents discover human capabilities, request real-world human execution, and track status.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables employees to check leave balance, apply for leave, and view leave history through natural language using Claude Desktop.-
- AlicenseNot gradedqualityDmaintenanceSimulates a leave management workflow for employees and managers, including leave application, balance checks, and approval processes.ISC
- FlicenseBqualityDmaintenanceEnables HR teams to query and manage employee leave through natural language using Claude Desktop, with tools for checking balances, applying leave, and viewing history.3-
- AlicenseAqualityDmaintenanceEnables LLM clients to handle leave applications by providing tools for initialization, organization selection, leave day calculation, attachment checks, uploads, and submission, with built-in business validation and environment switching.6MIT