팀스파르타
경력근거: 고객 폐쇄망에 Fortress(CLI·AXIQ·Governance Lake)를 직접 배포·운영
팀스파르타 AXOps 는 고객 폐쇄망에 Fortress(CLI·AXIQ·Governance Lake)를 직접 배포·운영하고, 고객 현장에 임베드해 업무 플로우를 파악하며 도메인 문서·코드·실패 로그를 정제해 거버넌스 레이크에 공급합니다. 적재까지가 끝이 아니라 현장 담당자가 그 데이터를 직접 물어볼 수 있어야 고객의 영업이익 개선이라는 성공 지표가 움직입니다. 이 CLI는 정확히 그 지점에 놓입니다. 인터넷도 외부 API 도 없는 자리에서 한국어 질문을 카탈로그 안 필드만 쓰는 질의 계획으로 옮기고, 결과와 근거 행을 터미널에 함께 내며, 표에 없는 걸 물으면 지어내는 대신 되묻습니다. 다만 이 포트폴리오가 보여주는 것은 폐쇄망 오프라인 자연어 질의 변환·실행 게이트·CLI 하네스라는 기술 역량이며, 국방·공공·금융·의료 SI 도메인 전문성은 주장하지 않습니다. 모르는 도메인 깊이는 면접에서 모른다고 말하는 편이 낫습니다.
국방·공공·금융·의료 현장은 보안 규정 때문에 Claude 도 Copilot 도 못 씁니다. 외부 API 한 줄 못 부르는 환경에서 로컬 모델만으로 도는 CLI여야 현장에 들어갑니다.
고객 현장에 임베드해 업무 플로우를 파악하고 도메인 문서·기록을 정제해 거버넌스 레이크에 공급합니다. 적재로 끝나지 않고 현장 담당자가 그 데이터를 직접 물어볼 수 있어야 성공 지표가 움직입니다.
Fortress 는 CLI 와 AXIQ 로 현장에 들어가고, 회수한 패턴은 재사용 가능한 도구와 하네스 표준이 됩니다. MCP 로 도구화하면 같은 질의 계층을 에이전트가 그대로 씁니다.
고객사에서 받은 운영대장 엑셀 추출본을 폐쇄망 안 PostgreSQL 한 표로 적재해 두고, 담당자가 `axq "6월에 반려된 반입 신청 사업장별로 세어줘"` 처럼 한국어로 물으면 로컬 모델이 그 문장을 질의 계획 JSON 으로 바꿉니다. 계획에 쓸 수 있는 필드와 연산은 표에서 자동으로 뽑은 카탈로그에 있는 것만 허용하고, 실행기가 그 계획을 SQL 로 옮겨 돌린 뒤 결과 표와 그 결과를 만든 기록 id 를 터미널에 출력합니다. 카탈로그에 없는 필드를 묻거나 기간이 모호하면 실행하지 않고 무엇이 없어서 못 하는지 되묻습니다. 모델이 만드는 것은 계획뿐이고 답은 실행 결과라, 정답 질의의 결과 행 집합과 비교해 맞았는지 기계로 판정합니다.
계약된 고객사(방산 부품 SI)의 폐쇄망 운영 담당자입니다. SQL 을 못 써서 '지난달 서명 검증에서 막힌 반입 신청이 몇 건이었나' 가 궁금할 때마다 인프라팀에 메신저로 부탁하고 반나절씩 기다립니다. 인터넷이 끊긴 자리라 외부 챗봇에 붙여넣을 수도 없어서, 결국 운영대장 엑셀을 눈으로 세다가 틀립니다. 숫자만 던져주는 도구는 믿지 못하므로 그 숫자가 어느 기록 행에서 나왔는지 같이 보이기를 바라고, 표에 없는 걸 물었을 때 그럴듯하게 지어내느니 못 한다고 말해주기를 바라는 사람입니다.
질의에 쓸 수 있는 필드와 연산을 표에서 뽑은 카탈로그로 좁혀 놓고 모델에게 답이 아니라 질의 계획만 만들게 하면, 외부 API 없이 로컬 모델만으로도 (a) 한국어 질문이 실행 가능한 계획으로 옮겨지고, (b) 카탈로그 밖 필드와 모호한 기간, 기록 수정 요청은 실행 전에 걸러져 되묻기로 돌아가며, (c) 맞았는지는 결과 행 집합을 정답 질의 결과와 대조해 사람 판단 없이 판정됩니다.
docker-compose 로 외부망을 끊은 채 한 번에 뜨고, 한국어 질문을 카탈로그 안 필드·연산만 쓰는 질의 계획으로 바꿔 실행한 뒤 결과 표와 근거 행 id 를 터미널에 출력하는 CLI입니다. 카탈로그에 없는 필드나 모호한 기간은 실행하지 않고 되묻습니다. 운영 기록은 이미 한 표로 적재됐다고 가정하고 HWP·PDF 인제스천은 2주차로 미뤄, 1주차는 질의 계획 변환과 실행·근거 출력 두 축에만 집중합니다.
품목 코드 대소문자·하이픈 차이와 자유 기술된 실패 사유의 표기 흔들림을 정규화 규칙으로 묶어 질의가 놓치지 않게 하고, 고객사 담당 2명이 계획과 되묻기가 타당했는지 라벨링해 일치도를 붙이는 단계입니다.
질의 계층을 MCP 도구로 노출해 에이전트가 여러 질문을 이어 던지게 하고, 온프레미스 GPU 위 vLLM 로 로컬 오픈웨이트 모델을 서빙해 완전 폐쇄망 구성으로 올리는 단계입니다.
함정 포함 질문 12개를 터미널에 넣으면, 실행 가능한 질문은 질의 계획과 결과 표에 근거 행 id 를 붙여 출력하고 카탈로그 밖 질문은 실행 없이 되묻기로 돌아오는 걸 시연합니다
실행된 질의의 결과 행 집합을 정답 질의 결과와 대조해 100% 자동 판정합니다. 일부러 만든 함정 4종(없는 필드·모호한 기간·표 밖 지식·기록 수정 요청)을 넣어도 실행 전 게이트가 한 번도 안 뚫립니다
외부망을 끊은 상태에서 docker-compose 한 번으로 app·PostgreSQL·로컬 모델이 뜨고 외부 API 호출 0으로 12문항이 도는 걸 보여줍니다
내가 만든 정답 12문항 중 결과 행 집합 일치가 10개(83%), 되물어야 할 4문항은 전부 되묻기입니다. 단, 질문과 기록을 모두 내가 만든 합성 기준이라 '성능 검증' 이 아니라 '데모 조건에서의 동작' 임을 밝힙니다
진짜 성능 검증은 여기서 멈춥니다. 위 수치는 전부 작성자가 만든 더미데이터 자가채점이며, 실제 계획 정확도와 되묻기 적중률은 팀스파르타 AXOps 가 들어간 고객사의 실제 운영 기록과 담당자 검수가 있어야 측정 가능합니다. 이 도구는 그 검증을 바로 돌릴 수 있는 상태로 만듭니다
이건 고객·실데이터 없이 혼자 만든 포트폴리오입니다. 모든 수치는 합성 데이터 자가채점이라 '성능 증명' 이 아닙니다. 면접에서 '데모 조건 동작 + 실검증 준비 상태' 로 솔직히 설명합니다
정답을 작성자가 만들었으니 잘 맞는 것이 당연합니다. 없는 필드·모호한 기간·표 밖 지식·기록 수정 요청·품목 코드 표기 흔들림 같은 경계 케이스를 더미에 섞어, 쉬운 문제만 푸는 게 아님을 보여줍니다
질의 계획이 맞아도 적재된 표가 현장을 덜 담고 있으면 답은 반쪽입니다. 카탈로그가 곧 이 도구의 한계선이고, 되묻기가 잦아지면 담당자가 도구를 버리므로 되묻기 비율 자체를 지표로 봐야 합니다
실제 도입 시 달라지는 것을 명시합니다. HWP·PDF 운영 문서 인제스천, 온프레미스 GPU 위 vLLM 서빙, 고객사 담당 검수로 교체해야 하며 이는 도입 단계 과제로 분리합니다
이 포트폴리오가 보여주는 것은 폐쇄망 오프라인 자연어 질의 변환·실행 게이트·CLI 하네스라는 기술 역량이며, 국방·공공·금융·의료 SI 도메인 전문성은 주장하지 않습니다. 모르는 도메인 깊이는 면접에서 모른다고 말하는 편이 낫습니다
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
고객사에서 받은 운영대장 엑셀 추출본을 폐쇄망 안 한 표로 적재한 기록입니다. 질의가 실행되는 대상이자 근거 행의 출처입니다. 발생일 결측, 품목 코드 대소문자 흔들림, 같은 품목의 중복 신청, 자유 기술된 실패 사유, 소요 시간 결측을 일부러 섞어 쉬운 데이터만 푸는 게 아님을 보여줍니다.
| record_id | record_type | site | occurred_on | status | owner_team | item_code | duration_min | fail_reason |
|---|---|---|---|---|---|---|---|---|
| r001 | 반입신청 | A사업장 | 2026-06-03 | 승인 | 인프라팀 | IMG-0421 | 35 | — |
| r002 | 반입신청 | A사업장 | 2026-06-05 | 반려 | 인프라팀 | img-0421 | — | 서명 검증 실패 |
| r003 | 배포 | A사업장 | 2026-06-07 | 완료 | 플랫폼팀 | REL-1130 | 48 | — |
| r004 | 배포 | B사업장 | 2026-06-09 | 실패 | 플랫폼팀 | REL-1131 | 120 | 이미지 서명 불일치 |
| r005 | 장애 | B사업장 | 2026-06-11 | 완료 | 운영팀 | — | 90 | 추론 서버 메모리 초과 |
아래 JSON Schema 에 맞는 폐쇄망 고객사 운영 기록 60건을 JSON 배열로 생성.
{"type":"object","properties":{"record_id":{"type":"string"},"record_type":{"enum":["반입신청","배포","장애","점검"]},"site":{"type":"string"},"occurred_on":{"type":["string","null"]},"status":{"enum":["승인","반려","완료","실패","대기"]},"owner_team":{"type":"string"},"item_code":{"type":["string","null"]},"duration_min":{"type":["number","null"]},"fail_reason":{"type":["string","null"]}},"required":["record_id","record_type","site","status","owner_team"]}
쉬운 데이터만 만들지 말 것. 다음 하드 케이스를 의도적으로 섞어라. (1) occurred_on 이 null 인 발생일 결측 행 4~5건, (2) 같은 품목을 대소문자·하이픈만 다르게 적은 표기 흔들림(IMG-0421 과 img_0421 등) 4~5쌍, (3) 같은 품목·같은 날짜로 두 번 올라온 중복 신청 3건(하나는 반려), (4) 같은 원인을 다른 문장으로 적은 fail_reason(서명 검증 실패 / 이미지 서명 불일치 / 서명키 미등록) 5~6건, (5) duration_min 이 null 인 행 5건. record_id 는 r001 부터 연속으로. JSON 배열만.{"type":"object","properties":{"record_id":{"type":"string"},"record_type":{"enum":["반입신청","배포","장애","점검"]},"site":{"type":"string"},"occurred_on":{"type":["string","null"]},"status":{"enum":["승인","반려","완료","실패","대기"]},"owner_team":{"type":"string"},"item_code":{"type":["string","null"]},"duration_min":{"type":["number","null"]},"fail_reason":{"type":["string","null"]}},"required":["record_id","record_type","site","status","owner_team"]}적재된 표에서 자동으로 뽑은 질의 가능 필드 목록입니다. 새 표준을 제정하는 것이 아니라 이미 있는 컬럼과 타입을 읽어 한국어 별칭과 허용 연산만 붙인 것입니다. 질의 계획은 여기 있는 필드와 연산만 쓸 수 있고, 여기에 없는 걸 물으면 실행 전에 되묻기로 돌아갑니다.
| field | table | type | aliases | allowed_ops | nullable |
|---|---|---|---|---|---|
| record_id | ops_records | string | ["기록번호","건번호"] | ["eq","in","count"] | false |
| record_type | ops_records | enum | ["구분","업무유형","종류"] | ["eq","in","count","group_by"] | false |
| site | ops_records | string | ["사업장","현장"] | ["eq","in","count","group_by"] | false |
| occurred_on | ops_records | date | ["발생일","접수일","날짜"] | ["eq","gte","lte","is_null","group_by"] | true |
| status | ops_records | enum | ["상태","처리결과"] | ["eq","in","count","group_by"] | false |
아래 JSON Schema 에 맞는 폐쇄망 운영 기록 표의 질의 가능 필드 카탈로그 25건을 JSON 배열로 생성. 새 표준을 제정하지 말고 이미 적재된 컬럼을 읽어 별칭과 허용 연산만 붙인다는 전제로.
{"type":"object","properties":{"field":{"type":"string"},"table":{"type":"string"},"type":{"enum":["string","enum","date","number"]},"aliases":{"type":"array","items":{"type":"string"}},"allowed_ops":{"type":"array","items":{"enum":["eq","neq","in","gte","lte","contains","is_null","count","avg","sum","group_by"]}},"nullable":{"type":"boolean"}},"required":["field","table","type","aliases","allowed_ops","nullable"]}
쉬운 데이터만 만들지 말 것. 하드 케이스 의도적 혼입. (1) 한국어 별칭이 서로 겹쳐 어느 필드인지 헷갈리는 쌍(원인 / 사유) 2~3건, (2) 별칭이 하나도 없어 한국어 질문으로는 못 짚는 필드 2건, (3) allowed_ops 가 두 개 이하로 좁아 집계 질문이 거절돼야 하는 필드 3건, (4) nullable=true 라 결측 처리 규칙이 필요한 필드 5건 이상. 담당자 개인 이름·연락처처럼 표에 없는 필드는 절대 만들지 말 것(그건 거절 정답이어야 한다). JSON 배열만.{"type":"object","properties":{"field":{"type":"string"},"table":{"type":"string"},"type":{"enum":["string","enum","date","number"]},"aliases":{"type":"array","items":{"type":"string"}},"allowed_ops":{"type":"array","items":{"enum":["eq","neq","in","gte","lte","contains","is_null","count","avg","sum","group_by"]}},"nullable":{"type":"boolean"}},"required":["field","table","type","aliases","allowed_ops","nullable"]}CLI를 채점할 한국어 질문과 정답 질의 계획·정답 결과 행 셋입니다. gold_decision 이 reject 인 질문은 되묻기가 정답입니다. 없는 필드·모호한 기간·표 밖 지식·기록 수정 요청을 reject_kind 로 표시해, 실행 전 게이트가 함정에도 안 뚫리는지 채점합니다.
| query_id | question | gold_decision | gold_plan | gold_row_ids | reject_kind | difficulty |
|---|---|---|---|---|---|---|
| q1 | 6월에 반려된 반입 신청 건을 사업장별로 세어줘. | run | {"table":"ops_records","filters":[{"field":"record_type","op":"eq","value":"반입신청"},{"field":"status","op":"eq","value":"반려"},{"field":"occurred_on","op":"gte","value":"2026-06-01"},{"field":"occurred_on","op":"lte","value":"2026-06-30"}],"group_by":["site"],"aggregate":{"fn":"count","field":null}} | ["r002","r009"] | none | mid |
| q2 | 서명 때문에 막힌 건 어떤 것들이야? | run | {"table":"ops_records","filters":[{"field":"fail_reason","op":"contains","value":"서명"}],"group_by":[],"aggregate":{"fn":"none","field":null}} | ["r002","r004","r010"] | none | hard |
| q3 | 최근에 장애 많았어? | reject | — | — | 기간모호 | mid |
| q4 | 장애 건 평균 처리 시간 알려줘. | run | {"table":"ops_records","filters":[{"field":"record_type","op":"eq","value":"장애"}],"group_by":[],"aggregate":{"fn":"avg","field":"duration_min"}} | ["r005","r006","r011"] | none | mid |
| q5 | 담당자 이름별로 반입 신청 건수 뽑아줘. | reject | — | — | 없는필드 | mid |
아래 JSON Schema 에 맞는 한국어 운영 질의 평가 질문 40건을 JSON 배열로 생성. gold_row_ids 는 ops_records 의 record_id 와 매칭되도록, gold_plan 의 field 는 field_catalog 에 있는 필드만 쓰도록.
{"type":"object","properties":{"query_id":{"type":"string"},"question":{"type":"string"},"gold_decision":{"enum":["run","reject"]},"gold_plan":{"type":["object","null"]},"gold_row_ids":{"type":["array","null"],"items":{"type":"string"}},"reject_kind":{"enum":["none","없는필드","기간모호","표밖지식","쓰기요청"]},"difficulty":{"enum":["easy","mid","hard"]}},"required":["query_id","question","gold_decision","reject_kind","difficulty"]}
쉬운 데이터만 만들지 말 것. 하드 케이스 의도적 혼입. (1) gold_decision='reject' 인 질문 30% 내외를 네 가지 reject_kind 에 고루 배분, (2) reject_kind='기간모호': '최근'·'요즘'·'지난번' 처럼 구간을 정할 수 없는 질문 3~4건, (3) reject_kind='없는필드': 담당자 개인 이름·연락처·비용처럼 카탈로그에 없는 걸 묻는 질문 3~4건, (4) reject_kind='쓰기요청': 기록을 고치거나 지워달라는 질문 2~3건, (5) 품목 코드 표기가 흔들려 정규화해야만 정답 행이 다 잡히는 질문 3~4건, (6) 결측값이 있어 집계에서 제외해야 정답이 맞는 질문 2~3건. gold_decision='reject' 이면 gold_plan 과 gold_row_ids 는 반드시 null. JSON 배열만.{"type":"object","properties":{"query_id":{"type":"string"},"question":{"type":"string"},"gold_decision":{"enum":["run","reject"]},"gold_plan":{"type":["object","null"]},"gold_row_ids":{"type":["array","null"],"items":{"type":"string"}},"reject_kind":{"enum":["none","없는필드","기간모호","표밖지식","쓰기요청"]},"difficulty":{"enum":["easy","mid","hard"]}},"required":["query_id","question","gold_decision","reject_kind","difficulty"]}| Python | 운영대장 적재·필드 카탈로그 추출·질의 계획 실행기와 결과 집합 대조 채점기를 구현하는 대표 언어 |
| TypeScript | 현장 담당자가 직접 치는 axq CLI 본체와 질의 계획 타입 정의(commander 기반, 화면 없이 터미널 출력만) |
| Docker | app·PostgreSQL·로컬 모델을 docker-compose 로 폐쇄망처럼 한 번에 기동하고 이미지 반입 절차를 재현 |
| Linux | 외부망을 끊은 네트워크 격리 상태를 재현하고 컨테이너 런타임·서비스 기동을 관리 |
| PostgreSQL | 운영 기록 단일 진실원이자 질의 계획이 실행되는 대상, pgvector 로 필드 설명 임베딩 보관 |
| vLLM | 3주차에 온프레미스 GPU 위 로컬 오픈웨이트 모델 서빙(1주차는 Ollama CPU 로 구동 가정) |
| RAG | 필드가 수십 개로 늘면 프롬프트에 다 넣지 못하므로, 질문과 가까운 필드 설명·과거 질의 예시만 검색해 계획 생성 컨텍스트로 붙이는 검색증강 |
| MCP | 3주차에 질의 계층을 도구로 노출해 에이전트가 여러 질문을 이어 던지게 하는 런타임 |
✓ pass → 그대로 멈춰도 완결입니다. 더 키우고 싶으면 2주차(표기 흔들림 정규화·사람 검수)로 진행합니다.
✓ pass → 여기서 멈춰도 완결입니다. 더 키우면 3주차(MCP 도구화·온프레미스 GPU 서빙)로 진행합니다.
질문마다 (1) 실행할지 되물을지 결정, (2) 실행하면 질의 계획과 옮겨진 SQL, 결과 표, 그 결과를 만든 근거 행 id, (3) 되물으면 무엇이 없어서 못 하는지와 확인 질문을 담습니다. 계획이 맞았는지와 되묻기가 맞았는지를 결정론으로 채점할 수 있게 필드를 둡니다.
{"type":"object","properties":{"query_id":{"type":"string"},"decision":{"enum":["run","reject"]},"plan":{"type":["object","null"]},"sql":{"type":["string","null"]},"row_ids":{"type":"array","items":{"type":"string"}},"result_rows":{"type":"array","items":{"type":"object"}},"missing_fields":{"type":"array","items":{"type":"string"}},"reject_reason":{"type":["string","null"]},"followup_question":{"type":["string","null"]}},"required":["query_id","decision","row_ids","result_rows","missing_fields"]}[
{
"input_ref": "q1 · '6월에 반려된 반입 신청 건을 사업장별로 세어줘.' (카탈로그 안 필드로 실행 가능)",
"output": {
"query_id": "q1",
"decision": "run",
"plan": {
"table": "ops_records",
"filters": [
{
"field": "record_type",
"op": "eq",
"value": "반입신청"
},
{
"field": "status",
"op": "eq",
"value": "반려"
},
{
"field": "occurred_on",
"op": "gte",
"value": "2026-06-01"
},
{
"field": "occurred_on",
"op": "lte",
"value": "2026-06-30"
}
],
"group_by": [
"site"
],
"aggregate": {
"fn": "count",
"field": null
}
},
"sql": "SELECT site, count(*) AS cnt FROM ops_records WHERE record_type = '반입신청' AND status = '반려' AND occurred_on BETWEEN '2026-06-01' AND '2026-06-30' GROUP BY site ORDER BY site",
"row_ids": [
"r002",
"r009"
],
"result_rows": [
{
"site": "A사업장",
"cnt": 1
},
{
"site": "C사업장",
"cnt": 1
}
],
"missing_fields": [],
"reject_reason": null,
"followup_question": null
}
},
{
"input_ref": "q3 · '최근에 장애 많았어?' (기간을 정할 수 없어 실행 전 되묻기)",
"output": {
"query_id": "q3",
"decision": "reject",
"plan": null,
"sql": null,
"row_ids": [],
"result_rows": [],
"missing_fields": [],
"reject_reason": "'최근' 이 어느 구간인지 카탈로그의 occurred_on 만으로는 정할 수 없어 실행하지 않았습니다.",
"followup_question": "기간을 지난 7일, 지난 30일, 6월 한 달 중 어느 것으로 잡을까요?"
}
},
{
"input_ref": "q5 · '담당자 이름별로 반입 신청 건수 뽑아줘.' (카탈로그에 담당자 개인 필드가 없음)",
"output": {
"query_id": "q5",
"decision": "reject",
"plan": null,
"sql": null,
"row_ids": [],
"result_rows": [],
"missing_fields": [
"owner_name"
],
"reject_reason": "적재된 표에는 담당팀(owner_team)만 있고 담당자 개인 이름 필드가 없어 실행하지 않았습니다.",
"followup_question": "담당팀 기준으로 세어드릴까요?"
}
},
{
"input_ref": "q10 · 'IMG-0421 관련 기록 전부 찾아줘.' (품목 코드 표기가 흔들려 정규화해야 두 행이 다 잡힘)",
"output": {
"query_id": "q10",
"decision": "run",
"plan": {
"table": "ops_records",
"filters": [
{
"field": "item_code",
"op": "eq",
"value": "IMG-0421",
"normalize": "upper_strip_hyphen"
}
],
"group_by": [],
"aggregate": {
"fn": "none",
"field": null
}
},
"sql": "SELECT * FROM ops_records WHERE replace(upper(item_code), '-', '') = 'IMG0421'",
"row_ids": [
"r001",
"r002"
],
"result_rows": [
{
"record_id": "r001",
"record_type": "반입신청",
"status": "승인",
"occurred_on": "2026-06-03"
},
{
"record_id": "r002",
"record_type": "반입신청",
"status": "반려",
"occurred_on": "2026-06-05"
}
],
"missing_fields": [],
"reject_reason": null,
"followup_question": null
}
}
][자동 검증: 결정론, 사람 없이 통과] 1. JSON Schema 통과: decision·row_ids·result_rows·missing_fields 필드 형식과 필수값을 기계로 검사해 형식이 깨진 출력을 거릅니다. 2. 계획 어휘 검사: plan 의 모든 field 가 field_catalog 에 있고 op 가 그 필드의 allowed_ops 안에 드는지 집합으로 대조합니다. 하나라도 벗어나면 실행 자체가 막힙니다. 3. 결과 집합 대조: 실행한 질의의 row_ids 를 nl_queries.gold_row_ids 와 집합 비교합니다. 순서와 중복은 무시하고 원소만 봅니다. 4. 되묻기 정답: gold_decision=reject 인 질문에 decision=reject 가 나왔는지, reject_kind 별로 missing_fields 나 followup_question 중 맞는 쪽이 채워졌는지 확인합니다. 5. 근거 행 재실행: 출력된 sql 을 다시 돌려 row_ids 가 그대로 재현되는지 대조해, 결과와 근거가 어긋난 출력을 거릅니다. [수동 검증: 사람 필요] 1. 고객사 담당 2명이 계획이 질문의 의도를 맞게 옮겼는지(필드는 맞아도 조건이 어긋나지 않았는지) 비동기로 라벨링하고, 두 사람 판정 일치도를 Cohen's κ(두 사람 답이 우연 이상으로 일치하는 정도, 0~1)로 봅니다. 2. 실행할 수 있는데 되물은 과잉 되묻기가 없는지 5% 표본을 사람이 점검합니다.