77일차 실행 기록 분석: 실습 15개 · 강의(개요 30장 · 단계 문서 · 4단계 정리 · 05 바이브), 정답본 LAB-A · answers · VIBE-A · tlog 포함
This commit is contained in:
1 parent
036eae1c6a
commit
dcd4cdd66b
148 files changed
+40344
No files matched your search
@@ -0,0 +1,27 @@
|
||||
$ uv run tools/flow.py ops --reset
|
||||
✔ live run logs, added FAQ, created tickets, DLQ and alerts emptied, fault switch all ok
|
||||
|
||||
$ uv run tools/flow.py off my-log-report my-log-summary my-weekly my-slo-alert my-helpdesk
|
||||
✔ unpublished my-log-report (id wfed832ca6c81732)
|
||||
✔ unpublished my-weekly (id w269ccf31103289f)
|
||||
✔ unpublished my-slo-alert (id w46a402ba838f33c)
|
||||
✔ unpublished my-helpdesk (id wc5ae6e8bcdf8e39)
|
||||
✔ n8n restarted. Turn it back on with: uv run tools/flow.py push <the workflow file>
|
||||
|
||||
$ uv run tools/flow.py push reference/baseline-report.json
|
||||
✔ pushed '77 Log report (team baseline, provided)' (id w3f6d15ae763203b)
|
||||
POST http://localhost:5678/webhook/base-report
|
||||
|
||||
$ 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
|
||||
✔ dead letter queue /dlq · alerts · fault switch dlq (day 76 data API)
|
||||
✔ run logs /logs (666 seeded runs) · FAQ additions /faq-extra (day 77 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,36 @@
|
||||
$ uv run tools/flow.py logs --from 2026-10-19 --to 2026-10-30 --last 5
|
||||
666 run(s) · errors 21 (3.2%)
|
||||
route: 자동 답변 591 · 담당자 연결 34 · 오류 21 · 잠시 후 20
|
||||
2026-10-30T17:05:38 success 자동 답변 1311ms 프린터 인쇄 대기열이 안 비워져요
|
||||
2026-10-30T17:11:46 success 자동 답변 1175ms 메일 메일함 용량이 꽉 찼대요
|
||||
2026-10-30T17:20:51 success 담당자 연결 79ms 와이파이 회의실 와이파이가 안 잡혀요
|
||||
2026-10-30T17:24:39 success 자동 답변 1205ms 메일 첨부 파일이 너무 커서 안 보내져요
|
||||
2026-10-30T17:59:20 success 자동 답변 1076ms 메일 메일함 용량이 꽉 찼대요
|
||||
|
||||
$ uv run tools/flow.py logs --from 2026-10-26 --to 2026-10-26 --last 0
|
||||
70 run(s) · errors 16 (22.9%)
|
||||
route: 자동 답변 50 · 오류 16 · 담당자 연결 4
|
||||
|
||||
$ uv run tools/flow.py call base-report requests/two-weeks.json
|
||||
HTTP 200 · 1.2s
|
||||
{
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
|
||||
$ uv run tools/flow.py call base-report requests/monday.json
|
||||
HTTP 200 · 0.1s
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"cases": "lab01.json",
|
||||
"webhook": "base-report",
|
||||
"passed": 0,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 전체",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 602 (expected 666)",
|
||||
"error_rate = 3.488372093023256 (expected 0.0315)",
|
||||
"p50_ms = null (expected 1205)",
|
||||
"p95_ms = null (expected 2537)"
|
||||
],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 갈래별",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_route.자동 답변 = 529 (expected 591)",
|
||||
"by_route.담당자 연결 = 32 (expected 34)",
|
||||
"by_route.오류 = null (expected 21)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 245 (expected 309)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 245,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"avg_ms": 1317.1551020408162,
|
||||
"by_route": {
|
||||
"자동 답변": 230,
|
||||
"담당자 연결": 15
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "둘째 주",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 293 (expected 357)",
|
||||
"error_rate = 7.167235494880546 (expected 0.0588)",
|
||||
"p95_ms = null (expected 2598)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 293,
|
||||
"errors": 21,
|
||||
"error_rate": 7.167235494880546,
|
||||
"avg_ms": 1173.1399317406144,
|
||||
"by_route": {
|
||||
"자동 답변": 237,
|
||||
"담당자 연결": 15,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "10월 26일 하루",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 0 (expected 70)",
|
||||
"errors = 0 (expected 16)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
$ uv run tools/flow.py test cases/lab01.json --webhook base-report -v
|
||||
POST http://localhost:5678/webhook/base-report · 5 case(s) × 1
|
||||
✘ 두 주 전체 · 0.2s · total = 602 (expected 666); error_rate = 3.488372093023256 (expected 0.0315); p50_ms = null (expected 1205); p95_ms = null (expected 2537)
|
||||
→ request POST /webhook/base-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 두 주 · 갈래별 · 0.1s · by_route.자동 답변 = 529 (expected 591); by_route.담당자 연결 = 32 (expected 34); by_route.오류 = null (expected 21)
|
||||
→ request POST /webhook/base-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 첫 주 · 0.1s · total = 245 (expected 309)
|
||||
→ request POST /webhook/base-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 245,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"avg_ms": 1317.1551020408162,
|
||||
"by_route": {
|
||||
"자동 답변": 230,
|
||||
"담당자 연결": 15
|
||||
}
|
||||
}
|
||||
✘ 둘째 주 · 0.1s · total = 293 (expected 357); error_rate = 7.167235494880546 (expected 0.0588); p95_ms = null (expected 2598)
|
||||
→ request POST /webhook/base-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 293,
|
||||
"errors": 21,
|
||||
"error_rate": 7.167235494880546,
|
||||
"avg_ms": 1173.1399317406144,
|
||||
"by_route": {
|
||||
"자동 답변": 237,
|
||||
"담당자 연결": 15,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 10월 26일 하루 · 0.1s · total = 0 (expected 70); errors = 0 (expected 16)
|
||||
→ request POST /webhook/base-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
Passed 0/5
|
||||
report: runs/lab01-20261031-074859.json
|
||||
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"cases": "lab01.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 5,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 전체",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 갈래별",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "둘째 주",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "10월 26일 하루",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
$ uv run tools/flow.py push reference/lab01-example2-report.json
|
||||
✔ pushed '77 Log report (reference)' (id w1971efa15af33a8)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 전체 · 0.9s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✔ 두 주 · 갈래별 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✔ 첫 주 · 0.2s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
}
|
||||
}
|
||||
✔ 둘째 주 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✔ 10월 26일 하루 · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
}
|
||||
}
|
||||
Passed 5/5
|
||||
report: runs/lab01-20261031-074912.json
|
||||
|
||||
$ uv run tools/flow.py call my-log-report requests/week2.json
|
||||
HTTP 200 · 0.1s
|
||||
{
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"cases": "lab01-edges.json",
|
||||
"webhook": "base-report",
|
||||
"passed": 2,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "하루 · from = to (포함)",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 0 (expected 70)",
|
||||
"errors = 0 (expected 16)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "빈 기간 · 0건이어도 200",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"error_rate = null (expected 0)",
|
||||
"p95_ms = null (expected 0)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주말 · 실행이 없는 날",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-24",
|
||||
"to": "2026-10-25"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": null,
|
||||
"avg_ms": null,
|
||||
"by_route": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "기간을 비우면 전체",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 3.153153153153153,
|
||||
"avg_ms": 1259.3843843843845,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "source seed · 두 주 기록만",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 602 (expected 666)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30",
|
||||
"source": "seed"
|
||||
},
|
||||
"response": {
|
||||
"total": 602,
|
||||
"errors": 21,
|
||||
"error_rate": 3.488372093023256,
|
||||
"avg_ms": 1249.2392026578073,
|
||||
"by_route": {
|
||||
"자동 답변": 529,
|
||||
"담당자 연결": 32,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "source live · 오늘 실습 기록 (아직 없음)",
|
||||
"webhook": "base-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"total = 666 (expected 0)",
|
||||
"error_rate = 3.153153153153153 (expected 0)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"source": "live"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 3.153153153153153,
|
||||
"avg_ms": 1259.3843843843845,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
$ uv run tools/flow.py test cases/lab01-edges.json -v
|
||||
✔ before: POST http://localhost:8800/logs-reset · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
→ request POST http://localhost:8800/logs-reset
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"cleared": 0
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 하루 · from = to (포함) · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
}
|
||||
}
|
||||
✔ 빈 기간 · 0건이어도 200 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {}
|
||||
}
|
||||
✔ 주말 · 실행이 없는 날 · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-24",
|
||||
"to": "2026-10-25"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {}
|
||||
}
|
||||
✔ 기간을 비우면 전체 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✔ source seed · 두 주 기록만 · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30",
|
||||
"source": "seed"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✔ source live · 오늘 실습 기록 (아직 없음) · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {}
|
||||
}
|
||||
Passed 6/6
|
||||
report: runs/lab01-edges-20261031-074913.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01-edges.json --webhook base-report
|
||||
✔ before: POST http://localhost:8800/logs-reset · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
POST http://localhost:5678/webhook/base-report · 6 case(s) × 1
|
||||
✘ 하루 · from = to (포함) · 0.1s · total = 0 (expected 70); errors = 0 (expected 16)
|
||||
✘ 빈 기간 · 0건이어도 200 · 0.1s · error_rate = null (expected 0); p95_ms = null (expected 0)
|
||||
✔ 주말 · 실행이 없는 날 · 0.1s
|
||||
✔ 기간을 비우면 전체 · 0.0s
|
||||
✘ source seed · 두 주 기록만 · 0.1s · total = 602 (expected 666)
|
||||
✘ source live · 오늘 실습 기록 (아직 없음) · 0.1s · total = 666 (expected 0); error_rate = 3.153153153153153 (expected 0)
|
||||
Passed 2/6
|
||||
report: runs/lab01-edges-20261031-074913.json
|
||||
@@ -0,0 +1,179 @@
|
||||
{
|
||||
"cases": "lab01-challenge.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 0,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "날짜별 · 월요일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_day.2026-10-26.total = null (expected 70)",
|
||||
"by_day.2026-10-26.errors = null (expected 16)",
|
||||
"by_day.2026-10-26.error_rate = null (expected 0.2286)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "날짜별 · 목요일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_day.2026-10-29.total = null (expected 97)",
|
||||
"by_day.2026-10-29.errors = null (expected 5)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "날짜는 10일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_day has none of ['\"2026-10-19\"']"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "릴리스별 다시 묻기 · 1.2.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_release has none of ['\"1.2.0\": {\"answers\": 8, \"repeats\": 5, \"repeat_rate\": 0.625}']"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "릴리스별 다시 묻기 · 1.0.0 · 1.1.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_release has none of ['\"1.0.0\": {\"answers\": 402, \"repeats\": 19, \"repeat_rate\": 0.0473}']"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "1.1.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"by_release has none of ['\"1.1.0\": {\"answers\": 181, \"repeats\": 14, \"repeat_rate\": 0.0773}']"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
$ uv run tools/flow.py test cases/lab01-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✘ 날짜별 · 월요일 · 0.1s · by_day.2026-10-26.total = null (expected 70); by_day.2026-10-26.errors = null (expected 16); by_day.2026-10-26.error_rate = null (expected 0.2286)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 날짜별 · 목요일 · 0.1s · by_day.2026-10-29.total = null (expected 97); by_day.2026-10-29.errors = null (expected 5)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 날짜는 10일 · 0.1s · by_day has none of ['"2026-10-19"']
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 릴리스별 다시 묻기 · 1.2.0 · 0.1s · by_release has none of ['"1.2.0": {"answers": 8, "repeats": 5, "repeat_rate": 0.625}']
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 릴리스별 다시 묻기 · 1.0.0 · 1.1.0 · 0.1s · by_release has none of ['"1.0.0": {"answers": 402, "repeats": 19, "repeat_rate": 0.0473}']
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
✘ 1.1.0 · 0.1s · by_release has none of ['"1.1.0": {"answers": 181, "repeats": 14, "repeat_rate": 0.0773}']
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
}
|
||||
}
|
||||
Passed 0/6
|
||||
report: runs/lab01-challenge-20261031-074914.json
|
||||
@@ -0,0 +1,578 @@
|
||||
{
|
||||
"cases": "lab01-challenge.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 6,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "날짜별 · 월요일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "날짜별 · 목요일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "날짜는 10일",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "릴리스별 다시 묻기 · 1.2.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "릴리스별 다시 묻기 · 1.0.0 · 1.1.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "1.1.0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
$ uv run tools/flow.py push answers/77-lab01-challenge-day-release.json
|
||||
! unpublished '77 Log report (reference)' (w1971efa15af33a8): it used the same webhook path
|
||||
✔ pushed '77 Log report + days and releases (answer)' (id w5ee2fc1d2930303)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 날짜별 · 월요일 · 1.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 날짜별 · 목요일 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 날짜는 10일 · 0.2s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 릴리스별 다시 묻기 · 1.2.0 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 릴리스별 다시 묻기 · 1.0.0 · 1.1.0 · 0.1s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 1.1.0 · 0.0s
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
Passed 6/6
|
||||
report: runs/lab01-challenge-20261031-074930.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 전체 · 0.1s
|
||||
✔ 두 주 · 갈래별 · 0.1s
|
||||
✔ 첫 주 · 0.1s
|
||||
✔ 둘째 주 · 0.1s
|
||||
✔ 10월 26일 하루 · 0.1s
|
||||
Passed 5/5
|
||||
report: runs/lab01-20261031-074930.json
|
||||
@@ -0,0 +1,374 @@
|
||||
{
|
||||
"cases": "lab01-challenge2.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 0,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 · 단계별 p95",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"steps.faq_ms.p95 = null (expected 39)",
|
||||
"steps.answer_ms.p95 = null (expected 2509)",
|
||||
"steps.answer_ms.p50 = null (expected 1179)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 단계마다 잰 실행 수",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"steps.faq_ms.runs = null (expected 646)",
|
||||
"steps.answer_ms.runs = null (expected 596)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "월요일 · FAQ 단계가 느림",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"steps.faq_ms.p95 = null (expected 1000)",
|
||||
"steps.answer_ms.p95 = null (expected 2482)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 비용",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"cost_usd = null (expected 0.07836)",
|
||||
"cost_per_run = null (expected 0.000118)",
|
||||
"llm_calls = null (expected 596)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "빈 기간 · 비용 0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"cost_usd = null (expected 0)",
|
||||
"cost_per_run = null (expected 0)",
|
||||
"llm_calls = null (expected 0)",
|
||||
"steps.faq_ms.p95 = null (expected 0)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
$ uv run tools/flow.py test cases/lab01-challenge2.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✘ 두 주 · 단계별 p95 · 0.1s · steps.faq_ms.p95 = null (expected 39); steps.answer_ms.p95 = null (expected 2509); steps.answer_ms.p50 = null (expected 1179)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✘ 두 주 · 단계마다 잰 실행 수 · 0.1s · steps.faq_ms.runs = null (expected 646); steps.answer_ms.runs = null (expected 596)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✘ 월요일 · FAQ 단계가 느림 · 0.1s · steps.faq_ms.p95 = null (expected 1000); steps.answer_ms.p95 = null (expected 2482)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✘ 두 주 · 비용 · 0.1s · cost_usd = null (expected 0.07836); cost_per_run = null (expected 0.000118); llm_calls = null (expected 596)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
}
|
||||
}
|
||||
✘ 빈 기간 · 비용 0 · 0.0s · cost_usd = null (expected 0); cost_per_run = null (expected 0); llm_calls = null (expected 0); steps.faq_ms.p95 = null (expected 0)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {}
|
||||
}
|
||||
Passed 0/5
|
||||
report: runs/lab01-challenge2-20261031-074930.json
|
||||
@@ -0,0 +1,430 @@
|
||||
{
|
||||
"cases": "lab01-challenge2.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 5,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 · 단계별 p95",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 단계마다 잰 실행 수",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "월요일 · FAQ 단계가 느림",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 70,
|
||||
"p50": 31,
|
||||
"p95": 1000
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 50,
|
||||
"p50": 1169,
|
||||
"p95": 2482
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00645,
|
||||
"cost_per_run": 9.2e-05,
|
||||
"llm_calls": 50
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 비용",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "빈 기간 · 비용 0",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
},
|
||||
"response": {
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
$ uv run tools/flow.py push answers/77-lab01-challenge2-steps-cost.json
|
||||
! unpublished '77 Log report + days and releases (answer)' (w5ee2fc1d2930303): it used the same webhook path
|
||||
✔ pushed '77 Log report + steps and cost (answer)' (id w0c1a020313bbffc)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01-challenge2.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 · 단계별 p95 · 0.9s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✔ 두 주 · 단계마다 잰 실행 수 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✔ 월요일 · FAQ 단계가 느림 · 0.2s · 50 calls · $0.006450
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 70,
|
||||
"p50": 31,
|
||||
"p95": 1000
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 50,
|
||||
"p50": 1169,
|
||||
"p95": 2482
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00645,
|
||||
"cost_per_run": 9.2e-05,
|
||||
"llm_calls": 50
|
||||
}
|
||||
✔ 두 주 · 비용 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✔ 빈 기간 · 비용 0 · 0.0s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-11-02",
|
||||
"to": "2026-11-06"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0
|
||||
}
|
||||
Passed 5/5
|
||||
report: runs/lab01-challenge2-20261031-074945.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 전체 · 0.1s · 596 calls · $0.078360
|
||||
✔ 두 주 · 갈래별 · 0.1s · 596 calls · $0.078360
|
||||
✔ 첫 주 · 0.1s · 292 calls · $0.038550
|
||||
✔ 둘째 주 · 0.1s · 304 calls · $0.039810
|
||||
✔ 10월 26일 하루 · 0.0s · 50 calls · $0.006450
|
||||
Passed 5/5
|
||||
report: runs/lab01-20261031-074945.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01-challenge.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 날짜별 · 월요일 · 0.1s · 596 calls · $0.078360
|
||||
✔ 날짜별 · 목요일 · 0.1s · 596 calls · $0.078360
|
||||
✔ 날짜는 10일 · 0.1s · 596 calls · $0.078360
|
||||
✔ 릴리스별 다시 묻기 · 1.2.0 · 0.1s · 596 calls · $0.078360
|
||||
✔ 릴리스별 다시 묻기 · 1.0.0 · 1.1.0 · 0.1s · 596 calls · $0.078360
|
||||
✔ 1.1.0 · 0.1s · 596 calls · $0.078360
|
||||
Passed 6/6
|
||||
report: runs/lab01-challenge-20261031-074946.json
|
||||
@@ -0,0 +1,651 @@
|
||||
{
|
||||
"cases": "lab02.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 0,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "예제 1 · 가장 많은 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"failures.0.node = null (expected Search FAQ)",
|
||||
"failures.0.message = null (expected timeout of 1000ms exceeded)",
|
||||
"failures.0.count = null (expected 16)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 두 번째 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"failures.1.node = null (expected Answer)",
|
||||
"failures.1.count = null (expected 5)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 답하지 못한 주제",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"unanswered_topics.0.topic = null (expected 와이파이)",
|
||||
"unanswered_topics.0.count = null (expected 34)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 1",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies.0.hour = null (expected 2026-10-26T09)",
|
||||
"anomalies.0.total = null (expected 16)",
|
||||
"anomalies.0.errors = null (expected 16)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 2",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies.1.hour = null (expected 2026-10-29T15)",
|
||||
"anomalies.1.total = null (expected 7)",
|
||||
"anomalies.1.errors = null (expected 5)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 첫 주에는 없음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies = null (expected [])"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
$ uv run tools/flow.py test cases/lab02.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✘ 예제 1 · 가장 많은 실패 · 0.1s · 596 calls · $0.078360 · failures.0.node = null (expected Search FAQ); failures.0.message = null (expected timeout of 1000ms exceeded); failures.0.count = null (expected 16)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✘ 예제 1 · 두 번째 실패 · 0.1s · 596 calls · $0.078360 · failures.1.node = null (expected Answer); failures.1.count = null (expected 5)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✘ 예제 1 · 답하지 못한 주제 · 0.1s · 596 calls · $0.078360 · unanswered_topics.0.topic = null (expected 와이파이); unanswered_topics.0.count = null (expected 34)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✘ 예제 2 · 이상 시간대 1 · 0.1s · 596 calls · $0.078360 · anomalies.0.hour = null (expected 2026-10-26T09); anomalies.0.total = null (expected 16); anomalies.0.errors = null (expected 16)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✘ 예제 2 · 이상 시간대 2 · 0.1s · 596 calls · $0.078360 · anomalies.1.hour = null (expected 2026-10-29T15); anomalies.1.total = null (expected 7); anomalies.1.errors = null (expected 5)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596
|
||||
}
|
||||
✘ 예제 2 · 첫 주에는 없음 · 0.0s · 292 calls · $0.038550 · anomalies = null (expected [])
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292
|
||||
}
|
||||
Passed 0/6
|
||||
report: runs/lab02-20261031-074946.json
|
||||
@@ -0,0 +1,738 @@
|
||||
{
|
||||
"cases": "lab02.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 3,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "예제 1 · 가장 많은 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 두 번째 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 답하지 못한 주제",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 1",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies.0.hour = null (expected 2026-10-26T09)",
|
||||
"anomalies.0.total = null (expected 16)",
|
||||
"anomalies.0.errors = null (expected 16)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 2",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies.1.hour = null (expected 2026-10-29T15)",
|
||||
"anomalies.1.total = null (expected 7)",
|
||||
"anomalies.1.errors = null (expected 5)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 첫 주에는 없음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"anomalies = null (expected [])"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,693 @@
|
||||
$ uv run tools/flow.py push reference/lab02-example1-problems.json
|
||||
! unpublished '77 Log report + steps and cost (answer)' (w0c1a020313bbffc): it used the same webhook path
|
||||
✔ pushed '77 Log report + failures and unanswered topics (reference)' (id w51c62bb8e6f4e90)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 예제 1 · 가장 많은 실패 · 0.9s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 1 · 두 번째 실패 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 1 · 답하지 못한 주제 · 0.3s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 예제 2 · 이상 시간대 1 · 0.1s · 596 calls · $0.078360 · anomalies.0.hour = null (expected 2026-10-26T09); anomalies.0.total = null (expected 16); anomalies.0.errors = null (expected 16)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 예제 2 · 이상 시간대 2 · 0.1s · 596 calls · $0.078360 · anomalies.1.hour = null (expected 2026-10-29T15); anomalies.1.total = null (expected 7); anomalies.1.errors = null (expected 5)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 예제 2 · 첫 주에는 없음 · 0.1s · 292 calls · $0.038550 · anomalies = null (expected [])
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
]
|
||||
}
|
||||
Passed 3/6
|
||||
report: runs/lab02-20261031-075001.json
|
||||
@@ -0,0 +1,799 @@
|
||||
{
|
||||
"cases": "lab02.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 6,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "예제 1 · 가장 많은 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 두 번째 실패",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 1 · 답하지 못한 주제",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 1",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 이상 시간대 2",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "예제 2 · 첫 주에는 없음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,906 @@
|
||||
$ uv run tools/flow.py push reference/lab02-example2-anomalies.json
|
||||
! unpublished '77 Log report + failures and unanswered topics (reference)' (w51c62bb8e6f4e90): it used the same webhook path
|
||||
✔ pushed '77 Log report + anomalies (reference)' (id w4957bb6724af44d)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 예제 1 · 가장 많은 실패 · 0.9s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 1 · 두 번째 실패 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 1 · 답하지 못한 주제 · 0.2s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 2 · 이상 시간대 1 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 2 · 이상 시간대 2 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 예제 2 · 첫 주에는 없음 · 0.0s · 292 calls · $0.038550
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
Passed 6/6
|
||||
report: runs/lab02-20261031-075015.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab01-challenge2.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 · 단계별 p95 · 0.1s · 596 calls · $0.078360
|
||||
✔ 두 주 · 단계마다 잰 실행 수 · 0.1s · 596 calls · $0.078360
|
||||
✔ 월요일 · FAQ 단계가 느림 · 0.0s · 50 calls · $0.006450
|
||||
✔ 두 주 · 비용 · 0.1s · 596 calls · $0.078360
|
||||
✔ 빈 기간 · 비용 0 · 0.0s · 0 calls · $0.000000
|
||||
Passed 5/5
|
||||
report: runs/lab01-challenge2-20261031-075015.json
|
||||
|
||||
$ uv run tools/flow.py call my-log-report requests/two-weeks.json
|
||||
HTTP 200 · 0.1s
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
{
|
||||
"cases": "lab02-periods.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 5,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "첫 주 · 오류 0 → 이상 없음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "둘째 주 · 기준 0.0588 × 3",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "월요일만 · 기준 0.2286 × 3",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
},
|
||||
"response": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 70,
|
||||
"p50": 31,
|
||||
"p95": 1000
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 50,
|
||||
"p50": 1169,
|
||||
"p95": 2482
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00645,
|
||||
"cost_per_run": 9.2e-05,
|
||||
"llm_calls": 50,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 4
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "목요일만 · 15시만",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-29",
|
||||
"to": "2026-10-29"
|
||||
},
|
||||
"response": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515,
|
||||
"p50_ms": 1150,
|
||||
"p95_ms": 2533,
|
||||
"by_route": {
|
||||
"자동 답변": 66,
|
||||
"담당자 연결": 6,
|
||||
"잠시 후": 20,
|
||||
"오류": 5
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 66,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0758
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 77,
|
||||
"p50": 24,
|
||||
"p95": 38
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 71,
|
||||
"p50": 1100,
|
||||
"p95": 2539
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00942,
|
||||
"cost_per_run": 9.7e-05,
|
||||
"llm_calls": 71,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 6
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 기준 0.0315 × 3",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
$ uv run tools/flow.py test cases/lab02-periods.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 첫 주 · 오류 0 → 이상 없음 · 0.1s · 292 calls · $0.038550
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
✔ 둘째 주 · 기준 0.0588 × 3 · 0.1s · 304 calls · $0.039810
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 월요일만 · 기준 0.2286 × 3 · 0.0s · 50 calls · $0.006450
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-26"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286,
|
||||
"p50_ms": 1082,
|
||||
"p95_ms": 2468,
|
||||
"by_route": {
|
||||
"오류": 16,
|
||||
"자동 답변": 50,
|
||||
"담당자 연결": 4
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 50,
|
||||
"repeats": 1,
|
||||
"repeat_rate": 0.02
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 70,
|
||||
"p50": 31,
|
||||
"p95": 1000
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 50,
|
||||
"p50": 1169,
|
||||
"p95": 2482
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00645,
|
||||
"cost_per_run": 9.2e-05,
|
||||
"llm_calls": 50,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 4
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 목요일만 · 15시만 · 0.1s · 71 calls · $0.009420
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-29",
|
||||
"to": "2026-10-29"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515,
|
||||
"p50_ms": 1150,
|
||||
"p95_ms": 2533,
|
||||
"by_route": {
|
||||
"자동 답변": 66,
|
||||
"담당자 연결": 6,
|
||||
"잠시 후": 20,
|
||||
"오류": 5
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 66,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0758
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 77,
|
||||
"p50": 24,
|
||||
"p95": 38
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 71,
|
||||
"p50": 1100,
|
||||
"p95": 2539
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.00942,
|
||||
"cost_per_run": 9.7e-05,
|
||||
"llm_calls": 71,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 6
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 두 주 · 기준 0.0315 × 3 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
Passed 5/5
|
||||
report: runs/lab02-periods-20261031-075016.json
|
||||
@@ -0,0 +1,671 @@
|
||||
{
|
||||
"cases": "lab02-challenge.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 1,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 · 다시 묻는 비율이 가장 높은 주제",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"repeat_topics.0.topic = null (expected 프린터)",
|
||||
"repeat_topics.0.answers = null (expected 81)",
|
||||
"repeat_topics.0.repeats = null (expected 8)",
|
||||
"repeat_topics.0.repeat_rate = null (expected 0.0988)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 수로는 비밀번호가 가장 많지만 비율은 2위",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"repeat_topics.1.topic = null (expected 비밀번호)",
|
||||
"repeat_topics.1.repeats = null (expected 9)",
|
||||
"repeat_topics.1.repeat_rate = null (expected 0.0647)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 다시 물은 주제는 6개",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"repeat_topics.5.topic = null (expected 설치)",
|
||||
"repeat_topics.5.repeat_rate = null (expected 0.0328)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주 · 설치는 다시 묻지 않음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"repeat_topics.0.topic = null (expected 프린터)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "앞의 지표는 그대로",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
$ uv run tools/flow.py test cases/lab02-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✘ 두 주 · 다시 묻는 비율이 가장 높은 주제 · 0.1s · 596 calls · $0.078360 · repeat_topics.0.topic = null (expected 프린터); repeat_topics.0.answers = null (expected 81); repeat_topics.0.repeats = null (expected 8); repeat_topics.0.repeat_rate = null (expected 0.0988)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 두 주 · 수로는 비밀번호가 가장 많지만 비율은 2위 · 0.1s · 596 calls · $0.078360 · repeat_topics.1.topic = null (expected 비밀번호); repeat_topics.1.repeats = null (expected 9); repeat_topics.1.repeat_rate = null (expected 0.0647)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 두 주 · 다시 물은 주제는 6개 · 0.1s · 596 calls · $0.078360 · repeat_topics.5.topic = null (expected 설치); repeat_topics.5.repeat_rate = null (expected 0.0328)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 첫 주 · 설치는 다시 묻지 않음 · 0.0s · 292 calls · $0.038550 · repeat_topics.0.topic = null (expected 프린터)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": []
|
||||
}
|
||||
✔ 앞의 지표는 그대로 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
]
|
||||
}
|
||||
Passed 1/5
|
||||
report: runs/lab02-challenge-20261031-075016.json
|
||||
@@ -0,0 +1,841 @@
|
||||
{
|
||||
"cases": "lab02-challenge.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 5,
|
||||
"total": 5,
|
||||
"results": [
|
||||
{
|
||||
"case": "두 주 · 다시 묻는 비율이 가장 높은 주제",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 수로는 비밀번호가 가장 많지만 비율은 2위",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "두 주 · 다시 물은 주제는 6개",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주 · 설치는 다시 묻지 않음",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "앞의 지표는 그대로",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,823 @@
|
||||
$ uv run tools/flow.py push answers/77-lab02-challenge-repeat-topics.json
|
||||
! unpublished '77 Log report + anomalies (reference)' (w4957bb6724af44d): it used the same webhook path
|
||||
✔ pushed '77 Log report + repeat topics (answer)' (id wf3d06e3d30eeee6)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 · 다시 묻는 비율이 가장 높은 주제 · 0.9s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 두 주 · 수로는 비밀번호가 가장 많지만 비율은 2위 · 0.3s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 두 주 · 다시 물은 주제는 6개 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 첫 주 · 설치는 다시 묻지 않음 · 0.0s · 292 calls · $0.038550
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 앞의 지표는 그대로 · 0.1s · 596 calls · $0.078360
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 666,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0315,
|
||||
"p50_ms": 1205,
|
||||
"p95_ms": 2537,
|
||||
"by_route": {
|
||||
"자동 답변": 591,
|
||||
"담당자 연결": 34,
|
||||
"오류": 21,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 402,
|
||||
"repeats": 19,
|
||||
"repeat_rate": 0.0473
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 646,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 596,
|
||||
"p50": 1179,
|
||||
"p95": 2509
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.07836,
|
||||
"cost_per_run": 0.000118,
|
||||
"llm_calls": 596,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 34
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 81,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0988
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 139,
|
||||
"repeats": 9,
|
||||
"repeat_rate": 0.0647
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 124,
|
||||
"repeats": 8,
|
||||
"repeat_rate": 0.0645
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 96,
|
||||
"repeats": 6,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 90,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0556
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 61,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0328
|
||||
}
|
||||
]
|
||||
}
|
||||
Passed 5/5
|
||||
report: runs/lab02-challenge-20261031-075031.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 6 case(s) × 1
|
||||
✔ 예제 1 · 가장 많은 실패 · 0.1s · 596 calls · $0.078360
|
||||
✔ 예제 1 · 두 번째 실패 · 0.1s · 596 calls · $0.078360
|
||||
✔ 예제 1 · 답하지 못한 주제 · 0.1s · 596 calls · $0.078360
|
||||
✔ 예제 2 · 이상 시간대 1 · 0.1s · 596 calls · $0.078360
|
||||
✔ 예제 2 · 이상 시간대 2 · 0.1s · 596 calls · $0.078360
|
||||
✔ 예제 2 · 첫 주에는 없음 · 0.0s · 292 calls · $0.038550
|
||||
Passed 6/6
|
||||
report: runs/lab02-20261031-075031.json
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"cases": "lab02-challenge2.json",
|
||||
"webhook": "my-log-summary",
|
||||
"passed": 0,
|
||||
"total": 3,
|
||||
"results": [
|
||||
{
|
||||
"case": "요약 · 숫자는 통계에 있는 것만",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"numbers_ok = null (expected true)",
|
||||
"top_topic = null (expected 와이파이)",
|
||||
"worst_hour = null (expected 2026-10-26T09)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "요약 · 주제와 시간대를 말함",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"summary has none of ['와이파이']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "요약 · 날짜",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"summary has none of ['10월 26일', '10-26', '26일']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
$ uv run tools/flow.py test cases/lab02-challenge2.json -v
|
||||
POST http://localhost:5678/webhook/my-log-summary · 3 case(s) × 1
|
||||
✘ 요약 · 숫자는 통계에 있는 것만 · 0.0s · status 404 (expected 200); numbers_ok = null (expected true); top_topic = null (expected 와이파이); worst_hour = null (expected 2026-10-26T09)
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 요약 · 주제와 시간대를 말함 · 0.0s · status 404 (expected 200); summary has none of ['와이파이']
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 요약 · 날짜 · 0.0s · status 404 (expected 200); summary has none of ['10월 26일', '10-26', '26일']
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-log-summary\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
Passed 0/3
|
||||
report: runs/lab02-challenge2-20261031-075032.json
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"cases": "lab02-challenge2.json",
|
||||
"webhook": "my-log-summary",
|
||||
"passed": 3,
|
||||
"total": 3,
|
||||
"results": [
|
||||
{
|
||||
"case": "요약 · 숫자는 통계에 있는 것만",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 3.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 현상은 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "요약 · 주제와 시간대를 말함",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.6,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "요약 · 날짜",
|
||||
"webhook": "my-log-summary",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.7,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건이 보고되었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
$ uv run tools/flow.py push answers/77-lab02-challenge2-summary.json
|
||||
✔ pushed '77 Log summary (answer)' (id w9c86d93226f0b0f)
|
||||
POST http://localhost:5678/webhook/my-log-summary
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02-challenge2.json -v
|
||||
POST http://localhost:5678/webhook/my-log-summary · 3 case(s) × 1
|
||||
✔ 요약 · 숫자는 통계에 있는 것만 · 3.4s
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 현상은 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
✔ 요약 · 주제와 시간대를 말함 · 2.6s
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
✔ 요약 · 날짜 · 1.7s
|
||||
→ request POST /webhook/my-log-summary
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 34건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건이 보고되었습니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
Passed 3/3
|
||||
report: runs/lab02-challenge2-20261031-075051.json
|
||||
|
||||
$ uv run tools/flow.py call my-log-summary requests/week2.json
|
||||
HTTP 200 · 1.8s
|
||||
{
|
||||
"summary": "가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.",
|
||||
"numbers_ok": true,
|
||||
"unknown_numbers": [],
|
||||
"top_topic": "와이파이",
|
||||
"worst_hour": "2026-10-26T09"
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
{
|
||||
"cases": "replay-before.json",
|
||||
"webhook": "my-helpdesk",
|
||||
"passed": 10,
|
||||
"total": 10,
|
||||
"results": [
|
||||
{
|
||||
"case": "before · 회의실 와이파이가 안 ",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 사내 Wi-Fi 비밀번",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 와이파이가 자주 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 노트북이 와이파이에 연",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 손님용 wifi도 있나",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · VPN이 자꾸 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "VPN",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 비밀번호를 잊어버렸어요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "비밀번호",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 프린터 출력이 안 돼요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "프린터",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 메일함 용량이 꽉 찼대",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "메일",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "before · 새 프로그램 설치는 어",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "before"
|
||||
},
|
||||
"response": {
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "설치",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
$ uv run tools/flow.py push reference/helpdesk-live.json
|
||||
✔ pushed '77 Live helpdesk (provided)' (id wb754efbe6b67af7)
|
||||
POST http://localhost:5678/webhook/my-helpdesk
|
||||
|
||||
$ uv run tools/flow.py test cases/replay-before.json -v
|
||||
✔ before: POST http://localhost:8800/logs-reset · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
→ request POST http://localhost:8800/logs-reset
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"cleared": 0
|
||||
}
|
||||
✔ before: POST http://localhost:8800/faq-reset · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
→ request POST http://localhost:8800/faq-reset
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"cleared": 0
|
||||
}
|
||||
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "prompts": "ok", "usage": "ok", "dlq": "ok", "slow_seconds": 1.2}
|
||||
→ request POST http://localhost:8800/admin/fault
|
||||
{
|
||||
"faq": "slow",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"faq": "slow",
|
||||
"tickets": "ok",
|
||||
"prompts": "ok",
|
||||
"usage": "ok",
|
||||
"dlq": "ok",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-helpdesk · 10 case(s) × 1
|
||||
✔ before · 회의실 와이파이가 안 · 2.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 사내 Wi-Fi 비밀번 · 1.2s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 와이파이가 자주 끊겨요 · 1.2s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 노트북이 와이파이에 연 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 손님용 wifi도 있나 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "와이파이",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · VPN이 자꾸 끊겨요 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "VPN",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 비밀번호를 잊어버렸어요 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "비밀번호",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 프린터 출력이 안 돼요 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "프린터",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 메일함 용량이 꽉 찼대 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "메일",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
✔ before · 새 프로그램 설치는 어 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "before"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "담당자 연결",
|
||||
"status": "error",
|
||||
"topic": "설치",
|
||||
"answer": "지금은 답할 수 없어 담당자에게 연결합니다.",
|
||||
"error": "timeout of 1000ms exceeded"
|
||||
}
|
||||
Passed 10/10
|
||||
report: runs/replay-before-20261031-075117.json
|
||||
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "prompts": "ok", "usage": "ok", "dlq": "ok", "slow_seconds": 1.2}
|
||||
→ request POST http://localhost:8800/admin/fault
|
||||
{
|
||||
"faq": "ok",
|
||||
"tickets": "ok"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"faq": "ok",
|
||||
"tickets": "ok",
|
||||
"prompts": "ok",
|
||||
"usage": "ok",
|
||||
"dlq": "ok",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
|
||||
$ uv run tools/flow.py logs --tag before --source live --last 3
|
||||
10 run(s) · errors 10 (100.0%)
|
||||
route: 오류 10
|
||||
2026-10-31T07:51:15 error None 1034ms 프린터 Search FAQ 프린터 출력이 안 돼요
|
||||
2026-10-31T07:51:16 error None 1035ms 메일 Search FAQ 메일함 용량이 꽉 찼대요
|
||||
2026-10-31T07:51:17 error None 1032ms 설치 Search FAQ 새 프로그램 설치는 어떻게 신청하나요?
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✔ 고치기 전 · 10건 모두 실패 · 0.1s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "before",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1,
|
||||
"p50_ms": 1043,
|
||||
"p95_ms": 1098,
|
||||
"by_route": {
|
||||
"오류": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1
|
||||
}
|
||||
},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1043,
|
||||
"p95": 1098
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✔ 고치기 전 · FAQ timeout · 0.1s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "before",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1,
|
||||
"p50_ms": 1043,
|
||||
"p95_ms": 1098,
|
||||
"by_route": {
|
||||
"오류": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1
|
||||
}
|
||||
},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1043,
|
||||
"p95": 1098
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✘ 고친 뒤 · 오류 없음 · 0.1s · 0 calls · $0.000000 · total = 0 (expected 10); by_route.자동 답변 = null (expected 10)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✔ 고친 뒤 · 답하지 못한 주제 없음 · 0.0s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 0,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 0,
|
||||
"p95_ms": 0,
|
||||
"by_route": {},
|
||||
"by_day": {},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
Passed 3/4
|
||||
report: runs/lab03-20261031-075118.json
|
||||
@@ -0,0 +1,218 @@
|
||||
{
|
||||
"cases": "replay-after.json",
|
||||
"webhook": "my-helpdesk",
|
||||
"passed": 10,
|
||||
"total": 10,
|
||||
"results": [
|
||||
{
|
||||
"case": "after · 회의실 와이파이가 안 ",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 4.5,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 사내 Wi-Fi 비밀번",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 비밀번호는 별도로 없으며, 사번으로 인증합니다. 회의실에서 연결이 안 되면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 와이파이가 자주 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실 등 특정 장소에서 와이파이가 자주 끊기면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 노트북이 와이파이에 연",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 손님용 wifi도 있나",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "손님용 와이파이에 대한 안내는 제공되지 않습니다. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인하여 사용합니다. 추가 문의 사항은 IT 포털에 문의해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · VPN이 자꾸 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "VPN",
|
||||
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 비밀번호를 잊어버렸어요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "비밀번호",
|
||||
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 프린터 출력이 안 돼요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "프린터",
|
||||
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 메일함 용량이 꽉 찼대",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.6,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "메일",
|
||||
"answer": "오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "after · 새 프로그램 설치는 어",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "after"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "설치",
|
||||
"answer": "새 프로그램 설치는 IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다.",
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
$ uv run tools/flow.py push reference/lab03-example1-helpdesk-fixed.json
|
||||
! unpublished '77 Live helpdesk (provided)' (wb754efbe6b67af7): it used the same webhook path
|
||||
✔ pushed '77 Live helpdesk fixed (reference)' (id wc5ae6e8bcdf8e39)
|
||||
POST http://localhost:5678/webhook/my-helpdesk
|
||||
|
||||
$ curl -s -XPOST localhost:8800/faq/add -H 'Content-Type: application/json' -d @requests/faq-wifi.json
|
||||
{"added": "FAQ-007", "size": 7}
|
||||
|
||||
$ uv run tools/flow.py test cases/replay-after.json -v
|
||||
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "slow", "tickets": "ok", "prompts": "ok", "usage": "ok", "dlq": "ok", "slow_seconds": 1.2}
|
||||
→ request POST http://localhost:8800/admin/fault
|
||||
{
|
||||
"faq": "slow",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"faq": "slow",
|
||||
"tickets": "ok",
|
||||
"prompts": "ok",
|
||||
"usage": "ok",
|
||||
"dlq": "ok",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-helpdesk · 10 case(s) × 1
|
||||
✔ after · 회의실 와이파이가 안 · 4.5s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 사내 Wi-Fi 비밀번 · 2.4s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 비밀번호는 별도로 없으며, 사번으로 인증합니다. 회의실에서 연결이 안 되면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 와이파이가 자주 끊겨요 · 2.4s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실 등 특정 장소에서 와이파이가 자주 끊기면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 노트북이 와이파이에 연 · 2.3s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 손님용 wifi도 있나 · 2.9s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "손님용 와이파이에 대한 안내는 제공되지 않습니다. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인하여 사용합니다. 추가 문의 사항은 IT 포털에 문의해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · VPN이 자꾸 끊겨요 · 2.4s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "VPN",
|
||||
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 비밀번호를 잊어버렸어요 · 2.3s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "비밀번호",
|
||||
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 프린터 출력이 안 돼요 · 2.3s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "프린터",
|
||||
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 메일함 용량이 꽉 찼대 · 2.6s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "메일",
|
||||
"answer": "오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ after · 새 프로그램 설치는 어 · 2.4s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "after"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "설치",
|
||||
"answer": "새 프로그램 설치는 IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다.",
|
||||
"error": null
|
||||
}
|
||||
Passed 10/10
|
||||
report: runs/replay-after-20261031-075158.json
|
||||
✔ after: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "prompts": "ok", "usage": "ok", "dlq": "ok", "slow_seconds": 1.2}
|
||||
→ request POST http://localhost:8800/admin/fault
|
||||
{
|
||||
"faq": "ok",
|
||||
"tickets": "ok"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"faq": "ok",
|
||||
"tickets": "ok",
|
||||
"prompts": "ok",
|
||||
"usage": "ok",
|
||||
"dlq": "ok",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✔ 고치기 전 · 10건 모두 실패 · 0.1s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "before",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1,
|
||||
"p50_ms": 1043,
|
||||
"p95_ms": 1098,
|
||||
"by_route": {
|
||||
"오류": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1
|
||||
}
|
||||
},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1043,
|
||||
"p95": 1098
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✔ 고치기 전 · FAQ timeout · 0.1s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "before",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1,
|
||||
"p50_ms": 1043,
|
||||
"p95_ms": 1098,
|
||||
"by_route": {
|
||||
"오류": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1
|
||||
}
|
||||
},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1043,
|
||||
"p95": 1098
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✔ 고친 뒤 · 오류 없음 · 0.1s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
✔ 고친 뒤 · 답하지 못한 주제 없음 · 0.0s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
Passed 4/4
|
||||
report: runs/lab03-20261031-075159.json
|
||||
|
||||
$ uv run tools/flow.py ops
|
||||
fault switch: all ok
|
||||
live run logs: 20 (tag before 10 · tag after 10)
|
||||
added FAQ: 1 (FAQ-007)
|
||||
created tickets: 0
|
||||
DLQ: 0 (parked 0)
|
||||
alerts: 0
|
||||
@@ -0,0 +1,459 @@
|
||||
{
|
||||
"cases": "lab03-slo.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 0,
|
||||
"total": 4,
|
||||
"results": [
|
||||
{
|
||||
"case": "둘째 주 · SLO 못 지킴",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"slo.ok = null (expected false)",
|
||||
"unanswered_rate = null (expected 0.0476)",
|
||||
"slo has none of ['\"error_rate\"', '\"unanswered_rate\"']"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 36,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.1111
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 24,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0833
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 70,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 56,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 48,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 65,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0615
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주 · 답 못 함만",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"slo.ok = null (expected false)",
|
||||
"slo.breaches = null (expected [\"unanswered_rate\"])"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "기준을 바꾸면 · 첫 주",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"slo.ok = null (expected true)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "고친 뒤 · 오류 · 답 못 함 기준은 지킴",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"unanswered_rate = null (expected 0)"
|
||||
],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
},
|
||||
"response": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
$ uv run tools/flow.py test cases/lab03-slo.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✘ 둘째 주 · SLO 못 지킴 · 0.1s · 304 calls · $0.039810 · slo.ok = null (expected false); unanswered_rate = null (expected 0.0476); slo has none of ['"error_rate"', '"unanswered_rate"']
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 36,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.1111
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 24,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0833
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 70,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 56,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 48,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 65,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0615
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 첫 주 · 답 못 함만 · 0.1s · 292 calls · $0.038550 · slo.ok = null (expected false); slo.breaches = null (expected ["unanswered_rate"])
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 기준을 바꾸면 · 첫 주 · 0.1s · 292 calls · $0.038550 · slo.ok = null (expected true)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
]
|
||||
}
|
||||
✘ 고친 뒤 · 오류 · 답 못 함 기준은 지킴 · 0.1s · 10 calls · $0.000000 · unanswered_rate = null (expected 0)
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": []
|
||||
}
|
||||
Passed 0/4
|
||||
report: runs/lab03-slo-20261031-075159.json
|
||||
@@ -0,0 +1,495 @@
|
||||
{
|
||||
"cases": "lab03-slo.json",
|
||||
"webhook": "my-log-report",
|
||||
"passed": 4,
|
||||
"total": 4,
|
||||
"results": [
|
||||
{
|
||||
"case": "둘째 주 · SLO 못 지킴",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 36,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.1111
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 24,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0833
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 70,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 56,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 48,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 65,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0615
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.0476,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "첫 주 · 답 못 함만",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.055,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"unanswered_rate"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "기준을 바꾸면 · 첫 주",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.055,
|
||||
"slo": {
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "고친 뒤 · 오류 · 답 못 함 기준은 지킴",
|
||||
"webhook": "my-log-report",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
},
|
||||
"response": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"p95_ms"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
$ uv run tools/flow.py push reference/lab03-example2-slo.json
|
||||
! unpublished '77 Log report + repeat topics (answer)' (wf3d06e3d30eeee6): it used the same webhook path
|
||||
✔ pushed '77 Log report + SLO (reference)' (id wfed832ca6c81732)
|
||||
POST http://localhost:5678/webhook/my-log-report
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03-slo.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✔ 둘째 주 · SLO 못 지킴 · 0.9s · 304 calls · $0.039810
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 357,
|
||||
"errors": 21,
|
||||
"error_rate": 0.0588,
|
||||
"p50_ms": 1171,
|
||||
"p95_ms": 2598,
|
||||
"by_route": {
|
||||
"오류": 21,
|
||||
"자동 답변": 299,
|
||||
"담당자 연결": 17,
|
||||
"잠시 후": 20
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-26": {
|
||||
"total": 70,
|
||||
"errors": 16,
|
||||
"error_rate": 0.2286
|
||||
},
|
||||
"2026-10-27": {
|
||||
"total": 62,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-28": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-29": {
|
||||
"total": 97,
|
||||
"errors": 5,
|
||||
"error_rate": 0.0515
|
||||
},
|
||||
"2026-10-30": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 110,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0273
|
||||
},
|
||||
"1.1.0": {
|
||||
"answers": 181,
|
||||
"repeats": 14,
|
||||
"repeat_rate": 0.0773
|
||||
},
|
||||
"1.2.0": {
|
||||
"answers": 8,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.625
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 337,
|
||||
"p50": 28,
|
||||
"p95": 40
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 304,
|
||||
"p50": 1162,
|
||||
"p95": 2532
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03981,
|
||||
"cost_per_run": 0.000112,
|
||||
"llm_calls": 304,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"node": "Answer",
|
||||
"message": "429 Rate limit reached for requests",
|
||||
"count": 5
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [
|
||||
{
|
||||
"hour": "2026-10-26T09",
|
||||
"total": 16,
|
||||
"errors": 16,
|
||||
"error_rate": 1
|
||||
},
|
||||
{
|
||||
"hour": "2026-10-29T15",
|
||||
"total": 7,
|
||||
"errors": 5,
|
||||
"error_rate": 0.7143
|
||||
}
|
||||
],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 36,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.1111
|
||||
},
|
||||
{
|
||||
"topic": "설치",
|
||||
"answers": 24,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0833
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 70,
|
||||
"repeats": 5,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 56,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0714
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 48,
|
||||
"repeats": 3,
|
||||
"repeat_rate": 0.0625
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 65,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0615
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.0476,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 첫 주 · 답 못 함만 · 0.1s · 292 calls · $0.038550
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.055,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"unanswered_rate"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 기준을 바꾸면 · 첫 주 · 0.1s · 292 calls · $0.038550
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 309,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1252,
|
||||
"p95_ms": 2525,
|
||||
"by_route": {
|
||||
"자동 답변": 292,
|
||||
"담당자 연결": 17
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-19": {
|
||||
"total": 73,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-20": {
|
||||
"total": 57,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-21": {
|
||||
"total": 60,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-22": {
|
||||
"total": 55,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
},
|
||||
"2026-10-23": {
|
||||
"total": 64,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.0.0": {
|
||||
"answers": 292,
|
||||
"repeats": 16,
|
||||
"repeat_rate": 0.0548
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 309,
|
||||
"p50": 27,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 292,
|
||||
"p50": 1200,
|
||||
"p95": 2470
|
||||
}
|
||||
},
|
||||
"cost_usd": 0.03855,
|
||||
"cost_per_run": 0.000125,
|
||||
"llm_calls": 292,
|
||||
"failures": [],
|
||||
"unanswered_topics": [
|
||||
{
|
||||
"topic": "와이파이",
|
||||
"count": 17
|
||||
}
|
||||
],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [
|
||||
{
|
||||
"topic": "프린터",
|
||||
"answers": 45,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0889
|
||||
},
|
||||
{
|
||||
"topic": "VPN",
|
||||
"answers": 59,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.0678
|
||||
},
|
||||
{
|
||||
"topic": "비밀번호",
|
||||
"answers": 69,
|
||||
"repeats": 4,
|
||||
"repeat_rate": 0.058
|
||||
},
|
||||
{
|
||||
"topic": "접수 현황",
|
||||
"answers": 40,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.05
|
||||
},
|
||||
{
|
||||
"topic": "메일",
|
||||
"answers": 42,
|
||||
"repeats": 2,
|
||||
"repeat_rate": 0.0476
|
||||
}
|
||||
],
|
||||
"unanswered_rate": 0.055,
|
||||
"slo": {
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 고친 뒤 · 오류 · 답 못 함 기준은 지킴 · 0.2s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"p95_ms"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
Passed 4/4
|
||||
report: runs/lab03-slo-20261031-075213.json
|
||||
|
||||
$ uv run tools/flow.py call my-log-report '{"tag": "after", "source": "live"}'
|
||||
HTTP 200 · 0.0s
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"p95_ms"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ uv run tools/flow.py test cases/lab02-challenge.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 5 case(s) × 1
|
||||
✔ 두 주 · 다시 묻는 비율이 가장 높은 주제 · 0.1s · 596 calls · $0.078360
|
||||
✔ 두 주 · 수로는 비밀번호가 가장 많지만 비율은 2위 · 0.1s · 596 calls · $0.078360
|
||||
✔ 두 주 · 다시 물은 주제는 6개 · 0.1s · 596 calls · $0.078360
|
||||
✔ 첫 주 · 설치는 다시 묻지 않음 · 0.1s · 292 calls · $0.038550
|
||||
✔ 앞의 지표는 그대로 · 0.1s · 596 calls · $0.078360
|
||||
Passed 5/5
|
||||
report: runs/lab02-challenge-20261031-075213.json
|
||||
@@ -0,0 +1,218 @@
|
||||
{
|
||||
"cases": "replay-normal.json",
|
||||
"webhook": "my-helpdesk",
|
||||
"passed": 10,
|
||||
"total": 10,
|
||||
"results": [
|
||||
{
|
||||
"case": "normal · 회의실 와이파이가 안 ",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 사내 Wi-Fi 비밀번",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 비밀번호는 별도로 없으며, 사번으로 인증합니다. 회의실에서 연결이 안 되면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 와이파이가 자주 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실 등 특정 장소에서 와이파이가 자주 끊기면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 노트북이 와이파이에 연",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 손님용 wifi도 있나",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "손님용 와이파이에 대한 안내는 제공되지 않습니다. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인하여 사용합니다. 추가 문의 사항은 IT 포털에 문의해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · VPN이 자꾸 끊겨요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "VPN",
|
||||
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 비밀번호를 잊어버렸어요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.8,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "비밀번호",
|
||||
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 프린터 출력이 안 돼요",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.2,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "프린터",
|
||||
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 메일함 용량이 꽉 찼대",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "메일",
|
||||
"answer": "오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다.",
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "normal · 새 프로그램 설치는 어",
|
||||
"webhook": "my-helpdesk",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "normal"
|
||||
},
|
||||
"response": {
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "설치",
|
||||
"answer": "새 프로그램 설치는 IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다.",
|
||||
"error": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
$ uv run tools/flow.py test cases/replay-normal.json -v
|
||||
✔ before: POST http://localhost:8800/admin/fault · HTTP 200 · 0.0s · {"faq": "ok", "tickets": "ok", "prompts": "ok", "usage": "ok", "dlq": "ok", "slow_seconds": 1.2}
|
||||
→ request POST http://localhost:8800/admin/fault
|
||||
{
|
||||
"faq": "ok"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"faq": "ok",
|
||||
"tickets": "ok",
|
||||
"prompts": "ok",
|
||||
"usage": "ok",
|
||||
"dlq": "ok",
|
||||
"slow_seconds": 1.2
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-helpdesk · 10 case(s) × 1
|
||||
✔ normal · 회의실 와이파이가 안 · 2.3s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "회의실 와이파이가 안 잡혀요",
|
||||
"user": "이준호",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 사내 Wi-Fi 비밀번 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "사내 Wi-Fi 비밀번호가 뭐예요?",
|
||||
"user": "박서연",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 비밀번호는 별도로 없으며, 사번으로 인증합니다. 회의실에서 연결이 안 되면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 와이파이가 자주 끊겨요 · 1.2s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "와이파이가 자주 끊겨요",
|
||||
"user": "최유진",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실 등 특정 장소에서 와이파이가 자주 끊기면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 노트북이 와이파이에 연 · 0.9s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "노트북이 와이파이에 연결이 안 돼요",
|
||||
"user": "한지훈",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "사내 와이파이는 'CORP-WIFI'에 사번으로 로그인합니다. 회의실에서 와이파이가 잡히지 않으면 가까운 공유기 번호를 IT 포털에 신고해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 손님용 wifi도 있나 · 1.2s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "손님용 wifi도 있나요?",
|
||||
"user": "김민지",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "와이파이",
|
||||
"answer": "손님용 와이파이에 대한 안내는 제공되지 않습니다. 사내 와이파이는 'CORP-WIFI'에 사번으로 로그인하여 사용합니다. 추가 문의 사항은 IT 포털에 문의해 주세요.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · VPN이 자꾸 끊겨요 · 0.9s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "VPN이 자꾸 끊겨요",
|
||||
"user": "정우성",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "VPN",
|
||||
"answer": "VPN 클라이언트를 최신 버전으로 업데이트하고, 인증서 만료 여부를 확인한 뒤 다시 접속합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 비밀번호를 잊어버렸어요 · 0.8s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "비밀번호를 잊어버렸어요",
|
||||
"user": "오세훈",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "비밀번호",
|
||||
"answer": "사내 포털 로그인 화면의 '비밀번호 찾기'에서 휴대폰 인증 후 새 비밀번호를 설정합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 프린터 출력이 안 돼요 · 1.2s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "프린터 출력이 안 돼요",
|
||||
"user": "윤하늘",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "프린터",
|
||||
"answer": "프린터 전원과 네트워크 연결을 확인하고, 인쇄 대기열을 비운 뒤 다시 출력합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 메일함 용량이 꽉 찼대 · 1.0s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "메일함 용량이 꽉 찼대요",
|
||||
"user": "강민수",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "메일",
|
||||
"answer": "오래된 메일과 큰 첨부 파일을 보관함으로 옮기거나 삭제해 용량을 확보합니다.",
|
||||
"error": null
|
||||
}
|
||||
✔ normal · 새 프로그램 설치는 어 · 1.1s
|
||||
→ request POST /webhook/my-helpdesk
|
||||
{
|
||||
"question": "새 프로그램 설치는 어떻게 신청하나요?",
|
||||
"user": "서지아",
|
||||
"tag": "normal"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"route": "자동 답변",
|
||||
"status": "success",
|
||||
"topic": "설치",
|
||||
"answer": "새 프로그램 설치는 IT 포털의 '소프트웨어 신청' 메뉴에서 승인 요청을 올리면 승인 후 원격으로 설치됩니다.",
|
||||
"error": null
|
||||
}
|
||||
Passed 10/10
|
||||
report: runs/replay-normal-20261031-075225.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03-cost.json -v
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✔ 고치기 전 · 느린 FAQ · 1초에 끊김 · 0.1s · 0 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "before",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1,
|
||||
"p50_ms": 1043,
|
||||
"p95_ms": 1098,
|
||||
"by_route": {
|
||||
"오류": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 10,
|
||||
"error_rate": 1
|
||||
}
|
||||
},
|
||||
"by_release": {},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1043,
|
||||
"p95": 1098
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 0,
|
||||
"p50": 0,
|
||||
"p95": 0
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 0,
|
||||
"failures": [
|
||||
{
|
||||
"node": "Search FAQ",
|
||||
"message": "timeout of 1000ms exceeded",
|
||||
"count": 10
|
||||
}
|
||||
],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 고친 뒤 · 느린 FAQ · 기다려서 답함 · 0.1s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "after",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 2342,
|
||||
"p95_ms": 3560,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1238,
|
||||
"p95": 1278
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 1103,
|
||||
"p95": 2282
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"p95_ms"
|
||||
],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 고친 뒤 · 평소 FAQ · 기다릴 일이 없음 · 0.0s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "normal",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1015,
|
||||
"p95_ms": 2147,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 17,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 997,
|
||||
"p95": 2108
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
✔ 고친 뒤 · 답 단계는 FAQ 속도와 상관없음 · 0.1s · 10 calls · $0.000000
|
||||
→ request POST /webhook/my-log-report
|
||||
{
|
||||
"tag": "normal",
|
||||
"source": "live"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0,
|
||||
"p50_ms": 1015,
|
||||
"p95_ms": 2147,
|
||||
"by_route": {
|
||||
"자동 답변": 10
|
||||
},
|
||||
"by_day": {
|
||||
"2026-10-31": {
|
||||
"total": 10,
|
||||
"errors": 0,
|
||||
"error_rate": 0
|
||||
}
|
||||
},
|
||||
"by_release": {
|
||||
"1.1.0": {
|
||||
"answers": 10,
|
||||
"repeats": 0,
|
||||
"repeat_rate": 0
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"faq_ms": {
|
||||
"runs": 10,
|
||||
"p50": 17,
|
||||
"p95": 39
|
||||
},
|
||||
"answer_ms": {
|
||||
"runs": 10,
|
||||
"p50": 997,
|
||||
"p95": 2108
|
||||
}
|
||||
},
|
||||
"cost_usd": 0,
|
||||
"cost_per_run": 0,
|
||||
"llm_calls": 10,
|
||||
"failures": [],
|
||||
"unanswered_topics": [],
|
||||
"anomalies": [],
|
||||
"repeat_topics": [],
|
||||
"unanswered_rate": 0,
|
||||
"slo": {
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"limits": {
|
||||
"error_rate": 0.01,
|
||||
"p95_ms": 3000,
|
||||
"unanswered_rate": 0.02
|
||||
}
|
||||
}
|
||||
}
|
||||
Passed 4/4
|
||||
report: runs/lab03-cost-20261031-075226.json
|
||||
|
||||
$ uv run tools/flow.py ops
|
||||
fault switch: all ok
|
||||
live run logs: 30 (tag before 10 · tag after 10 · tag normal 10)
|
||||
added FAQ: 1 (FAQ-007)
|
||||
created tickets: 0
|
||||
DLQ: 0 (parked 0)
|
||||
alerts: 0
|
||||
@@ -0,0 +1,172 @@
|
||||
{
|
||||
"cases": "lab03-challenge.json",
|
||||
"webhook": "my-weekly",
|
||||
"passed": 0,
|
||||
"total": 7,
|
||||
"results": [
|
||||
{
|
||||
"case": "주간 보고 · 숫자",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"total = null (expected 357)",
|
||||
"error_rate = null (expected 0.0588)",
|
||||
"slo_ok = null (expected false)",
|
||||
"numbers_ok = null (expected true)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 절",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of ['## 숫자']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 문제 절",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of ['## 문제']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 다음 할 일",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of ['## 다음 할 일']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 비교 숫자",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of ['| 요청 | 309 | 357 |']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 이상 시간대",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of ['2026-10-26 09시']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 와이파이",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"markdown has none of [\"FAQ에 '와이파이' 항목 추가\"]"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
$ uv run tools/flow.py test cases/lab03-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-weekly · 7 case(s) × 1
|
||||
✘ 주간 보고 · 숫자 · 0.0s · status 404 (expected 200); total = null (expected 357); error_rate = null (expected 0.0588); slo_ok = null (expected false); numbers_ok = null (expected true)
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 절 · 0.0s · status 404 (expected 200); markdown has none of ['## 숫자']
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 문제 절 · 0.0s · status 404 (expected 200); markdown has none of ['## 문제']
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 다음 할 일 · 0.0s · status 404 (expected 200); markdown has none of ['## 다음 할 일']
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 비교 숫자 · 0.0s · status 404 (expected 200); markdown has none of ['| 요청 | 309 | 357 |']
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 이상 시간대 · 0.0s · status 404 (expected 200); markdown has none of ['2026-10-26 09시']
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 주간 보고 · 와이파이 · 0.0s · status 404 (expected 200); markdown has none of ["FAQ에 '와이파이' 항목 추가"]
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-weekly\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
Passed 0/7
|
||||
report: runs/lab03-challenge-20261031-075226.json
|
||||
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"cases": "lab03-challenge.json",
|
||||
"webhook": "my-weekly",
|
||||
"passed": 7,
|
||||
"total": 7,
|
||||
"results": [
|
||||
{
|
||||
"case": "주간 보고 · 숫자",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 3.6,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로 오류 16건, 전체 16건입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 절",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.9,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 문제 절",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.4,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 다음 할 일",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.7,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 비교 숫자",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 2.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로 오류 16건, 전체 16건입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 이상 시간대",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.8,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "주간 보고 · 와이파이",
|
||||
"webhook": "my-weekly",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.7,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
},
|
||||
"response": {
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
$ uv run tools/flow.py push answers/77-lab03-challenge-weekly.json
|
||||
✔ pushed '77 Weekly report (answer)' (id w269ccf31103289f)
|
||||
POST http://localhost:5678/webhook/my-weekly
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03-challenge.json -v
|
||||
POST http://localhost:5678/webhook/my-weekly · 7 case(s) × 1
|
||||
✔ 주간 보고 · 숫자 · 3.6s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로 오류 16건, 전체 16건입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 절 · 1.9s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 문제 절 · 2.4s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 다음 할 일 · 1.7s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 징후는 10월 26일 09시에 발생했으며, 오류 16건과 총 16건의 요청이 있었습니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 비교 숫자 · 2.0s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로 오류 16건, 전체 16건입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 이상 시간대 · 1.8s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
✔ 주간 보고 · 와이파이 · 1.7s
|
||||
→ request POST /webhook/my-weekly
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30",
|
||||
"prev_from": "2026-10-19",
|
||||
"prev_to": "2026-10-23"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 이상 징후가 가장 심한 시간대는 10월 26일 09시로, 총 16건 중 16건이 오류입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
Passed 7/7
|
||||
report: runs/lab03-challenge-20261031-075252.json
|
||||
|
||||
$ uv run tools/flow.py call my-weekly requests/weekly.json
|
||||
HTTP 200 · 1.9s
|
||||
{
|
||||
"markdown": "# IT 헬프데스크 주간 운영 보고 (2026-10-26 ~ 2026-10-30)\n\n## 숫자\n\n| 지표 | 지난주 | 이번 주 |\n| --- | --- | --- |\n| 요청 | 309 | 357 |\n| 오류율 | 0% | 5.88% |\n| p95 | 2525ms | 2598ms |\n| 답 못 함 | 5.5% | 4.76% |\n| SLO | 못 지킴 unanswered_rate | 못 지킴 error_rate, unanswered_rate |\n\n## 문제\n\n- 실패 16건 · Search FAQ · timeout of 1000ms exceeded\n- 실패 5건 · Answer · 429 Rate limit reached for requests\n- 답하지 못한 질문 17건 · 와이파이\n- 이상 시간대 2026-10-26 09시 · 16/16 실패\n- 이상 시간대 2026-10-29 15시 · 5/7 실패\n\n## 요약 (LLM, 숫자 확인 통과)\n\n가장 많은 미응답 주제는 와이파이로 17건입니다. 가장 흔한 장애는 Search FAQ 노드에서 발생한 \"timeout of 1000ms exceeded\" 메시지로 16건입니다. 가장 심각한 이상 시간대는 10월 26일 09시로 오류 16건, 전체 16건입니다.\n\n## 다음 할 일\n\n- FAQ에 '와이파이' 항목 추가\n- FAQ 검색 timeout · 재시도 설정 점검 (72일차)\n- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)",
|
||||
"total": 357,
|
||||
"error_rate": 0.0588,
|
||||
"slo_ok": false,
|
||||
"numbers_ok": true
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"cases": "lab03-challenge2.json",
|
||||
"webhook": "my-slo-alert",
|
||||
"passed": 0,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "둘째 주 · SLO 못 지킴 → 알림",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"ok = null (expected false)",
|
||||
"breaches = null (expected [\"error_rate\", \"unanswered_rate\"])",
|
||||
"alert = null (expected sent)",
|
||||
"message has none of ['error_rate 0.0588 > 0.01']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "알림 확인 (high · slo)",
|
||||
"webhook": "http://localhost:8800/alerts/latest",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"workflow = null (expected slo)",
|
||||
"severity = null (expected high)",
|
||||
"reason = null (expected slo)",
|
||||
"message has none of ['unanswered_rate']"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {},
|
||||
"response": {}
|
||||
},
|
||||
{
|
||||
"case": "10분 안에 다시 확인 → 보내지 않음",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"ok = null (expected false)",
|
||||
"alert = null (expected suppressed)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "slo 알림은 1개",
|
||||
"webhook": "http://localhost:8800/alerts/list",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"count = 0 (expected 1)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"workflow": "slo"
|
||||
},
|
||||
"response": {
|
||||
"count": 0,
|
||||
"alerts": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "기준 안이면 알림 없음",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"status 404 (expected 200)",
|
||||
"ok = null (expected true)",
|
||||
"breaches = null (expected [])",
|
||||
"alert = null (expected none)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 404,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "그래도 알림은 1개",
|
||||
"webhook": "http://localhost:8800/alerts/list",
|
||||
"run": 1,
|
||||
"passed": false,
|
||||
"problems": [
|
||||
"count = 0 (expected 1)"
|
||||
],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"workflow": "slo"
|
||||
},
|
||||
"response": {
|
||||
"count": 0,
|
||||
"alerts": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
$ uv run tools/flow.py test cases/lab03-challenge2.json -v
|
||||
✔ before: POST http://localhost:8800/alerts-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
→ request POST http://localhost:8800/alerts-clear
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"cleared": 0
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-slo-alert · 6 case(s) × 1
|
||||
✘ 둘째 주 · SLO 못 지킴 → 알림 · 0.0s · status 404 (expected 200); ok = null (expected false); breaches = null (expected ["error_rate", "unanswered_rate"]); alert = null (expected sent); message has none of ['error_rate 0.0588 > 0.01']
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 알림 확인 (high · slo) · 0.0s · workflow = null (expected slo); severity = null (expected high); reason = null (expected slo); message has none of ['unanswered_rate']
|
||||
→ request POST http://localhost:8800/alerts/latest
|
||||
{}
|
||||
← response HTTP 200
|
||||
{}
|
||||
✘ 10분 안에 다시 확인 → 보내지 않음 · 0.0s · status 404 (expected 200); ok = null (expected false); alert = null (expected suppressed)
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ slo 알림은 1개 · 0.0s · count = 0 (expected 1)
|
||||
→ request POST http://localhost:8800/alerts/list
|
||||
{
|
||||
"workflow": "slo"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"count": 0,
|
||||
"alerts": []
|
||||
}
|
||||
✘ 기준 안이면 알림 없음 · 0.0s · status 404 (expected 200); ok = null (expected true); breaches = null (expected []); alert = null (expected none)
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
← response HTTP 404
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested webhook \"POST my-slo-alert\" is not registered.",
|
||||
"hint": "The workflow must be active for a production URL to run successfully. You can activate the workflow using the toggle in the top-right of the editor. Note that unlike test URL calls, production URL calls aren't shown on the canvas (only in the executions list)"
|
||||
}
|
||||
✘ 그래도 알림은 1개 · 0.0s · count = 0 (expected 1)
|
||||
→ request POST http://localhost:8800/alerts/list
|
||||
{
|
||||
"workflow": "slo"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"count": 0,
|
||||
"alerts": []
|
||||
}
|
||||
Passed 0/6
|
||||
report: runs/lab03-challenge2-20261031-075255.json
|
||||
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"cases": "lab03-challenge2.json",
|
||||
"webhook": "my-slo-alert",
|
||||
"passed": 6,
|
||||
"total": 6,
|
||||
"results": [
|
||||
{
|
||||
"case": "둘째 주 · SLO 못 지킴 → 알림",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 1.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"alert": "sent",
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"total": 357
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "알림 확인 (high · slo)",
|
||||
"webhook": "http://localhost:8800/alerts/latest",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {},
|
||||
"response": {
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "10분 안에 다시 확인 → 보내지 않음",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.3,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
},
|
||||
"response": {
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"alert": "suppressed",
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"total": 357
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "slo 알림은 1개",
|
||||
"webhook": "http://localhost:8800/alerts/list",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"workflow": "slo"
|
||||
},
|
||||
"response": {
|
||||
"count": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "기준 안이면 알림 없음",
|
||||
"webhook": "my-slo-alert",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.1,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"alert": "none",
|
||||
"message": "",
|
||||
"total": 309
|
||||
}
|
||||
},
|
||||
{
|
||||
"case": "그래도 알림은 1개",
|
||||
"webhook": "http://localhost:8800/alerts/list",
|
||||
"run": 1,
|
||||
"passed": true,
|
||||
"problems": [],
|
||||
"seconds": 0.0,
|
||||
"status": 200,
|
||||
"request": {
|
||||
"workflow": "slo"
|
||||
},
|
||||
"response": {
|
||||
"count": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
$ uv run tools/flow.py push answers/77-lab03-challenge2-slo-alert.json
|
||||
✔ pushed '77 SLO alert (answer)' (id w46a402ba838f33c)
|
||||
POST http://localhost:5678/webhook/my-slo-alert
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03-challenge2.json -v
|
||||
✔ before: POST http://localhost:8800/alerts-clear · HTTP 200 · 0.0s · {"cleared": 0}
|
||||
→ request POST http://localhost:8800/alerts-clear
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"cleared": 0
|
||||
}
|
||||
POST http://localhost:5678/webhook/my-slo-alert · 6 case(s) × 1
|
||||
✔ 둘째 주 · SLO 못 지킴 → 알림 · 1.0s
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"alert": "sent",
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"total": 357
|
||||
}
|
||||
✔ 알림 확인 (high · slo) · 0.0s
|
||||
→ request POST http://localhost:8800/alerts/latest
|
||||
{}
|
||||
← response HTTP 200
|
||||
{
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
✔ 10분 안에 다시 확인 → 보내지 않음 · 0.3s
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-26",
|
||||
"to": "2026-10-30"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"ok": false,
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"alert": "suppressed",
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"total": 357
|
||||
}
|
||||
✔ slo 알림은 1개 · 0.0s
|
||||
→ request POST http://localhost:8800/alerts/list
|
||||
{
|
||||
"workflow": "slo"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"count": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
]
|
||||
}
|
||||
✔ 기준 안이면 알림 없음 · 0.1s
|
||||
→ request POST /webhook/my-slo-alert
|
||||
{
|
||||
"from": "2026-10-19",
|
||||
"to": "2026-10-23",
|
||||
"slo": {
|
||||
"unanswered_rate": 0.06
|
||||
}
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"ok": true,
|
||||
"breaches": [],
|
||||
"alert": "none",
|
||||
"message": "",
|
||||
"total": 309
|
||||
}
|
||||
✔ 그래도 알림은 1개 · 0.0s
|
||||
→ request POST http://localhost:8800/alerts/list
|
||||
{
|
||||
"workflow": "slo"
|
||||
}
|
||||
← response HTTP 200
|
||||
{
|
||||
"count": 1,
|
||||
"alerts": [
|
||||
{
|
||||
"workflow": "slo",
|
||||
"severity": "high",
|
||||
"reason": "slo",
|
||||
"breaches": [
|
||||
"error_rate",
|
||||
"unanswered_rate"
|
||||
],
|
||||
"message": "SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476 > 0.02",
|
||||
"at": 1793400787.7696092
|
||||
}
|
||||
]
|
||||
}
|
||||
Passed 6/6
|
||||
report: runs/lab03-challenge2-20261031-075308.json
|
||||
|
||||
$ uv run tools/flow.py test cases/lab03-slo.json
|
||||
POST http://localhost:5678/webhook/my-log-report · 4 case(s) × 1
|
||||
✔ 둘째 주 · SLO 못 지킴 · 0.1s · 304 calls · $0.039810
|
||||
✔ 첫 주 · 답 못 함만 · 0.1s · 292 calls · $0.038550
|
||||
✔ 기준을 바꾸면 · 첫 주 · 0.1s · 292 calls · $0.038550
|
||||
✔ 고친 뒤 · 오류 · 답 못 함 기준은 지킴 · 0.1s · 10 calls · $0.000000
|
||||
Passed 4/4
|
||||
report: runs/lab03-slo-20261031-075308.json
|
||||
|
||||
$ uv run tools/flow.py ops
|
||||
fault switch: all ok
|
||||
live run logs: 30 (tag before 10 · tag after 10 · tag normal 10)
|
||||
added FAQ: 1 (FAQ-007)
|
||||
created tickets: 0
|
||||
DLQ: 0 (parked 0)
|
||||
alerts: 1
|
||||
[high] slo · slo · - · SLO breach: error_rate 0.0588 > 0.01, unanswered_rate 0.0476
|
||||
|
||||
$ uv run tools/flow.py ops --reset
|
||||
✔ live run logs, added FAQ, created tickets, DLQ and alerts emptied, fault switch all ok
|
||||
|
||||
$ 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
|
||||
✔ dead letter queue /dlq · alerts · fault switch dlq (day 76 data API)
|
||||
✔ run logs /logs (666 seeded runs) · FAQ additions /faq-extra (day 77 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,101 @@
|
||||
{
|
||||
"name": "77 Log report + days and releases (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Report webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-log-report",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get logs",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://data-api:8800/logs/query",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from ?? '', to: $json.body.to ?? '', tag: $json.body.tag ?? '', source: $json.body.source ?? 'all' }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Compute metrics",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const pct = (vals, p) => { const s = [...vals].sort((a, b) => a - b); return s.length ? s[Math.max(0, Math.ceil(p * s.length) - 1)] : 0; };\nconst rate = (a, b) => (b ? Math.round((a / b) * 10000) / 10000 : 0);\nconst rows = $json.logs ?? [];\nconst errors = rows.filter((r) => r.status === 'error').length;\nconst by_route = {};\nfor (const r of rows) { const k = r.route ?? '오류'; by_route[k] = (by_route[k] ?? 0) + 1; }\nconst d = rows.map((r) => r.duration_ms);\nconst out = { total: rows.length, errors, error_rate: rate(errors, rows.length), p50_ms: pct(d, 0.5), p95_ms: pct(d, 0.95), by_route };\nconst group = (key) => { const g = {}; for (const r of rows) (g[key(r)] ??= []).push(r); return g; };\nout.by_day = Object.fromEntries(Object.entries(group((r) => r.started_at.slice(0, 10))).sort().map(([k, v]) => {\n const e = v.filter((r) => r.status === 'error').length; return [k, { total: v.length, errors: e, error_rate: rate(e, v.length) }]; }));\nout.by_release = {};\nfor (const r of rows.filter((x) => x.route === '자동 답변')) { const b = (out.by_release[r.release] ??= { answers: 0, repeats: 0 }); b.answers += 1; if (r.repeat) b.repeats += 1; }\nfor (const b of Object.values(out.by_release)) b.repeat_rate = rate(b.repeats, b.answers);\nreturn [{ json: out }];"
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send report",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
660,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Report webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get logs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get logs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Compute metrics",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Compute metrics": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "77 Log report + steps and cost (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Report webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-log-report",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get logs",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://data-api:8800/logs/query",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from ?? '', to: $json.body.to ?? '', tag: $json.body.tag ?? '', source: $json.body.source ?? 'all' }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Compute metrics",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const pct = (vals, p) => { const s = [...vals].sort((a, b) => a - b); return s.length ? s[Math.max(0, Math.ceil(p * s.length) - 1)] : 0; };\nconst rate = (a, b) => (b ? Math.round((a / b) * 10000) / 10000 : 0);\nconst rows = $json.logs ?? [];\nconst errors = rows.filter((r) => r.status === 'error').length;\nconst by_route = {};\nfor (const r of rows) { const k = r.route ?? '오류'; by_route[k] = (by_route[k] ?? 0) + 1; }\nconst d = rows.map((r) => r.duration_ms);\nconst out = { total: rows.length, errors, error_rate: rate(errors, rows.length), p50_ms: pct(d, 0.5), p95_ms: pct(d, 0.95), by_route };\nconst group = (key) => { const g = {}; for (const r of rows) (g[key(r)] ??= []).push(r); return g; };\nout.by_day = Object.fromEntries(Object.entries(group((r) => r.started_at.slice(0, 10))).sort().map(([k, v]) => {\n const e = v.filter((r) => r.status === 'error').length; return [k, { total: v.length, errors: e, error_rate: rate(e, v.length) }]; }));\nout.by_release = {};\nfor (const r of rows.filter((x) => x.route === '자동 답변')) { const b = (out.by_release[r.release] ??= { answers: 0, repeats: 0 }); b.answers += 1; if (r.repeat) b.repeats += 1; }\nfor (const b of Object.values(out.by_release)) b.repeat_rate = rate(b.repeats, b.answers);\nout.steps = {};\nfor (const k of ['faq_ms', 'answer_ms']) { const v = rows.map((r) => r.steps?.[k] ?? 0).filter((x) => x > 0); out.steps[k] = { runs: v.length, p50: pct(v, 0.5), p95: pct(v, 0.95) }; }\nout.cost_usd = Math.round(rows.reduce((a, r) => a + (r.cost_usd ?? 0), 0) * 100000) / 100000;\nout.cost_per_run = rows.length ? Math.round((out.cost_usd / rows.length) * 1000000) / 1000000 : 0;\nout.llm_calls = rows.reduce((a, r) => a + (r.llm_calls ?? 0), 0);\nreturn [{ json: out }];"
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send report",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
660,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Report webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get logs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get logs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Compute metrics",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Compute metrics": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "77 Log report + repeat topics (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Report webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-log-report",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get logs",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://data-api:8800/logs/query",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from ?? '', to: $json.body.to ?? '', tag: $json.body.tag ?? '', source: $json.body.source ?? 'all' }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Compute metrics",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const pct = (vals, p) => { const s = [...vals].sort((a, b) => a - b); return s.length ? s[Math.max(0, Math.ceil(p * s.length) - 1)] : 0; };\nconst rate = (a, b) => (b ? Math.round((a / b) * 10000) / 10000 : 0);\nconst rows = $json.logs ?? [];\nconst errors = rows.filter((r) => r.status === 'error').length;\nconst by_route = {};\nfor (const r of rows) { const k = r.route ?? '오류'; by_route[k] = (by_route[k] ?? 0) + 1; }\nconst d = rows.map((r) => r.duration_ms);\nconst out = { total: rows.length, errors, error_rate: rate(errors, rows.length), p50_ms: pct(d, 0.5), p95_ms: pct(d, 0.95), by_route };\nconst group = (key) => { const g = {}; for (const r of rows) (g[key(r)] ??= []).push(r); return g; };\nout.by_day = Object.fromEntries(Object.entries(group((r) => r.started_at.slice(0, 10))).sort().map(([k, v]) => {\n const e = v.filter((r) => r.status === 'error').length; return [k, { total: v.length, errors: e, error_rate: rate(e, v.length) }]; }));\nout.by_release = {};\nfor (const r of rows.filter((x) => x.route === '자동 답변')) { const b = (out.by_release[r.release] ??= { answers: 0, repeats: 0 }); b.answers += 1; if (r.repeat) b.repeats += 1; }\nfor (const b of Object.values(out.by_release)) b.repeat_rate = rate(b.repeats, b.answers);\nout.steps = {};\nfor (const k of ['faq_ms', 'answer_ms']) { const v = rows.map((r) => r.steps?.[k] ?? 0).filter((x) => x > 0); out.steps[k] = { runs: v.length, p50: pct(v, 0.5), p95: pct(v, 0.95) }; }\nout.cost_usd = Math.round(rows.reduce((a, r) => a + (r.cost_usd ?? 0), 0) * 100000) / 100000;\nout.cost_per_run = rows.length ? Math.round((out.cost_usd / rows.length) * 1000000) / 1000000 : 0;\nout.llm_calls = rows.reduce((a, r) => a + (r.llm_calls ?? 0), 0);\nconst fails = {};\nfor (const r of rows.filter((x) => x.status === 'error')) { const k = `${r.error_node}|${r.error_message}`; fails[k] = (fails[k] ?? 0) + 1; }\nout.failures = Object.entries(fails).map(([k, count]) => ({ node: k.split('|')[0], message: k.split('|')[1], count })).sort((a, b) => b.count - a.count);\nconst un = {};\nfor (const r of rows.filter((x) => x.route === '담당자 연결')) un[r.topic] = (un[r.topic] ?? 0) + 1;\nout.unanswered_topics = Object.entries(un).map(([topic, count]) => ({ topic, count })).sort((a, b) => b.count - a.count);\nconst hours = {};\nfor (const r of rows) (hours[r.started_at.slice(0, 13)] ??= []).push(r);\nout.anomalies = Object.entries(hours).sort().map(([hour, v]) => { const e = v.filter((r) => r.status === 'error').length; return { hour, total: v.length, errors: e, error_rate: rate(e, v.length) }; })\n .filter((h) => h.total >= 5 && h.error_rate >= 3 * out.error_rate && h.errors > 0);\nconst rep = {};\nfor (const r of rows.filter((x) => x.route === '자동 답변')) { const t = (rep[r.topic] ??= { topic: r.topic, answers: 0, repeats: 0 }); t.answers += 1; if (r.repeat) t.repeats += 1; }\nout.repeat_topics = Object.values(rep).filter((t) => t.repeats > 0).map((t) => ({ ...t, repeat_rate: rate(t.repeats, t.answers) }))\n .sort((a, b) => b.repeat_rate - a.repeat_rate || b.repeats - a.repeats);\nreturn [{ json: out }];"
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send report",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
660,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Report webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get logs",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get logs": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Compute metrics",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Compute metrics": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
{
|
||||
"name": "77 Log summary (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Summary webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-log-summary",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get report",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:5678/webhook/my-log-report",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from ?? '', to: $json.body.to ?? '', tag: $json.body.tag ?? '', source: $json.body.source ?? 'all' }) }}",
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write summary",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"typeVersion": 3.1,
|
||||
"parameters": {
|
||||
"promptType": "define",
|
||||
"text": "={{ JSON.stringify({ total: $json.total, errors: $json.errors, error_rate: $json.error_rate, failures: $json.failures, unanswered_topics: $json.unanswered_topics, anomalies: $json.anomalies }) }}",
|
||||
"options": {
|
||||
"systemMessage": "You write a short Korean operations summary for an IT help desk team from the statistics JSON given. Write exactly 3 sentences in 합니다체. Use only numbers that appear in the statistics (you may write a rate like 0.0588 as 5.88%). Sentence 1: the biggest unanswered topic and its count. Sentence 2: the most common failure (node, message, count). Sentence 3: the worst hour from anomalies, written like 10월 26일 09시, with its errors and total. Do not guess causes that are not in the data. Do not add any other numbers.",
|
||||
"maxIterations": 1
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Summary model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"typeVersion": 1.2,
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "id",
|
||||
"value": "gpt-4.1-mini"
|
||||
},
|
||||
"options": {
|
||||
"temperature": 0
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
520
|
||||
],
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "openAiCourse0001",
|
||||
"name": "OpenAI (.env)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Check numbers",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const stats = $('Get report').first().json;\nconst summary = String($json.output ?? '').trim();\nconst allowed = new Set();\nconst addNum = (v) => { allowed.add(String(v)); for (const k of [100, 10, 1]) allowed.add(String(Math.round(v * 100 * k) / k)); };\nconst walk = (v) => {\n if (Array.isArray(v)) v.forEach(walk);\n else if (v && typeof v === 'object') Object.entries(v).forEach(([k, x]) => { (String(k).match(/\\d+(\\.\\d+)?/g) ?? []).forEach((n) => allowed.add(String(Number(n)))); walk(x); });\n else if (typeof v === 'string') (v.match(/\\d+(\\.\\d+)?/g) ?? []).forEach((n) => allowed.add(String(Number(n))));\n else if (typeof v === 'number') addNum(v);\n};\nwalk(stats);\nconst found = (summary.match(/\\d+(\\.\\d+)?/g) ?? []).map((n) => String(Number(n)));\nconst unknown = [...new Set(found.filter((n) => !allowed.has(n)))];\nconst worst = [...(stats.anomalies ?? [])].sort((a, b) => b.error_rate - a.error_rate)[0];\nreturn [{ json: { summary, numbers_ok: unknown.length === 0, unknown_numbers: unknown,\n top_topic: stats.unanswered_topics?.[0]?.topic ?? null, worst_hour: worst?.hour ?? null } }];"
|
||||
},
|
||||
"position": [
|
||||
760,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send summary",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
980,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Summary webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get report": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Write summary",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Write summary": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check numbers",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check numbers": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send summary",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Summary model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "Write summary",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"name": "77 Weekly report (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "Weekly webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-weekly",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "This week",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:5678/webhook/my-log-report",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from, to: $json.body.to }) }}",
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Last week",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:5678/webhook/my-log-report",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $('Weekly webhook').first().json.body.prev_from, to: $('Weekly webhook').first().json.body.prev_to }) }}",
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Summary",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:5678/webhook/my-log-summary",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $('Weekly webhook').first().json.body.from, to: $('Weekly webhook').first().json.body.to }) }}",
|
||||
"options": {
|
||||
"timeout": 60000
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
660,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Write report",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const b = $('Weekly webhook').first().json.body;\nconst now = $('This week').first().json, prev = $('Last week').first().json, s = $('Summary').first().json;\nconst pctS = (v) => `${Math.round(v * 10000) / 100}%`;\nconst hour = (h) => `${h.slice(0, 10)} ${h.slice(11, 13)}시`;\nconst slo = (r) => (r.slo?.ok ? '지킴' : '못 지킴 ' + (r.slo?.breaches ?? []).join(', '));\nconst lines = [`# IT 헬프데스크 주간 운영 보고 (${b.from} ~ ${b.to})`, ''];\nlines.push('## 숫자', '', '| 지표 | 지난주 | 이번 주 |', '| --- | --- | --- |');\nlines.push(`| 요청 | ${prev.total} | ${now.total} |`, `| 오류율 | ${pctS(prev.error_rate)} | ${pctS(now.error_rate)} |`,\n `| p95 | ${prev.p95_ms}ms | ${now.p95_ms}ms |`, `| 답 못 함 | ${pctS(prev.unanswered_rate)} | ${pctS(now.unanswered_rate)} |`, `| SLO | ${slo(prev)} | ${slo(now)} |`, '');\nlines.push('## 문제', '');\nfor (const f of (now.failures ?? []).slice(0, 3)) lines.push(`- 실패 ${f.count}건 · ${f.node} · ${f.message}`);\nfor (const u of (now.unanswered_topics ?? []).slice(0, 3)) lines.push(`- 답하지 못한 질문 ${u.count}건 · ${u.topic}`);\nfor (const a of now.anomalies ?? []) lines.push(`- 이상 시간대 ${hour(a.hour)} · ${a.errors}/${a.total} 실패`);\nlines.push('', '## 요약 (LLM, 숫자 확인 ' + (s.numbers_ok ? '통과' : '실패: ' + s.unknown_numbers.join(', ')) + ')', '', s.summary, '');\nlines.push('## 다음 할 일', '');\nif ((now.unanswered_topics ?? [])[0]) lines.push(`- FAQ에 '${now.unanswered_topics[0].topic}' 항목 추가`);\nif ((now.failures ?? []).some((f) => f.node === 'Search FAQ')) lines.push('- FAQ 검색 timeout · 재시도 설정 점검 (72일차)');\nif ((now.failures ?? []).some((f) => /429/.test(f.message))) lines.push('- LLM 사용량 한도 · 대체 경로 점검 (75 · 72일차)');\nreturn [{ json: { markdown: lines.join('\\n'), total: now.total, error_rate: now.error_rate, slo_ok: now.slo?.ok ?? null, numbers_ok: s.numbers_ok } }];"
|
||||
},
|
||||
"position": [
|
||||
880,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send weekly",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
1100,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Weekly webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "This week",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"This week": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Last week",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Last week": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Summary",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Summary": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Write report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Write report": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send weekly",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
{
|
||||
"name": "77 SLO alert (answer)",
|
||||
"nodes": [
|
||||
{
|
||||
"name": "SLO webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"httpMethod": "POST",
|
||||
"path": "my-slo-alert",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Get report",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:5678/webhook/my-log-report",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ from: $json.body.from ?? '', to: $json.body.to ?? '', tag: $json.body.tag ?? '', source: $json.body.source ?? 'all', slo: $json.body.slo ?? {} }) }}",
|
||||
"options": {
|
||||
"timeout": 30000
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Recent alerts",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://data-api:8800/alerts/list",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ workflow: 'slo', reason: 'slo', within: 600 }) }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
440,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Decide",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "const r = $('Get report').first().json;\nconst recent = $json.count ?? 0;\nconst breaches = r.slo?.breaches ?? [];\nconst message = breaches.length ? 'SLO breach: ' + breaches.map((k) => `${k} ${r[k]} > ${r.slo.limits[k]}`).join(', ') : '';\nconst alert = breaches.length === 0 ? 'none' : recent > 0 ? 'suppressed' : 'sent';\nreturn [{ json: { ok: breaches.length === 0, breaches, alert, message, total: r.total } }];"
|
||||
},
|
||||
"position": [
|
||||
660,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send it?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 2.2,
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"options": {
|
||||
"caseSensitive": true,
|
||||
"leftValue": "",
|
||||
"typeValidation": "loose",
|
||||
"version": 2
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"id": "c1",
|
||||
"leftValue": "={{ $json.alert === 'sent' }}",
|
||||
"rightValue": "",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "true",
|
||||
"singleValue": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"combinator": "and"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
880,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send alert",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.2,
|
||||
"parameters": {
|
||||
"method": "POST",
|
||||
"url": "http://data-api:8800/alerts",
|
||||
"sendBody": true,
|
||||
"specifyBody": "json",
|
||||
"jsonBody": "={{ JSON.stringify({ workflow: 'slo', severity: 'high', reason: 'slo', breaches: $json.breaches, message: $json.message }) }}",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"neverError": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"position": [
|
||||
1100,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Result",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"parameters": {
|
||||
"jsCode": "return [{ json: $('Decide').first().json }];"
|
||||
},
|
||||
"position": [
|
||||
1320,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Send result",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.1,
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {}
|
||||
},
|
||||
"position": [
|
||||
1540,
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"SLO webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get report",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get report": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Recent alerts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Recent alerts": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Decide",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Decide": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send it?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send it?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send alert",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Result",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Send alert": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Result",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Result": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send result",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user