From c2760c804feb79d8596b6242a3cd5f6d856fee74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AF=BC=ED=97=8C?= Date: Tue, 7 Jul 2026 18:10:45 +0900 Subject: [PATCH] =?UTF-8?q?fix(agent):=20=ED=95=98=EB=93=9C=EC=BD=94?= =?UTF-8?q?=EB=94=A9=20'=EC=9E=AC=EC=9B=90=EB=B6=80=EC=A1=B1'=20=EC=99=80?= =?UTF-8?q?=EC=9D=BC=EB=93=9C=EC=B9=B4=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _pick_wildcard 가 실제 선택 와일드카드와 매핑되지 않은 채 '와일드카드를 하나라도 골랐으면' 조건만으로 wild_card_budget(재원부족)을 반환해, 선택하지 않은 재원부족 멘트가 노출되는 오작동이 있었다. - 재원부족 분기 삭제 → 해당 구간은 일반 가격협상으로 폴백. 와일드카드는 앵커<제시가≤앵커*1.02 구간의 1% 인하(wild_card_1pct)만 발동한다. - wildcard_used 마킹을 1% 분기 안으로 이동 — 가격협상으로 돌아갈 때도 마킹하면 이후 라운드의 정당한 1% 카드까지 억제되는 부작용 방지. - test_p7_chat: 와일드카드 발동 단언을 wild_card_1pct 로 교체. Co-Authored-By: Claude Opus 4.8 (1M context) --- agent/negotiation/chat/service/chat_engine.py | 18 +++++++++++------- agent/tests/test_p7_chat.py | 11 ++++++----- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/agent/negotiation/chat/service/chat_engine.py b/agent/negotiation/chat/service/chat_engine.py index 77e1790..a0bcb99 100644 --- a/agent/negotiation/chat/service/chat_engine.py +++ b/agent/negotiation/chat/service/chat_engine.py @@ -173,20 +173,24 @@ class ChatEngine: return "가격협상" def _pick_wildcard(self, session: ChatSession) -> str: - """앵커가 살짝 초과 구간에서 인하 압박 카드 선택. - - 앵커가에 아주 근접(≤ anchor*1.02): 1% 인하 요청(wild_card_1pct) → 앵커가 이하로 유도. - - 그 외: 목표 매입가 맞춰달라(wild_card_budget). + """앵커가에 아주 근접(≤ anchor*1.02)한 구간에서만 1% 인하 요청(wild_card_1pct)으로 + 앵커가 이하로 유도한다. 그 외 구간은 일반 가격협상(카드 플레이)으로 돌린다. + + 과거 여기서 반환하던 '재원부족'(wild_card_budget) 하드코딩 카드는 제거했다 — + 견적에서 실제 선택한 와일드카드(중간값 절충·목표가 선제안 등)와 매핑되지 않은 채 + '와일드카드를 하나라도 골랐으면' 조건만으로 발동해, 선택하지도 않은 재원부족 멘트가 + 노출되는 오작동이 있었다. """ ctx = session.context price = ctx.get("input_price", 0) anchor = ctx.get("anchor_price", 0) - ctx["wildcard_used"] = True if anchor > 0 and price <= anchor * 1.02: + # 와일드카드는 실제로 노출할 때만 '사용됨'으로 마킹한다 — 가격협상으로 돌아가는 + # 경우에도 마킹하면 이후 라운드에서 정당한 1% 카드까지 억제된다. + ctx["wildcard_used"] = True ctx["offer_1pct"] = int(round(price * 0.99)) # 1% 인하가 return "wild_card_1pct" - if not ctx.get("allow_selected_wildcards", True): - return "가격협상" - return "wild_card_budget" + return "가격협상" # ---- render -------------------------------------------------------- def vars_for(self, session: ChatSession) -> Dict[str, Any]: diff --git a/agent/tests/test_p7_chat.py b/agent/tests/test_p7_chat.py index 42fce19..866a926 100644 --- a/agent/tests/test_p7_chat.py +++ b/agent/tests/test_p7_chat.py @@ -2,7 +2,7 @@ 1. 전체 대화: 서비스안내→담당자확인→협상품목안내→가격협상→와일드카드→협상완료→협상종료(chat_end). 2. 가격협상 턴에서 카드 선택 + 학습(card_id, updated_q). -3. 와일드카드 발동(wild_card_budget) + 종료 보상(success). +3. 와일드카드 발동(wild_card_1pct) + 종료 보상(success). 4. 브랜드 치환 테넌트별(데모상사 A/B), 클린룸(스크립트에 KT 흔적 없음). 5. 경험로그 적재 + 종료 후 진행 시 에러. 6. (HTTP) 헤더로 새 세션 시작 + 한 턴 진행. @@ -46,8 +46,9 @@ async def test_full_conversation_reaches_completion(db_engine): reset_sessions() eng = await _reg().get_engine("ktcommerce") svc = ChatService() - # anchor=9900, target=10000(기본). 11000(>10395)→가격협상(카드), 10200(9900~10395)→와일드카드, 9800(≤anchor)→우선협상 - turns = [None, "확인", "예", "확인", "11000", "예", "10200", "예", "9800", "예", + # anchor=9900, target=10000(기본). 11000(>anchor*1.02)→가격협상(카드), + # 10000(anchor