부록 C · 실제 LLM 대화 기록 (스냅샷)¶
자동 생성 문서 — 손으로 편집하지 마세요. 갱신:
cd agent && uv run python scripts/export_llm_log.py후./deploy-docs.sh출처:
logs/llm/llm-2026-07-03.jsonl· 생성: 2026-07-03T15:04 · 모델: openai/gpt-oss-120b
에이전트가 실제로 OpenRouter(gpt-oss-120b)와 주고받은 기록입니다. 거래 데이터는 전부 fixture(가짜)이며, 운영에서도 개인정보는 WAS 단계에서 마스킹됩니다(ADR-009).
시스템 프롬프트 (모든 호출 공통)¶
펼쳐서 보기 — app/llm/prompts.py 가 조립한 실제 전송본
당신은 카드 선결제 서비스의 거래 선택 어시스턴트입니다.
## 역할
사용자의 자연어 요청을 해석해서, 제공된 선결제 대상 거래 목록에서 조건에 맞는 거래를 고릅니다.
당신은 거래를 '고르기만' 합니다 — 결제 실행, 합계 계산, 사용자 안내 문구 작성은 시스템의 다른 부분이 담당합니다.
## 거래 데이터 필드
- tx_id: 거래 고유 ID (선택 결과로 반환할 값)
- merchant: 가맹점명
- approved_at: 승인 일시 (ISO 8601, 로컬 시간)
- amount: 결제 금액 (원)
## 판단 규칙
1. intent 결정 기준:
- "select": 사용자가 조건에 맞는 거래를 찾거나 확인하려는 단계. "~하고 싶어", "~할래", "~보여줘" 같은
희망·탐색 표현은 결제 단어가 있어도 select 입니다 (시스템이 목록을 먼저 보여주고 확인받습니다)
- "pay": 사용자가 즉시 실행을 지시하는 경우만 — 명령형 지시("결제해줘", "진행해", "지금 바로 결제")
또는 직전 제시 목록의 특정 항목 지칭("2번째 것만 결제해줘")
- "clarify": 요청이 모호해서 조건을 되물어야 하는 경우 — question 에 되물을 문구를, 가능하면 options 에 선택지를 담기
- "out_of_scope": 선결제와 무관한 요청 (리볼빙, 카드 발급, 한도 조회, 일상 잡담 등)
2. tx_ids 에는 반드시 제공된 목록에 실재하는 tx_id 만 넣습니다. 절대 지어내지 않습니다.
3. 제공된 거래 목록은 이미 선결제 대상으로 조회된 범위입니다. "이번달"처럼 목록 전체 범위를 가리키는 기간 표현은
목록 전체를 대상으로 삼고, 그 위에 시간대·가맹점 등 나머지 조건만 적용합니다.
4. 시간대 해석 (approved_at 의 시각 기준): "아침"=05:00~11:59, "점심"=12:00~14:59, "저녁/밤"=17:00~23:59.
5. 직전 제시 목록(presented)이 주어지고 사용자가 "2번째", "첫 번째 거"처럼 서수로 지칭하면,
그 목록의 order 값을 기준으로 해석합니다.
6. 조건에 맞는 거래가 하나도 없으면 intent="select", tx_ids=[] 로 답합니다 (시스템이 되묻기로 처리합니다).
## 판단 예시
- "이번달 아침에 스타벅스에서 커피 먹은 거 전부 결제하고 싶어" → intent="select" ("~하고 싶어"는 탐색·확인 단계)
- "아까 보여준 것 중 2번째 것만 결제해줘" → intent="pay" (제시된 항목에 대한 명령형 지시)
- "커피값 미리 낼게" (기간·가맹점 불명확, 후보가 여러 갈래) → intent="clarify"
- "카드 한도 좀 올려줘" → intent="out_of_scope"
## 출력 형식
반드시 아래 형식의 JSON 하나만 출력합니다. JSON 외의 텍스트·설명·코드펜스를 출력하지 않습니다.
{"intent": "select|pay|clarify|out_of_scope", "tx_ids": ["..."], "question": null, "options": null, "reason": "판단 근거 한 줄"}
user 프롬프트의 형태 (사례 1의 실제 전송본)¶
펼쳐서 보기 — 거래 목록 + 사용자 발화가 이렇게 담겨 간다
오늘 날짜: 2026-07-03
## 선결제 대상 거래 목록
[
{
"tx_id": "TX20260601-001",
"merchant": "스타벅스 강남점",
"approved_at": "2026-06-01T08:12:00",
"amount": 6100
},
{
"tx_id": "TX20260605-014",
"merchant": "스타벅스 역삼점",
"approved_at": "2026-06-05T08:45:00",
"amount": 5600
},
{
"tx_id": "TX20260610-002",
"merchant": "버거하우스",
"approved_at": "2026-06-10T12:30:00",
"amount": 12000
},
{
"tx_id": "TX20260618-007",
"merchant": "스타벅스 선릉점",
"approved_at": "2026-06-18T09:02:00",
"amount": 6800
},
{
"tx_id": "TX20260625-011",
"merchant": "스타벅스 역삼점",
"approved_at": "2026-06-25T19:40:00",
"amount": 5600
}
]
## 사용자 요청
이번달 아침에 스타벅스에서 커피 먹은 거 전부 결제하고 싶어
정상 사례¶
사례 1 — “이번달 아침에 스타벅스에서 커피 먹은 거 전부 결제하고 싶어”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| Together | 2497ms | 1203 / 173 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [
"TX20260601-001",
"TX20260605-014",
"TX20260618-007"
],
"question": null,
"options": null,
"reason": "사용자가 이번달 아침에 스타벅스에서 커피 먹은 거래 전부 결제하고 싶어 함, 해당 조건에 맞는 거래 3건 선택"
}
사례 2 — “아침에 스벅 간 거 다 미리 낼게”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| DekaLLM | 4587ms | 1191 / 159 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [
"TX20260601-001",
"TX20260605-014",
"TX20260618-007"
],
"question": null,
"options": null,
"reason": "아침(05:00~11:59) 시간대에 승인된 스타벅스 거래만 선택"
}
사례 3 — “오전에 스타벅스에서 쓴 커피값 전부 정리해줘”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| WandB | 2969ms | 1193 / 177 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [
"TX20260601-001",
"TX20260605-014",
"TX20260618-007"
],
"question": null,
"options": null,
"reason": "사용자가 오전에 스타벅스에서 사용한 거래를 모두 보고 싶어함"
}
사례 4 — “스타벅스에서 쓴 거 전부 보여줘”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| Mancer 2 | 3598ms | 1189 / 118 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [
"TX20260601-001",
"TX20260605-014",
"TX20260618-007",
"TX20260625-011"
],
"question": null,
"options": null,
"reason": "사용자가 스타벅스 거래 전부를 보고 싶다고 요청했으므로 select이며 해당 가맹점의 tx_id만 반환"
}
사례 5 — “버거하우스에서 쓴 거 지금 바로 결제해줘”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| WandB | 2239ms | 1190 / 126 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "pay",
"tx_ids": [
"TX20260610-002"
],
"question": null,
"options": null,
"reason": "사용자가 '버거하우스에서 쓴 거 지금 바로 결제해줘'라며 즉시 결제를 지시했으며 해당 거래는 목록에 존재"
}
사례 6 — “리볼빙 신청하고 싶은데”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| DeepInfra | 1237ms | 1174 / 59 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "out_of_scope",
"tx_ids": [],
"question": null,
"options": null,
"reason": "리볼빙 신청은 선결제와 무관한 요청"
}
사례 7 — “새벽 2시에 결제한 내역 선결제해줘”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| Novita | 1477ms | 1194 / 188 | 미사용(프롬프트 강제) | — |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [],
"question": null,
"options": null,
"reason": "Requested time 02:00 does not match any transaction in the list"
}
사례 8 — “이번달 아침에 스타벅스에서 커피 먹은 거 전부 결제하고 싶어”¶
| 하위 제공자 | 지연 | 토큰 (입력/출력) | structured output | trace_id |
|---|---|---|---|---|
| DekaLLM | 8166ms | 1199 / 178 | 미사용(프롬프트 강제) | tr-9f8e7d |
모델 원문 응답:
{
"intent": "select",
"tx_ids": [
"TX20260601-001",
"TX20260605-014",
"TX20260618-007"
],
"question": null,
"options": null,
"reason": "요청은 이번달 아침(05:00~11:59) 스타벅스 거래 전부 선택하는 것으로, 해당 조건에 맞는 3건을 반환"
}
오염 응답 사례 — ADR-010 의 증거¶
structured output(json_schema) 모드에서 일부 하위 제공자가 반환한 실제 오염 응답입니다. 깨진 이스케이프(\":) 뒤로 공백이 토큰 한도까지 이어집니다:
| 하위 제공자 | 지연 | structured output |
|---|---|---|
| (기록 전) | 25118ms | 사용 |