o2o-negosium-original/agent/tests/test_agent_integration.py
hbyang 1682481b01 [feat] agent: 협상 고도화 — LLM 표현/이해층 + 카드 전술 실행계층 + ktcommerce 정리
카드 재설계("멘트 카드 → 전술 카드"):
- tactics.py 신설: 카드번호→전술(카운터 산식) 레지스트리, min(counter,target) 클램프
- 카운터 수락=즉시 타결(pending_counter_price 일반화, 구 offer_1pct 흡수)
- 목표가 초과 타결 금지(성공스텝 진입 가드) + "카드 소진=실패" 폐지→종결 국면
- 선택형 와일드카드(WC-*) 발동 + card.wild_cards 멘트 DB 어댑터

LLM 계층:
- Phase 2 표현층 ScriptNaturalizer(카드 멘트 자연화, 마커·치환자·숫자 보존 검증)
- Phase 3 이해층 InputInterpreter(자유발화 NLU→기대입력, 한국어 가격 파서)
- OPENAI_API_KEY env override(server_configs) + 전역 자격증명 게이트

결정 스택(Phase 1):
- 협상 규칙 데이터화(negotiation.wildcard_*_ratio/max_counter_rounds)
- 선택카드 우선순위 prior(UCB 방문수 감쇠, Q-table 오염 없음)

버그픽스:
- 인하율 음수 표기 제거 + 인상/동일/인하 구분(discount_phrase)
- 자연화 강조마커 보존(볼드/색 소실 시 원본 폴백)
- 카드 시드 가격변수(prev_partner_price·target_mid_price·middle_price 등) 치환

정리:
- ktcommerce 테넌트 삭제 + 테스트 21파일 imarketkorea/_base 로 마이그레이션
- 실 LLM 호출 차단 conftest 가드

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 13:37:36 +09:00

71 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""AGENT_INTEGRATION.md 규약 검증 (backend ↔ agent).
4-1 session_id honoring: backend 가 보낸 session_id(=negotiation.sessions.session_id)를
새 uuid 발급 없이 그대로 세션 키로 사용.
4-4 tenant 키 = company_id(uuid): 미등록 company_id 는 _base 자동 온보딩(cold-start) 으로 수용.
"""
import os
import pytest
from router.v1.chat.protocol import Req_Chat
from services.chat_service import ChatService, reset_sessions
from negotiation.chat.service.chat_session_repository import ChatSessionRepository
from tenancy.config_loader import TenantConfigLoader
from tenancy.registry import TenantEngineRegistry
_TENANTS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "tenants")
# backend 가 보내는 값 형식: session_id = negotiation.sessions.session_id(uuid), X-Tenant-ID = company_id(uuid)
BACKEND_SESSION_ID = "11111111-1111-1111-1111-111111111111"
COMPANY_ID = "00000000-0000-0000-0000-000000000099"
def _reg():
return TenantEngineRegistry(loader=TenantConfigLoader(tenants_dir=_TENANTS_DIR, cache_ttl_seconds=0))
@pytest.mark.asyncio
async def test_4_1_honors_backend_session_id(db_engine):
reset_sessions()
eng = await _reg().get_engine("imarketkorea")
svc = ChatService()
# 첫 턴: backend 의 session_id 를 그대로 키로 써야 함 (새 uuid 발급 X)
# 컨텍스트는 DB 조회(행 없음 → 기본값 폴백: target=10000, anchor=9900)
r = await svc.chat(eng, Req_Chat(session_id=BACKEND_SESSION_ID))
assert r.session_id == BACKEND_SESSION_ID
assert r.step == "서비스안내"
# 같은 session_id 로 이어가면 그 세션이 DB 에서 복원됨
r2 = await svc.chat(eng, Req_Chat(session_id=BACKEND_SESSION_ID, user_input="확인"))
assert r2.session_id == BACKEND_SESSION_ID and r2.step == "담당자확인"
# learning.chat_sessions 에 backend 의 session_id 그대로 저장됨
saved = await ChatSessionRepository(eng.company_id).get(BACKEND_SESSION_ID)
assert saved is not None and saved.session_id == BACKEND_SESSION_ID
@pytest.mark.asyncio
async def test_4_4_company_id_auto_onboard():
# 미등록 company_id → _base 자동 온보딩 (전용 tenant.yaml 없이도 엔진 생성)
eng = await _reg().get_engine(COMPANY_ID)
assert eng.tenant_id == COMPANY_ID
assert eng.company_id == COMPANY_ID # 학습/세션이 이 company_id 로 격리
assert eng.action_space_size == 11 # base 기본 카드(162×11 정합)
assert eng.state_space_size == 162
@pytest.mark.asyncio
async def test_4_4_company_id_chat_end_to_end(db_engine):
reset_sessions()
eng = await _reg().get_engine(COMPANY_ID) # 자동 온보딩 테넌트
svc = ChatService()
r = await svc.chat(eng, Req_Chat(session_id=BACKEND_SESSION_ID))
assert r.session_id == BACKEND_SESSION_ID and r.step == "서비스안내"
# 카드선택 턴까지 진행 → company_id 스코프로 학습 기록
for ui in ["확인", "", "확인", "11000", ""]:
r = await svc.chat(eng, Req_Chat(session_id=BACKEND_SESSION_ID, user_input=ui))
assert r.step == "가격협상" and r.card_id is not None