72일차 정답 추가 (실습 정답본 · answers · VIBE-A · tbench)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
baf72561f7
commit
d88c5122d5
105 files changed
+24165
No files matched your search
@@ -0,0 +1,117 @@
|
|||||||
|
# Lab 01 · 재고, 단계별 시간을 남기고, 병목 찾기
|
||||||
|
|
||||||
|
> **강사용 정답본** · 학생 저장소에 올리지 않습니다. 학생용 문제는 [LAB-01-Q-MEASURE-BOTTLENECK.md](LAB-01-Q-MEASURE-BOTTLENECK.md).
|
||||||
|
> 요청문은 예시이며 CLI 에이전트로 보내 재지는 않았습니다. 결과 숫자는 정답 워크플로우를 2026-10-18에 1회씩 실행한 값입니다 (강사 Mac, gpt-4.1-mini).
|
||||||
|
> 10/24에 실습을 5개로 늘리며 정답을 학생 순서대로 다시 돌렸습니다 (45단계). 명령 · `-v` 출력 전부와 codex 프롬프트: [answers/LAB-TEST-RESULT.md](answers/LAB-TEST-RESULT.md).
|
||||||
|
|
||||||
|
## 예제 1 · 기준
|
||||||
|
|
||||||
|
| 요청 | 결과 |
|
||||||
|
| --- | --- |
|
||||||
|
| `base-helpdesk` push 직후 첫 호출 (`faq-vpn`) | 6.5초 |
|
||||||
|
| `bench base-helpdesk requests/faq-vpn.json -n 5` | p50 2.61초 · p95 2.83초 · 실패 0 |
|
||||||
|
| `call base-helpdesk` `ticket-own` · `off-topic` | 3.3초 · 3.1초 |
|
||||||
|
|
||||||
|
- `off-topic`(점심 메뉴)도 LLM을 세 번 부르고 `route: 자동 답변`으로 "찾지 못했습니다"를 답합니다. 3단계 대체 경로와 2단계 규칙에서 다시 이야기합니다.
|
||||||
|
|
||||||
|
## 예제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 은 reference/baseline-helpdesk.json 을 복사한 것이야. 웹훅 경로를 my-helpdesk, 이름을 "72 IT helpdesk (my)"로 바꾸고,
|
||||||
|
답은 그대로 두고 결과에 다음을 더해 줘 (AGENTS.md 3절, 5절의 시각 남기기).
|
||||||
|
- Has question? 다음에 Start clock, Parse category 뒤에 After classify, Get ticket 뒤에 After data, Answer 뒤에 After answer, Review 뒤에 After review (각각 Date.now()).
|
||||||
|
- Build result 에서 timings {classify_ms, data_ms, answer_ms, review_ms, total_ms}, steps(지나간 단계), llm_calls(지나간 Agent 수), cached:false.
|
||||||
|
지나갔는지는 $('노드').isExecuted 로.
|
||||||
|
기대 결과 cases/lab01.json.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `After data` | 다른 시각 노드처럼 "After + 끝난 단계". 데이터(FAQ · 접수 조회)가 끝난 뒤 |
|
||||||
|
| ② | `Date.now()` | 지금 시각을 ms 숫자로 (AGENTS.md 5절 `Start clock` 예) |
|
||||||
|
| ③ | `data_ms` | `timings` 칸 이름 규칙 `<단계>_ms` (AGENTS.md 3절) |
|
||||||
|
| ④ | `$('노드').isExecuted` | 노드가 실행됐으면 true (AGENTS.md 5절) |
|
||||||
|
|
||||||
|
정답: [reference/lab01-example2-timings.json](reference/lab01-example2-timings.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab01.json` | 4/4 (기준 `--webhook base-helpdesk`는 1/4) |
|
||||||
|
| `faq-vpn`의 `timings` | classify 524 · data 21 · answer 1017 · review 919 · total 2500 ms (다른 실행: 565 · 23 · 1063 · 879 · 2546) |
|
||||||
|
|
||||||
|
- LLM 세 번이 전체의 약 99%. 데이터 API 두 번은 20ms대.
|
||||||
|
- 10/24 다시 돌린 결과: `lab01` **3/4** — 접수 케이스에서 검토 LLM이 "처리 중"을 "승인 대기 중"으로 바꿔 써 실패 (정답의 잘못이 아님). VPN `timings` 616 · 18 · 1124 · 920 · 2698ms.
|
||||||
|
|
||||||
|
## 예제 3 · 결과 (에이전트 없이, 10/24)
|
||||||
|
|
||||||
|
예제 2 정답(`reference/lab01-example2-timings.json`) 그대로, 캐시 없음.
|
||||||
|
|
||||||
|
| 질문 종류 | p50 | p95 | classify | data | answer | review | total | LLM 몫 |
|
||||||
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
| FAQ (VPN) | 2.91초 | 3.08초 | 616 | 18 | 1124 | 920 | 2698 | 98.6% |
|
||||||
|
| 접수 번호 (T-1002) | 2.99초 | 3.12초 | 818 | 43 | 877 | 895 | 2647 | 97.8% |
|
||||||
|
| IT 밖 (점심 메뉴) | 2.48초 | 2.84초 | 732 | 19 | 740 | 978 | 2482 | 98.7% |
|
||||||
|
|
||||||
|
(p50 · p95는 `bench -n 5`, 단계 칸은 `call -v` 한 번의 ms. FAQ 줄의 단계 칸은 예제 2 `call` 값)
|
||||||
|
|
||||||
|
- 학생이 쓸 한 줄 예: "세 종류 모두 LLM이 98%. 점심 메뉴는 데이터가 없는데도 LLM 세 번 → 규칙으로 걸러 낼 후보."
|
||||||
|
- 같은 실행에서 `lab01` 접수 케이스가 한 번 실패했습니다 (검토 LLM이 "처리 중"을 "승인 대기 중"으로 바꿈). Lab 02 과제 1의 이야기 거리.
|
||||||
|
|
||||||
|
## 과제 1 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 Search FAQ 와 Get ticket 사이에 After FAQ(Date.now()) 를 더해, timings 의 data_ms 를 faq_ms 와 ticket_ms 로 나눠 줘.
|
||||||
|
total_ms 를 뺀 timings 중 가장 큰 단계 이름(_ms 뺀 것)을 slowest_step 으로. cases/lab01-challenge.json, cases/lab01.json 둘 다 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `After FAQ` | FAQ 검색이 끝난 뒤의 시각 노드 |
|
||||||
|
| ② | `faq_ms` | `cases/lab01-challenge.json`의 `expect` 칸 이름 |
|
||||||
|
| ③ | `ticket_ms` | ②와 짝. `data_ms`를 FAQ와 접수 조회로 나눔 |
|
||||||
|
| ④ | 가장 큰 | "가장 느린" = `_ms` 값이 가장 큰 칸 |
|
||||||
|
|
||||||
|
정답: [answers/72-lab01-challenge-timings.json](answers/72-lab01-challenge-timings.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab01-challenge.json` | 4/4 (예제 2 상태 0/4) |
|
||||||
|
| `cases/lab01.json` | 4/4 |
|
||||||
|
| `faq-vpn` | classify 565 · faq 14 · ticket 9 · answer 1063 · review 879 · total 2546 ms, `slowest_step: answer` |
|
||||||
|
|
||||||
|
10/24 다시 돌린 결과: `lab01-challenge` 4/4, `slowest_step`은 네 번 중 answer 2 · review 1 · classify 1 (push 직후 첫 호출에서 classify 1365ms).
|
||||||
|
|
||||||
|
## 과제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 Build result 에서, 본문 budget_ms (없으면 3000, 숫자로)를 읽어
|
||||||
|
결과에 llm_ms(classify · answer · review 의 _ms 합, timings 밖에 둔다), budget_ms, over_budget(timings.total_ms > budget_ms)을 더해 줘.
|
||||||
|
timings, slowest_step 은 그대로. cases/lab01-challenge2.json, cases/lab01-challenge.json, cases/lab01.json 모두 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `budget_ms` | 본문 입력 칸 = 결과 칸 이름 (AGENTS.md 3절 "입력") |
|
||||||
|
| ② | `3000` | 기본 예산 3초 (조건 표 · AGENTS.md 3절) |
|
||||||
|
| ③ | 밖 | `timings` 안에 두면 `slowest_step`이 `llm` 합계를 고르게 됨 |
|
||||||
|
| ④ | `timings.total_ms` | 요청 하나 전체 시간 |
|
||||||
|
|
||||||
|
정답: [answers/72-lab01-challenge2-budget.json](answers/72-lab01-challenge2-budget.json) (과제 1 정답의 `Build result`에 세 줄)
|
||||||
|
|
||||||
|
| 10/24 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| 고치기 전 (과제 1 정답) | `lab01-challenge2` 0/4 (칸이 없음) |
|
||||||
|
| 고친 뒤 | `lab01-challenge2` 4/4 · `lab01-challenge` 4/4 · `lab01` 4/4 |
|
||||||
|
| 예산 60000 | `total_ms` 3357 (push 직후) · `llm_ms` 3295 · `over_budget: false` |
|
||||||
|
| 예산 500 | 2609 · 2563 · `true` |
|
||||||
|
| 기본 3000 (VPN) | 3023 · 2973 · **`true`** (23ms 넘음) |
|
||||||
|
| 기본 3000 (접수) | 2904 · 2858 · `false` |
|
||||||
|
|
||||||
|
- 기본 예산에서 `over_budget`이 실행마다 바뀌는 것이 정상입니다 (VPN이 3초 안팎). 풀이 때 "그래서 2단계에서 줄인다"로 잇습니다.
|
||||||
|
- 처음 만든 케이스 파일은 접수 케이스에 답 "처리 중"을 기대했는데, 검토 LLM이 문장을 바꿔 실패했습니다 (10/24). 모양을 보는 테스트이므로 `category: ticket`으로 바꿨습니다.
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# Lab 02 · 규칙 먼저, 캐시, LLM 한 번
|
||||||
|
|
||||||
|
> **강사용 정답본** · 학생 저장소에 올리지 않습니다. 학생용 문제는 [LAB-02-Q-OPTIMIZE-FLOW.md](LAB-02-Q-OPTIMIZE-FLOW.md).
|
||||||
|
> 요청문은 예시이며 CLI 에이전트로 보내 재지는 않았습니다. 결과 숫자는 정답 워크플로우를 2026-10-18에 1회씩 실행한 값입니다 (강사 Mac, gpt-4.1-mini, bench는 n=5).
|
||||||
|
> 10/24에 실습을 5개로 늘리며 정답을 학생 순서대로 다시 돌렸습니다 (45단계). 명령 · `-v` 출력 전부와 codex 프롬프트: [answers/LAB-TEST-RESULT.md](answers/LAB-TEST-RESULT.md). 10/24 점수: 예제 1 `lab02` 7/9 (캐시 케이스 · 접수 답에서 검토 LLM이 "처리 중"을 바꿈), 나머지는 모두 기대대로.
|
||||||
|
|
||||||
|
## 예제 1 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 순서를 바꿔 줘: Has question? → Start clock → Search FAQ → Get ticket → After data → Rule classify(Code) → Known?(If).
|
||||||
|
Rule classify: 질문에 T-1234 같은 번호(/T-?\d{4}/i)가 있으면 ticket, FAQ 결과가 1개 이상이면 faq, 아니면 unknown. {category, by:'rule'}.
|
||||||
|
Known? true → Answer, false → Classify → Parse category → After classify → Answer. Answer 는 $json.category 로 분류를 읽는다.
|
||||||
|
Build result: 분류는 Parse category 가 실행됐으면 그것, 아니면 Rule classify. steps 에 rule 또는 classify.
|
||||||
|
나머지(답, 검토, timings)는 그대로. 기대 결과 cases/lab02.json 앞 네 케이스.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `/T-?\d{4}/i` | 하이픈 있어도 없어도, 대소문자 무시, 숫자 네 자리 (`T-1002` · `t1002`) |
|
||||||
|
| ② | 1개 이상 | `/faq`의 `results`가 비어 있지 않으면 FAQ로 답할 수 있음 |
|
||||||
|
| ③ | `Parse category` | LLM 분류 길에서 분류 결과를 만드는 마지막 노드 |
|
||||||
|
| ④ | `rule` | 규칙 길을 지났다는 `steps` 이름 (AGENTS.md 3절) |
|
||||||
|
|
||||||
|
정답: [reference/lab02-example1-rules.json](reference/lab02-example1-rules.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab02.json` | 7/9 (예제 2의 "같은 질문" 캐시 케이스와, 답 문장이 기대 표현과 달랐던 "없는 접수 번호" 실패. 10/23~24에는 "없는 접수 번호"가 통과해 8/9) |
|
||||||
|
| `cases/lab01.json` | 4/4 |
|
||||||
|
| 규칙 질문 시간 | 접수 1.9~2.3초 (예제 2 timings 상태에서 2.4~2.8초) |
|
||||||
|
|
||||||
|
- 처음 쓴 "없는 접수 번호" 기대(`찾지 못`)가 틀렸습니다: 검토 LLM이 "확인되지 않습니다"로 바꿔 말함. 기대를 "없다는 여러 표현 + 상태를 지어내지 않음(`not_contains` 처리 중 등)"으로 고쳤습니다 (71일차 "모른다의 여러 모양"과 같음).
|
||||||
|
|
||||||
|
## 예제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 에 캐시를 더해 줘 (AGENTS.md 2절 데이터 API, 3절 캐시 규칙).
|
||||||
|
Start clock 다음: Cache key(Code: question 을 trim·소문자·연속 공백 하나, use_cache = body.no_cache !== true)
|
||||||
|
→ Cache lookup(GET http://data-api:8800/cache/<encodeURIComponent(key)>, neverError) → Cached?(value 있고 use_cache)
|
||||||
|
true → Cached result(route·category·answer·sources + llm_calls 0, cached true, steps ['cache'], timings.total_ms) → Send cached
|
||||||
|
false → 지금 흐름(Search FAQ ...).
|
||||||
|
Build result 뒤: Cache it?(category faq 이고 use_cache) true → Store in cache(POST /cache/<key> {value:{route,category,answer,sources}, ttl 600}) → Send result.
|
||||||
|
접수(ticket)는 저장하지 않는다. 기대 결과 cases/lab02.json 전체, lab01 도 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | trim · 소문자 · 연속 공백 하나 | 띄어쓰기 · 대소문자만 다른 질문을 같은 키로 (AGENTS.md 3절) |
|
||||||
|
| ② | `body.no_cache !== true` | 본문 `no_cache`가 true가 아닐 때만 캐시 사용 |
|
||||||
|
| ③ | `neverError` | 캐시에 없을 때의 404에서 멈추지 않게 (AGENTS.md 5절) |
|
||||||
|
| ④ | 접수(ticket) | 상태가 바뀌는 답은 캐시하지 않음 (AGENTS.md 3절 굵은 줄) |
|
||||||
|
|
||||||
|
정답: [reference/lab02-example2-cache.json](reference/lab02-example2-cache.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab02.json` | 9/9 · 같은 질문(띄어쓰기·대소문자 다름) 0.1초, `llm_calls: 0` |
|
||||||
|
| `cases/lab01.json` · `lab03.json` | 4/4 · 2/4 (3단계 전) |
|
||||||
|
| 캐시 없이 (`faq-vpn-nocache`) bench | p50 2.01초 · p95 2.23초 |
|
||||||
|
|
||||||
|
- 처음에는 "규칙으로 모르는 질문"(회의실 화면 공유)이 LLM 분류로 `faq`가 되어 **캐시에 들어갔고**, 다음 테스트에서 0.1초로 나와 `llm_calls` 기대가 깨졌습니다. 그 케이스에 `no_cache: true`를 넣었습니다 → 수업 이야기 거리: "캐시는 테스트도 바꾼다".
|
||||||
|
|
||||||
|
## 예제 3 · 결과 (에이전트 없이, 10/24)
|
||||||
|
|
||||||
|
예제 2 정답(`reference/lab02-example2-cache.json`), 캐시를 비운 뒤.
|
||||||
|
|
||||||
|
| 재기 | 첫 호출 | p50 | max |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| 캐시 씀 (`faq-vpn` × 5) | 2.07초 (LLM 2번) | **0.05초** | 2.07초 |
|
||||||
|
| 캐시 안 씀 (`faq-vpn-nocache` × 5) | 1.99초 | 1.99초 | 2.24초 |
|
||||||
|
|
||||||
|
- `/cache-stats` 뒤: `entries 1 · hits 9 · misses 1`. `no_cache` 요청도 캐시를 **조회는** 하므로 hits로 셉니다 (쓰지는 않음). 학생이 "적중 9번?"이라고 하면 이 이야기를 합니다.
|
||||||
|
- 적중률 어림 (0.05초 · 2.0초): 10% → 1.81초, 30% → 1.42초, 60% → 0.83초.
|
||||||
|
|
||||||
|
## 과제 1 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 에서 Review(검토 LLM)와 Review model 을 지우고, 그 자리에 Review check(Code)를 둬:
|
||||||
|
Answer 의 output 을 trim, 비었으면 empty, 한글이 없으면 not_korean, 400자 넘으면 자르고 too_long 을 review_problems 에.
|
||||||
|
Answer 지시문에 "합니다체, 3문장 이내"가 이미 있는지 확인. steps 에는 review 대신 check, llm_calls 는 검토를 세지 않는다.
|
||||||
|
나머지는 그대로. cases/lab02-challenge.json, lab02.json, lab01.json 모두 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `400` | 답 길이 상한 (교안 2단계 6절 검사 표) |
|
||||||
|
| ② | 합니다체, 3문장 이내 | 검토 LLM이 하던 말투 · 길이 다듬기를 지시문으로 |
|
||||||
|
| ③ | `check` | 검토 대신 코드 검사를 지났다는 단계 이름 (다른 짧은 이름도 됨) |
|
||||||
|
| ④ | 검토를 세지 않는다 | 지운 Review는 빼고 셈: 규칙 길 1, LLM 분류 길 2 |
|
||||||
|
|
||||||
|
정답: [answers/72-lab02-challenge-fast.json](answers/72-lab02-challenge-fast.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab02-challenge.json` | 5/5 (VPN 1.2초 · 비밀번호 1.1초 · 접수 1.3초) · 캐시 단계 상태 1/5 |
|
||||||
|
| `cases/lab02.json` · `lab01.json` | 9/9 · 4/4 |
|
||||||
|
| 캐시 없이 bench | p50 2.01 → **1.15초** · p95 2.23 → **1.16초** |
|
||||||
|
|
||||||
|
- 처음 쓴 `lab01` · `lab02` 기대에 `llm_calls: 3`, `steps`에 `review` 같은 **구조**가 들어 있어, 과제로 정당하게 빨라진 워크플로우가 실패했습니다. 품질 테스트는 **답의 내용**만 보게 고쳤습니다 (구조 기대는 그 단계의 테스트에만) → 이야기 거리: "최적화하면 깨지는 테스트는 무엇을 재고 있었나".
|
||||||
|
|
||||||
|
10/24 다시 돌린 결과: 고치기 전 `lab02-challenge` 1/5, bench p50 2.22 · p95 2.45초 → 고친 뒤 15/15 (`--repeat 3`, 0.9~1.6초), `lab02` 9/9, `lab01` 4/4, bench p50 **1.12** · p95 1.32초.
|
||||||
|
|
||||||
|
## 과제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 에 FAQ 번호 캐시를 더해 줘 (AGENTS.md 3절 캐시 키 표).
|
||||||
|
Rule classify 다음: FAQ cache key(Code: category 가 faq 이고 use_cache 이고 FAQ 결과가 있으면 faq_key = 'faq-' + 첫 FAQ id, 아니면 'none')
|
||||||
|
→ FAQ cache lookup(GET /cache/<faq_key>, neverError) → FAQ cached?(value 있고 faq_key 가 'none' 아님)
|
||||||
|
true → FAQ cached result(llm_calls 0, cached true, cache_by 'faq', steps ['faq', 'rule', 'cache']) → Send
|
||||||
|
false → Back to rule(Code: Rule classify 의 결과를 그대로) → Known?
|
||||||
|
Store in cache 뒤에 Store by FAQ(POST /cache/<faq_key>, 같은 value, ttl 600) → Result again.
|
||||||
|
질문 캐시의 Cached result 에는 cache_by 'question', Build result 에는 cache_by null.
|
||||||
|
기대 결과 cases/lab02-challenge2.json, lab02-challenge.json, lab02.json 도 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `faq-` | 캐시 키 표 둘째 줄: `faq-<FAQ id>` |
|
||||||
|
| ② | `faq` | FAQ 번호 키로 찾았을 때 (AGENTS.md 3절 Lab 02 과제 2) |
|
||||||
|
| ③ | `600` | 질문 캐시와 같은 ttl (초) |
|
||||||
|
| ④ | `question` | ②와 짝. 질문 키로 찾았을 때 |
|
||||||
|
|
||||||
|
정답: [answers/72-lab02-challenge2-faqcache.json](answers/72-lab02-challenge2-faqcache.json) (과제 1 정답에 노드 7개: FAQ cache key · FAQ cache lookup · FAQ cached? · FAQ cached result · Send FAQ cached · Back to rule · Store by FAQ)
|
||||||
|
|
||||||
|
| 10/24 결과 (`cases/lab02-challenge2.json`) | 고치기 전 (과제 1 정답) | 고친 뒤 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 처음 묻는 VPN | ✔ 1.1초 | ✔ 2.5초 (push 직후) |
|
||||||
|
| 다른 말 VPN | ✘ `cached: false`, LLM 1번, 1.2초 | ✔ `cache_by: faq`, LLM 0번, **0.1초** |
|
||||||
|
| 같은 말 | ✘ `cache_by` 칸 없음 (캐시는 맞음, 0.1초) | ✔ `cache_by: question`, 0.0초 |
|
||||||
|
| `no_cache` | ✔ | ✔ 1.2초 |
|
||||||
|
| 프린터 처음 · 접수 현황 | ✔ | ✔ |
|
||||||
|
| 합 | 4/6 | 6/6, `lab02-challenge` 5/5, `lab02` 9/9 |
|
||||||
|
|
||||||
|
- `Back to rule`이 없으면 `Known?`이 캐시 응답을 읽어 분류가 사라집니다. 학생이 가장 많이 막힐 곳으로 예상합니다 (수업 준비 때 예상).
|
||||||
|
- 풀이 때 "잃는 것": FAQ-003에 묶인 질문은 모두 처음 질문에 맞춘 답을 받습니다.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
# Lab 03 · 멈춰도 답하게
|
||||||
|
|
||||||
|
> **강사용 정답본** · 학생 저장소에 올리지 않습니다. 학생용 문제는 [LAB-03-Q-STABILITY.md](LAB-03-Q-STABILITY.md).
|
||||||
|
> 요청문은 예시이며 CLI 에이전트로 보내 재지는 않았습니다. 결과 숫자는 정답 워크플로우를 2026-10-18에 1회씩 실행한 값입니다 (강사 Mac, gpt-4.1-mini).
|
||||||
|
> 10/24에 실습을 5개로 늘리며 정답을 학생 순서대로 다시 돌렸습니다 (45단계). 명령 · `-v` 출력 전부와 codex 프롬프트: [answers/LAB-TEST-RESULT.md](answers/LAB-TEST-RESULT.md).
|
||||||
|
|
||||||
|
## 예제 1 · 고치기 전 (캐시 단계 워크플로우)
|
||||||
|
|
||||||
|
| 상황 | 결과 |
|
||||||
|
| --- | --- |
|
||||||
|
| FAQ 느림 (8초) | **9.0초** 기다린 뒤 `자동 답변` (사용자는 9초를 기다림) |
|
||||||
|
| FAQ 오류 (500) | 0.1초 만에 **HTTP 500** `Error in workflow` |
|
||||||
|
| 캐시에 있는 질문 + FAQ 오류 | 0.1초, `cached: true` (캐시 확인이 FAQ 앞에 있어서 이미 됨) |
|
||||||
|
| `cases/lab03.json` | 2/4 |
|
||||||
|
|
||||||
|
## 예제 1 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 Search FAQ 와 Get ticket 에 options.timeout 2000, 노드 설정 retryOnFail true, maxTries 2, waitBetweenTries 500,
|
||||||
|
onError continueErrorOutput 을 넣어 줘. 두 노드의 오류 출력(main 1번)은 Data down(Set: route 담당자 연결, answer "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.", reason data_api_down)
|
||||||
|
→ Send data down(Respond). Get ticket 은 404 를 받아야 하니 neverError 는 그대로 둔다. 기대 결과 cases/lab03.json, lab02 도 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `2000` | 2초를 ms로 |
|
||||||
|
| ② | `2` | 처음 + 다시 한 번. 최악 2 × 2000 + 500 = 4.5초로 느림 케이스 안 |
|
||||||
|
| ③ | `continueErrorOutput` | 실패하면 두 번째(오류) 출력으로 (AGENTS.md 5절) |
|
||||||
|
| ④ | `neverError` | 404(없는 번호)를 보통 결과로 받기 |
|
||||||
|
|
||||||
|
정답: [reference/lab03-example1-resilient.json](reference/lab03-example1-resilient.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| `cases/lab03.json` | 4/4 · FAQ 느림 **4.6초** `담당자 연결` · FAQ 오류 **0.6초** `담당자 연결` · 캐시 0.1초 |
|
||||||
|
|
||||||
|
- **바로잡음 (10/24):** 수업 준비 때 여기에 "`neverError` 때문에 접수 API 500이어도 실패로 보지 않는다"고 적었는데, 시험 워크플로우로 재 보니 이 n8n(2.42.6)에서 `neverError`는 **4xx만** 보통 결과로 받고 500은 재시도 뒤 오류 출력으로 갑니다 ([AGENTS.md](AGENTS.md) 5절 표). 대신 404도 재시도를 해서, 이 정답에서 VPN 질문의 `data_ms`가 605ms입니다 (예제 2 단계 32~76ms). 둘 다 예제 3에서 다룹니다.
|
||||||
|
|
||||||
|
## 예제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 Classify 와 Answer Agent 에 onError continueErrorOutput 을 넣고, 두 노드의 오류 출력을 Simple answer(Code) → Send simple answer 로 이어 줘.
|
||||||
|
Simple answer: 질문에 접수 번호가 있고 Get ticket 결과에 status 가 있으면 route 간단 답변, "<번호> 접수 건은 현재 '<status>' 상태입니다. <note>".
|
||||||
|
FAQ 결과가 있으면 route 간단 답변, 첫 FAQ answer + " (자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다.)", sources [그 id].
|
||||||
|
아니면 route 담당자 연결. 모두 llm_calls 0, reason llm_down. 기대 결과 cases/lab03-llm-down.json.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `간단 답변` | LLM 없이 데이터 그대로 답하는 `route` (두 곳 같음) |
|
||||||
|
| ② | 자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다. | 평소 답이 아니라는 안내 (문장은 학생이 정해도 됨) |
|
||||||
|
| ③ | `담당자 연결` | 데이터로도 답할 수 없을 때 사람에게 |
|
||||||
|
| ④ | `llm_down` | AGENTS.md 3절 Lab 03 `reason` 중 LLM이 멈춘 경우 |
|
||||||
|
|
||||||
|
정답: [reference/lab03-example2-llm-fallback.json](reference/lab03-example2-llm-fallback.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 | |
|
||||||
|
| --- | --- |
|
||||||
|
| 고치기 전 (`lab03-llm-down`) | 1/4 · 세 질문 모두 **HTTP 500** (빈 질문 400만 통과) |
|
||||||
|
| 고친 뒤 | 4/4 · VPN 2.4초 `간단 답변` · 접수 0.3초 `간단 답변` · 점심 1.1초 `담당자 연결` |
|
||||||
|
| `llm up` 뒤 `lab03` | 4/4 |
|
||||||
|
|
||||||
|
- `flow.py llm down`은 `Last3Mon/65/n8n/register-credentials.sh`를 틀린 키로 실행합니다. n8n 재시작 없이 바로 적용되고, `llm up`도 바로 돌아왔습니다.
|
||||||
|
|
||||||
|
## 예제 3 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 의 Get ticket 응답 설정에 fullResponse 와 neverError 를 켜 줘 (결과가 {statusCode, body}).
|
||||||
|
Get ticket 보통 출력 다음에 Ticket check(Code): ticket = statusCode 500 이상이면 {} 아니면 body, ticket_status = statusCode,
|
||||||
|
tickets_down = statusCode 500 이상이고 질문에 접수 번호(/T-?\d{4}/i)가 있을 때만 true.
|
||||||
|
→ Tickets down?(If) true → Tickets down(Set: route 담당자 연결, category ticket, answer "접수 현황 시스템이 응답하지 않아 담당자에게 연결합니다. 잠시 뒤 다시 물어봐 주세요.", reason tickets_down, llm_calls 0) → Send tickets down
|
||||||
|
false → 지금 흐름(After data …).
|
||||||
|
$('Get ticket').first().json 을 읽던 곳은 모두 $('Ticket check').first().json.ticket 으로 바꾼다. 기대 결과 cases/lab03-ticket.json, lab03.json, lab03-llm-down.json 도 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `fullResponse` | `{statusCode, body}`를 통째로 받기 (AGENTS.md 5절 표 셋째 줄) |
|
||||||
|
| ② | `500 이상` | 서버 고장 범위. 404는 고장이 아님 (두 곳 같음) |
|
||||||
|
| ③ | `tickets_down` | AGENTS.md 3절 `reason` 중 접수 API만 멈춘 경우 |
|
||||||
|
| ④ | `$('Ticket check').first().json.ticket` | 이제 접수 건은 `Ticket check`의 `ticket` 칸에 |
|
||||||
|
|
||||||
|
정답: [reference/lab03-example3-ticket-down.json](reference/lab03-example3-ticket-down.json) (예제 2 정답에 `fullResponse` + 노드 4개: Ticket check · Tickets down? · Tickets down · Send tickets down)
|
||||||
|
|
||||||
|
| 10/24 결과 (`cases/lab03-ticket.json`) | 고치기 전 (예제 2 정답) | 고친 뒤 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 평소 접수 현황 | ✔ | ✔ 2.4초 |
|
||||||
|
| 접수 API 500 · 접수 질문 | ✘ `reason: data_api_down` (0.6초) | ✔ `tickets_down`, 0.1초 |
|
||||||
|
| 접수 API 500 · VPN 질문 | ✘ **`담당자 연결`** (0.6초) | ✔ `자동 답변`, 1.1초, "인증서" |
|
||||||
|
| 없는 번호 (404) | ✔ | ✔ 1.3초 |
|
||||||
|
| 합 | 2/4 | 4/4, `lab03` 4/4, `lab03-llm-down` 4/4 |
|
||||||
|
|
||||||
|
- 고친 뒤 VPN 질문의 `data_ms` 38ms (예제 1 정답 605ms). 404 재시도가 사라졌기 때문입니다.
|
||||||
|
- 풀이 때 짚을 것: "오류 출력은 어느 질문인지 모른다" → 실패를 보통 결과로 받아 워크플로우가 판단.
|
||||||
|
|
||||||
|
## 과제 1 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/health.json 을 새로 만들어 줘 (웹훅 my-health, AGENTS.md 3절).
|
||||||
|
Start(Code t0) → Check data API(GET http://data-api:8800/health, timeout 2000, onError continueRegularOutput, alwaysOutputData)
|
||||||
|
→ Check FAQ(GET /faq?q=vpn, 같은 설정) → Check LLM(Agent "Reply with the single word OK.", onError continueRegularOutput, alwaysOutputData;
|
||||||
|
모델 options timeout 8000, maxRetries 0) → Summarize(Code): data_api = status ok, faq = results 가 배열, llm = output 에 ok.
|
||||||
|
failed = 거짓인 것 이름. status: 모두 참 ok, data_api 나 llm 중 하나라도 참이면 degraded, 아니면 down. seconds.
|
||||||
|
기대 결과 cases/lab03-challenge.json.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `continueRegularOutput` | 실패해도 **보통** 출력으로 다음 점검에 (두 곳 같음) |
|
||||||
|
| ② | `status` 가 `ok` | `/health`가 평소 돌려주는 `{"status":"ok"}` |
|
||||||
|
| ③ | `output` 에 `ok` | "OK."처럼 대문자 · 마침표가 붙어도 참이 되게 소문자로 비교 |
|
||||||
|
| ④ | `data_api` 나 `llm` 중 하나라도 참이면 `degraded` | 일부는 된다 = 둘 중 하나는 살아 있음 (학생 기준을 적어도 됨) |
|
||||||
|
|
||||||
|
정답: [answers/72-lab03-challenge-health.json](answers/72-lab03-challenge-health.json)
|
||||||
|
|
||||||
|
| 수업 준비 때 결과 (`cases/lab03-challenge.json` 4/4) | |
|
||||||
|
| --- | --- |
|
||||||
|
| 평소 | `ok`, 2.2초 |
|
||||||
|
| FAQ 오류 | `degraded`, `checks.faq: false`, 0.8초 |
|
||||||
|
| FAQ 느림 (8초) | `degraded`, 2.8초 (timeout 2초에서 끊고 계속) |
|
||||||
|
| LLM 멈춤 | `degraded`, `checks.llm: false`, 0.6초 |
|
||||||
|
|
||||||
|
10/24 다시 돌린 결과: `lab03-challenge` 4/4 · 평소 `ok` 2.2초 · FAQ 오류 `degraded` 1.0초 · FAQ 느림 3.0초 · LLM 멈춤 0.4초.
|
||||||
|
|
||||||
|
## 과제 2 · 요청문 예
|
||||||
|
|
||||||
|
```text
|
||||||
|
my-work/helpdesk.json 에 FAQ 차단기를 더해 줘 (AGENTS.md 3절 캐시 키 표).
|
||||||
|
Cached? false → Breaker check(GET http://data-api:8800/cache/breaker-faq, neverError) → Breaker open?(value 있음)
|
||||||
|
true → Breaker open(Set: route 담당자 연결, answer "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.", reason breaker_open, llm_calls 0) → Send breaker open
|
||||||
|
false → Search FAQ.
|
||||||
|
Search FAQ 의 오류 출력 → Open breaker(POST /cache/breaker-faq {value:{open:true, since: Date.now()}, ttl 30}, neverError) → Data down.
|
||||||
|
닫는 노드는 만들지 않는다 (ttl 30초가 지나면 데이터 API가 지움). 기대 결과 cases/lab03-challenge2.json, lab03.json 도 통과.
|
||||||
|
```
|
||||||
|
|
||||||
|
**빈칸 답** (Q 문서 ▶ 빈칸 채우기 `____①`~`____④`)
|
||||||
|
|
||||||
|
| 빈칸 | 답 | 왜 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| ① | `breaker-faq` | 캐시 키 표 마지막 줄 (두 곳 같음) |
|
||||||
|
| ② | `breaker_open` | AGENTS.md 3절 `reason` 중 차단기가 열린 경우 |
|
||||||
|
| ③ | `30` | 초. 조건 표 · 캐시 키 표의 ttl |
|
||||||
|
| ④ | `Data down` | 이번 사용자에게는 예제 1의 정해진 답 |
|
||||||
|
|
||||||
|
정답: [answers/72-lab03-challenge2-breaker.json](answers/72-lab03-challenge2-breaker.json) (예제 3 정답에 노드 5개: Breaker check · Breaker open? · Breaker open · Send breaker open · Open breaker)
|
||||||
|
|
||||||
|
| 10/24 결과 (`cases/lab03-challenge2.json`) | 고치기 전 (예제 3 정답) | 고친 뒤 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 평소 | ✔ 1.3초 | ✔ 2.7초 (push 직후) |
|
||||||
|
| FAQ 느림 · 처음 | ✔ 4.6초 `data_api_down` | ✔ 4.8초 `data_api_down` |
|
||||||
|
| FAQ 느림 · 다음 질문 | ✘ **4.6초** `data_api_down` | ✔ **0.1초** `breaker_open` |
|
||||||
|
| 되살아남 (캐시 비움) | ✔ 1.2초 | ✔ 1.1초 |
|
||||||
|
| 합 | 3/4 | 4/4, `lab03` 4/4 |
|
||||||
|
|
||||||
|
- 손으로 30초 닫힘을 보여 줄 때: `fault faq slow` → `call` 둘 → `curl -s localhost:8800/cache/breaker-faq` → 30초 뒤 `call` (다시 4초대, 다시 열림) → `fault all ok`.
|
||||||
|
- 이야기 거리: 차단기 상태를 워크플로우 밖(캐시)에 둔 까닭 — 웹훅 실행은 요청마다 새로 시작.
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
# 7교시 바이브 코딩 · `tbench` 만들기
|
||||||
|
|
||||||
|
> **정답본** · 수업이 끝난 뒤(8교시 뒤) 학생 저장소에 올립니다. 그 전에는 올리지 않습니다. 요청서는 [VIBE-REQ-TBENCH.md](VIBE-REQ-TBENCH.md), 문제는 [VIBE-Q-TBENCH.md](VIBE-Q-TBENCH.md).
|
||||||
|
> 68일차부터 바이브 코딩 프로그램은 **수업 준비 때 미리 만들어 보지 않습니다.** 아래 TODO · SPEC · IMP-PLAN · 요청문과 확인 기준은 예시이고, 걸린 시간은 8교시 강사 바이브 코딩에서 재어 기록합니다.
|
||||||
|
> **10/26 추가:** 강사 요청으로 R1~R14를 모두 넣은 본을 [tbench/](tbench/)에 직접 만들어 봤습니다 (7절). 요청문으로 CLI 에이전트에 시킨 것이 아니라 Claude Code가 SPEC · IMP-PLAN을 보고 쓴 것이라, 8교시 걸린 시간 표와는 따로 봅니다.
|
||||||
|
|
||||||
|
## 1. `Doc/TODO.md` 예
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# tbench TODO
|
||||||
|
|
||||||
|
## 만들고 싶은 것
|
||||||
|
어느 폴더에서나 `tbench`로 웹훅을 여러 번 불러, 보통 · 느린 쪽 시간과 실패를 보여 주고,
|
||||||
|
바꾸기 전 · 후를 같은 조건으로 비교하는 터미널 도구. 나중에는 기준을 주면 통과 · 실패만 알려 줌.
|
||||||
|
|
||||||
|
## 요청의 말 → 할 일
|
||||||
|
| # | 요청의 말 (짧게) | 할 일 (내 말) | 꼭 · 나중 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| ① | 여러 번 · 몇 번 실패 · 보통 · 느린 쪽 | N번 부르고 한 번마다 상태 · 시간, 끝에 p50 · p95 · max · 실패 | 꼭 |
|
||||||
|
| ② | 기다리기 싫음 · 모양 | 오는 대로 표에 한 줄씩, 스파크라인 | 꼭 |
|
||||||
|
| ③ | 숫자로 비교 · 다른 질문 비교 망신 | 결과 저장, 두 결과 비교, 조건이 다르면 경고 | 꼭 |
|
||||||
|
| ④ | 한꺼번에 | 동시에 k개 | 나중 |
|
||||||
|
| ⑤ | 만들어 둔 요청 고르기 | requests 폴더에서 고르기 (turl은 형식 확인 뒤) | 나중 |
|
||||||
|
| ⑥ | 첫 번만 느림 | 첫 호출 표시, 빼고도 p50, 미리 불러 두기 | 꼭 |
|
||||||
|
| ⑦ | 왜 실패했는지 | 연결 · 시간 초과 · 서버 오류를 따로 셈 | 꼭 |
|
||||||
|
| ⑧ | 단계별 시간 평균 | 응답의 timings를 모아 단계 평균 | 나중 |
|
||||||
|
| ⑨ | 화면 없이 통과 · 실패 | 기준을 넘으면 실패, 종료 코드 | 나중 |
|
||||||
|
| ⑩ | 줄줄이 기록 | 한 번마다 한 줄 파일 | 나중 |
|
||||||
|
| ⑪ | 설정 저장 · 이름으로 | 설정 파일, 이름으로 실행 | 나중 |
|
||||||
|
| ⑫ | 잰 것 목록 | 저장한 결과 목록 | 나중 |
|
||||||
|
| 끝 | 키가 찍히면 안 됨 | 저장 · 기록에서 키 가리기 | 꼭 |
|
||||||
|
| 끝 | 진짜 헬프데스크 두드리지 않기 | 시험은 가짜 서버로 | 꼭 |
|
||||||
|
| 첫 문단 | 어느 폴더에서나 | uv tool install | 꼭 |
|
||||||
|
|
||||||
|
## 요청에 없지만 걱정되는 것
|
||||||
|
- 시간을 어디서부터 어디까지 재나, 끝없이 기다리면?
|
||||||
|
- p95를 어떻게 계산하나 (사람마다 다르면 비교가 안 됨)
|
||||||
|
- 저장 폴더 · 설정 파일을 어디에 (어느 폴더에서 실행해도 같은 곳)
|
||||||
|
|
||||||
|
## 오늘 50분 안에 못 할 것 같은 것
|
||||||
|
④ ⑤ ⑧ ⑨ ⑩ ⑪ ⑫ — IMP-PLAN의 다음 단계로
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. `Doc/SPEC.md` 예 (요구 표는 VIBE-Q와 같음, 확인 기준을 구체적으로)
|
||||||
|
|
||||||
|
| 요구 | 확인 기준 |
|
||||||
|
| --- | --- |
|
||||||
|
| R1 | 가짜 서버(지연 0.1초, 3번째마다 500)로 n=6 → 실패 2, 시간 6개. `stats.py` 테스트: 1~10초 → p50 5 · p95 10 (nearest-rank, 자리 = 올림(p × n)) |
|
||||||
|
| R2 | worker로 부름, 결과마다 표 한 줄 · 스파크라인 · 요약 갱신 (가짜 서버 지연 0.5초 × 6에서 3초 동안 한 줄씩 늘어남) |
|
||||||
|
| R3 | 저장 JSON: `{url, method, body_sha256, n, concurrency, warmup, started, seconds:[...], statuses:[...], errors:[...]}`. compare에서 url · body_sha256 · n이 다르면 `WARNING: different ...` 줄 |
|
||||||
|
| R4 | 가짜 서버 지연 0.3초 × 6번: `--concurrency 1` 약 1.8초, `3` 약 0.6초 안팎 |
|
||||||
|
| R5 | `requests/*.json` 목록을 TUI `OptionList`로, 고르면 그 본문으로 실행 |
|
||||||
|
| R6 | 가짜 서버 첫 응답만 2초 → 1번 줄 note `first call`, "p50 excl. first" 따로. `--warmup 1` → 시간 목록 n개(warmup 제외) |
|
||||||
|
| R7 | 없는 포트 → `connect` 1, 지연 3초 + `--timeout 1` → `timeout` 1, 500 → `http_5xx` 1, 404 → `http_4xx` 1 |
|
||||||
|
| R8 | `--header "Authorization: Bearer sk-test"` → 저장 · 기록 파일 `grep -c sk-test` 0, 대신 `"Authorization": "***"` |
|
||||||
|
| R9 | 응답 JSON의 `timings`(숫자 칸만)를 칸마다 평균. 내 헬프데스크 → `classify_ms … total_ms` 평균 한 줄. `timings`가 없으면 표 없음 |
|
||||||
|
| R10 | `--max-p95 2.5 --max-failures 0`: 넘으면 마지막 줄 `FAIL p95 3.1 > 2.5`, 종료 코드 1. 아니면 `PASS`, 0 |
|
||||||
|
| R11 | `--log runs.jsonl`: 줄마다 `{"t", "i", "status", "seconds", "error_kind"}`, 줄 수 = N(+ warmup은 `"warmup": true`) |
|
||||||
|
| R12 | `~/.tbench/profiles.toml`, `tbench profile save/list`, `tbench run <이름>`: 다른 폴더에서도 같은 결과 파일 조건 |
|
||||||
|
| R13 | 기본 저장 폴더 `~/.tbench/results/`, `tbench history` → 최신이 위, 날짜 · URL · N · p50 · p95 · 실패 |
|
||||||
|
| R14 | `uv tool install --editable .` 뒤 `cd /tmp && tbench --help` 성공, `uv tool list`에 `tbench` |
|
||||||
|
|
||||||
|
숨은 요구 · 범위 밖은 VIBE-Q 표와 같습니다.
|
||||||
|
|
||||||
|
## 3. `Doc/IMP-PLAN.md` 예
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# tbench IMP-PLAN
|
||||||
|
|
||||||
|
## 엔진 1단계 · 부르고 재기 (R1 · R6 · R7 · R8) ← 7교시
|
||||||
|
- [ ] src/tbench/runner.py: call_once(url, method, body, headers, timeout) → {i, status, seconds, error_kind}
|
||||||
|
- [ ] src/tbench/stats.py: percentile(nearest-rank), summarize(results) → p50 · p95 · max · failures · by_kind · p50_excl_first
|
||||||
|
- [ ] tests/conftest.py: 가짜 서버 (지연 · N번째마다 500 · 첫 응답만 느림 · 404)
|
||||||
|
- [ ] cli.py: tbench <url> --body-file --n --method --timeout --header --warmup --plain
|
||||||
|
- 확인: uv run pytest -q · uv run tbench <url> -n 5 --plain
|
||||||
|
|
||||||
|
## TUI 1단계 · 오는 대로 보이기 (R2) ← 7교시
|
||||||
|
- [ ] app.py: Sparkline · 요약 Static · DataTable, worker 가 runner 를 돌리며 결과마다 call_from_thread 로 갱신
|
||||||
|
- 확인: uv run tbench <url> -n 20 (가짜 서버 지연 0.5초)
|
||||||
|
|
||||||
|
## 설치 (R14) ← 7교시 끝
|
||||||
|
- [ ] pyproject [project.scripts] tbench = "tbench.cli:main", uv tool install --editable .
|
||||||
|
|
||||||
|
## 엔진 2단계 · 남기고 비교 (R3 · R13 · R11)
|
||||||
|
- [ ] save(result, path=None) 기본 ~/.tbench/results/<시각>.json, compare(a, b), history()
|
||||||
|
- [ ] --log runs.jsonl
|
||||||
|
## 엔진 3단계 · 동시 · 기준 · 응답 분석 · 설정 (R4 · R10 · R9 · R12)
|
||||||
|
## TUI 2단계 · 고르기 · 비교 · 기록 화면 (R5 · R3 · R13)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 단계별 요청문 예
|
||||||
|
|
||||||
|
### 엔진 1단계 · 부르고 재기
|
||||||
|
|
||||||
|
```text
|
||||||
|
Doc/SPEC.md 와 Doc/IMP-PLAN.md 를 읽고 "엔진 1단계"만 만들어 줘. uv 프로젝트(src/tbench), 명령 이름 tbench, 코드와 화면 글자는 영어.
|
||||||
|
tbench <url> [--body-file f.json] [-n 10] [--method POST] [--timeout 30] [--header "K: V" ...] [--warmup 0] --plain
|
||||||
|
- 한 번마다 "#i status seconds note", 끝에 p50 · p95 · max · failures(종류별: connect · timeout · http_5xx · http_4xx), p50 excl. first.
|
||||||
|
시간은 보내기 직전 ~ 본문을 다 받을 때까지. 실패도 한 번으로 셈. 1번 줄 note 는 "first call". warmup 은 재지 않고 버림.
|
||||||
|
- 백분위: 정렬 후 nearest-rank (ceil(p*n) 번째). stats.py 로 분리하고 pytest 로 1..10 → p50 5, p95 10.
|
||||||
|
- 엔진은 결과를 하나씩 돌려주는 제너레이터로 (나중에 TUI가 받음). 화면 코드는 아직 쓰지 않는다.
|
||||||
|
- 테스트는 pytest 안에서 http.server 로 가짜 서버(지연, N번째마다 500, 첫 응답만 느림, 404)를 띄워서. 진짜 n8n 을 부르지 않는다.
|
||||||
|
- Authorization 등 키 머리글은 출력 · 저장에서 "***".
|
||||||
|
끝나면 IMP-PLAN 의 엔진 1단계 체크를 표시하고 확인 명령을 알려 줘.
|
||||||
|
```
|
||||||
|
|
||||||
|
### TUI 1단계 · 오는 대로 보이기
|
||||||
|
|
||||||
|
```text
|
||||||
|
IMP-PLAN 의 "TUI 1단계"만 해 줘. tbench <url> ... (--plain 없이) 는 Textual 앱:
|
||||||
|
위 Sparkline(시간 목록), 요약 줄(완료/전체, 실패, p50, p95, max, p50 excl. first), 아래 DataTable(#, status, seconds, note).
|
||||||
|
엔진 제너레이터를 worker(thread)에서 돌리고 결과마다 call_from_thread 로 표 · 스파크라인 · 요약을 갱신. 엔진 코드는 바꾸지 않는다.
|
||||||
|
키: q 끝. 확인: 가짜 서버 지연 0.5초 × 6 에서 한 줄씩 늘어나는지.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 설치
|
||||||
|
|
||||||
|
```text
|
||||||
|
pyproject.toml 에 [project.scripts] tbench = "tbench.cli:main" 이 있는지 보고, 없으면 넣어 줘.
|
||||||
|
그리고 내가 실행할 명령만 알려 줘: uv tool install --editable . , uv tool list, cd /tmp && tbench --help
|
||||||
|
```
|
||||||
|
|
||||||
|
### 엔진 2단계 · 남기고 비교
|
||||||
|
|
||||||
|
```text
|
||||||
|
IMP-PLAN 의 "엔진 2단계"만: --save [path] (없으면 ~/.tbench/results/<YYYYMMDD-HHMMSS>.json) 에
|
||||||
|
{url, method, body_sha256, n, concurrency, warmup, started, seconds, statuses, errors} 저장 (키 머리글은 "***").
|
||||||
|
tbench compare a.json b.json: p50 · p95 · max · failures 와 변화(%) 표, url · body_sha256 · n 이 다르면 WARNING 줄.
|
||||||
|
tbench history: ~/.tbench/results 의 파일을 최신부터 날짜 · url · n · p50 · p95 · failures 표. --log runs.jsonl: 한 번마다 한 줄.
|
||||||
|
테스트: 다른 본문 두 결과 → WARNING, 저장 파일에 sk- 없음.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 엔진 3단계 · 동시 · 기준 · 응답 분석 · 설정
|
||||||
|
|
||||||
|
```text
|
||||||
|
IMP-PLAN 의 "엔진 3단계"만: --concurrency k (스레드 풀, 결과는 끝난 순서로 내보내되 번호 유지),
|
||||||
|
--max-p95 초 · --max-failures 수 → 마지막 줄 PASS/FAIL, 종료 코드 0/1,
|
||||||
|
응답 JSON 에 timings 가 있으면 숫자 칸마다 평균 (요약 아래 한 줄),
|
||||||
|
~/.tbench/profiles.toml: tbench profile save <name> <url> [옵션], tbench profile list, tbench run <name> [-n ...].
|
||||||
|
가짜 서버 테스트: 지연 0.3초 × 6 에서 k=3 이 k=1 보다 빠름, 기준을 넘으면 종료 코드 1.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 7교시에 볼 것
|
||||||
|
|
||||||
|
| 볼 것 | 막혔을 때 한마디 |
|
||||||
|
| --- | --- |
|
||||||
|
| TODO · SPEC 없이 에이전트부터 | "요청서로 Doc/TODO.md부터. 확인 방법이 없으면 됐는지 모릅니다" |
|
||||||
|
| IMP-PLAN이 한 단계짜리 | "엔진과 화면을 나누고, 엔진도 1 · 2 · 3으로. 단계마다 commit" |
|
||||||
|
| 첫 요청에 14개를 다 넣음 | "IMP-PLAN의 엔진 1단계만" |
|
||||||
|
| 다 끝난 뒤에야 화면이 나옴 | "worker로 부르고 결과마다 표에 더하게" |
|
||||||
|
| p95가 max와 늘 같음 | "n이 작으면 그럴 수 있어요. 계산 방법을 테스트로 고정" |
|
||||||
|
| 테스트가 진짜 n8n을 부름 | "pytest 안에서 가짜 서버" |
|
||||||
|
| 비교가 다른 요청끼리 | "저장할 때 url · 본문 해시를 같이" |
|
||||||
|
| `tbench`를 못 찾음 | `uv tool update-shell` 뒤 새 터미널, `uv tool list` |
|
||||||
|
| 설치한 `tbench`가 예전 코드 | `--editable`로 다시, 또는 `uv tool install --force .` |
|
||||||
|
|
||||||
|
### 자주 빠뜨리는 것 (수업 준비 때 예상)
|
||||||
|
|
||||||
|
| 빠뜨린 것 | 생기는 일 |
|
||||||
|
| --- | --- |
|
||||||
|
| 백분위 계산 방법 | 사람마다 · 실행마다 p95가 달라 비교가 흔들림 |
|
||||||
|
| 실패도 한 번으로 셈 · 실패 종류 | 연결이 안 되면 프로그램이 멈추거나, 실패가 시간 목록에서 빠져 p95가 좋아 보임 |
|
||||||
|
| 저장 파일에 조건(url · 본문 해시 · N) | 다른 질문으로 잰 것끼리 비교 (요청서의 "망신") |
|
||||||
|
| 엔진과 화면 나누기 | `--plain` · `compare` · 종료 코드를 화면 없이 못 씀, 테스트가 어려움 |
|
||||||
|
| 가짜 서버 | 테스트할 때마다 진짜 헬프데스크 · OpenAI 호출 |
|
||||||
|
| `[project.scripts]` | `uv tool install`은 되는데 `tbench` 명령이 없음 |
|
||||||
|
| 저장 · 설정 위치를 집 폴더에 | 다른 폴더에서 실행하면 history · profile이 비어 보임 |
|
||||||
|
|
||||||
|
## 6. 강사 바이브 코딩 (8교시, 15분 · 시간 기록)
|
||||||
|
|
||||||
|
| 할 것 | 걸린 시간 | 메모 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 엔진 1단계 + TUI 1단계 + 설치를 위 요청문으로 | (8교시에 잼) | |
|
||||||
|
| R10 `--max-p95` 종료 코드 (74일차 배포 전 검사의 씨앗) | (8교시에 잼) | |
|
||||||
|
|
||||||
|
## 7. 수업 준비 때 직접 만들어 본 본 (10/26, `tbench/`)
|
||||||
|
|
||||||
|
| 확인 | 결과 |
|
||||||
|
| --- | --- |
|
||||||
|
| `uv run pytest -q` (가짜 서버) | **12 passed** (18.8초): 백분위 · 실패 2/6 · 첫 호출 · warmup · 실패 종류 4가지 · 키 가리기 · compare WARNING · history · log · 동시 · 종료 코드 · timings · profile · TUI 한 줄씩 · `--pick` |
|
||||||
|
| `uv tool install --editable .` → `cd /tmp && tbench --help` | 됨, `uv tool list`에 `tbench v0.1.0` |
|
||||||
|
| 진짜 헬프데스크 `my-helpdesk` + `requests/faq-vpn-nocache.json` `-n 5 --plain --save --max-p95 4` | 3.947 · 1.579 · 1.532 · 1.824 · 1.429초, 1번 줄 `first call`. **p50 1.579 · p95 3.947 · max 3.947 · p50 excl. first 1.532**, `PASS` |
|
||||||
|
| 같은 때 `flow.py bench my-helpdesk requests/faq-vpn-nocache.json -n 5` | p50 1.38 · p95 1.83 · max 1.83초 (바로 앞 tbench가 데워 둠) |
|
||||||
|
|
||||||
|
(강사 Mac의 n8n에 올라가 있던 `my-helpdesk`는 규칙 분류 · 코드 검사가 들어간 판이라 `llm_calls` 1, `timings`에 `classify_ms`가 없음.)
|
||||||
|
|
||||||
|
만들며 알게 된 것 (학생이 막힐 만한 곳):
|
||||||
|
|
||||||
|
| 알게 된 것 | 정한 방법 |
|
||||||
|
| --- | --- |
|
||||||
|
| n이 작으면 p95 = max (위 실측: 5번이면 p95는 5번째 = 첫 호출 3.9초) | nearest-rank 그대로 두고, 첫 호출 제외 p50과 `--warmup`으로 따로 봄 (⑥) |
|
||||||
|
| `profile save`에 키를 넣으면 "남기는 파일에 키가 찍히면 안 돼요"와 부딪힘 | 키 머리글은 `$MY_KEY`처럼 환경 변수로만 저장, 글자 키는 `***`로 바꾸고 안내 (숨은 요구) |
|
||||||
|
| 응답마다 `timings` 칸이 다름 (규칙 길은 `classify_ms` 없음) | 칸마다 그 칸이 있는 응답끼리 평균 |
|
||||||
|
| 동시(`--concurrency`)면 결과가 끝난 순서로 옴 | 번호(`i`)는 보낸 순서로 유지, 저장은 번호순 |
|
||||||
|
| 본문 해시를 JSON을 다시 쓴 글자로 내면 같은 파일도 해시가 달라질 수 있음 | 파일 글자 그대로 보내고 그 글자로 해시 |
|
||||||
|
| TUI 테스트 | Textual `run_test()` + 가짜 서버 지연 0.5초: 1.3초 뒤 1~5줄, 끝나면 6줄 |
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
$ uv run tools/flow.py call base-helpdesk requests/faq-vpn.json -v
|
||||||
|
HTTP 200 · 4.4s
|
||||||
|
→ request POST /webhook/base-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench base-helpdesk requests/faq-vpn.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/base-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.98s · route=자동 답변
|
||||||
|
✔ #2 · HTTP 200 · 2.52s · route=자동 답변
|
||||||
|
✔ #3 · HTTP 200 · 2.69s · route=자동 답변
|
||||||
|
✔ #4 · HTTP 200 · 2.84s · route=자동 답변
|
||||||
|
✔ #5 · HTTP 200 · 2.54s · route=자동 답변
|
||||||
|
p50 2.69s · p95 2.98s · max 2.98s · failures 0/5
|
||||||
|
report: runs/bench-base-helpdesk-20261024-174234.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench base-helpdesk requests/ticket-own.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/base-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 3.04s · route=자동 답변
|
||||||
|
✔ #2 · HTTP 200 · 3.14s · route=자동 답변
|
||||||
|
✔ #3 · HTTP 200 · 2.61s · route=자동 답변
|
||||||
|
✔ #4 · HTTP 200 · 3.36s · route=자동 답변
|
||||||
|
✔ #5 · HTTP 200 · 2.68s · route=자동 답변
|
||||||
|
p50 3.04s · p95 3.36s · max 3.36s · failures 0/5
|
||||||
|
report: runs/bench-base-helpdesk-20261024-174249.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench base-helpdesk requests/off-topic.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/base-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.90s · route=자동 답변
|
||||||
|
✔ #2 · HTTP 200 · 2.55s · route=자동 답변
|
||||||
|
✔ #3 · HTTP 200 · 2.62s · route=자동 답변
|
||||||
|
✔ #4 · HTTP 200 · 2.56s · route=자동 답변
|
||||||
|
✔ #5 · HTTP 200 · 2.44s · route=자동 답변
|
||||||
|
p50 2.56s · p95 2.90s · max 2.90s · failures 0/5
|
||||||
|
report: runs/bench-base-helpdesk-20261024-174302.json
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "base-helpdesk",
|
||||||
|
"passed": 1,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "base-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"timings has none of ['answer_ms']"
|
||||||
|
],
|
||||||
|
"seconds": 3.5,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "base-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"timings has none of ['total_ms']",
|
||||||
|
"steps has none of ['answer']",
|
||||||
|
"llm_calls has none of ['1', '2', '3']"
|
||||||
|
],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "base-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"timings has none of ['answer_ms']"
|
||||||
|
],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "base-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
POST http://localhost:5678/webhook/base-helpdesk · 4 case(s) × 1
|
||||||
|
✘ VPN FAQ · 단계별 시간 · 3.5s · timings has none of ['answer_ms']
|
||||||
|
→ request POST /webhook/base-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
✘ VPN FAQ · 전체 시간 · LLM 호출 수 · 2.9s · timings has none of ['total_ms']; steps has none of ['answer']; llm_calls has none of ['1', '2', '3']
|
||||||
|
→ request POST /webhook/base-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
✘ 접수 현황 · 2.8s · timings has none of ['answer_ms']
|
||||||
|
→ request POST /webhook/base-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": []
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/base-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 1/4
|
||||||
|
report: runs/lab01-20261024-174311.json
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 3,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1210,
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 1119,
|
||||||
|
"review_ms": 1042,
|
||||||
|
"total_ms": 3426
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 753,
|
||||||
|
"data_ms": 19,
|
||||||
|
"answer_ms": 1058,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 2731
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"answer has none of ['처리 중']"
|
||||||
|
],
|
||||||
|
"seconds": 3.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 929,
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1118,
|
||||||
|
"review_ms": 1029,
|
||||||
|
"total_ms": 3108
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN FAQ · 단계별 시간 · 4.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1210,
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 1119,
|
||||||
|
"review_ms": 1042,
|
||||||
|
"total_ms": 3426
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ VPN FAQ · 전체 시간 · LLM 호출 수 · 2.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 753,
|
||||||
|
"data_ms": 19,
|
||||||
|
"answer_ms": 1058,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 2731
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 접수 현황 · 3.3s · answer has none of ['처리 중']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 929,
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1118,
|
||||||
|
"review_ms": 1029,
|
||||||
|
"total_ms": 3108
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 3/4
|
||||||
|
report: runs/lab01-20261024-174333.json
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
$ uv run tools/flow.py call my-helpdesk requests/faq-vpn.json -v
|
||||||
|
HTTP 200 · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하시고, 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 616,
|
||||||
|
"data_ms": 18,
|
||||||
|
"answer_ms": 1124,
|
||||||
|
"review_ms": 920,
|
||||||
|
"total_ms": 2698
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-vpn-nocache.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.57s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 3.08s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 2.91s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 2.99s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.80s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
p50 2.91s · p95 3.08s · max 3.08s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174351.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/ticket-own.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.86s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 3.05s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 3.12s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 2.94s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.99s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
p50 2.99s · p95 3.12s · max 3.12s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174406.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/off-topic.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.38s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 2.36s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 2.48s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 2.49s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.84s · route=자동 답변, llm_calls=3, cached=false
|
||||||
|
p50 2.48s · p95 2.84s · max 2.84s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174419.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py call my-helpdesk requests/ticket-own.json -v
|
||||||
|
HTTP 200 · 2.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 818,
|
||||||
|
"data_ms": 43,
|
||||||
|
"answer_ms": 877,
|
||||||
|
"review_ms": 895,
|
||||||
|
"total_ms": 2647
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py call my-helpdesk requests/off-topic.json -v
|
||||||
|
HTTP 200 · 2.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "other",
|
||||||
|
"answer": "찾으시는 정보를 확인하지 못했습니다. 점심 메뉴 추천은 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 732,
|
||||||
|
"data_ms": 19,
|
||||||
|
"answer_ms": 740,
|
||||||
|
"review_ms": 978,
|
||||||
|
"total_ms": 2482
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 0,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "FAQ · 접수 시간 따로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"timings has none of ['faq_ms']"
|
||||||
|
],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 595,
|
||||||
|
"data_ms": 18,
|
||||||
|
"answer_ms": 1005,
|
||||||
|
"review_ms": 1012,
|
||||||
|
"total_ms": 2643
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 조회 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"timings has none of ['ticket_ms']"
|
||||||
|
],
|
||||||
|
"seconds": 2.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 716,
|
||||||
|
"data_ms": 19,
|
||||||
|
"answer_ms": 958,
|
||||||
|
"review_ms": 881,
|
||||||
|
"total_ms": 2594
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "가장 오래 걸린 단계",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"slowest_step has none of ['classify', 'answer', 'review']"
|
||||||
|
],
|
||||||
|
"seconds": 3.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 991,
|
||||||
|
"data_ms": 18,
|
||||||
|
"answer_ms": 1024,
|
||||||
|
"review_ms": 991,
|
||||||
|
"total_ms": 3037
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황도 같은 모양",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"answer has none of ['처리 중']",
|
||||||
|
"slowest_step has none of ['classify', 'answer', 'review']"
|
||||||
|
],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 803,
|
||||||
|
"data_ms": 36,
|
||||||
|
"answer_ms": 884,
|
||||||
|
"review_ms": 1043,
|
||||||
|
"total_ms": 2781
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✘ FAQ · 접수 시간 따로 · 2.7s · timings has none of ['faq_ms']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 595,
|
||||||
|
"data_ms": 18,
|
||||||
|
"answer_ms": 1005,
|
||||||
|
"review_ms": 1012,
|
||||||
|
"total_ms": 2643
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 접수 조회 시간 · 2.6s · timings has none of ['ticket_ms']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 716,
|
||||||
|
"data_ms": 19,
|
||||||
|
"answer_ms": 958,
|
||||||
|
"review_ms": 881,
|
||||||
|
"total_ms": 2594
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 가장 오래 걸린 단계 · 3.1s · slowest_step has none of ['classify', 'answer', 'review']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 991,
|
||||||
|
"data_ms": 18,
|
||||||
|
"answer_ms": 1024,
|
||||||
|
"review_ms": 991,
|
||||||
|
"total_ms": 3037
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 접수 현황도 같은 모양 · 2.8s · answer has none of ['처리 중']; slowest_step has none of ['classify', 'answer', 'review']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 803,
|
||||||
|
"data_ms": 36,
|
||||||
|
"answer_ms": 884,
|
||||||
|
"review_ms": 1043,
|
||||||
|
"total_ms": 2781
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 0/4
|
||||||
|
report: runs/lab01-challenge-20261024-174435.json
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "FAQ · 접수 시간 따로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.5,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1365,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1196,
|
||||||
|
"review_ms": 993,
|
||||||
|
"total_ms": 3614
|
||||||
|
},
|
||||||
|
"slowest_step": "classify"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 조회 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 774,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 971,
|
||||||
|
"review_ms": 815,
|
||||||
|
"total_ms": 2601
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "가장 오래 걸린 단계",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 680,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 1044,
|
||||||
|
"review_ms": 1007,
|
||||||
|
"total_ms": 2769
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황도 같은 모양",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 723,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1029,
|
||||||
|
"review_ms": 1162,
|
||||||
|
"total_ms": 2951
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ FAQ · 접수 시간 따로 · 4.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1365,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1196,
|
||||||
|
"review_ms": 993,
|
||||||
|
"total_ms": 3614
|
||||||
|
},
|
||||||
|
"slowest_step": "classify"
|
||||||
|
}
|
||||||
|
✔ 접수 조회 시간 · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 774,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 971,
|
||||||
|
"review_ms": 815,
|
||||||
|
"total_ms": 2601
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✔ 가장 오래 걸린 단계 · 3.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 680,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 1044,
|
||||||
|
"review_ms": 1007,
|
||||||
|
"total_ms": 2769
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✔ 접수 현황도 같은 모양 · 3.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 723,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1029,
|
||||||
|
"review_ms": 1162,
|
||||||
|
"total_ms": 2951
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-challenge-20261024-174502.json
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 844,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 990,
|
||||||
|
"review_ms": 927,
|
||||||
|
"total_ms": 2807
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 715,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1064,
|
||||||
|
"review_ms": 988,
|
||||||
|
"total_ms": 2810
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 810,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1014,
|
||||||
|
"review_ms": 1058,
|
||||||
|
"total_ms": 2925
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN FAQ · 단계별 시간 · 2.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 844,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 990,
|
||||||
|
"review_ms": 927,
|
||||||
|
"total_ms": 2807
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✔ VPN FAQ · 전체 시간 · LLM 호출 수 · 2.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 715,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1064,
|
||||||
|
"review_ms": 988,
|
||||||
|
"total_ms": 2810
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · 3.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 810,
|
||||||
|
"faq_ms": 16,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1014,
|
||||||
|
"review_ms": 1058,
|
||||||
|
"total_ms": 2925
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-20261024-174511.json
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 0,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예산 안 (넉넉한 예산)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"over_budget = null (expected false)",
|
||||||
|
"budget_ms = null (expected 60000)",
|
||||||
|
"llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']"
|
||||||
|
],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 60000
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 628,
|
||||||
|
"faq_ms": 11,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1142,
|
||||||
|
"review_ms": 1028,
|
||||||
|
"total_ms": 2862
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예산 넘음 (0.5초 예산)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"over_budget = null (expected true)",
|
||||||
|
"budget_ms = null (expected 500)"
|
||||||
|
],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 500
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 658,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1013,
|
||||||
|
"review_ms": 930,
|
||||||
|
"total_ms": 2640
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예산을 안 주면 3초",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"budget_ms = null (expected 3000)",
|
||||||
|
"llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']"
|
||||||
|
],
|
||||||
|
"seconds": 3.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 650,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 1767,
|
||||||
|
"review_ms": 1061,
|
||||||
|
"total_ms": 3520
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황도 같은 모양",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"budget_ms = null (expected 3000)",
|
||||||
|
"llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']"
|
||||||
|
],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 772,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 12,
|
||||||
|
"answer_ms": 895,
|
||||||
|
"review_ms": 1006,
|
||||||
|
"total_ms": 2719
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✘ 예산 안 (넉넉한 예산) · 2.9s · over_budget = null (expected false); budget_ms = null (expected 60000); llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 60000
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 628,
|
||||||
|
"faq_ms": 11,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1142,
|
||||||
|
"review_ms": 1028,
|
||||||
|
"total_ms": 2862
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✘ 예산 넘음 (0.5초 예산) · 2.7s · over_budget = null (expected true); budget_ms = null (expected 500)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 500
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 658,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 1013,
|
||||||
|
"review_ms": 930,
|
||||||
|
"total_ms": 2640
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✘ 예산을 안 주면 3초 · 3.6s · budget_ms = null (expected 3000); llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 650,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 1767,
|
||||||
|
"review_ms": 1061,
|
||||||
|
"total_ms": 3520
|
||||||
|
},
|
||||||
|
"slowest_step": "answer"
|
||||||
|
}
|
||||||
|
✘ 접수 현황도 같은 모양 · 2.8s · budget_ms = null (expected 3000); llm_ms has none of ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 772,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 12,
|
||||||
|
"answer_ms": 895,
|
||||||
|
"review_ms": 1006,
|
||||||
|
"total_ms": 2719
|
||||||
|
},
|
||||||
|
"slowest_step": "review"
|
||||||
|
}
|
||||||
|
Passed 0/4
|
||||||
|
report: runs/lab01-challenge2-20261024-174523.json
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예산 안 (넉넉한 예산)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 60000
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1208,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1230,
|
||||||
|
"review_ms": 857,
|
||||||
|
"total_ms": 3357
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 3295,
|
||||||
|
"budget_ms": 60000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예산 넘음 (0.5초 예산)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 500
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 667,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 980,
|
||||||
|
"review_ms": 916,
|
||||||
|
"total_ms": 2609
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2563,
|
||||||
|
"budget_ms": 500,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예산을 안 주면 3초",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 761,
|
||||||
|
"faq_ms": 18,
|
||||||
|
"ticket_ms": 18,
|
||||||
|
"answer_ms": 1027,
|
||||||
|
"review_ms": 1185,
|
||||||
|
"total_ms": 3023
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2973,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황도 같은 모양",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 795,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1017,
|
||||||
|
"review_ms": 1046,
|
||||||
|
"total_ms": 2904
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2858,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 예산 안 (넉넉한 예산) · 4.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 60000
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 1208,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1230,
|
||||||
|
"review_ms": 857,
|
||||||
|
"total_ms": 3357
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 3295,
|
||||||
|
"budget_ms": 60000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ 예산 넘음 (0.5초 예산) · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true,
|
||||||
|
"budget_ms": 500
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 667,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 11,
|
||||||
|
"answer_ms": 980,
|
||||||
|
"review_ms": 916,
|
||||||
|
"total_ms": 2609
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2563,
|
||||||
|
"budget_ms": 500,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
✔ 예산을 안 주면 3초 · 3.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 761,
|
||||||
|
"faq_ms": 18,
|
||||||
|
"ticket_ms": 18,
|
||||||
|
"answer_ms": 1027,
|
||||||
|
"review_ms": 1185,
|
||||||
|
"total_ms": 3023
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2973,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
✔ 접수 현황도 같은 모양 · 2.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 795,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 1017,
|
||||||
|
"review_ms": 1046,
|
||||||
|
"total_ms": 2904
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2858,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-challenge2-20261024-174548.json
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "FAQ · 접수 시간 따로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 603,
|
||||||
|
"faq_ms": 9,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 969,
|
||||||
|
"review_ms": 1043,
|
||||||
|
"total_ms": 2677
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2615,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 조회 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 614,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 1019,
|
||||||
|
"review_ms": 876,
|
||||||
|
"total_ms": 2543
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2509,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "가장 오래 걸린 단계",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 687,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 1146,
|
||||||
|
"review_ms": 965,
|
||||||
|
"total_ms": 2838
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2798,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황도 같은 모양",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 767,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 1053,
|
||||||
|
"review_ms": 1007,
|
||||||
|
"total_ms": 2865
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2827,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ FAQ · 접수 시간 따로 · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 603,
|
||||||
|
"faq_ms": 9,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 969,
|
||||||
|
"review_ms": 1043,
|
||||||
|
"total_ms": 2677
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 2615,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ 접수 조회 시간 · 2.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 614,
|
||||||
|
"faq_ms": 13,
|
||||||
|
"ticket_ms": 13,
|
||||||
|
"answer_ms": 1019,
|
||||||
|
"review_ms": 876,
|
||||||
|
"total_ms": 2543
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2509,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ 가장 오래 걸린 단계 · 2.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 687,
|
||||||
|
"faq_ms": 17,
|
||||||
|
"ticket_ms": 10,
|
||||||
|
"answer_ms": 1146,
|
||||||
|
"review_ms": 965,
|
||||||
|
"total_ms": 2838
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2798,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ 접수 현황도 같은 모양 · 2.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 767,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 1053,
|
||||||
|
"review_ms": 1007,
|
||||||
|
"total_ms": 2865
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2827,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-challenge-20261024-174559.json
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 785,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 1053,
|
||||||
|
"review_ms": 1031,
|
||||||
|
"total_ms": 2902
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2869,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 750,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 984,
|
||||||
|
"review_ms": 895,
|
||||||
|
"total_ms": 2672
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2629,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.4,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 938,
|
||||||
|
"faq_ms": 10,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 981,
|
||||||
|
"review_ms": 1355,
|
||||||
|
"total_ms": 3310
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 3274,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN FAQ · 단계별 시간 · 3.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 785,
|
||||||
|
"faq_ms": 14,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 1053,
|
||||||
|
"review_ms": 1031,
|
||||||
|
"total_ms": 2902
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2869,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ VPN FAQ · 전체 시간 · LLM 호출 수 · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 750,
|
||||||
|
"faq_ms": 15,
|
||||||
|
"ticket_ms": 14,
|
||||||
|
"answer_ms": 984,
|
||||||
|
"review_ms": 895,
|
||||||
|
"total_ms": 2672
|
||||||
|
},
|
||||||
|
"slowest_step": "answer",
|
||||||
|
"llm_ms": 2629,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": false
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · 3.4s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"classify_ms": 938,
|
||||||
|
"faq_ms": 10,
|
||||||
|
"ticket_ms": 9,
|
||||||
|
"answer_ms": 981,
|
||||||
|
"review_ms": 1355,
|
||||||
|
"total_ms": 3310
|
||||||
|
},
|
||||||
|
"slowest_step": "review",
|
||||||
|
"llm_ms": 3274,
|
||||||
|
"budget_ms": 3000,
|
||||||
|
"over_budget": true
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-20261024-174609.json
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 7,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하시고, 인쇄 대기열을 비운 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 41,
|
||||||
|
"answer_ms": 1766,
|
||||||
|
"review_ms": 1115,
|
||||||
|
"total_ms": 2945
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.5,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 1209,
|
||||||
|
"total_ms": 2295
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 현재 확인되지 않습니다. 담당 직원이 확인 후 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 23,
|
||||||
|
"answer_ms": 1209,
|
||||||
|
"review_ms": 797,
|
||||||
|
"total_ms": 2040
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.4,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"classify_ms": 582,
|
||||||
|
"answer_ms": 908,
|
||||||
|
"review_ms": 877,
|
||||||
|
"total_ms": 2401
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 20,
|
||||||
|
"answer_ms": 1202,
|
||||||
|
"review_ms": 994,
|
||||||
|
"total_ms": 2234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"took 1.9s (expected at most 1s)",
|
||||||
|
"cached = false (expected true)",
|
||||||
|
"llm_calls = 2 (expected 0)"
|
||||||
|
],
|
||||||
|
"seconds": 1.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 문제가 계속되면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 24,
|
||||||
|
"answer_ms": 967,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 1898
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 17,
|
||||||
|
"answer_ms": 1128,
|
||||||
|
"review_ms": 1048,
|
||||||
|
"total_ms": 2213
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 829,
|
||||||
|
"review_ms": 1300,
|
||||||
|
"total_ms": 2171
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"answer has none of ['처리 중']"
|
||||||
|
],
|
||||||
|
"seconds": 2.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 20,
|
||||||
|
"answer_ms": 951,
|
||||||
|
"review_ms": 940,
|
||||||
|
"total_ms": 1924
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 2}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 3.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하시고, 인쇄 대기열을 비운 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 41,
|
||||||
|
"answer_ms": 1766,
|
||||||
|
"review_ms": 1115,
|
||||||
|
"total_ms": 2945
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 2.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 1209,
|
||||||
|
"total_ms": 2295
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 2.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 현재 확인되지 않습니다. 담당 직원이 확인 후 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 23,
|
||||||
|
"answer_ms": 1209,
|
||||||
|
"review_ms": 797,
|
||||||
|
"total_ms": 2040
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 2.4s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"classify_ms": 582,
|
||||||
|
"answer_ms": 908,
|
||||||
|
"review_ms": 877,
|
||||||
|
"total_ms": 2401
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 2.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 20,
|
||||||
|
"answer_ms": 1202,
|
||||||
|
"review_ms": 994,
|
||||||
|
"total_ms": 2234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 1.9s · took 1.9s (expected at most 1s); cached = false (expected true); llm_calls = 2 (expected 0)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 문제가 계속되면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 24,
|
||||||
|
"answer_ms": 967,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 1898
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 2.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 17,
|
||||||
|
"answer_ms": 1128,
|
||||||
|
"review_ms": 1048,
|
||||||
|
"total_ms": 2213
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 829,
|
||||||
|
"review_ms": 1300,
|
||||||
|
"total_ms": 2171
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 예제 2 · 접수 현황은 캐시 안 함 (2) · 2.0s · answer has none of ['처리 중']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 20,
|
||||||
|
"answer_ms": 951,
|
||||||
|
"review_ms": 940,
|
||||||
|
"total_ms": 1924
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 7/9
|
||||||
|
report: runs/lab02-20261024-174642.json
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-printer.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.50s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 2.45s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 2.51s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 2.39s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.12s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
p50 2.45s · p95 2.51s · max 2.51s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174654.json
|
||||||
|
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 9,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하시고, 인쇄 대기열을 비운 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 76,
|
||||||
|
"answer_ms": 1837,
|
||||||
|
"review_ms": 1048,
|
||||||
|
"total_ms": 2981
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 911,
|
||||||
|
"review_ms": 1002,
|
||||||
|
"total_ms": 1957
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 현재 확인되지 않습니다. 담당 직원이 확인 후 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 40,
|
||||||
|
"answer_ms": 1008,
|
||||||
|
"review_ms": 870,
|
||||||
|
"total_ms": 1932
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"classify_ms": 767,
|
||||||
|
"answer_ms": 810,
|
||||||
|
"review_ms": 962,
|
||||||
|
"total_ms": 2586
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 58,
|
||||||
|
"answer_ms": 1140,
|
||||||
|
"review_ms": 929,
|
||||||
|
"total_ms": 2143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1024,
|
||||||
|
"review_ms": 1125,
|
||||||
|
"total_ms": 2194
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 35,
|
||||||
|
"answer_ms": 964,
|
||||||
|
"review_ms": 1161,
|
||||||
|
"total_ms": 2173
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 886,
|
||||||
|
"review_ms": 1239,
|
||||||
|
"total_ms": 2168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 3.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하시고, 인쇄 대기열을 비운 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직원에게 문의해 주십시오.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 76,
|
||||||
|
"answer_ms": 1837,
|
||||||
|
"review_ms": 1048,
|
||||||
|
"total_ms": 2981
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 2.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 911,
|
||||||
|
"review_ms": 1002,
|
||||||
|
"total_ms": 1957
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 2.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 현재 확인되지 않습니다. 담당 직원이 확인 후 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 40,
|
||||||
|
"answer_ms": 1008,
|
||||||
|
"review_ms": 870,
|
||||||
|
"total_ms": 1932
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 2.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"classify_ms": 767,
|
||||||
|
"answer_ms": 810,
|
||||||
|
"review_ms": 962,
|
||||||
|
"total_ms": 2586
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 58,
|
||||||
|
"answer_ms": 1140,
|
||||||
|
"review_ms": 929,
|
||||||
|
"total_ms": 2143
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 2.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1024,
|
||||||
|
"review_ms": 1125,
|
||||||
|
"total_ms": 2194
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 35,
|
||||||
|
"answer_ms": 964,
|
||||||
|
"review_ms": 1161,
|
||||||
|
"total_ms": 2173
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (2) · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 886,
|
||||||
|
"review_ms": 1239,
|
||||||
|
"total_ms": 2168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 9/9
|
||||||
|
report: runs/lab02-20261024-174725.json
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 3,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 34,
|
||||||
|
"answer_ms": 1125,
|
||||||
|
"review_ms": 852,
|
||||||
|
"total_ms": 2029
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 1182,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 2114
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"answer has none of ['처리 중']"
|
||||||
|
],
|
||||||
|
"seconds": 2.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 50,
|
||||||
|
"answer_ms": 884,
|
||||||
|
"review_ms": 1046,
|
||||||
|
"total_ms": 1992
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN FAQ · 단계별 시간 · 2.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 34,
|
||||||
|
"answer_ms": 1125,
|
||||||
|
"review_ms": 852,
|
||||||
|
"total_ms": 2029
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ VPN FAQ · 전체 시간 · LLM 호출 수 · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 1182,
|
||||||
|
"review_ms": 890,
|
||||||
|
"total_ms": 2114
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 접수 현황 · 2.1s · answer has none of ['처리 중']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 인증서 발급 승인 대기 중이며, 10월 20일에 완료될 예정입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 50,
|
||||||
|
"answer_ms": 884,
|
||||||
|
"review_ms": 1046,
|
||||||
|
"total_ms": 1992
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 3/4
|
||||||
|
report: runs/lab01-20261024-174732.json
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
$ curl -s localhost:8800/cache-stats; echo
|
||||||
|
{"entries": 2, "hits": 4, "misses": 8}
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
$ curl -s -XPOST localhost:8800/cache-clear; echo
|
||||||
|
{"cleared": 2}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-vpn.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.07s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 0.06s · route=자동 답변, llm_calls=0, cached=true
|
||||||
|
✔ #3 · HTTP 200 · 0.05s · route=자동 답변, llm_calls=0, cached=true
|
||||||
|
✔ #4 · HTTP 200 · 0.04s · route=자동 답변, llm_calls=0, cached=true
|
||||||
|
✔ #5 · HTTP 200 · 0.05s · route=자동 답변, llm_calls=0, cached=true
|
||||||
|
p50 0.05s · p95 2.07s · max 2.07s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174734.json
|
||||||
|
|
||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-vpn-nocache.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 1.99s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 1.98s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 2.21s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 1.97s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.24s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
p50 1.99s · p95 2.24s · max 2.24s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174745.json
|
||||||
|
|
||||||
|
$ curl -s localhost:8800/cache-stats; echo
|
||||||
|
{"entries": 1, "hits": 9, "misses": 1}
|
||||||
|
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 1,
|
||||||
|
"total": 5,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · LLM 한 번 · 2.5초 안",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"llm_calls = 2 (expected 1)"
|
||||||
|
],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1054,
|
||||||
|
"review_ms": 1038,
|
||||||
|
"total_ms": 2141
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "비밀번호 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"llm_calls = 2 (expected 1)"
|
||||||
|
],
|
||||||
|
"seconds": 2.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 언제든 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 72,
|
||||||
|
"answer_ms": 943,
|
||||||
|
"review_ms": 950,
|
||||||
|
"total_ms": 1984
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"llm_calls = 2 (expected 1)"
|
||||||
|
],
|
||||||
|
"seconds": 2.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 932,
|
||||||
|
"review_ms": 1241,
|
||||||
|
"total_ms": 2213
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "규칙으로 모르면 분류 + 답 두 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"llm_calls = 3 (expected 2)"
|
||||||
|
],
|
||||||
|
"seconds": 2.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 730,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 926,
|
||||||
|
"total_ms": 2736
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문은 LLM 없이",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 2.0s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하시고, 인쇄 대기열을 비운 후 다시 시도해 보시기 바랍니다. 추가 도움이 필요하시면 담당 직
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 5 case(s) × 1
|
||||||
|
✘ VPN · LLM 한 번 · 2.5초 안 · 2.2s · llm_calls = 2 (expected 1)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 후 다시 접속해 보시기 바랍니다. 추가 도움이 필요하시면 담당자에게 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1054,
|
||||||
|
"review_ms": 1038,
|
||||||
|
"total_ms": 2141
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 비밀번호 · LLM 한 번 · 2.0s · llm_calls = 2 (expected 1)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 언제든 문의해 주시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 72,
|
||||||
|
"answer_ms": 943,
|
||||||
|
"review_ms": 950,
|
||||||
|
"total_ms": 1984
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 접수 현황 · LLM 한 번 · 2.3s · llm_calls = 2 (expected 1)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 완료 예정일은 10월 20일입니다. 추가 문의 사항이 있으시면 010-2718-2818로 연락해 주시기 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 932,
|
||||||
|
"review_ms": 1241,
|
||||||
|
"total_ms": 2213
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 규칙으로 모르면 분류 + 답 두 번 · 2.8s · llm_calls = 3 (expected 2)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "죄송합니다만, 해당 정보를 찾지 못했습니다. 담당 직원이 곧 도움을 드리겠습니다. 감사합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 3,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 730,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 926,
|
||||||
|
"total_ms": 2736
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문은 LLM 없이 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 1/5
|
||||||
|
report: runs/lab02-challenge-20261024-174756.json
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-vpn-nocache.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 2.26s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 2.45s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 2.11s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 2.22s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 2.03s · route=자동 답변, llm_calls=2, cached=false
|
||||||
|
p50 2.22s · p95 2.45s · max 2.45s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174807.json
|
||||||
|
|
||||||
@@ -0,0 +1,491 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 15,
|
||||||
|
"total": 15,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · LLM 한 번 · 2.5초 안",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 37,
|
||||||
|
"answer_ms": 986,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1046
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN · LLM 한 번 · 2.5초 안",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 2,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 1057,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1107
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN · LLM 한 번 · 2.5초 안",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 3,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 41,
|
||||||
|
"answer_ms": 952,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1023
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "비밀번호 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 도움이 필요하시면 언제든 문의해 주세요.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 81,
|
||||||
|
"answer_ms": 868,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 974
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "비밀번호 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 2,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 824,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 881
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "비밀번호 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 3,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 48,
|
||||||
|
"answer_ms": 811,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 879
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 798,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 848
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 2,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 924,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 971
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 3,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 1001,
|
||||||
|
"review_ms": 45,
|
||||||
|
"total_ms": 1096
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "규칙으로 모르면 분류 + 답 두 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 23,
|
||||||
|
"classify_ms": 723,
|
||||||
|
"answer_ms": 838,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1606
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "규칙으로 모르면 분류 + 답 두 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 2,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.4,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 594,
|
||||||
|
"answer_ms": 698,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 1339
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "규칙으로 모르면 분류 + 답 두 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 3,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"classify_ms": 746,
|
||||||
|
"answer_ms": 801,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1599
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문은 LLM 없이",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문은 LLM 없이",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 2,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문은 LLM 없이",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 3,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 2.4s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 5 case(s) × 3
|
||||||
|
✔ VPN · LLM 한 번 · 2.5초 안 #1 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 37,
|
||||||
|
"answer_ms": 986,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1046
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ VPN · LLM 한 번 · 2.5초 안 #2 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 1057,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1107
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ VPN · LLM 한 번 · 2.5초 안 #3 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 41,
|
||||||
|
"answer_ms": 952,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1023
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 비밀번호 · LLM 한 번 #1 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 도움이 필요하시면 언제든 문의해 주세요.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 81,
|
||||||
|
"answer_ms": 868,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 974
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 비밀번호 · LLM 한 번 #2 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 824,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 881
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 비밀번호 · LLM 한 번 #3 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 48,
|
||||||
|
"answer_ms": 811,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 879
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · LLM 한 번 #1 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 798,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 848
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · LLM 한 번 #2 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 924,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 971
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · LLM 한 번 #3 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 1001,
|
||||||
|
"review_ms": 45,
|
||||||
|
"total_ms": 1096
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 규칙으로 모르면 분류 + 답 두 번 #1 · 1.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 23,
|
||||||
|
"classify_ms": 723,
|
||||||
|
"answer_ms": 838,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1606
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 규칙으로 모르면 분류 + 답 두 번 #2 · 1.4s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 594,
|
||||||
|
"answer_ms": 698,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 1339
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 규칙으로 모르면 분류 + 답 두 번 #3 · 1.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"classify_ms": 746,
|
||||||
|
"answer_ms": 801,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1599
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문은 LLM 없이 #1 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
✔ 빈 질문은 LLM 없이 #2 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
✔ 빈 질문은 LLM 없이 #3 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 15/15
|
||||||
|
report: runs/lab02-challenge-20261024-174837.json
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 9,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 999,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1047
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 802,
|
||||||
|
"review_ms": 50,
|
||||||
|
"total_ms": 899
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 901,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 951
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"classify_ms": 572,
|
||||||
|
"answer_ms": 898,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1522
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 1106,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1149
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 22,
|
||||||
|
"answer_ms": 1013,
|
||||||
|
"review_ms": 21,
|
||||||
|
"total_ms": 1063
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 989,
|
||||||
|
"review_ms": 33,
|
||||||
|
"total_ms": 1069
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 24,
|
||||||
|
"answer_ms": 786,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 838
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 999,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1047
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 29,
|
||||||
|
"answer_ms": 802,
|
||||||
|
"review_ms": 50,
|
||||||
|
"total_ms": 899
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 901,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 951
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 1.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"classify_ms": 572,
|
||||||
|
"answer_ms": 898,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1522
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 1106,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1149
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 22,
|
||||||
|
"answer_ms": 1013,
|
||||||
|
"review_ms": 21,
|
||||||
|
"total_ms": 1063
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 989,
|
||||||
|
"review_ms": 33,
|
||||||
|
"total_ms": 1069
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (2) · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 24,
|
||||||
|
"answer_ms": 786,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 838
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 9/9
|
||||||
|
report: runs/lab02-20261024-174846.json
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab01.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 단계별 시간",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 1122,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1173
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "VPN FAQ · 전체 시간 · LLM 호출 수",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 1219,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1268
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 907,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 956
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN FAQ · 단계별 시간 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 32,
|
||||||
|
"answer_ms": 1122,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1173
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ VPN FAQ · 전체 시간 · LLM 호출 수 · 1.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 1219,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1268
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 907,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 956
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab01-20261024-174849.json
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
$ uv run tools/flow.py bench my-helpdesk requests/faq-vpn-nocache.json -n 5
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk × 5
|
||||||
|
✔ #1 · HTTP 200 · 1.32s · route=자동 답변, llm_calls=1, cached=false
|
||||||
|
✔ #2 · HTTP 200 · 1.14s · route=자동 답변, llm_calls=1, cached=false
|
||||||
|
✔ #3 · HTTP 200 · 1.11s · route=자동 답변, llm_calls=1, cached=false
|
||||||
|
✔ #4 · HTTP 200 · 1.12s · route=자동 답변, llm_calls=1, cached=false
|
||||||
|
✔ #5 · HTTP 200 · 1.05s · route=자동 답변, llm_calls=1, cached=false
|
||||||
|
p50 1.12s · p95 1.32s · max 1.32s · failures 0/5
|
||||||
|
report: runs/bench-my-helpdesk-20261024-174855.json
|
||||||
|
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 6,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "처음 묻는 VPN (캐시 없음)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 39,
|
||||||
|
"answer_ms": 947,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1009
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "다른 말로 묻는 VPN → FAQ 번호 캐시",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"took 1.2s (expected at most 1s)",
|
||||||
|
"cached = false (expected true)",
|
||||||
|
"cache_by = null (expected faq)",
|
||||||
|
"llm_calls = 1 (expected 0)"
|
||||||
|
],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 현재 접수된 관련 티켓은 없습니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1067,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1117
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "같은 말은 질문 캐시",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"cache_by = null (expected question)"
|
||||||
|
],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "no_cache면 FAQ 번호 캐시도 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1014,
|
||||||
|
"review_ms": 9,
|
||||||
|
"total_ms": 1051
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "다른 FAQ는 따로 (프린터 처음)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1091
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황은 캐시 안 함",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 869,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 913
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 2}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 6 case(s) × 1
|
||||||
|
✔ 처음 묻는 VPN (캐시 없음) · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 39,
|
||||||
|
"answer_ms": 947,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1009
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 다른 말로 묻는 VPN → FAQ 번호 캐시 · 1.2s · took 1.2s (expected at most 1s); cached = false (expected true); cache_by = null (expected faq); llm_calls = 1 (expected 0)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 현재 접수된 관련 티켓은 없습니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1067,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1117
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✘ 같은 말은 질문 캐시 · 0.1s · cache_by = null (expected question)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ no_cache면 FAQ 번호 캐시도 안 씀 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1014,
|
||||||
|
"review_ms": 9,
|
||||||
|
"total_ms": 1051
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 다른 FAQ는 따로 (프린터 처음) · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 26,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1091
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황은 캐시 안 함 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 869,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 913
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 4/6
|
||||||
|
report: runs/lab02-challenge2-20261024-174901.json
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 6,
|
||||||
|
"total": 6,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "처음 묻는 VPN (캐시 없음)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.5,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 71,
|
||||||
|
"answer_ms": 1556,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 1665
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "다른 말로 묻는 VPN → FAQ 번호 캐시",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "faq",
|
||||||
|
"steps": [
|
||||||
|
"faq",
|
||||||
|
"rule",
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 44
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "같은 말은 질문 캐시",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "question",
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "no_cache면 FAQ 번호 캐시도 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 현재 접수된 관련 티켓은 없습니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1052,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1110
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "다른 FAQ는 따로 (프린터 처음)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 42,
|
||||||
|
"answer_ms": 1035,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1102
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황은 캐시 안 함",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 34,
|
||||||
|
"answer_ms": 796,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 852
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 3}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 6 case(s) × 1
|
||||||
|
✔ 처음 묻는 VPN (캐시 없음) · 2.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 71,
|
||||||
|
"answer_ms": 1556,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 1665
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 다른 말로 묻는 VPN → FAQ 번호 캐시 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "faq",
|
||||||
|
"steps": [
|
||||||
|
"faq",
|
||||||
|
"rule",
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 44
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 같은 말은 질문 캐시 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "question",
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ no_cache면 FAQ 번호 캐시도 안 씀 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 연결이 계속 끊어지는데요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 연결이 계속 끊어질 경우, VPN 클라이언트를 최신 버전으로 업데이트하고 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 현재 접수된 관련 티켓은 없습니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1052,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1110
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 다른 FAQ는 따로 (프린터 처음) · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 42,
|
||||||
|
"answer_ms": 1035,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1102
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황은 캐시 안 함 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 34,
|
||||||
|
"answer_ms": 796,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 852
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 6/6
|
||||||
|
report: runs/lab02-challenge2-20261024-174920.json
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02-challenge.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 5,
|
||||||
|
"total": 5,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · LLM 한 번 · 2.5초 안",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1017,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1067
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "비밀번호 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 도움이 필요하시면 언제든 문의해 주세요.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 840,
|
||||||
|
"review_ms": 40,
|
||||||
|
"total_ms": 928
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · LLM 한 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 835,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 891
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "규칙으로 모르면 분류 + 답 두 번",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 842,
|
||||||
|
"answer_ms": 771,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1659
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문은 LLM 없이",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 1.1s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 5 case(s) × 1
|
||||||
|
✔ VPN · LLM 한 번 · 2.5초 안 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1017,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1067
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 비밀번호 · LLM 한 번 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요. 재설정은 어떻게 하나요?",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 도움이 필요하시면 언제든 문의해 주세요.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 840,
|
||||||
|
"review_ms": 40,
|
||||||
|
"total_ms": 928
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · LLM 한 번 · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 28,
|
||||||
|
"answer_ms": 835,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 891
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 규칙으로 모르면 분류 + 답 두 번 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"classify_ms": 842,
|
||||||
|
"answer_ms": 771,
|
||||||
|
"review_ms": 11,
|
||||||
|
"total_ms": 1659
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 빈 질문은 LLM 없이 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 5/5
|
||||||
|
report: runs/lab02-challenge-20261024-174926.json
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 9,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1111,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 895,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 945
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 946,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"classify_ms": 623,
|
||||||
|
"answer_ms": 1026,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1699
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1112,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 1168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "question",
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1065,
|
||||||
|
"review_ms": 32,
|
||||||
|
"total_ms": 1137
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 936,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 988
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 822,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 873
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 4}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 1111,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 895,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 945
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"answer_ms": 946,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 25,
|
||||||
|
"classify_ms": 623,
|
||||||
|
"answer_ms": 1026,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1699
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 1112,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 1168
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"cache_by": "question",
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 21,
|
||||||
|
"answer_ms": 1065,
|
||||||
|
"review_ms": 32,
|
||||||
|
"total_ms": 1137
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 30,
|
||||||
|
"answer_ms": 936,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 988
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (2) · 0.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"cache_by": null,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 822,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 873
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 9/9
|
||||||
|
report: runs/lab02-20261024-174935.json
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
$ uv run tools/flow.py fault faq slow
|
||||||
|
✔ fault switch: {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py call my-helpdesk '{"question": "비밀번호를 잊어버렸어요", "no_cache": true}' -v
|
||||||
|
HTTP 200 · 11.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정하시면 됩니다. 추가 도움이 필요하시면 언제든 문의해 주세요.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-001"
|
||||||
|
],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"review"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 8144,
|
||||||
|
"answer_ms": 1518,
|
||||||
|
"review_ms": 1013,
|
||||||
|
"total_ms": 10701
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py fault faq error
|
||||||
|
✔ fault switch: {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py call my-helpdesk '{"question": "비밀번호를 잊어버렸어요", "no_cache": true}' -v
|
||||||
|
HTTP 500 · 0.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 500
|
||||||
|
{
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py fault all ok
|
||||||
|
✔ fault switch: {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 605,
|
||||||
|
"answer_ms": 1634,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 2270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 느릴 때 (8초)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 오류일 때 (500)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "캐시에 있으면 FAQ가 멈춰도 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 3.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 605,
|
||||||
|
"answer_ms": 1634,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 2270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 느릴 때 (8초) · 4.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 오류일 때 (500) · 0.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 4}
|
||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 2.1s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 캐시에 있으면 FAQ가 멈춰도 답 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-20261024-175023.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 9,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 585,
|
||||||
|
"answer_ms": 1071,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1687
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 71,
|
||||||
|
"answer_ms": 1030,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 1128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 555,
|
||||||
|
"answer_ms": 1179,
|
||||||
|
"review_ms": 24,
|
||||||
|
"total_ms": 1774
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 554,
|
||||||
|
"classify_ms": 650,
|
||||||
|
"answer_ms": 927,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 2152
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 553,
|
||||||
|
"answer_ms": 1015,
|
||||||
|
"review_ms": 28,
|
||||||
|
"total_ms": 1605
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류는 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 551,
|
||||||
|
"answer_ms": 1081,
|
||||||
|
"review_ms": 34,
|
||||||
|
"total_ms": 1685
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 1062,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1114
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 47,
|
||||||
|
"answer_ms": 977,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1049
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 1.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 585,
|
||||||
|
"answer_ms": 1071,
|
||||||
|
"review_ms": 20,
|
||||||
|
"total_ms": 1687
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 71,
|
||||||
|
"answer_ms": 1030,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 1128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 1.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 555,
|
||||||
|
"answer_ms": 1179,
|
||||||
|
"review_ms": 24,
|
||||||
|
"total_ms": 1774
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 554,
|
||||||
|
"classify_ms": 650,
|
||||||
|
"answer_ms": 927,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 2152
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 553,
|
||||||
|
"answer_ms": 1015,
|
||||||
|
"review_ms": 28,
|
||||||
|
"total_ms": 1605
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류는 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 551,
|
||||||
|
"answer_ms": 1081,
|
||||||
|
"review_ms": 34,
|
||||||
|
"total_ms": 1685
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 1062,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1114
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (2) · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 47,
|
||||||
|
"answer_ms": 977,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1049
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 9/9
|
||||||
|
report: runs/lab02-20261024-175036.json
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
$ uv run tools/flow.py llm down
|
||||||
|
✔ OpenAI credential: broken key (drill). Restore with: uv run tools/flow.py llm up
|
||||||
|
|
||||||
|
$ uv run tools/flow.py call my-helpdesk requests/faq-vpn-nocache.json -v
|
||||||
|
HTTP 500 · 1.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 500
|
||||||
|
{
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
|
||||||
|
$ uv run tools/flow.py llm up
|
||||||
|
✔ OpenAI credential: real key from .env restored
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-llm-down.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 1,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · FAQ 원문으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"status 500 (expected 200)",
|
||||||
|
"route = null (expected 간단 답변)",
|
||||||
|
"answer has none of ['인증서']"
|
||||||
|
],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 500,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · 상태 그대로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"status 500 (expected 200)",
|
||||||
|
"route = null (expected 간단 답변)",
|
||||||
|
"answer has none of ['처리 중']"
|
||||||
|
],
|
||||||
|
"seconds": 0.4,
|
||||||
|
"status": 500,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "모르는 질문 · 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"status 500 (expected 200)",
|
||||||
|
"route = null (expected 담당자 연결)"
|
||||||
|
],
|
||||||
|
"seconds": 0.9,
|
||||||
|
"status": 500,
|
||||||
|
"request": {
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 2}
|
||||||
|
✔ OpenAI credential: broken key (drill). Restore with: uv run tools/flow.py llm up
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✘ VPN · FAQ 원문으로 · 0.9s · status 500 (expected 200); route = null (expected 간단 답변); answer has none of ['인증서']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 500
|
||||||
|
{
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
✘ 접수 현황 · 상태 그대로 · 0.4s · status 500 (expected 200); route = null (expected 간단 답변); answer has none of ['처리 중']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 500
|
||||||
|
{
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
✘ 모르는 질문 · 담당자 · 0.9s · status 500 (expected 200); route = null (expected 담당자 연결)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 500
|
||||||
|
{
|
||||||
|
"message": "Error in workflow"
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 1/4
|
||||||
|
report: runs/lab03-llm-down-20261024-175047.json
|
||||||
|
✔ OpenAI credential: real key from .env restored
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-llm-down.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · FAQ 원문으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다. (자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다.)",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · 상태 그대로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 접수 건은 현재 '처리 중' 상태입니다. 인증서 발급 승인 대기 중이며 10월 20일 완료 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "모르는 질문 · 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "other",
|
||||||
|
"answer": "지금은 자동 답변을 할 수 없어 담당자에게 연결합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
✔ OpenAI credential: broken key (drill). Restore with: uv run tools/flow.py llm up
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN · FAQ 원문으로 · 2.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다. (자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다.)",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · 상태 그대로 · 0.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 접수 건은 현재 '처리 중' 상태입니다. 인증서 발급 승인 대기 중이며 10월 20일 완료 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 모르는 질문 · 담당자 · 0.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "other",
|
||||||
|
"answer": "지금은 자동 답변을 할 수 없어 담당자에게 연결합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-llm-down-20261024-175108.json
|
||||||
|
✔ OpenAI credential: real key from .env restored
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab02.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 9,
|
||||||
|
"total": 9,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 프린터 FAQ는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 568,
|
||||||
|
"answer_ms": 1078,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1679
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 접수 번호는 규칙으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 38,
|
||||||
|
"answer_ms": 969,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1032
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 없는 접수 번호",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 546,
|
||||||
|
"answer_ms": 1011,
|
||||||
|
"review_ms": 46,
|
||||||
|
"total_ms": 1632
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 1 · 규칙으로 모르면 LLM이 분류",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"classify_ms": 881,
|
||||||
|
"answer_ms": 734,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 2206
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 처음 묻는 VPN",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.9,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"answer_ms": 1222,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1803
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 같은 질문 (띄어쓰기·대소문자 다름)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · no_cache면 캐시 안 씀",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1625
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (1)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 934,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 986
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "예제 2 · 접수 현황은 캐시 안 함 (2)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 1016,
|
||||||
|
"review_ms": 43,
|
||||||
|
"total_ms": 1110
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 9 case(s) × 1
|
||||||
|
✔ 예제 1 · 프린터 FAQ는 규칙으로 · 1.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 568,
|
||||||
|
"answer_ms": 1078,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1679
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 접수 번호는 규칙으로 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 38,
|
||||||
|
"answer_ms": 969,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1032
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 없는 접수 번호 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 546,
|
||||||
|
"answer_ms": 1011,
|
||||||
|
"review_ms": 46,
|
||||||
|
"total_ms": 1632
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 1 · 규칙으로 모르면 LLM이 분류 · 2.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "회의실 화면 공유가 안 되는데 누구에게 물어보나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "찾지 못했습니다. 담당 직원이 도움을 드리겠습니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 2,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"classify",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"classify_ms": 881,
|
||||||
|
"answer_ms": 734,
|
||||||
|
"review_ms": 23,
|
||||||
|
"total_ms": 2206
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 처음 묻는 VPN · 1.9s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"answer_ms": 1222,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1803
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 같은 질문 (띄어쓰기·대소문자 다름) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": " vpn 접속이 자꾸 끊겨요. 어떻게 하나요? ",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · no_cache면 캐시 안 씀 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 560,
|
||||||
|
"answer_ms": 1041,
|
||||||
|
"review_ms": 17,
|
||||||
|
"total_ms": 1625
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (1) · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락 바랍니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 27,
|
||||||
|
"answer_ms": 934,
|
||||||
|
"review_ms": 19,
|
||||||
|
"total_ms": 986
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ 예제 2 · 접수 현황은 캐시 안 함 (2) · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 31,
|
||||||
|
"answer_ms": 1016,
|
||||||
|
"review_ms": 43,
|
||||||
|
"total_ms": 1110
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 9/9
|
||||||
|
report: runs/lab02-20261024-175123.json
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 571,
|
||||||
|
"answer_ms": 1006,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 느릴 때 (8초)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 오류일 때 (500)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "캐시에 있으면 FAQ가 멈춰도 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 1.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 571,
|
||||||
|
"answer_ms": 1006,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 느릴 때 (8초) · 4.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 오류일 때 (500) · 0.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 2}
|
||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 1.9s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 캐시에 있으면 FAQ가 멈춰도 답 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-20261024-175132.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-ticket.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 2,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소 접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 915,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 970
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 API 500 · 접수 질문은 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"reason = data_api_down (expected tickets_down)"
|
||||||
|
],
|
||||||
|
"seconds": 0.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 API 500 · FAQ 질문은 그대로 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"route = 담당자 연결 (expected 자동 답변)",
|
||||||
|
"category = null (expected faq)",
|
||||||
|
"answer has none of ['인증서']"
|
||||||
|
],
|
||||||
|
"seconds": 0.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "없는 접수 번호는 그대로 (404는 고장이 아님)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 564,
|
||||||
|
"answer_ms": 978,
|
||||||
|
"review_ms": 10,
|
||||||
|
"total_ms": 1558
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 접수 현황 · 1.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 처리 중이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 33,
|
||||||
|
"answer_ms": 915,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 970
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "error", "slow_seconds": 8.0}
|
||||||
|
✘ 접수 API 500 · 접수 질문은 담당자 · 0.6s · reason = data_api_down (expected tickets_down)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "error", "slow_seconds": 8.0}
|
||||||
|
✘ 접수 API 500 · FAQ 질문은 그대로 답 · 0.6s · route = 담당자 연결 (expected 자동 답변); category = null (expected faq); answer has none of ['인증서']
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 없는 접수 번호는 그대로 (404는 고장이 아님) · 1.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 564,
|
||||||
|
"answer_ms": 978,
|
||||||
|
"review_ms": 10,
|
||||||
|
"total_ms": 1558
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 2/4
|
||||||
|
report: runs/lab03-ticket-20261024-175136.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-ticket.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소 접수 현황",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.4,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 102,
|
||||||
|
"answer_ms": 1458,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 1594
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 API 500 · 접수 질문은 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "접수 현황 시스템이 응답하지 않아 담당자에게 연결합니다. 잠시 뒤 다시 물어봐 주세요.",
|
||||||
|
"reason": "tickets_down",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 API 500 · FAQ 질문은 그대로 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 38,
|
||||||
|
"answer_ms": 1023,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1083
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "없는 접수 번호는 그대로 (404는 고장이 아님)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 44,
|
||||||
|
"answer_ms": 1192,
|
||||||
|
"review_ms": 22,
|
||||||
|
"total_ms": 1270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 접수 현황 · 2.4s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 티켓은 현재 \"처리 중\" 상태이며, 인증서 발급 승인 대기 중입니다. 10월 20일에 완료될 예정입니다. 추가 문의가 필요하면 연락처 010-2718-2818로 연락해 주세요.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 102,
|
||||||
|
"answer_ms": 1458,
|
||||||
|
"review_ms": 13,
|
||||||
|
"total_ms": 1594
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "error", "slow_seconds": 8.0}
|
||||||
|
✔ 접수 API 500 · 접수 질문은 담당자 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "접수 현황 시스템이 응답하지 않아 담당자에게 연결합니다. 잠시 뒤 다시 물어봐 주세요.",
|
||||||
|
"reason": "tickets_down",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "error", "slow_seconds": 8.0}
|
||||||
|
✔ 접수 API 500 · FAQ 질문은 그대로 답 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 38,
|
||||||
|
"answer_ms": 1023,
|
||||||
|
"review_ms": 12,
|
||||||
|
"total_ms": 1083
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 없는 접수 번호는 그대로 (404는 고장이 아님) · 1.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-9999 접수 건은 어떻게 됐나요?",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-9999 접수 건은 찾지 못했습니다. 담당 직원이 도와드릴 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 44,
|
||||||
|
"answer_ms": 1192,
|
||||||
|
"review_ms": 22,
|
||||||
|
"total_ms": 1270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-ticket-20261024-175154.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 61,
|
||||||
|
"answer_ms": 1037,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 느릴 때 (8초)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 오류일 때 (500)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "캐시에 있으면 FAQ가 멈춰도 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 61,
|
||||||
|
"answer_ms": 1037,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 느릴 때 (8초) · 4.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 오류일 때 (500) · 0.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 1.3s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 캐시에 있으면 FAQ가 멈춰도 답 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-20261024-175202.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-llm-down.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "VPN · FAQ 원문으로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다. (자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다.)",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "접수 현황 · 상태 그대로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.5,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 접수 건은 현재 '처리 중' 상태입니다. 인증서 발급 승인 대기 중이며 10월 20일 완료 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "모르는 질문 · 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "other",
|
||||||
|
"answer": "지금은 자동 답변을 할 수 없어 담당자에게 연결합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "빈 질문",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.0,
|
||||||
|
"status": 400,
|
||||||
|
"request": {
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
✔ OpenAI credential: broken key (drill). Restore with: uv run tools/flow.py llm up
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ VPN · FAQ 원문으로 · 0.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다. (자동 답변 기능을 잠시 쓸 수 없어 FAQ 원문으로 안내합니다.)",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 접수 현황 · 상태 그대로 · 0.5s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "T-1002 처리 상태 알려 주세요",
|
||||||
|
"user": "박서연"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "간단 답변",
|
||||||
|
"category": "ticket",
|
||||||
|
"answer": "T-1002 접수 건은 현재 '처리 중' 상태입니다. 인증서 발급 승인 대기 중이며 10월 20일 완료 예정입니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 모르는 질문 · 담당자 · 0.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "오늘 점심 메뉴 추천해 주세요",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"category": "other",
|
||||||
|
"answer": "지금은 자동 답변을 할 수 없어 담당자에게 연결합니다.",
|
||||||
|
"sources": [],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"reason": "llm_down"
|
||||||
|
}
|
||||||
|
✔ 빈 질문 · 0.0s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "",
|
||||||
|
"user": "이준호"
|
||||||
|
}
|
||||||
|
← response HTTP 400
|
||||||
|
{
|
||||||
|
"error": "question is required"
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-llm-down-20261024-175206.json
|
||||||
|
✔ OpenAI credential: real key from .env restored
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-challenge.json",
|
||||||
|
"webhook": "my-health",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-health",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {},
|
||||||
|
"response": {
|
||||||
|
"status": "ok",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": true,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [],
|
||||||
|
"seconds": 1.4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 오류",
|
||||||
|
"webhook": "my-health",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {},
|
||||||
|
"response": {
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": false,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"faq"
|
||||||
|
],
|
||||||
|
"seconds": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 느림 (8초) · 점검은 빨리 끝남",
|
||||||
|
"webhook": "my-health",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 3.0,
|
||||||
|
"status": 200,
|
||||||
|
"request": {},
|
||||||
|
"response": {
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": false,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"faq"
|
||||||
|
],
|
||||||
|
"seconds": 2.9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "LLM 멈춤",
|
||||||
|
"webhook": "my-health",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.4,
|
||||||
|
"status": 200,
|
||||||
|
"request": {},
|
||||||
|
"response": {
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": true,
|
||||||
|
"llm": false
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"llm"
|
||||||
|
],
|
||||||
|
"seconds": 0.3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-health · 4 case(s) × 1
|
||||||
|
✔ 평소 · 2.2s
|
||||||
|
→ request POST /webhook/my-health
|
||||||
|
{}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": true,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [],
|
||||||
|
"seconds": 1.4
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ 오류 · 1.0s
|
||||||
|
→ request POST /webhook/my-health
|
||||||
|
{}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": false,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"faq"
|
||||||
|
],
|
||||||
|
"seconds": 1
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ 느림 (8초) · 점검은 빨리 끝남 · 3.0s
|
||||||
|
→ request POST /webhook/my-health
|
||||||
|
{}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": false,
|
||||||
|
"llm": true
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"faq"
|
||||||
|
],
|
||||||
|
"seconds": 2.9
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ OpenAI credential: broken key (drill). Restore with: uv run tools/flow.py llm up
|
||||||
|
✔ LLM 멈춤 · 0.4s
|
||||||
|
→ request POST /webhook/my-health
|
||||||
|
{}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"status": "degraded",
|
||||||
|
"checks": {
|
||||||
|
"data_api": true,
|
||||||
|
"faq": true,
|
||||||
|
"llm": false
|
||||||
|
},
|
||||||
|
"failed": [
|
||||||
|
"llm"
|
||||||
|
],
|
||||||
|
"seconds": 0.3
|
||||||
|
}
|
||||||
|
✔ OpenAI credential: real key from .env restored
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-challenge-20261024-175230.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 3,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.3,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 72,
|
||||||
|
"answer_ms": 1098,
|
||||||
|
"review_ms": 18,
|
||||||
|
"total_ms": 1212
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 느림 · 처음 한 번은 기다린 뒤 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 느림 · 다음 질문은 기다리지 않음 (차단기 열림)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": false,
|
||||||
|
"problems": [
|
||||||
|
"took 4.6s (expected at most 1s)",
|
||||||
|
"reason = data_api_down (expected breaker_open)"
|
||||||
|
],
|
||||||
|
"seconds": 4.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 돌아오고 차단기를 비우면 평소대로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 43,
|
||||||
|
"answer_ms": 1092,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1161
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 1.3s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 72,
|
||||||
|
"answer_ms": 1098,
|
||||||
|
"review_ms": 18,
|
||||||
|
"total_ms": 1212
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ 느림 · 처음 한 번은 기다린 뒤 담당자 · 4.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✘ FAQ 느림 · 다음 질문은 기다리지 않음 (차단기 열림) · 4.6s · took 4.6s (expected at most 1s); reason = data_api_down (expected breaker_open)
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
✔ FAQ가 돌아오고 차단기를 비우면 평소대로 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 43,
|
||||||
|
"answer_ms": 1092,
|
||||||
|
"review_ms": 15,
|
||||||
|
"total_ms": 1161
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 3/4
|
||||||
|
report: runs/lab03-challenge2-20261024-175242.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ after: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03-challenge2.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 2.7,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 114,
|
||||||
|
"answer_ms": 1678,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1827
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 느림 · 처음 한 번은 기다린 뒤 담당자",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.8,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ 느림 · 다음 질문은 기다리지 않음 (차단기 열림)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "breaker_open",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 돌아오고 차단기를 비우면 평소대로",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 52,
|
||||||
|
"answer_ms": 1005,
|
||||||
|
"review_ms": 18,
|
||||||
|
"total_ms": 1084
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 2.7s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 114,
|
||||||
|
"answer_ms": 1678,
|
||||||
|
"review_ms": 14,
|
||||||
|
"total_ms": 1827
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ 느림 · 처음 한 번은 기다린 뒤 담당자 · 4.8s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ FAQ 느림 · 다음 질문은 기다리지 않음 (차단기 열림) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "breaker_open",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
✔ FAQ가 돌아오고 차단기를 비우면 평소대로 · 1.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 52,
|
||||||
|
"answer_ms": 1005,
|
||||||
|
"review_ms": 18,
|
||||||
|
"total_ms": 1084
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-challenge2-20261024-175304.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ after: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
{
|
||||||
|
"cases": "lab03.json",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"passed": 4,
|
||||||
|
"total": 4,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"case": "평소",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 1.2,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 77,
|
||||||
|
"answer_ms": 1000,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1104
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 느릴 때 (8초)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 4.6,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "FAQ가 오류일 때 (500)",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "breaker_open",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "캐시에 있으면 FAQ가 멈춰도 답",
|
||||||
|
"webhook": "my-helpdesk",
|
||||||
|
"run": 1,
|
||||||
|
"passed": true,
|
||||||
|
"problems": [],
|
||||||
|
"seconds": 0.1,
|
||||||
|
"status": 200,
|
||||||
|
"request": {
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
POST http://localhost:5678/webhook/my-helpdesk · 4 case(s) × 1
|
||||||
|
✔ 평소 · 1.2s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "VPN 접속이 자꾸 끊겨요. 어떻게 하나요?",
|
||||||
|
"user": "이준호",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "VPN 접속 오류가 발생하면 VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속해 보시기 바랍니다. 추가 도움이 필요하면 담당자가 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-003"
|
||||||
|
],
|
||||||
|
"llm_calls": 1,
|
||||||
|
"cached": false,
|
||||||
|
"steps": [
|
||||||
|
"rule",
|
||||||
|
"faq",
|
||||||
|
"ticket",
|
||||||
|
"answer",
|
||||||
|
"check"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"data_ms": 77,
|
||||||
|
"answer_ms": 1000,
|
||||||
|
"review_ms": 16,
|
||||||
|
"total_ms": 1104
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 느릴 때 (8초) · 4.6s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "data_api_down"
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ FAQ가 오류일 때 (500) · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "비밀번호를 잊어버렸어요",
|
||||||
|
"user": "한지훈",
|
||||||
|
"no_cache": true
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "담당자 연결",
|
||||||
|
"answer": "사내 데이터 시스템이 응답하지 않아 담당자에게 연결합니다.",
|
||||||
|
"reason": "breaker_open",
|
||||||
|
"llm_calls": 0
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ before: POST http://localhost:8800/cache-clear · HTTP 200 · 0.0s · {"cleared": 1}
|
||||||
|
✔ before: POST /webhook/my-helpdesk · HTTP 200 · 1.3s · {"route": "자동 답변", "category": "faq", "answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리
|
||||||
|
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "error", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
✔ 캐시에 있으면 FAQ가 멈춰도 답 · 0.1s
|
||||||
|
→ request POST /webhook/my-helpdesk
|
||||||
|
{
|
||||||
|
"question": "프린터 출력이 안 돼요",
|
||||||
|
"user": "최유진"
|
||||||
|
}
|
||||||
|
← response HTTP 200
|
||||||
|
{
|
||||||
|
"route": "자동 답변",
|
||||||
|
"category": "faq",
|
||||||
|
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력해 보세요. 추가 도움이 필요하면 담당 직원이 도와드리겠습니다.",
|
||||||
|
"sources": [
|
||||||
|
"FAQ-004"
|
||||||
|
],
|
||||||
|
"llm_calls": 0,
|
||||||
|
"cached": true,
|
||||||
|
"steps": [
|
||||||
|
"cache"
|
||||||
|
],
|
||||||
|
"timings": {
|
||||||
|
"total_ms": 21
|
||||||
|
}
|
||||||
|
}
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
|
Passed 4/4
|
||||||
|
report: runs/lab03-20261024-175311.json
|
||||||
|
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "slow_seconds": 8.0}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
$ uv run tools/flow.py doctor
|
||||||
|
! n8n install folder /Users/mcchae/day65-n8n
|
||||||
|
→ day 65 SETUP-N8N 5단계 (get.n8n.io 설치). 다른 곳에 설치했다면 Last3Mon/65/n8n/.env 에 N8N_HOME=<폴더>
|
||||||
|
✔ n8n container day65-n8n-n8n-1
|
||||||
|
✔ n8n http://localhost:5678
|
||||||
|
✔ data API http://localhost:8800
|
||||||
|
✔ fault switch /admin/fault (day 72 data API)
|
||||||
|
✔ n8n → http://data-api:8800
|
||||||
|
✔ credential 'OpenAI (.env)' (openAiCourse0001)
|
||||||
|
✔ node catalog (for check)
|
||||||
|
✔ CLI coding agent: codex, claude, agy
|
||||||
|
ready
|
||||||
|
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
{
|
||||||
|
"name": "72 IT helpdesk + step timings (answer)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "Question webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "my-helpdesk",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"webhookId": "de518e93-ed19-541f-a6fc-687a4efaf1b2",
|
||||||
|
"position": [
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f7a423a6-a6a2-57a3-954f-e8540a1ce022"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Has question?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.body.question }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "notEmpty",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
220,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "6dce76b0-2dd0-5181-99be-b0b894c87c8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reject empty",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "{ \"error\": \"question is required\" }",
|
||||||
|
"options": {
|
||||||
|
"responseCode": 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
560
|
||||||
|
],
|
||||||
|
"id": "75371917-2483-583e-9135-5c663a18b08b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Start clock",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "34377f95-81c5-5aa3-9e2c-6b63a8e5ae54"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "={{ $('Question webhook').first().json.body.question }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You sort one company IT help-desk question. Categories: faq (how to fix or use something: password, account lock, VPN, printer, mail box, software install), ticket (asks about the status of an IT request ticket like T-1002), other (anything else). Return only JSON, no code fence: {\"category\": \"faq\"}",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "7a93b236-dbff-52c5-9397-61087bc0688d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "357e7611-4597-5ba3-a6ba-9a4d4d63cda7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Parse category",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const raw = String($json.output ?? '').trim().replace(/^```(json)?\\s*|```$/g, '');\nlet category = 'other';\ntry { category = JSON.parse(raw).category ?? 'other'; } catch (error) { category = 'other'; }\nif (!['faq', 'ticket', 'other'].includes(category)) category = 'other';\nreturn [{ json: { category, by: 'llm' } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
880,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "9327634c-6764-5403-b7bc-3e666e7ae3d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After classify",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1100,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8f3da4f6-ce34-5505-b543-e5d68d990a96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Search FAQ",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "http://data-api:8800/faq",
|
||||||
|
"options": {},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.question }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1320,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "103c6254-01ea-5e80-81bb-7e3b9ea7beb5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After FAQ",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1540,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d03181b0-715d-5fae-98a0-92c52a2b3b3c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Get ticket",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "=http://data-api:8800/tickets/{{ (($('Question webhook').first().json.body.question.match(/T-?\\d{4}/i) || ['NONE'])[0]).toUpperCase().replace(/^T(\\d)/, 'T-$1') }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "requester",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.user ?? '' }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1760,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f20b5c78-0224-5d55-8d77-0a9e98061a6b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After data",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1980,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "cca89aef-ef17-5b5f-a3c3-a53e0d762b43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "=질문: {{ $('Question webhook').first().json.body.question }}\n분류: {{ $('Parse category').first().json.category }}\nFAQ: {{ JSON.stringify($('Search FAQ').first().json.results ?? []) }}\n접수 건: {{ JSON.stringify($('Get ticket').first().json) }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You are the company IT help desk. Answer in Korean in at most 3 sentences, using only the FAQ entries and the ticket record given. Never invent steps, dates or names. For a ticket, say its status and note. If the data does not answer the question, say \"찾지 못했습니다\" and that a staff member will help.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2200,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d7ec1c62-265f-5d16-b67a-c5922305bb8a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2200,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "5c7f8b81-cd4c-59a2-9899-9bdb4813f7ab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After answer",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2420,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "43dab328-4901-5fd6-8238-6e0f836cc31d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Review",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "=초안: {{ $('Answer').first().json.output }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You review a Korean IT help-desk answer before it is sent. Keep the facts exactly. Make it polite (합니다체), remove anything not supported by the data, keep at most 3 sentences. Return only the final answer text.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2640,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "a4583946-f582-5ad8-963b-b866b74e9ba4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Review model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2640,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "3be9acc9-4b3d-5a41-b6a4-00fa1b04d777"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After review",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2860,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d07d9992-7e5e-5aab-b925-9387021bbaff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Build result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const ran = (name) => { try { return $(name).isExecuted; } catch (error) { return false; } };\nconst body = $('Question webhook').first().json.body;\nconst category = $('Parse category').first().json.category;\nconst answer = String($('Review').first().json.output ?? '').trim();\nconst results = $('Search FAQ').first().json.results ?? [];\nconst sources = category === 'faq' ? results.map((r) => r.id) : [];\nconst steps = [];\nif (ran('Rule classify')) steps.push('rule');\nif (ran('Classify')) steps.push('classify');\nsteps.push('faq', 'ticket', 'answer');\nsteps.push('review');\nconst llm_calls = (ran('Classify') ? 1 : 0) + 1 + 1;\nconst result = { route: '자동 답변', category, answer, sources };\nconst t = (name) => (ran(name) ? $(name).first().json.t : null);\nconst t0 = t('Start clock');\nconst marks = [['classify', t('After classify')], ['faq', t('After FAQ')], ['ticket', t('After data')], ['answer', t('After answer')], ['review', t('After review')]];\nconst timings = {};\nlet last = t0;\nfor (const [label, mark] of marks) {\n if (mark === null) continue;\n timings[`${label}_ms`] = mark - last;\n last = mark;\n}\ntimings.total_ms = Date.now() - t0;\nObject.assign(result, { llm_calls, cached: false, steps, timings });\nconst parts = Object.entries(timings).filter(([key]) => key !== 'total_ms');\nresult.slowest_step = parts.sort((a, b) => b[1] - a[1])[0][0].replace(/_ms$/, '');\nreturn [{ json: result }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3080,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "079e44f0-da3e-57dd-be6a-87025b6a320f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ $json }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3300,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8ac7b99c-d9fa-5a1e-a168-33eb7ab5e2a3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Question webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Has question?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Has question?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Start clock",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Reject empty",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Start clock": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse category",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse category": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Search FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Search FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get ticket",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get ticket": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After data",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After data": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Review",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Review model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Review",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Review": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After review",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After review": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
{
|
||||||
|
"name": "72 IT helpdesk + time budget (answer)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "Question webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "my-helpdesk",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"webhookId": "de518e93-ed19-541f-a6fc-687a4efaf1b2",
|
||||||
|
"position": [
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f7a423a6-a6a2-57a3-954f-e8540a1ce022"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Has question?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.body.question }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "notEmpty",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
220,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "6dce76b0-2dd0-5181-99be-b0b894c87c8b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reject empty",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "{ \"error\": \"question is required\" }",
|
||||||
|
"options": {
|
||||||
|
"responseCode": 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
560
|
||||||
|
],
|
||||||
|
"id": "75371917-2483-583e-9135-5c663a18b08b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Start clock",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "34377f95-81c5-5aa3-9e2c-6b63a8e5ae54"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "={{ $('Question webhook').first().json.body.question }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You sort one company IT help-desk question. Categories: faq (how to fix or use something: password, account lock, VPN, printer, mail box, software install), ticket (asks about the status of an IT request ticket like T-1002), other (anything else). Return only JSON, no code fence: {\"category\": \"faq\"}",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "7a93b236-dbff-52c5-9397-61087bc0688d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "357e7611-4597-5ba3-a6ba-9a4d4d63cda7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Parse category",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const raw = String($json.output ?? '').trim().replace(/^```(json)?\\s*|```$/g, '');\nlet category = 'other';\ntry { category = JSON.parse(raw).category ?? 'other'; } catch (error) { category = 'other'; }\nif (!['faq', 'ticket', 'other'].includes(category)) category = 'other';\nreturn [{ json: { category, by: 'llm' } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
880,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "9327634c-6764-5403-b7bc-3e666e7ae3d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After classify",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1100,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8f3da4f6-ce34-5505-b543-e5d68d990a96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Search FAQ",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "http://data-api:8800/faq",
|
||||||
|
"options": {},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.question }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1320,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "103c6254-01ea-5e80-81bb-7e3b9ea7beb5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After FAQ",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1540,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d03181b0-715d-5fae-98a0-92c52a2b3b3c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Get ticket",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "=http://data-api:8800/tickets/{{ (($('Question webhook').first().json.body.question.match(/T-?\\d{4}/i) || ['NONE'])[0]).toUpperCase().replace(/^T(\\d)/, 'T-$1') }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "requester",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.user ?? '' }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1760,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f20b5c78-0224-5d55-8d77-0a9e98061a6b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After data",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1980,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "cca89aef-ef17-5b5f-a3c3-a53e0d762b43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "=질문: {{ $('Question webhook').first().json.body.question }}\n분류: {{ $('Parse category').first().json.category }}\nFAQ: {{ JSON.stringify($('Search FAQ').first().json.results ?? []) }}\n접수 건: {{ JSON.stringify($('Get ticket').first().json) }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You are the company IT help desk. Answer in Korean in at most 3 sentences, using only the FAQ entries and the ticket record given. Never invent steps, dates or names. For a ticket, say its status and note. If the data does not answer the question, say \"찾지 못했습니다\" and that a staff member will help.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2200,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d7ec1c62-265f-5d16-b67a-c5922305bb8a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2200,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "5c7f8b81-cd4c-59a2-9899-9bdb4813f7ab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After answer",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2420,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "43dab328-4901-5fd6-8238-6e0f836cc31d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Review",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "=초안: {{ $('Answer').first().json.output }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You review a Korean IT help-desk answer before it is sent. Keep the facts exactly. Make it polite (합니다체), remove anything not supported by the data, keep at most 3 sentences. Return only the final answer text.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2640,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "a4583946-f582-5ad8-963b-b866b74e9ba4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Review model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2640,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "3be9acc9-4b3d-5a41-b6a4-00fa1b04d777"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After review",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2860,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "d07d9992-7e5e-5aab-b925-9387021bbaff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Build result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const ran = (name) => { try { return $(name).isExecuted; } catch (error) { return false; } };\nconst body = $('Question webhook').first().json.body;\nconst category = $('Parse category').first().json.category;\nconst answer = String($('Review').first().json.output ?? '').trim();\nconst results = $('Search FAQ').first().json.results ?? [];\nconst sources = category === 'faq' ? results.map((r) => r.id) : [];\nconst steps = [];\nif (ran('Rule classify')) steps.push('rule');\nif (ran('Classify')) steps.push('classify');\nsteps.push('faq', 'ticket', 'answer');\nsteps.push('review');\nconst llm_calls = (ran('Classify') ? 1 : 0) + 1 + 1;\nconst result = { route: '자동 답변', category, answer, sources };\nconst t = (name) => (ran(name) ? $(name).first().json.t : null);\nconst t0 = t('Start clock');\nconst marks = [['classify', t('After classify')], ['faq', t('After FAQ')], ['ticket', t('After data')], ['answer', t('After answer')], ['review', t('After review')]];\nconst timings = {};\nlet last = t0;\nfor (const [label, mark] of marks) {\n if (mark === null) continue;\n timings[`${label}_ms`] = mark - last;\n last = mark;\n}\ntimings.total_ms = Date.now() - t0;\nObject.assign(result, { llm_calls, cached: false, steps, timings });\nconst parts = Object.entries(timings).filter(([key]) => key !== 'total_ms');\nresult.slowest_step = parts.sort((a, b) => b[1] - a[1])[0][0].replace(/_ms$/, '');\nconst budget_ms = Number(body.budget_ms ?? 3000);\nresult.llm_ms = ['classify', 'answer', 'review'].reduce((sum, label) => sum + (timings[`${label}_ms`] ?? 0), 0);\nresult.budget_ms = budget_ms;\nresult.over_budget = timings.total_ms > budget_ms;\nreturn [{ json: result }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3080,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "079e44f0-da3e-57dd-be6a-87025b6a320f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ $json }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3300,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8ac7b99c-d9fa-5a1e-a168-33eb7ab5e2a3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Question webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Has question?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Has question?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Start clock",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Reject empty",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Start clock": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse category",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse category": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Search FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Search FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get ticket",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get ticket": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After data",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After data": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Review",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Review model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Review",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Review": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After review",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After review": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,852 @@
|
|||||||
|
{
|
||||||
|
"name": "72 IT helpdesk + one LLM call (answer)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "Question webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "my-helpdesk",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"webhookId": "de518e93-ed19-541f-a6fc-687a4efaf1b2",
|
||||||
|
"position": [
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "4a893fec-ca3a-5349-a7b1-a8de0ac4350c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Has question?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.body.question }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "notEmpty",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
220,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "da287ac6-5b2c-5d11-a358-2c0d575d61ce"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reject empty",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "{ \"error\": \"question is required\" }",
|
||||||
|
"options": {
|
||||||
|
"responseCode": 400
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
560
|
||||||
|
],
|
||||||
|
"id": "049a20b6-3c29-5159-a0e6-f5617cf9d8d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Start clock",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "3b52fb72-af9e-54d9-8928-a1f87966b69f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cache key",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const body = $('Question webhook').first().json.body;\nconst key = String(body.question ?? '').trim().toLowerCase().replace(/\\s+/g, ' ');\nreturn [{ json: { ...$json, key, use_cache: body.no_cache !== true } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "1aed5faa-1a89-578c-8093-61fe2e9a2555"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cache lookup",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "=http://data-api:8800/cache/{{ encodeURIComponent($json.key) }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
880,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "33b97251-cad7-50a2-805b-ecc7d70001de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cached?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.value !== undefined && $('Cache key').first().json.use_cache }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "true",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1100,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "7ad20324-27b3-5f30-9637-a4fa4d977a53"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cached result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const value = $json.value;\nconst t0 = $('Start clock').first().json.t;\nreturn [{ json: { route: value.route, category: value.category, answer: value.answer, sources: value.sources ?? [],\n llm_calls: 0, cached: true, steps: ['cache'], timings: { total_ms: t0 ? Date.now() - t0 : 0 } } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1320,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"id": "b102773a-d6b3-5fd8-9ada-e250f648cc44"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send cached",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ $json }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1540,
|
||||||
|
40
|
||||||
|
],
|
||||||
|
"id": "ff6ce5c6-903d-53f6-96f7-a17baabcc1b6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Search FAQ",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "http://data-api:8800/faq",
|
||||||
|
"options": {},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.question }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1320,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "0e3e780d-eaa1-5c13-a60a-f3112a296820"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Get ticket",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "=http://data-api:8800/tickets/{{ (($('Question webhook').first().json.body.question.match(/T-?\\d{4}/i) || ['NONE'])[0]).toUpperCase().replace(/^T(\\d)/, 'T-$1') }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "requester",
|
||||||
|
"value": "={{ $('Question webhook').first().json.body.user ?? '' }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1540,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f345aacb-db55-5226-bf79-822fd2f814ca"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After data",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1760,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "4c8c6887-4a50-589b-abdf-c8741f1cf589"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Rule classify",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const question = String($('Question webhook').first().json.body.question ?? '');\nconst results = $('Search FAQ').first().json.results ?? [];\nlet category = 'unknown';\nif (/T-?\\d{4}/i.test(question)) category = 'ticket';\nelse if (results.length > 0) category = 'faq';\nreturn [{ json: { category, by: 'rule' } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1980,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "b01cd9fc-72fc-5ace-860b-6d607bf1e570"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Known?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.category }}",
|
||||||
|
"rightValue": "unknown",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "notEquals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2200,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "2d154b76-eb12-5038-a639-c5608c6bf788"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "={{ $('Question webhook').first().json.body.question }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You sort one company IT help-desk question. Categories: faq (how to fix or use something: password, account lock, VPN, printer, mail box, software install), ticket (asks about the status of an IT request ticket like T-1002), other (anything else). Return only JSON, no code fence: {\"category\": \"faq\"}",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2420,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8ba05958-c916-52e4-95ef-0f38f92246c3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Classify model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2420,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "14555336-1d5b-5860-98cc-cc7febd5fdab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Parse category",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const raw = String($json.output ?? '').trim().replace(/^```(json)?\\s*|```$/g, '');\nlet category = 'other';\ntry { category = JSON.parse(raw).category ?? 'other'; } catch (error) { category = 'other'; }\nif (!['faq', 'ticket', 'other'].includes(category)) category = 'other';\nreturn [{ json: { category, by: 'llm' } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2640,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "53522d0c-94f5-5e97-84ca-dfc99cbea797"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After classify",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
2860,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "ed776d70-2fb9-5b97-86c8-aa43796e8cb3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "=질문: {{ $('Question webhook').first().json.body.question }}\n분류: {{ $json.category }}\nFAQ: {{ JSON.stringify($('Search FAQ').first().json.results ?? []) }}\n접수 건: {{ JSON.stringify($('Get ticket').first().json) }}",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You are the company IT help desk. Answer in Korean in at most 3 sentences, using only the FAQ entries and the ticket record given. Never invent steps, dates or names. For a ticket, say its status and note. If the data does not answer the question, say \"찾지 못했습니다\" and that a staff member will help.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3080,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "3cd227a5-1bb5-50e6-9402-69206c7ca546"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Answer model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3080,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "f3dad63e-7b9c-5d29-b79e-077754d581b7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After answer",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3300,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "0432c08a-5b0d-5d75-8e06-eab20db15ab1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Review check",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "let answer = String($('Answer').first().json.output ?? '').trim();\nconst problems = [];\nif (!answer) problems.push('empty');\nif (!/[가-힣]/.test(answer)) problems.push('not_korean');\nif (answer.length > 400) { answer = answer.slice(0, 400); problems.push('too_long'); }\nreturn [{ json: { ...$json, answer, review_problems: problems } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3520,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "c2d5b0e4-e7e1-5972-8b49-d2aa51694714"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "After review",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { ...$json, t: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3740,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f031c87a-b44b-55fa-a122-05d93f51fa6e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Build result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const ran = (name) => { try { return $(name).isExecuted; } catch (error) { return false; } };\nconst body = $('Question webhook').first().json.body;\nconst category = $('Parse category').isExecuted ? $('Parse category').first().json.category : $('Rule classify').first().json.category;\nconst answer = $('Review check').first().json.answer;\nconst results = $('Search FAQ').first().json.results ?? [];\nconst sources = category === 'faq' ? results.map((r) => r.id) : [];\nconst steps = [];\nif (ran('Rule classify')) steps.push('rule');\nif (ran('Classify')) steps.push('classify');\nsteps.push('faq', 'ticket', 'answer');\nsteps.push('check');\nconst llm_calls = (ran('Classify') ? 1 : 0) + 1 + 0;\nconst result = { route: '자동 답변', category, answer, sources };\nconst t = (name) => (ran(name) ? $(name).first().json.t : null);\nconst t0 = t('Start clock');\nconst marks = [['data', t('After data')], ['classify', t('After classify')], ['answer', t('After answer')], ['review', t('After review')]];\nconst timings = {};\nlet last = t0;\nfor (const [label, mark] of marks) {\n if (mark === null) continue;\n timings[`${label}_ms`] = mark - last;\n last = mark;\n}\ntimings.total_ms = Date.now() - t0;\nObject.assign(result, { llm_calls, cached: false, steps, timings });\nreturn [{ json: result }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
3960,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "fc92aa99-281c-5e7a-96fb-6868624c4f34"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cache it?",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 2.2,
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"leftValue": "",
|
||||||
|
"typeValidation": "loose",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "c1",
|
||||||
|
"leftValue": "={{ $json.category === 'faq' && $('Cache key').first().json.use_cache }}",
|
||||||
|
"rightValue": "",
|
||||||
|
"operator": {
|
||||||
|
"type": "boolean",
|
||||||
|
"operation": "true",
|
||||||
|
"singleValue": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
4180,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "f14260a7-8d0f-5916-a219-c233bc484860"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Store in cache",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "=http://data-api:8800/cache/{{ encodeURIComponent($('Cache key').first().json.key) }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"method": "POST",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ value: { route: $json.route, category: $json.category, answer: $json.answer, sources: $json.sources }, ttl: 600 }) }}"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
4400,
|
||||||
|
180
|
||||||
|
],
|
||||||
|
"id": "8c73289d-f4b4-50c2-a71e-229d368e7b39"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Result again",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: $('Build result').first().json }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
4620,
|
||||||
|
180
|
||||||
|
],
|
||||||
|
"id": "4033230f-4e3a-515a-a980-ed987bc3372e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ $json }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
4840,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "43408f1e-819c-54ef-bb7b-7039f738e6f1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Question webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Has question?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Has question?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Start clock",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Reject empty",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Start clock": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Cache key",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Cache key": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Cache lookup",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Cache lookup": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Cached?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Cached?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Cached result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Search FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Cached result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send cached",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Search FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get ticket",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get ticket": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After data",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After data": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Rule classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Rule classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Known?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Known?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Classify",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse category",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse category": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After classify",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After classify": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After answer",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After answer": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Review check",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Review check": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "After review",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"After review": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Cache it?",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Cache it?": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Store in cache",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Store in cache": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Result again",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Result again": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,238 @@
|
|||||||
|
{
|
||||||
|
"name": "72 Health check (answer)",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "Health webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "my-health",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"webhookId": "bdf5dd1e-edb4-503e-b800-c8653ff5fbb4",
|
||||||
|
"position": [
|
||||||
|
0,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "cd5b6b9d-06a3-57ed-91ab-0ab44b344fde"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Start",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "return [{ json: { t0: Date.now() } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
220,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "0445c332-e249-5cce-915d-fca703b570b8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Check data API",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "http://data-api:8800/health",
|
||||||
|
"options": {
|
||||||
|
"timeout": 2000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"onError": "continueRegularOutput",
|
||||||
|
"alwaysOutputData": true,
|
||||||
|
"position": [
|
||||||
|
440,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "8ce80344-ec8e-5bd5-9a2e-839d49d9f1e9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Check FAQ",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"parameters": {
|
||||||
|
"url": "http://data-api:8800/faq",
|
||||||
|
"options": {
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
"sendQuery": true,
|
||||||
|
"queryParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "q",
|
||||||
|
"value": "vpn"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"onError": "continueRegularOutput",
|
||||||
|
"alwaysOutputData": true,
|
||||||
|
"position": [
|
||||||
|
660,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "a935db0a-fc45-5a01-aa52-405740f82e9f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Check LLM",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||||
|
"typeVersion": 3.1,
|
||||||
|
"parameters": {
|
||||||
|
"promptType": "define",
|
||||||
|
"text": "Reply with the single word OK.",
|
||||||
|
"options": {
|
||||||
|
"systemMessage": "You are a health check. Reply with OK only.",
|
||||||
|
"maxIterations": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"onError": "continueRegularOutput",
|
||||||
|
"alwaysOutputData": true,
|
||||||
|
"position": [
|
||||||
|
880,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "96a58520-65ae-548a-ac2f-0b41d004933f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Health model",
|
||||||
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||||
|
"typeVersion": 1.2,
|
||||||
|
"parameters": {
|
||||||
|
"model": {
|
||||||
|
"__rl": true,
|
||||||
|
"mode": "id",
|
||||||
|
"value": "gpt-4.1-mini"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"temperature": 0,
|
||||||
|
"timeout": 8000,
|
||||||
|
"maxRetries": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openAiCourse0001",
|
||||||
|
"name": "OpenAI (.env)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
880,
|
||||||
|
520
|
||||||
|
],
|
||||||
|
"id": "5aa10812-b13e-5bae-831e-7ccd24e72e62"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Summarize",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "const data_api = $('Check data API').first().json.status === 'ok';\nconst faq = Array.isArray($('Check FAQ').first().json.results);\nconst llm = /ok/i.test(String($('Check LLM').first().json.output ?? ''));\nconst checks = { data_api, faq, llm };\nconst failed = Object.entries(checks).filter(([, good]) => !good).map(([name]) => name);\nconst status = failed.length === 0 ? 'ok' : (data_api || llm ? 'degraded' : 'down');\nreturn [{ json: { status, checks, failed, seconds: Math.round((Date.now() - $('Start').first().json.t0) / 100) / 10 } }];"
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1100,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "54ee87ba-881d-5ad1-a032-1262876fdd48"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Send status",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ $json }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"position": [
|
||||||
|
1320,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"id": "2340a4f9-d3c9-5717-ad49-b782bcd9f808"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Health model": {
|
||||||
|
"ai_languageModel": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check LLM",
|
||||||
|
"type": "ai_languageModel",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Health webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Start",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Start": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check data API",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Check data API": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check FAQ",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Check FAQ": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check LLM",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Check LLM": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Summarize",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Summarize": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send status",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"executionOrder": "v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
|||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# tbench IMP-PLAN (강사가 직접 만든 기록)
|
||||||
|
|
||||||
|
## 엔진 1단계 · 부르고 재기 (R1 · R6 · R7 · R8)
|
||||||
|
- [x] runner.py: call_once → {status, seconds, error_kind, timings}, run() 제너레이터
|
||||||
|
- [x] stats.py: nearest-rank (1~10 → p50 5 · p95 10), 실패는 세고 시간 목록에서 뺌, p50 excl. first
|
||||||
|
- [x] tests/conftest.py 가짜 서버, --plain · --warmup · --header (가리기)
|
||||||
|
## TUI 1단계 (R2)
|
||||||
|
- [x] app.py: Sparkline · 요약 · DataTable, worker(thread) + call_from_thread. 테스트: 지연 0.5초 × 6, 1.3초 뒤 1~5줄 → 끝에 6줄
|
||||||
|
## 설치 (R14)
|
||||||
|
- [x] [project.scripts] tbench = "tbench.cli:main", uv tool install --editable ., /tmp 에서 tbench --help
|
||||||
|
## 엔진 2단계 (R3 · R13 · R11)
|
||||||
|
- [x] --save (기본 ~/.tbench/results/), compare (url · method · body_sha256 · n · concurrency · warmup 다르면 WARNING), history (최신 위), --log
|
||||||
|
## 엔진 3단계 (R4 · R10 · R9 · R12)
|
||||||
|
- [x] --concurrency (0.3초 × 6: k=1 1.8초대, k=3 1초 안), --max-p95 · --max-failures 종료 코드 0 · 1, timings 평균, profile save/list · run
|
||||||
|
## TUI 2단계 (R5)
|
||||||
|
- [x] --pick DIR: OptionList, Enter 로 그 본문 실행
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# tbench · 강사가 수업 준비 때 직접 만든 본 (2026-10-26)
|
||||||
|
|
||||||
|
> **정답 자료** · 학생 저장소에는 수업 뒤 [VIBE-A-TBENCH.md](../VIBE-A-TBENCH.md)와 함께 올립니다.
|
||||||
|
> 이 본은 강사가 CLI 에이전트에 단계 요청문을 보내 만든 것이 아니라, Claude Code가 VIBE-A의 SPEC · IMP-PLAN을 보고 직접 썼습니다. 학생이 만든 것과 파일 구성이 달라도 됩니다. 요구 R1~R14의 **확인 방법**이 같으면 됩니다.
|
||||||
|
|
||||||
|
## 설치 · 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/aio-01d4-stu/Last3Mon/72/tbench
|
||||||
|
uv sync && uv run pytest -q # 가짜 서버 테스트 12개 (진짜 헬프데스크를 부르지 않음)
|
||||||
|
uv tool install --editable . # 어느 폴더에서나 tbench (R14)
|
||||||
|
cd /tmp && tbench --help
|
||||||
|
```
|
||||||
|
|
||||||
|
| 하고 싶은 것 | 명령 |
|
||||||
|
| --- | --- |
|
||||||
|
| 화면(TUI)으로 재기 | `tbench http://localhost:5678/webhook/my-helpdesk --body-file requests/faq-vpn-nocache.json -n 20` |
|
||||||
|
| 글자로 · 저장 · 기준 | `tbench <url> --body-file x.json -n 10 --plain --save --max-p95 2.5 --max-failures 0` |
|
||||||
|
| 요청 고르기 | `tbench <url> --pick requests` |
|
||||||
|
| 동시에 · 미리 부르기 | `--concurrency 3` · `--warmup 1` |
|
||||||
|
| 키 머리글 | `--header 'Authorization: Bearer $MY_KEY'` (저장 · 기록에는 `***`) |
|
||||||
|
| 비교 · 목록 | `tbench compare a.json b.json` · `tbench history` |
|
||||||
|
| 설정 저장 · 부르기 | `tbench profile save vpn <url> --body-file requests/faq-vpn-nocache.json -n 10` · `tbench profile list` · `tbench run vpn --plain` |
|
||||||
|
| 한 번마다 한 줄 | `--log runs.jsonl` |
|
||||||
|
|
||||||
|
저장 위치는 `~/.tbench/` (`results/`, `profiles.toml`). 테스트는 `TBENCH_HOME`을 임시 폴더로 바꿔 씁니다.
|
||||||
|
|
||||||
|
## 파일
|
||||||
|
|
||||||
|
| 파일 | 하는 일 |
|
||||||
|
| --- | --- |
|
||||||
|
| `src/tbench/runner.py` | 엔진: 한 번 부르기(시간 · 상태 · 실패 종류 · `timings`), `run()` 제너레이터 (warmup · 동시) |
|
||||||
|
| `src/tbench/stats.py` | nearest-rank 백분위, 요약, 기준 검사(PASS · FAIL) |
|
||||||
|
| `src/tbench/store.py` | 저장(조건 · 본문 해시 · 키 가리기), compare, history, 기록 줄 |
|
||||||
|
| `src/tbench/profiles.py` | `~/.tbench/profiles.toml` |
|
||||||
|
| `src/tbench/cli.py` | 명령줄, `--plain` 출력, 종료 코드 |
|
||||||
|
| `src/tbench/app.py` | Textual 화면 (worker + `call_from_thread`), `--pick` 고르기 |
|
||||||
|
| `tests/conftest.py` | pytest 안의 가짜 서버 (지연 · N번째마다 500 · 첫 응답만 느림 · 상태 코드 · `timings`) |
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[project]
|
||||||
|
name = "tbench"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Call a webhook many times and show how long each call took"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = ["httpx>=0.27", "textual>=0.80"]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
tbench = "tbench.cli:main"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/tbench"]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = ["pytest>=8", "pytest-asyncio>=0.23"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_mode = "auto"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""tbench: call a webhook many times and show how long each call took."""
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
"""Textual screen: sparkline and summary on top, one table row per call as it arrives."""
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from textual import work
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import DataTable, Footer, Header, OptionList, Sparkline, Static
|
||||||
|
from textual.widgets.option_list import Option
|
||||||
|
|
||||||
|
from .runner import read_body, run
|
||||||
|
from .stats import fmt, summarize, summary_lines
|
||||||
|
|
||||||
|
|
||||||
|
class BenchApp(App):
|
||||||
|
TITLE = "tbench"
|
||||||
|
BINDINGS = [("q", "quit", "Quit")]
|
||||||
|
CSS = """
|
||||||
|
Sparkline { height: 4; margin: 0 1; }
|
||||||
|
#summary { height: auto; margin: 0 1; }
|
||||||
|
OptionList { height: 1fr; }
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, settings, headers, pick=None):
|
||||||
|
super().__init__()
|
||||||
|
self.settings, self.headers, self.pick = settings, headers, pick
|
||||||
|
self.results, self.body = [], None
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield Header()
|
||||||
|
if self.pick:
|
||||||
|
files = sorted(Path(self.pick).glob("*.json"))
|
||||||
|
yield OptionList(*[Option(f.name, id=str(f)) for f in files], id="pick")
|
||||||
|
yield Sparkline([], id="spark")
|
||||||
|
yield Static("starting...", id="summary")
|
||||||
|
yield DataTable(id="calls")
|
||||||
|
yield Footer()
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
table = self.query_one("#calls", DataTable)
|
||||||
|
table.add_columns("#", "status", "seconds", "note")
|
||||||
|
if self.pick:
|
||||||
|
self.sub_title = f"choose a body from {self.pick}"
|
||||||
|
self.query_one("#summary", Static).update("Enter: run with this body")
|
||||||
|
else:
|
||||||
|
self.start(self.settings.get("body_file"))
|
||||||
|
|
||||||
|
def on_option_list_option_selected(self, event):
|
||||||
|
event.option_list.remove()
|
||||||
|
self.settings["body_file"] = event.option.id
|
||||||
|
self.start(event.option.id)
|
||||||
|
|
||||||
|
def start(self, body_file):
|
||||||
|
s = self.settings
|
||||||
|
self.body = read_body(body_file)
|
||||||
|
self.sub_title = f"{s['method']} {s['url']} body={Path(body_file).name if body_file else '-'}"
|
||||||
|
self.measure()
|
||||||
|
|
||||||
|
@work(thread=True, exclusive=True)
|
||||||
|
def measure(self):
|
||||||
|
s = self.settings
|
||||||
|
for r in run(s["url"], self.body, s["n"], s["method"], self.headers, s["timeout"], s["warmup"], s["concurrency"]):
|
||||||
|
self.call_from_thread(self.add_result, r)
|
||||||
|
self.call_from_thread(self.finish)
|
||||||
|
|
||||||
|
def add_result(self, r):
|
||||||
|
self.results.append(r)
|
||||||
|
label = f"w{r['i']}" if r["warmup"] else str(r["i"])
|
||||||
|
status = str(r["status"]) if r["status"] is not None else r["error_kind"]
|
||||||
|
note = " ".join(x for x in (r["note"], r["error_kind"] if r["status"] else "") if x)
|
||||||
|
self.query_one("#calls", DataTable).add_row(label, status, fmt(r["seconds"]), note)
|
||||||
|
measured = [x for x in self.results if not x["warmup"]]
|
||||||
|
self.query_one("#spark", Sparkline).data = [x["seconds"] for x in measured if x["error_kind"] is None]
|
||||||
|
self.query_one("#summary", Static).update("\n".join(summary_lines(summarize(measured, self.settings["n"]))))
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
self.sub_title += " (done, q to quit)"
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
"""Command line: `tbench <url> ...` (TUI, or --plain), compare, history, profile, run."""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from . import profiles, store
|
||||||
|
from .runner import read_body, run
|
||||||
|
from .stats import check, fmt, summarize, summary_lines
|
||||||
|
|
||||||
|
COMMANDS = ("compare", "history", "profile", "run")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_headers(items):
|
||||||
|
headers = {}
|
||||||
|
for item in items or []:
|
||||||
|
if ":" not in item:
|
||||||
|
raise SystemExit(f"bad --header {item!r}, use 'Name: value'")
|
||||||
|
k, v = item.split(":", 1)
|
||||||
|
headers[k.strip()] = os.path.expandvars(v.strip())
|
||||||
|
return headers
|
||||||
|
|
||||||
|
|
||||||
|
def add_run_options(p, url=True):
|
||||||
|
if url:
|
||||||
|
p.add_argument("url")
|
||||||
|
p.add_argument("--body-file", help="JSON file to send as the body")
|
||||||
|
p.add_argument("-n", type=int, default=None, help="measured calls (default 10)")
|
||||||
|
p.add_argument("--method", default=None, help="HTTP method (default POST)")
|
||||||
|
p.add_argument("--timeout", type=float, default=None, help="seconds before a call counts as timeout (default 30)")
|
||||||
|
p.add_argument("--header", action="append", default=[], help="'Name: value', may repeat; $VARS are expanded")
|
||||||
|
p.add_argument("--warmup", type=int, default=None, help="calls to make and throw away first")
|
||||||
|
p.add_argument("--concurrency", type=int, default=None, help="calls in flight at once (default 1)")
|
||||||
|
p.add_argument("--plain", action="store_true", help="print lines instead of the TUI")
|
||||||
|
p.add_argument("--save", nargs="?", const="", default=None, help="save result JSON (default ~/.tbench/results/)")
|
||||||
|
p.add_argument("--log", help="append one JSON line per call to this file")
|
||||||
|
p.add_argument("--max-p95", type=float, default=None, help="FAIL (exit 1) if p95 seconds is above this")
|
||||||
|
p.add_argument("--max-failures", type=int, default=None, help="FAIL (exit 1) if failures are above this")
|
||||||
|
p.add_argument("--pick", metavar="DIR", help="choose the body from *.json in DIR (TUI)")
|
||||||
|
|
||||||
|
|
||||||
|
def settings_from(args, base=None):
|
||||||
|
s = dict(base or {})
|
||||||
|
for key in ("url", "body_file", "n", "method", "timeout", "warmup", "concurrency", "max_p95", "max_failures"):
|
||||||
|
value = getattr(args, key, None)
|
||||||
|
if value is not None:
|
||||||
|
s[key] = value
|
||||||
|
if getattr(args, "header", None):
|
||||||
|
s["headers"] = args.header
|
||||||
|
s.setdefault("n", 10)
|
||||||
|
s.setdefault("method", "POST")
|
||||||
|
s.setdefault("timeout", 30.0)
|
||||||
|
s.setdefault("warmup", 0)
|
||||||
|
s.setdefault("concurrency", 1)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def execute(s, plain, save_to, log_path, pick=None):
|
||||||
|
headers = parse_headers(s.get("headers"))
|
||||||
|
started = datetime.now().astimezone().isoformat(timespec="seconds")
|
||||||
|
if not plain:
|
||||||
|
from .app import BenchApp
|
||||||
|
app = BenchApp(s, headers, pick=pick)
|
||||||
|
app.run()
|
||||||
|
results, body = app.results, app.body
|
||||||
|
if not results:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
body = read_body(s.get("body_file"))
|
||||||
|
results = []
|
||||||
|
log = open(log_path, "a", encoding="utf-8") if log_path else None
|
||||||
|
print(f"{s['method']} {s['url']} n={s['n']} concurrency={s['concurrency']} warmup={s['warmup']}")
|
||||||
|
try:
|
||||||
|
for r in run(s["url"], body, s["n"], s["method"], headers, s["timeout"], s["warmup"], s["concurrency"]):
|
||||||
|
results.append(r)
|
||||||
|
if log:
|
||||||
|
store.log_line(log, r)
|
||||||
|
label = f"w{r['i']}" if r["warmup"] else f"#{r['i']}"
|
||||||
|
status = r["status"] if r["status"] is not None else r["error_kind"]
|
||||||
|
note = " ".join(x for x in (r["note"], r["error_kind"] if r["status"] else "") if x)
|
||||||
|
print(f"{label:>4} {status!s:>8} {fmt(r['seconds']):>8}s {note}".rstrip())
|
||||||
|
finally:
|
||||||
|
if log:
|
||||||
|
log.close()
|
||||||
|
if log_path and not plain:
|
||||||
|
with open(log_path, "a", encoding="utf-8") as log:
|
||||||
|
for r in results:
|
||||||
|
store.log_line(log, r)
|
||||||
|
measured = [r for r in results if not r["warmup"]]
|
||||||
|
summary = summarize(measured, s["n"])
|
||||||
|
for line in summary_lines(summary):
|
||||||
|
print(line)
|
||||||
|
if save_to is not None:
|
||||||
|
record = store.build_record(s["url"], s["method"], body, headers, s["n"], s["concurrency"],
|
||||||
|
s["warmup"], started, results)
|
||||||
|
print(f"saved {store.save(record, save_to or None)}")
|
||||||
|
if s.get("max_p95") is not None or s.get("max_failures") is not None:
|
||||||
|
passed, line = check(summary, s.get("max_p95"), s.get("max_failures"))
|
||||||
|
print(line)
|
||||||
|
return 0 if passed else 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv=None):
|
||||||
|
argv = sys.argv[1:] if argv is None else argv
|
||||||
|
if argv and argv[0] in COMMANDS:
|
||||||
|
return sys.exit(sub_main(argv))
|
||||||
|
p = argparse.ArgumentParser(prog="tbench", description="Call a URL many times and show how long each call took. "
|
||||||
|
"Also: tbench compare A B | history | profile save/list | run NAME")
|
||||||
|
add_run_options(p)
|
||||||
|
args = p.parse_args(argv)
|
||||||
|
sys.exit(execute(settings_from(args), args.plain, args.save, args.log, args.pick))
|
||||||
|
|
||||||
|
|
||||||
|
def sub_main(argv):
|
||||||
|
p = argparse.ArgumentParser(prog="tbench")
|
||||||
|
sub = p.add_subparsers(dest="cmd", required=True)
|
||||||
|
c = sub.add_parser("compare", help="compare two saved results")
|
||||||
|
c.add_argument("a")
|
||||||
|
c.add_argument("b")
|
||||||
|
sub.add_parser("history", help="list saved results, newest first")
|
||||||
|
prof = sub.add_parser("profile", help="save or list named settings")
|
||||||
|
psub = prof.add_subparsers(dest="action", required=True)
|
||||||
|
ps = psub.add_parser("save")
|
||||||
|
ps.add_argument("name")
|
||||||
|
add_run_options(ps)
|
||||||
|
psub.add_parser("list")
|
||||||
|
r = sub.add_parser("run", help="run a saved profile")
|
||||||
|
r.add_argument("name")
|
||||||
|
add_run_options(r, url=False)
|
||||||
|
args = p.parse_args(argv)
|
||||||
|
|
||||||
|
if args.cmd == "compare":
|
||||||
|
for line in store.compare(store.load(args.a), store.load(args.b)):
|
||||||
|
print(line)
|
||||||
|
return 0
|
||||||
|
if args.cmd == "history":
|
||||||
|
rows = store.history()
|
||||||
|
print(f"{'started':19} {'n':>3} {'p50':>7} {'p95':>7} {'fail':>4} url")
|
||||||
|
for started, url, n, p50, p95, failures, _ in rows:
|
||||||
|
print(f"{started:19} {n:>3} {p50:>7} {p95:>7} {failures:>4} {url}")
|
||||||
|
return 0
|
||||||
|
if args.cmd == "profile" and args.action == "list":
|
||||||
|
for name, values in profiles.load_all().items():
|
||||||
|
print(f"{name:12} {values.get('url')} body={values.get('body_file', '-')} n={values.get('n', 10)}")
|
||||||
|
return 0
|
||||||
|
if args.cmd == "profile":
|
||||||
|
s = {k: v for k, v in vars(args).items() if k in profiles.FIELDS and v is not None}
|
||||||
|
if s.get("body_file"):
|
||||||
|
s["body_file"] = str(Path(s["body_file"]).resolve())
|
||||||
|
safe = []
|
||||||
|
for h in args.header:
|
||||||
|
name, _, value = h.partition(":")
|
||||||
|
if store.mask_headers({name: 1})[name] == "***" and "$" not in value:
|
||||||
|
print(f"note: {name.strip()} is kept as *** — pass it as an env var, e.g. 'Authorization: Bearer $MY_KEY'")
|
||||||
|
value = " ***"
|
||||||
|
safe.append(f"{name}:{value}")
|
||||||
|
s["headers"] = safe
|
||||||
|
print(f"saved {args.name} to {profiles.save(args.name, s)}")
|
||||||
|
return 0
|
||||||
|
base = profiles.load_all().get(args.name)
|
||||||
|
if base is None:
|
||||||
|
print(f"no profile {args.name!r} in {profiles.path()}")
|
||||||
|
return 2
|
||||||
|
return execute(settings_from(args, base), args.plain, args.save, args.log, args.pick)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""Named settings in ~/.tbench/profiles.toml, so `tbench run <name>` works from any folder."""
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
from .store import home
|
||||||
|
|
||||||
|
FIELDS = ("url", "body_file", "n", "method", "timeout", "warmup", "concurrency", "headers", "max_p95", "max_failures")
|
||||||
|
|
||||||
|
|
||||||
|
def path():
|
||||||
|
return home() / "profiles.toml"
|
||||||
|
|
||||||
|
|
||||||
|
def load_all():
|
||||||
|
p = path()
|
||||||
|
return tomllib.loads(p.read_text(encoding="utf-8")) if p.exists() else {}
|
||||||
|
|
||||||
|
|
||||||
|
def _value(v):
|
||||||
|
if isinstance(v, str):
|
||||||
|
return '"' + v.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
||||||
|
if isinstance(v, list):
|
||||||
|
return "[" + ", ".join(_value(x) for x in v) + "]"
|
||||||
|
return str(v).lower() if isinstance(v, bool) else str(v)
|
||||||
|
|
||||||
|
|
||||||
|
def save(name, settings):
|
||||||
|
profiles = load_all()
|
||||||
|
profiles[name] = {k: v for k, v in settings.items() if k in FIELDS and v not in (None, [])}
|
||||||
|
out = []
|
||||||
|
for pname, values in profiles.items():
|
||||||
|
out.append(f'["{pname}"]')
|
||||||
|
out += [f"{k} = {_value(v)}" for k, v in values.items()]
|
||||||
|
out.append("")
|
||||||
|
p = path()
|
||||||
|
p.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
p.write_text("\n".join(out), encoding="utf-8")
|
||||||
|
return p
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"""Call a URL many times. run() yields one result dict per call, as it finishes."""
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
|
||||||
|
def call_once(client, url, method, body, headers, timeout):
|
||||||
|
"""One call, timed from just before sending until the whole body is read."""
|
||||||
|
start = time.perf_counter()
|
||||||
|
status, error_kind, timings = None, None, None
|
||||||
|
try:
|
||||||
|
response = client.request(method, url, content=body, headers=headers, timeout=timeout)
|
||||||
|
response.read()
|
||||||
|
status = response.status_code
|
||||||
|
if status >= 500:
|
||||||
|
error_kind = "http_5xx"
|
||||||
|
elif status >= 400:
|
||||||
|
error_kind = "http_4xx"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = response.json()
|
||||||
|
if isinstance(data, dict) and isinstance(data.get("timings"), dict):
|
||||||
|
timings = data["timings"]
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
except httpx.TimeoutException:
|
||||||
|
error_kind = "timeout"
|
||||||
|
except httpx.TransportError:
|
||||||
|
error_kind = "connect"
|
||||||
|
return {"status": status, "seconds": round(time.perf_counter() - start, 4),
|
||||||
|
"error_kind": error_kind, "timings": timings}
|
||||||
|
|
||||||
|
|
||||||
|
def run(url, body=None, n=10, method="POST", headers=None, timeout=30.0, warmup=0, concurrency=1):
|
||||||
|
"""Yield warmup calls (warmup=True) first, then n measured calls numbered 1..n."""
|
||||||
|
headers = dict(headers or {})
|
||||||
|
if body is not None and not any(k.lower() == "content-type" for k in headers):
|
||||||
|
headers["Content-Type"] = "application/json"
|
||||||
|
content = body.encode() if isinstance(body, str) else body
|
||||||
|
with httpx.Client() as client:
|
||||||
|
for w in range(warmup):
|
||||||
|
r = call_once(client, url, method, content, headers, timeout)
|
||||||
|
yield {"i": w + 1, "warmup": True, "note": "warmup", **r}
|
||||||
|
first_note = "first call" if warmup == 0 else ""
|
||||||
|
if concurrency <= 1:
|
||||||
|
for i in range(1, n + 1):
|
||||||
|
r = call_once(client, url, method, content, headers, timeout)
|
||||||
|
yield {"i": i, "warmup": False, "note": first_note if i == 1 else "", **r}
|
||||||
|
return
|
||||||
|
with ThreadPoolExecutor(max_workers=concurrency) as pool:
|
||||||
|
futures = {pool.submit(call_once, client, url, method, content, headers, timeout): i
|
||||||
|
for i in range(1, n + 1)}
|
||||||
|
for future in as_completed(futures):
|
||||||
|
i = futures[future]
|
||||||
|
yield {"i": i, "warmup": False, "note": first_note if i == 1 else "", **future.result()}
|
||||||
|
|
||||||
|
|
||||||
|
def read_body(path):
|
||||||
|
"""Read a JSON body file; keep the exact text so its hash matches what was sent."""
|
||||||
|
if path is None:
|
||||||
|
return None
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
text = f.read()
|
||||||
|
json.loads(text)
|
||||||
|
return text
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
"""Percentiles and summaries. Nearest-rank: sort, take the ceil(p * n)-th value."""
|
||||||
|
import math
|
||||||
|
|
||||||
|
KINDS = ("connect", "timeout", "http_5xx", "http_4xx")
|
||||||
|
|
||||||
|
|
||||||
|
def percentile(values, p):
|
||||||
|
if not values:
|
||||||
|
return None
|
||||||
|
ordered = sorted(values)
|
||||||
|
rank = max(1, math.ceil(p * len(ordered) - 1e-9))
|
||||||
|
return ordered[rank - 1]
|
||||||
|
|
||||||
|
|
||||||
|
def average_timings(results):
|
||||||
|
"""Average every numeric field of response `timings` over successful calls."""
|
||||||
|
sums, counts = {}, {}
|
||||||
|
for r in results:
|
||||||
|
for key, value in (r.get("timings") or {}).items():
|
||||||
|
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
||||||
|
sums[key] = sums.get(key, 0) + value
|
||||||
|
counts[key] = counts.get(key, 0) + 1
|
||||||
|
return {k: round(sums[k] / counts[k], 1) for k in sums}
|
||||||
|
|
||||||
|
|
||||||
|
def summarize(results, total=None):
|
||||||
|
"""results: measured calls (no warmup). Failures are counted, not timed."""
|
||||||
|
ok = [r for r in results if r["error_kind"] is None]
|
||||||
|
times = [r["seconds"] for r in ok]
|
||||||
|
rest = [r["seconds"] for r in ok if r.get("note") != "first call"]
|
||||||
|
by_kind = {k: sum(1 for r in results if r["error_kind"] == k) for k in KINDS}
|
||||||
|
return {
|
||||||
|
"done": len(results),
|
||||||
|
"total": total if total is not None else len(results),
|
||||||
|
"ok": len(ok),
|
||||||
|
"failures": len(results) - len(ok),
|
||||||
|
"by_kind": by_kind,
|
||||||
|
"p50": percentile(times, 0.5),
|
||||||
|
"p95": percentile(times, 0.95),
|
||||||
|
"max": max(times) if times else None,
|
||||||
|
"p50_excl_first": percentile(rest, 0.5) if len(rest) < len(times) else None,
|
||||||
|
"timings": average_timings(ok),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fmt(seconds):
|
||||||
|
return "-" if seconds is None else f"{seconds:.3f}"
|
||||||
|
|
||||||
|
|
||||||
|
def summary_lines(s):
|
||||||
|
kinds = " ".join(f"{k}={v}" for k, v in s["by_kind"].items() if v)
|
||||||
|
lines = [
|
||||||
|
f"done {s['done']}/{s['total']} failures {s['failures']}" + (f" ({kinds})" if kinds else ""),
|
||||||
|
f"p50 {fmt(s['p50'])}s p95 {fmt(s['p95'])}s max {fmt(s['max'])}s"
|
||||||
|
+ (f" p50 excl. first {fmt(s['p50_excl_first'])}s" if s["p50_excl_first"] is not None else ""),
|
||||||
|
]
|
||||||
|
if s["timings"]:
|
||||||
|
lines.append("timings avg ms " + " ".join(f"{k} {v:g}" for k, v in s["timings"].items()))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def check(s, max_p95=None, max_failures=None):
|
||||||
|
"""Return (passed, line) for the given limits."""
|
||||||
|
problems = []
|
||||||
|
if max_p95 is not None and (s["p95"] is None or s["p95"] > max_p95):
|
||||||
|
problems.append(f"p95 {fmt(s['p95'])} > {max_p95}")
|
||||||
|
if max_failures is not None and s["failures"] > max_failures:
|
||||||
|
problems.append(f"failures {s['failures']} > {max_failures}")
|
||||||
|
return (not problems, "FAIL " + ", ".join(problems) if problems else "PASS")
|
||||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user