Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions experiments/consultation-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
47 changes: 47 additions & 0 deletions experiments/consultation-agent/docs/RESULTS.md
Original file line number Diff line number Diff line change
@@ -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의 정확히 한 번 실행을 보장하지 않는다.
45 changes: 45 additions & 0 deletions experiments/consultation-agent/docs/TESTS.md
Original file line number Diff line number Diff line change
@@ -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 서버 전체 테스트와 운영 배포 검증은 실행하지 않았다. 코드가 추가된 독립 실험 디렉터리의 동작을 확인했다. 감정 정확도, 실제 고객 조치와 이탈률 변화는 이 테스트로 증명하지 않는다.
Original file line number Diff line number Diff line change
@@ -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"
]
}
106 changes: 106 additions & 0 deletions experiments/consultation-agent/docs/evidence/human-keyword-scores.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
Original file line number Diff line number Diff line change
@@ -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."
}
}
Original file line number Diff line number Diff line change
@@ -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"
]
}
26 changes: 26 additions & 0 deletions experiments/consultation-agent/docs/evidence/provenance.json
Original file line number Diff line number Diff line change
@@ -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."
}
]
Loading