모두의포트폴리오
PE · FDE 직군을 위한 포트폴리오 밀키트
예전 판입니다 (1판)이 문서는 2026-08-25 에 새 판으로 개정됐습니다. 오너가 산출물 형태 불일치로 반려해 재생성했습니다. 제목이 가리키는 형태와 예측 UI·1주차 산출물이 서로 달랐습니다.최신 판 보기 →
경력빌드잇 · Product Engineer (Backend/Frontend, 5년 이상)

업무 요청을 처리하는 담당자를 위해 요청마다 실행할 시스템 액션을 정해 큐로 넘기는 AI Agent

빌드잇 공고에서 뽑은 세 가지 시그널.

빌드잇은 엔터프라이즈 솔루션 10년 위에서 'AI Agent 기반 프로세스 자동화 솔루션 제품화' 로 전환 중인 회사입니다. 자유 텍스트 업무 요청을 사내 시스템 액션으로 잇는 이 라우터는, 빌드잇이 만들려는 바로 그 제품의 최소 뼈대이고 Kotlin/Spring·FastAPI·메시지 큐·EKS 라는 JD 스택을 결정·실행 분리 MSA 로 그대로 씁니다. 다만 이 포트폴리오가 보여주는 것은 자유 텍스트에서 구조화된 액션 결정을 뽑고 인용으로 검증해 큐로 디스패치하는 기술이지, 특정 고객사의 결재·ERP 도메인 전문성이 아닙니다. 실 커넥터 권한과 고객사별 업무 규칙의 깊이는 모르면 면접에서 모른다고 말하는 편이 낫습니다.

01

AI 중심 전환 · 프로세스 자동화 제품화

엔터프라이즈 솔루션 10년 위에서 'AI Agent 기반 프로세스 자동화 솔루션' 을 제품화하는 단계입니다. 자유 텍스트 업무 요청을 사내 시스템 액션으로 잇는 워크플로우가 바로 그 제품의 최소 뼈대입니다.

AI Agent프로세스 자동화제품화AI 중심
02

Product Engineer · 구현부터 개선까지 end-to-end

작은 팀에서 요구사항 분석부터 배포까지 자기주도적으로 완결하는 역할입니다. 2주면 요청 인테이크 → 액션 결정 → 큐 디스패치 → 콘솔 추적 한 루프가 끝나는 최소 단위입니다.

Product Engineerend-to-end자기주도적배포까지
03

엔터프라이즈 백엔드 스택 (Kotlin/Spring·FastAPI·메시지큐·K8s·AWS)

Kotlin/Spring 과 Python/FastAPI, 메시지 큐(Kafka·RabbitMQ), Docker/Kubernetes, AWS 가 주요 스택입니다. 결정 서비스와 실행 커넥터를 큐로 분리한 MSA 로 이 스택을 그대로 골조에 씁니다.

KotlinFastAPIApache KafkaKubernetesAWS

포트폴리오 요약

네이버웍스·슬랙으로 들어오는 사내 업무 요청(자유 텍스트)을 AI Agent 가 읽어, 의도와 실행할 액션 타입(승인·조회·등록), 액션 ID, 필요한 파라미터를 뽑고 파라미터마다 원문 인용 근거를 붙입니다. 값이 빠졌거나 매핑되는 액션이 없으면 지어내지 않고 결측·미매핑으로 알린 뒤 사람에게 라우팅하고, 결정된 액션은 Kotlin/Spring 게이트웨이가 메시지 큐로 커넥터에 디스패치합니다. 1주차만으로 요청→액션→감사 추적이 도는 완성품이고, 분류가 실제로 맞았는지 확인(2주차)·실 커넥터 연동과 레지스트리 확장(3주차)으로 단계적으로 키웁니다. (근거는 채용공고 정렬이고, 데이터는 작성자가 만든 더미입니다.)

고객 정의

빌드잇 PE. 사내로 쏟아지는 '다음 주 연차 올려주세요', '법카 3월 사용내역 조회', '회의실 프로젝터 고장 수리 티켓 등록' 같은 자유 텍스트 요청을 사람이 일일이 읽고 어느 시스템에 어떤 값으로 넣을지 손으로 판단하던 상황을 끝내고, 에이전트가 근거를 붙여 액션을 정하되 실제 실행은 큐로 안전하게 넘기게 하고 싶은 사람입니다.

고민 지점

업무 요청 원문과 실행 가능한 액션 카탈로그(required_params 포함)를 한 스키마로 묶으면, LLM 이 (a) 의도와 실행할 액션 타입·액션 ID 를, (b) 원문에서 인용한 근거(evidence_span)를 붙인 파라미터를, 몇 초 안에 뽑고, (c) 필요한 값이 빠졌거나 매핑되는 액션이 없으면 지어내지 않고 결측·미매핑으로 표시한 뒤 사람에게 라우팅합니다.

주차별 일정

1주차여기까지로 완성

요청→액션 결정 + 인용 검증 + 큐 디스패치 + 콘솔 추적

자유 텍스트 업무 요청을 받아 action plan(의도·액션 타입·액션 ID·파라미터·원문 인용 근거)을 생성하고, 인용이 원문에 실제로 있는지 검증한 뒤 목(mock) 커넥터로 큐 디스패치하고, 콘솔에서 요청→액션→커넥터 결과를 추적하는 동작하는 도구입니다. 실 사내 시스템(네이버웍스 결재·ERP·Jira)은 이번 주에는 목 API 로 가정하고, 실 커넥터 연동은 3주차로 분리합니다.

  1. 1일 · 업무 요청과 액션 레지스트리 표준 표 만들기work_requests·action_registry 스키마 설계 + 별칭 정규화 사전(법카→법인카드, 미팅룸→회의실)
  2. 2-3일 · 자유 텍스트에서 의도·액션·파라미터 뽑기FastAPI 에이전트(Claude Sonnet) + JSON Schema 로 action plan 출력 강제, 모든 파라미터에 원문 인용(evidence_span) 요구
  3. 4일 · 인용 근거 검증과 결측·미매핑 플래그evidence_span 이 원문 substring 인지 결정론 체크 + required_params 대비 결측 표시 + 레지스트리에 없으면 미매핑 라우팅
  4. 5일 · 액션 게이트웨이 + 목 커넥터 디스패치Kotlin/Spring 게이트웨이가 결정된 액션을 큐로 발행, 목 커넥터가 소비해 결과를 감사 로그에 적재
  5. 6일 · 요청→액션 콘솔Next.js/TypeScript/Zustand 로 인입 요청 큐·결정된 액션·원문 인용 하이라이트·감사 로그, Zod 런타임 검증
  6. 7일 · 7일치 시연 영상 + 직접 만든 정답과 비교한 기준선여기까지가 1주차 완성품
2주차선택 성장

분류가 실제로 맞았는지 확인 + 재시도·감사 + EKS 배포

정답셋 40건으로 액션 타입·액션 ID 분류가 맞았는지 확인하고, Claude Haiku 저비용 2차 분류로 불일치·저확신 건을 사람 큐로 라우팅하며, RabbitMQ 재시도·DLQ 와 Kafka 감사 스트림을 붙인 뒤 2주차부터 AWS EKS 로 배포·운영하는 단계입니다.

  1. 8-9일 · 분류가 실제로 맞았는지 확인정답셋 40건으로 액션 타입·액션 ID 일치율 측정 + 인용 span 존재율 자동 집계
  2. 10일 · 저비용 2차 분류 + 사람 라우팅Claude Haiku 2차 분류 불일치·저확신 건을 사람 큐로 보내고 5% 표본은 사람 spot check
  3. 11-12일 · 메시지 큐 안정화RabbitMQ 재시도·DLQ 로 커넥터 실패 격리 + Kafka 감사 이벤트 스트림(결정·실행 결과 append-only)
  4. 13일 · AWS EKS 배포Docker 이미지로 에이전트·게이트웨이·커넥터를 EKS 에 배포·운영
3주차선택 성장

실 커넥터 어댑터 교체 + 레지스트리 확장 + 멱등·권한 가드

목 커넥터를 실 시스템 어댑터 인터페이스로 교체 가능하게 추상화하고, 액션 카탈로그가 수백 개로 늘어도 후보를 검색(retrieval)으로 압축하며, Redis 멱등 키와 권한 가드로 중복 실행을 막는 단계입니다.

  1. 15-16일 · 커넥터 어댑터 추상화목 커넥터를 실 시스템 어댑터 인터페이스로 교체 가능하게 + Redis 멱등 키로 중복 실행 차단
  2. 17-18일 · 레지스트리 확장액션 카탈로그 수백 개면 후보 검색(retrieval)으로 압축 후 Sonnet 에 전달, 권한 가드 추가
  3. 19-21일 · 3주 통합 시연 영상 + 실 도입 계획실 커넥터·권한·감사 연동 4주 로드맵으로 분리

데모 · 측정 지표

01

업무 요청 12건을 넣으면 각 요청의 action plan(의도·액션 타입·액션 ID·파라미터·원문 인용 근거)이 몇 초 안에 나오고 큐로 디스패치되는 걸 시연합니다

02

일부러 만든 결측·표기흔들림·미매핑·잡담 입력 30종을 넣어도 출력 형식(JSON Schema)이 한 번도 안 깨지고, 인용 span 이 원문에 실제로 존재하는지 자동 검증을 100% 통과합니다

03

내가 만든 정답 40건 중 액션 타입 분류가 일치한 게 34건(85%), 액션 ID 해소(미매핑→null 포함)가 일치한 게 31건입니다. 단, 정답과 데이터를 모두 작성자가 만든 합성 기준이므로 '성능 검증' 이 아니라 '데모 조건에서의 동작' 임을 명시합니다

04

매 결정과 커넥터 실행 결과가 감사 이벤트로 Kafka 에 남고, 콘솔에서 요청→액션→커넥터 결과가 추적되는 걸 시연합니다

정직 경계선

진짜 성능 검증은 여기서 멈춥니다

위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 분류 정확도·인용 정합은 빌드잇 실제 업무 요청과 실 커넥터 연동이 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다

미리 알아둬야 할 것들

이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 데이터 자가채점이라 '성능 증명' 이 아닙니다. 면접에서 솔직히 '데모 조건 동작 + 실검증 준비 상태' 로 설명합니다

정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 일부러 헷갈리는 결측(금액 미기재)·표기흔들림(법카/월차)·미매핑(레지스트리에 없는 요청)·잡담·맥락부족 케이스를 더미에 섞어, 쉬운 문제만 푸는 게 아님을 보여줘야 합니다

자유 텍스트 의도 분류는 표면 단어에 휘둘리기 쉽고, 한 요청에 조회+결재가 섞인 멀티인텐트를 단정하면 위험합니다. 저확신·불일치 건은 지어내지 않고 사람에게 라우팅하며, 멀티인텐트는 1차 액션만 실행하고 후속을 별도 요청으로 분해합니다

실제 도입 시 달라지는 것을 명시합니다. 여기서는 사내 시스템 커넥터(네이버웍스 결재·ERP·Jira)를 목 API 로 두고 action plan 생성부터 큐 디스패치까지만 만들며, 실 커넥터 연동·실행 권한·감사 연계와 고객사별 업무 규칙의 깊이는 도입 단계 과제로 분리합니다

더미 데이터

① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.

work_requestsTABLE· 14 rows

네이버웍스·슬랙·이메일·폼으로 들어온 사내 업무 요청 원문과, 규칙으로 정해지는 정답 라벨(액션 타입·액션 ID·파라미터·결측). 에이전트가 가장 먼저 받는 입력이자 자가채점의 정답입니다. 운영 현실대로 금액 결측·별칭 표기·미매핑·맥락부족·잡담·멀티인텐트를 일부러 섞어, 쉬운 데이터만 푸는 게 아님을 보여줍니다.

② work_requests.seed.json 다운로드
① 인라인 샘플 (프리뷰 5 / 14)
request_idchannelraw_textsubmitted_bygold_action_typegold_action_idgold_paramsgold_missing_paramsnote
req_001slack다음 주 월화 이틀 연차 상신 부탁드려요인사팀approvalact_leave_approval{"start_date":"다음 주 월","days":"이틀"}[]—
req_002naverworks3월 팀 회식비 법카 결재 좀 올려주세요영업1팀approvalact_expense_approval{"category":"회식비","period":"3월"}["amount"]결측: 금액(amount) 미기재. 지어내지 말고 결측 플래그
req_003slack판교 창고에 프로젝터 재고 몇 대 남았는지 확인해줘총무팀lookupact_inventory_lookup{"item":"프로젝터","location":"판교 창고"}[]—
req_004emailA커머스 계약서 만료일 언제인지 조회 부탁합니다제휴팀lookupact_contract_lookup{"counterparty":"A커머스"}[]—
req_005slack회의실 미팅룸 빔프로젝터 고장났어요. 수리 티켓 등록해주세요개발팀registeract_issue_register{"asset":"빔프로젝터","symptom":"고장","location":"회의실"}[]표기흔들림: '미팅룸'='회의실' 별칭 정규화 필요
③ 증강 프롬프트 (본인 LLM 에 붙여서 N 배 늘리기)
아래 JSON Schema 에 맞는 한국 엔터프라이즈 사내 업무 요청 로그 40건을 JSON 배열로 생성하세요. 채널 4종(slack/naverworks/email/form), 부서 분포 자연스럽게, 액션 타입 approval/lookup/register 를 고루 포함하세요.

{"type":"object","properties":{"request_id":{"type":"string"},"channel":{"enum":["slack","naverworks","email","form"]},"raw_text":{"type":"string"},"submitted_by":{"type":"string"},"gold_action_type":{"enum":["approval","lookup","register","route","reject"]},"gold_action_id":{"type":["string","null"]},"gold_params":{"type":"object"},"gold_missing_params":{"type":"array","items":{"type":"string"}},"note":{"type":["string","null"]}},"required":["request_id","channel","raw_text","gold_action_type","gold_action_id"]}

쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞으세요(쉬운 문제만 푸는 함정 회피): (1) 결측: required_params 중 일부(금액·기간 등)가 원문에 없는 요청 4~6건, gold_missing_params 에 그 필드명 표기, (2) 표기흔들림: 같은 액션의 별칭 원문('법카'/'법인카드', '월차'/'연차', '미팅룸'/'회의실') 3~4건, (3) 미매핑/orphan: action_registry 에 대응 액션이 없는 요청 2~3건(gold_action_id=null, gold_action_type='route'), (4) 잡담·인사 등 업무 요청이 아닌 것 2건(gold_action_type='reject', gold_action_id=null), (5) 경계값: 맥락이 극단적으로 부족한 한두 단어 요청('그거 처리해줘') 1~2건(route), (6) 멀티인텐트: 한 요청에 조회+결재가 섞인 것 2건(1차 액션만 gold, note 에 분해 필요 표기). JSON 배열만 출력하세요.
schema (3 층의 단일 출처)
{"type":"object","properties":{"request_id":{"type":"string"},"channel":{"enum":["slack","naverworks","email","form"]},"raw_text":{"type":"string"},"submitted_by":{"type":"string"},"gold_action_type":{"enum":["approval","lookup","register","route","reject"]},"gold_action_id":{"type":["string","null"]},"gold_params":{"type":"object"},"gold_missing_params":{"type":"array","items":{"type":"string"}},"note":{"type":["string","null"]}},"required":["request_id","channel","raw_text","gold_action_type","gold_action_id"]}
action_registryTABLE· 10 rows

에이전트가 트리거할 수 있는 사내 시스템 액션 카탈로그. 각 액션의 필수 파라미터(required_params)와 대상 시스템, 커넥터, 트리거 별칭을 담습니다. 요청의 gold_action_id 가 이 표에 조인되어야 하며, 조인 실패(orphan)면 지어내지 말고 미매핑으로 알려야 합니다. 결측 판정의 기준(required_params)이자 별칭 정규화의 사전입니다.

② action_registry.seed.json 다운로드
① 인라인 샘플 (프리뷰 5 / 10)
action_idnameaction_typetarget_systemrequired_paramsaliasesconnector
act_leave_approval연차/휴가 결재 상신approval네이버웍스결재["start_date","days"]["연차","휴가","반차","월차"]connector_naverworks
act_expense_approval지출 결재 상신approvalERP["amount","category","period"]["법인카드","법카","지출","경비","회식비"]connector_erp
act_purchase_approval구매 품의approvalERP["item","qty","amount"]["구매","품의","발주"]connector_erp
act_inventory_lookup재고 조회lookupWMS["item","location"]["재고","창고","남은 수량"]connector_wms
act_contract_lookup계약 조회lookup계약관리["counterparty"]["계약","계약서","만료일"]connector_contract
③ 증강 프롬프트 (본인 LLM 에 붙여서 N 배 늘리기)
아래 JSON Schema 에 맞는 한국 엔터프라이즈 사내 시스템 액션 카탈로그 20행을 JSON 배열로 생성하세요. action_type 은 approval/lookup/register 세 종을 고루, 대상 시스템(target_system)은 네이버웍스결재·ERP·WMS·Jira·CRM·계약관리 등으로 자연스럽게 분포시키세요.

{"type":"object","properties":{"action_id":{"type":"string"},"name":{"type":"string"},"action_type":{"enum":["approval","lookup","register"]},"target_system":{"type":"string"},"required_params":{"type":"array","items":{"type":"string"}},"aliases":{"type":"array","items":{"type":"string"}},"connector":{"type":"string"}},"required":["action_id","name","action_type","target_system","required_params"]}

조건: (1) 각 액션의 required_params 는 실제 결재·조회·등록에 필요한 2~3개로 현실적으로, (2) aliases 에 구어체 별칭('법카','월차','미팅룸' 같은)을 1~4개씩 넣어 표기흔들림 정규화가 가능하게, (3) connector 는 target_system 과 정합되는 이름으로. JSON 배열만 출력하세요.
schema (3 층의 단일 출처)
{"type":"object","properties":{"action_id":{"type":"string"},"name":{"type":"string"},"action_type":{"enum":["approval","lookup","register"]},"target_system":{"type":"string"},"required_params":{"type":"array","items":{"type":"string"}},"aliases":{"type":"array","items":{"type":"string"}},"connector":{"type":"string"}},"required":["action_id","name","action_type","target_system","required_params"]}

기술 상세 · 아키텍처·운영·출력 스키마

기술 스택 매핑
KotlinSpring Boot 액션 게이트웨이의 대표 언어. 결정된 action plan 을 사내 시스템 커넥터로 디스패치하는 오케스트레이터입니다
Spring요청 인테이크 API + 액션 게이트웨이 (`POST /requests` 로 요청을 받아 큐 발행, 커넥터 라우팅, 감사 로그 적재)
PythonFastAPI 에이전트의 전처리, 인용 검증(evidence_span 이 원문 substring 인지 결정론 매칭), 분류 정확도 평가 스크립트
FastAPIAI Agent 추론 엔드포인트 (`POST /plan`. 자유 텍스트 요청 → action plan 반환)
PostgreSQLwork_requests·action_registry·audit_log 단일 진실원 + 결정·실행 감사 추적
Redis요청 멱등(idempotency) 키 + 커넥터 호출 rate-limit
RabbitMQ액션 디스패치 작업 큐. 결정과 커넥터 실행을 분리하고 재시도·DLQ 로 실패를 격리합니다
Apache Kafka감사 이벤트 스트림. 모든 결정과 커넥터 실행 결과를 append-only 로 남겨 재생 가능하게 합니다
Docker에이전트·게이트웨이·커넥터를 각각 컨테이너로 분리 패키징
Kubernetes세 서비스를 MSA 배포 단위로 오케스트레이션
AWS(EKS)2주차부터 EKS 로 배포·운영합니다 (JD 명시 인프라)
TypeScript요청→액션 콘솔의 타입 안전 (프론트 전반)
Next.js콘솔 UI (인입 요청 큐·결정된 액션·원문 인용 하이라이트·감사 로그)
Zustand콘솔 요청 큐·필터·선택 상태 관리
Zod프론트 런타임 action plan 스키마 검증
LLM 선택
model
Claude Sonnet (action plan 생성: 의도 분류·파라미터 추출·원문 인용) + Claude Haiku (저비용 2차 분류 judge)
prompt
few-shot 4 예시 + JSON Schema 강제(action plan 형식) + 인용 강제(모든 파라미터에 evidence_span 요구) + '없는 값은 지어내지 말고 결측/미매핑으로 표시' 가드 + 후행 Haiku 2차 분류로 저확신·불일치 건 검출합니다.
rationale
의도 분류·파라미터 추출·근거 인용은 정확도가 중요해 Sonnet, 2차 분류 judge 는 비용·속도 우선 Haiku 를 씁니다. action_registry 가 수십 개 규모라 컨텍스트 윈도우에 다 들어가 RAG 불필요, 수백 개로 늘면 3주차에 후보 검색(retrieval)을 붙입니다.
결정 게이트
1주차 끝자유 텍스트 요청에 대해 action plan 이 생성되고, 인용(evidence_span)이 원문과 일치하며, 목 커넥터로 큐 디스패치되어 콘솔에서 추적됩니까? 여기까지가 그 자체로 제출 가능한 완성품입니다.

✓ pass → 그대로 멈춰도 완결입니다. 더 키우고 싶으면 2주차(분류가 실제로 맞았는지 확인 + 재시도·감사 + EKS 배포)로 진행합니다.

2주차 끝정답셋 대비 액션 타입·액션 ID 분류가 맞는지 확인되고, 재시도·DLQ·감사 스트림과 EKS 배포가 붙습니까?

✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(실 커넥터 어댑터 교체 + 레지스트리 확장 + 멱등·권한 가드)로 진행합니다.

재사용 레이어 · 코어 70% / 도메인 30%
core (재사용 가능)
  • 자유 텍스트 → JSON Schema 강제 action plan 생성 파이프라인 (의도·액션·파라미터)
  • evidence_span 이 원문 substring 인지 결정론 검증 + 결측·미매핑 플래그
  • 액션 게이트웨이(Kotlin/Spring) + 메시지 큐 디스패치(RabbitMQ 재시도·DLQ)
  • 감사 이벤트 스트림(Kafka append-only, 재생 가능)
  • Claude Haiku 저비용 2차 분류 불일치·저확신 → 사람 라우팅 큐
  • 요청→액션 추적 콘솔(Next.js) + 5% 사람 spot check 샘플러
domain (교체 대상)
  • work_requests / action_registry 도메인 액션 카탈로그(승인·조회·등록 액션 세트)
  • action_type·required_params enum 은 도메인별로 갈아끼웁니다(다른 회사는 다른 액션 집합·필수값)
  • 별칭 정규화 사전(법카→법인카드, 미팅룸→회의실 같은 도메인 어휘)
  • 커넥터 어댑터(네이버웍스 결재·ERP·Jira 등 실 시스템 연동부)
출력 스키마 · work_request_action_plan

자유 텍스트 업무 요청 하나에 대해 에이전트가 내놓는 실행 계획입니다. 1) 의도 한 줄, 2) 실행할 액션 타입·액션 ID(미매핑이면 null), 3) 원문에서 인용한 근거(evidence_span)와 함께 뽑은 파라미터, 4) 빠진 필수값(missing_params), 5) 라우팅 대상·확신도·판단 근거. 이 한 스키마가 다음 커넥터 호출의 입력이 됩니다.

schema (JSON Schema)
{"type":"object","properties":{"request_id":{"type":"string"},"intent":{"type":"string"},"action_type":{"enum":["approval","lookup","register","route","reject"]},"action_id":{"type":["string","null"]},"params":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"},"evidence_span":{"type":"string"}},"required":["name","value","evidence_span"]}},"missing_params":{"type":"array","items":{"type":"string"}},"route_to":{"type":["string","null"]},"confidence":{"enum":["low","mid","high"]},"rationale":{"type":"string"}},"required":["request_id","action_type","action_id","params","missing_params","confidence"]}
모범 출력 (4)
[
  {
    "input_ref": "req_002 · naverworks · '3월 팀 회식비 법카 결재 좀 올려주세요'",
    "output": {
      "request_id": "req_002",
      "intent": "3월 팀 회식비를 법인카드 지출로 결재 상신",
      "action_type": "approval",
      "action_id": "act_expense_approval",
      "params": [
        {
          "name": "category",
          "value": "회식비",
          "evidence_span": "회식비"
        },
        {
          "name": "period",
          "value": "3월",
          "evidence_span": "3월"
        }
      ],
      "missing_params": [
        "amount"
      ],
      "route_to": null,
      "confidence": "mid",
      "rationale": "'법카'·'회식비'가 지출 결재(act_expense_approval) 별칭과 일치합니다. required_params 중 amount 가 원문에 없어 지어내지 않고 결측으로 표시했습니다. 금액 확인 후 상신이 필요합니다."
    }
  },
  {
    "input_ref": "req_003 · slack · '판교 창고에 프로젝터 재고 몇 대 남았는지 확인해줘'",
    "output": {
      "request_id": "req_003",
      "intent": "판교 창고 프로젝터 재고 수량 조회",
      "action_type": "lookup",
      "action_id": "act_inventory_lookup",
      "params": [
        {
          "name": "item",
          "value": "프로젝터",
          "evidence_span": "프로젝터"
        },
        {
          "name": "location",
          "value": "판교 창고",
          "evidence_span": "판교 창고"
        }
      ],
      "missing_params": [],
      "route_to": null,
      "confidence": "high",
      "rationale": "'재고'·'창고'가 act_inventory_lookup 별칭과 일치하고 item·location 이 원문에서 모두 추출됩니다. 필수값 결측이 없어 바로 조회 디스패치가 가능합니다."
    }
  },
  {
    "input_ref": "req_008 · slack · '고객사 상주 교통비 정산은 어느 팀에 문의해야 하나요?'",
    "output": {
      "request_id": "req_008",
      "intent": "고객사 상주 교통비 정산 담당 문의",
      "action_type": "route",
      "action_id": null,
      "params": [],
      "missing_params": [],
      "route_to": "총무팀",
      "confidence": "mid",
      "rationale": "교통비 정산에 대응하는 레지스트리 액션이 없어(미매핑) 액션을 지어내지 않고 총무팀으로 라우팅합니다."
    }
  },
  {
    "input_ref": "req_009 · slack · '다들 점심 뭐 드셨어요 ㅎㅎ'",
    "output": {
      "request_id": "req_009",
      "intent": "잡담(업무 요청 아님)",
      "action_type": "reject",
      "action_id": null,
      "params": [],
      "missing_params": [],
      "route_to": null,
      "confidence": "high",
      "rationale": "실행 가능한 업무 요청이 아니어서 어떤 커넥터로도 디스패치하지 않습니다."
    }
  }
]
검증 노트 (자동 vs 수동)

[자동 검증: 규칙·결정론으로 채점] 1. JSON Schema parse: action plan 이 위 스키마 형식을 통과하는지(필드 누락·타입 깨짐 차단). 2. 인용 청크 일치: 각 params[].evidence_span 이 요청 raw_text 의 실제 substring 인지(공백 정규화 후). 지어낸 근거를 차단합니다. 3. 액션 타입 정확도: action_type 이 gold_action_type 과 일치한 비율. 4. 액션 ID 해소 정확도: action_id 가 gold_action_id 와 일치한 비율(미매핑은 null 로 표기 포함). action_registry 조인 실패 시 null 이어야 정답. 5. 결측 재현율/정밀도: missing_params 가 action_registry.required_params 대비 실제 빠진 값과 일치하는지. [수동 검증: 사람 표본] 1. Claude Haiku 저비용 2차 분류와 1차 결정이 불일치한 건, 그리고 confidence=low 건을 사람 큐로 보내 검토합니다. 2. route/reject 경계 케이스(맥락 부족·멀티인텐트)는 전수 대비 5% 를 사람이 spot check 합니다. (κ 등 통계 지표는 이 포트폴리오 범위에서 쓰지 않고, 분류 일치율과 인용 존재율 같은 직접 지표로만 채점합니다.)