diff --git a/experiments/consultation-agent/README.md b/experiments/consultation-agent/README.md index 038d936..a694704 100644 --- a/experiments/consultation-agent/README.md +++ b/experiments/consultation-agent/README.md @@ -50,3 +50,8 @@ python3 -m venv .venv 준비 단계는 API를 호출하지 않는다. 실행은 최대 50건으로 제한한다. `run_human50.py`는 별도로 동결한 `runs/human_keywords_v1`이 있어야 하며, 원자료 없이 실행할 수 없다. `replay_catalog.py`는 완료된 `runs/development_v2`의 최초 응답 50개를 재사용한다. 원본 실험의 코드 스냅샷 해시는 원본에 대한 기록이다. 경로 정리 후 코드는 해시가 달라지므로 과거 실행의 protocol을 그대로 재사용하지 않는다. 새 실행을 준비하면 현재 소스와 입력으로 새 해시가 만들어진다. + +## 결과와 검증 기록 + +- [비교 결과와 한계](docs/RESULTS.md) +- [테스트 성공·실패 기록](docs/TESTS.md) diff --git a/experiments/consultation-agent/docs/RESULTS.md b/experiments/consultation-agent/docs/RESULTS.md new file mode 100644 index 0000000..9bb82c3 --- /dev/null +++ b/experiments/consultation-agent/docs/RESULTS.md @@ -0,0 +1,47 @@ +# 상담 분석 비교 결과와 한계 + +사람 정답 50건에서는 네 방식의 원시 키워드 집합이 사례별로 모두 같았다. 에이전트 비용은 단일 호출보다 33.96% 높았다. 현재 키워드 과제에서 에이전트의 추가 이점을 확인하지 못했다. + +| 방식 | 원시 Micro-F1 | 전달 Micro-F1 | 보류 | 모델 호출 | 추정 비용 USD | +|---|---:|---:|---:|---:|---:| +| 단일 호출 | 76.23% | 76.34% | 1 | 50 | 0.0174957 | +| 조건부 재검토 | 76.23% | 74.51% | 3 | 62 | 0.0216285 | +| 반복 재검토 | 76.23% | 74.51% | 3 | 86 | 0.02996505 | +| MCP 에이전트 | 76.23% | 74.51% | 3 | 64 | 0.0234369 | + +```mermaid +xychart-beta + title "사람 정답 50건: 방식별 논리적 추정 비용" + x-axis ["단일", "조건부", "반복", "MCP"] + y-axis "USD" 0 --> 0.032 + bar [0.0174957, 0.0216285, 0.02996505, 0.0234369] +``` + +공유한 최초 응답 비용이 방식별 비용에 각각 포함된다. 성공한 신규 API 응답 112회의 추정 비용 합계는 $0.04003905다. 청구서의 확정 금액은 아니다. 첫 샌드박스 실행의 DNS 실패는 성공 응답 비용과 구분했다. + +### 개발 자료에서 확인한 변화 + +| 비교 | 변경 전 | 변경 후 | 해석 | +|---|---:|---:|---| +| 중복 구조 검사 제거: 모델 호출 | 82 | 67 | 개발 50건의 에이전트 내부 비교 | +| 같은 비교: 도구 호출 | 16 | 1 | 호스트가 이미 하던 검사를 모델 도구 목록에서 제거 | +| 같은 비교: 추정 비용 | $0.0571458 | $0.0482058 | 15.64% 감소, 단일 호출 대비 절감이 아님 | +| 같은 비교: 검토 보류 | 0 | 2 | 의미 품질 유지까지 확인한 결과가 아님 | +| 장애 주입: 모의 모델 호출 | 18 | 12 | 알려진 응답 기록이 있는 네 조건 | +| 장애 주입: 모의 도구 호출 | 6 | 4 | 운영 장애율·실제 비용 측정이 아님 | + +초기 단일 2,000건 실행과 에이전트 부분 실행 기록은 존재한다. 이후 MCP 개선판의 새 2,000건 확대 검증은 하지 않았다. 서로 다른 자료·비교 조건을 하나의 성과로 합산하지 않는다. + +## 근거 파일 + +- [사람 정답 키워드 채점](evidence/human-keyword-scores.json) +- [호출·비용 집계](evidence/human-run-summary.json) +- [도구 목록 변경 전후](evidence/catalog-replay-summary.json) +- [장애 주입 집계](evidence/operational-before-after.json) +- [원본 경로와 SHA-256](evidence/provenance.json) + +집계 파일의 수치는 기존 실험에서 가져왔다. 이번 PR 준비 중 유료 모델 비교를 다시 실행하지 않았다. 사례별 상세와 상담 원문은 배포하지 않으므로 공개 파일만으로 원래 50건을 다시 채점할 수는 없다. + +`human-run-summary.json`의 `human_gold_verified: 0`은 공통 실행기가 사람 정답을 읽지 않는다는 기록이다. 사람 제공 키워드 정답은 실행 후 별도 `human-keyword-scores.json`에서 채점했다. 감정 정확도나 근거 문장의 의미 적절성은 검증하지 않았다. + +개발 자료 50건의 중복 검사 제거에서는 결과 두 건이 바뀌고 보류가 늘었다. 비용 감소만으로 의미 품질이 유지됐다고 판단하지 않았다. 장애 주입에서는 응답 기록 전 중단 시 `STOPPED_UNCERTAIN`으로 멈췄다. 무조건적인 완료나 외부 API의 정확히 한 번 실행을 보장하지 않는다. diff --git a/experiments/consultation-agent/docs/TESTS.md b/experiments/consultation-agent/docs/TESTS.md new file mode 100644 index 0000000..9c56826 --- /dev/null +++ b/experiments/consultation-agent/docs/TESTS.md @@ -0,0 +1,45 @@ +# 게시 전 테스트 기록 + +검증 날짜: 2026-09-15. Python 3.12, mcp 2.2.0의 기존 실험용 가상환경을 사용했다. 테스트는 실제 과금 모델을 호출하지 않는다. + +| 대상 | 명령 | 결과 | +|---|---|---| +| 원본, 기본 Python | `python3 -m unittest discover -s tests -v` | 48개 탐색, 오류 9개. mcp 미설치로 일부 테스트 수집 실패 | +| 원본, MCP 가상환경 | `python -m unittest discover -s tests -v` | 50개 통과, 1.682초 | +| 실행 엔진만 분리 | 같은 명령 | 39개 통과, 1.487초 | +| 환경변수 연결 구현 전 | `python -m unittest discover -s tests -p test_portable_client.py` | 2개 오류. client_from_env 미구현 확인 | +| 비교 실행기와 환경변수 연결 포함 | `python -m unittest discover -s tests -v` | 52개 통과, 1.194초 | + +명령은 `experiments/consultation-agent`에서 실행한다. 준비 중 저장소 루트에서 테스트 경로만 지정한 한 번의 실행은 `experiment` 모듈을 찾지 못해 수집에 실패했다. 문서의 실행 위치로 이동해 다시 실행했다. + +## 확인한 동작 + +| 영역 | 확인 내용 | +|---|---| +| 입력·출력 | 공통 최초 응답, 같은 출력 계약, 상담원 발화를 고객 근거로 사용하지 않음 | +| MCP | 실제 stdio 연결, 도구 목록과 호출 결과, 입력 해시 불일치 거부 | +| 실행 | 호출·비용 상한, 반복 도구 호출 중단, 도구 오류와 잘못된 응답 기록 | +| 복구 | 완료 결과 재사용, 단계 응답 재사용, 불확실한 외부 호출 자동 반복 금지 | +| 채점 | 누락 짝 거부, 과검출·누락 계산, 보류를 완전 일치로 세지 않음 | +| 인증 | OPENAI_API_KEY 전달, 키가 없으면 HTTP 클라이언트 생성 전 중단 | + +```mermaid +sequenceDiagram + participant T as 테스트 + participant R as 실행기 + participant J as SQLite 기록 + participant M as 모의 모델 + T->>R: 상담 분석 실행 + R->>J: 호출 시작 기록 + R->>M: 요청 + M-->>R: 응답 + R->>J: 응답 저장 + T->>R: 중단 후 재개 + R->>J: 저장된 응답 조회 + J-->>R: 같은 응답 재사용 + R-->>T: 결과 확인 +``` + +## 남은 범위 + +FastAPI 서버 전체 테스트와 운영 배포 검증은 실행하지 않았다. 코드가 추가된 독립 실험 디렉터리의 동작을 확인했다. 감정 정확도, 실제 고객 조치와 이탈률 변화는 이 테스트로 증명하지 않는다. diff --git a/experiments/consultation-agent/docs/evidence/catalog-replay-summary.json b/experiments/consultation-agent/docs/evidence/catalog-replay-summary.json new file mode 100644 index 0000000..a208c5a --- /dev/null +++ b/experiments/consultation-agent/docs/evidence/catalog-replay-summary.json @@ -0,0 +1,31 @@ +{ + "cases": 50, + "before": { + "structural_valid": 50, + "review_required": 0, + "model_calls": 82, + "tool_calls": 16, + "estimated_usd": 0.0571458, + "uncached_usd": 0.0703266 + }, + "after": { + "structural_valid": 50, + "review_required": 2, + "model_calls": 67, + "tool_calls": 1, + "estimated_usd": 0.0482058, + "uncached_usd": 0.0559338 + }, + "labels_changed": 2, + "human_gold": 0, + "incremental_new_success_calls": 17, + "incremental_new_estimated_usd": 0.013293749999999998, + "imported_base_calls": 50, + "same_initial_responses": true, + "semantic_quality_change": "unknown", + "limitations": [ + "Previously used development data", + "One replay per intervention; review-generation stochasticity not separated", + "No semantic accuracy or 2000-case gate pass" + ] +} diff --git a/experiments/consultation-agent/docs/evidence/human-keyword-scores.json b/experiments/consultation-agent/docs/evidence/human-keyword-scores.json new file mode 100644 index 0000000..1a25127 --- /dev/null +++ b/experiments/consultation-agent/docs/evidence/human-keyword-scores.json @@ -0,0 +1,106 @@ +{ + "label_source": "user-attested human labels, original unmodified", + "cases": 50, + "delivered_keywords_primary": { + "single": { + "cases": 50, + "tp": 100, + "fp": 35, + "fn": 27, + "micro_precision": 0.7407407407407407, + "micro_recall": 0.7874015748031497, + "micro_f1": 0.7633587786259542, + "exact_matches": 12, + "review_required": 1 + }, + "fixed": { + "cases": 50, + "tp": 95, + "fp": 33, + "fn": 32, + "micro_precision": 0.7421875, + "micro_recall": 0.7480314960629921, + "micro_f1": 0.7450980392156863, + "exact_matches": 12, + "review_required": 3 + }, + "budget_review": { + "cases": 50, + "tp": 95, + "fp": 33, + "fn": 32, + "micro_precision": 0.7421875, + "micro_recall": 0.7480314960629921, + "micro_f1": 0.7450980392156863, + "exact_matches": 12, + "review_required": 3 + }, + "adaptive": { + "cases": 50, + "tp": 95, + "fp": 33, + "fn": 32, + "micro_precision": 0.7421875, + "micro_recall": 0.7480314960629921, + "micro_f1": 0.7450980392156863, + "exact_matches": 12, + "review_required": 3 + } + }, + "raw_keywords_secondary": { + "single": { + "cases": 50, + "tp": 101, + "fp": 37, + "fn": 26, + "micro_precision": 0.7318840579710145, + "micro_recall": 0.7952755905511811, + "micro_f1": 0.7622641509433963, + "exact_matches": 12, + "review_required": 0 + }, + "fixed": { + "cases": 50, + "tp": 101, + "fp": 37, + "fn": 26, + "micro_precision": 0.7318840579710145, + "micro_recall": 0.7952755905511811, + "micro_f1": 0.7622641509433963, + "exact_matches": 12, + "review_required": 0 + }, + "budget_review": { + "cases": 50, + "tp": 101, + "fp": 37, + "fn": 26, + "micro_precision": 0.7318840579710145, + "micro_recall": 0.7952755905511811, + "micro_f1": 0.7622641509433963, + "exact_matches": 12, + "review_required": 0 + }, + "adaptive": { + "cases": 50, + "tp": 101, + "fp": 37, + "fn": 26, + "micro_precision": 0.7318840579710145, + "micro_recall": 0.7952755905511811, + "micro_f1": 0.7622641509433963, + "exact_matches": 12, + "review_required": 0 + } + }, + "sentiment_accuracy": null, + "sentiment_reason": "original labels conflict with user-confirmed definition; AI proposals not approved gold", + "usefulness_gate": false, + "limitations": [ + "50 designed cases, no proven population representativeness", + "prior tuning exposure and complete keyword annotation rubric unverified", + "shared initial completion; one stochastic follow-up run", + "held cases deliver no labels in primary metric; raw keyword scores also shown", + "known-success API cost excludes uncertain archived sandbox attempt" + ] +} diff --git a/experiments/consultation-agent/docs/evidence/human-run-summary.json b/experiments/consultation-agent/docs/evidence/human-run-summary.json new file mode 100644 index 0000000..b9affaf --- /dev/null +++ b/experiments/consultation-agent/docs/evidence/human-run-summary.json @@ -0,0 +1,95 @@ +{ + "cases": 50, + "arms": { + "single": { + "structural_valid": 50, + "validated_status": 49, + "review_required": 1, + "estimated_usd": 0.0174957, + "uncached_usd": 0.0285837, + "logical_model_calls": 50, + "tool_calls": 0, + "tool_errors": 0, + "mean_logical_request_seconds": 0.9998336233012378, + "mean_current_elapsed_seconds": 0.27881030757998815, + "tool_seconds": 0, + "stop_reasons": { + "single_completed": 50 + } + }, + "fixed": { + "structural_valid": 50, + "validated_status": 47, + "review_required": 3, + "estimated_usd": 0.0216285, + "uncached_usd": 0.0357501, + "logical_model_calls": 62, + "tool_calls": 0, + "tool_errors": 0, + "mean_logical_request_seconds": 1.2173710166415548, + "mean_current_elapsed_seconds": 0.48350175003812185, + "tool_seconds": 0, + "stop_reasons": { + "no_review_signal": 38, + "fixed_completed": 12 + } + }, + "budget_review": { + "structural_valid": 50, + "validated_status": 47, + "review_required": 3, + "estimated_usd": 0.029965049999999997, + "uncached_usd": 0.05073945, + "logical_model_calls": 86, + "tool_calls": 0, + "tool_errors": 0, + "mean_logical_request_seconds": 1.6767308058022172, + "mean_current_elapsed_seconds": 0.9213408549604355, + "tool_seconds": 0, + "stop_reasons": { + "no_review_signal": 38, + "budget_review_completed": 12 + } + }, + "adaptive": { + "structural_valid": 50, + "validated_status": 47, + "review_required": 3, + "estimated_usd": 0.023436899999999997, + "uncached_usd": 0.0374337, + "logical_model_calls": 64, + "tool_calls": 3, + "tool_errors": 0, + "mean_logical_request_seconds": 1.2846497933019418, + "mean_current_elapsed_seconds": 0.5065559900004882, + "tool_seconds": 0.017832458994234912, + "stop_reasons": { + "no_review_signal": 38, + "adaptive_completed": 11, + "model_call_limit": 1 + } + } + }, + "human_gold_verified": 0, + "actual_success_receipt_cost_usd": 0.04003905, + "actual_success_calls": 112, + "uncertain_calls": 0, + "fairness": { + "same_initial_payload_and_response": true, + "shared_final_contract": true, + "complete_input_and_dictionary_all_arms": true, + "equal_max_model_calls_for_budget_review_and_adaptive": true, + "same_actual_token_budget": false + }, + "limitations": [ + "Shared initial completion design; logical arm costs include the reused base receipt", + "Request seconds include historical shared receipt latency; current elapsed excludes MCP session startup", + "No human semantic gold; authored regression checks are development-only", + "Equal call ceilings are not equal realized token spend" + ], + "usefulness_gate": { + "passed": false, + "status": "pending_external_evidence_review", + "reason": "Structural metrics and authored development constraints alone do not establish useful adaptive-agent superiority or semantic accuracy." + } +} diff --git a/experiments/consultation-agent/docs/evidence/operational-before-after.json b/experiments/consultation-agent/docs/evidence/operational-before-after.json new file mode 100644 index 0000000..fece8d6 --- /dev/null +++ b/experiments/consultation-agent/docs/evidence/operational-before-after.json @@ -0,0 +1,26 @@ +{ + "kind": "deterministic_injected_fault_test", + "real_llm_calls": 0, + "consultation_fixture_count": 1, + "conditions": 5, + "known_receipt_summary": { + "final_only": { + "cases": 4, + "completed": 4, + "model_calls": 18, + "tool_calls": 6 + }, + "step_receipts": { + "cases": 4, + "completed": 4, + "model_calls": 12, + "tool_calls": 4 + } + }, + "limitations": [ + "Scripted identical model and tools; not semantic accuracy", + "Final-only baseline models checkpoint placement, not a full historical release benchmark", + "Unknown in-flight response stops rather than claiming exactly-once API execution", + "Not a 2000-case run or production outage measurement" + ] +} diff --git a/experiments/consultation-agent/docs/evidence/provenance.json b/experiments/consultation-agent/docs/evidence/provenance.json new file mode 100644 index 0000000..239d25c --- /dev/null +++ b/experiments/consultation-agent/docs/evidence/provenance.json @@ -0,0 +1,26 @@ +[ + { + "published_file": "human-keyword-scores.json", + "original_relative_path": "runs/human_keywords_v1/keyword_scores.json", + "original_sha256": "aed6a0d618c0871a7da7cab7ad3a68005151b1d0ef755fc7235c8a55abe4e7a6", + "transformation": "Remove per-case details; operational report retains only aggregate fields. No metric recalculation." + }, + { + "published_file": "human-run-summary.json", + "original_relative_path": "runs/human_keywords_v1/summary.json", + "original_sha256": "b59b543510094b244c2912773d2906ea8dcdbb5e6c5a846a52daa33d4aea67a1", + "transformation": "Remove per-case details; operational report retains only aggregate fields. No metric recalculation." + }, + { + "published_file": "catalog-replay-summary.json", + "original_relative_path": "runs/catalog_v3/summary.json", + "original_sha256": "893cd295a08e13019e07fe3a002081e5f0594b9c62419823e82efbd88ceceb81", + "transformation": "Remove per-case details; operational report retains only aggregate fields. No metric recalculation." + }, + { + "published_file": "operational-before-after.json", + "original_relative_path": "evidence/operational_before_after.json", + "original_sha256": "a1c56b907ce671e76779fa98734ceb37b7e16f99bb9103fa52062bb916895ea5", + "transformation": "Remove per-case details; operational report retains only aggregate fields. No metric recalculation." + } +]