o2o-negosium-original/negodata/docs/imk-0803-requests.md
Mina Choi 71ef28110b [feat] negodata·agent·negosium: 협상 기준가 회사별 선택(공급가/매입가) + 공급사 포털 안내 회사설정화
어느 가격이 "공급사에 지불하는 단가"인지는 회사마다 달라, 컬럼을 합치는 대신 회사가 고르게 했다.
판정은 settings.features.nego_baseline_field 1순위, 미설정 회사는 price 만 숨겼으면 purchase_price 폴백.
판정식 정본은 negodata/backend/common/nego_baseline.py (agent·negosium backend 가 같은 규칙 미러).

- negodata front: 회사 설정에 협상 기준가 라디오(선택지마다 실제 나갈 문장 미리보기 + 학습 경고),
  '공급사 포털 안내' 탭 신설(협상 유의사항·헬프데스크 연락처), 용어 카탈로그에 target_price·supplier 추가.
  인터넷 최저가는 숨김 대상에서 제외(신규 견적의 유일한 목표가 후보).
  상품 등록 기본값에서 개발용 더미 제거(price 1,000,000·PROD-BAT-###·800,000·대한민국·10 EA·14).
- agent: get_item_price → get_item_baseline (기준가·호칭·회사 용어사전을 한 쿼리로),
  "기존 공급가 대비" 하드코딩을 회사 용어로 치환 + 받침 기준 조사 자동 보정,
  협상 스크립트 4종의 협력사·목표가·배송형태 용어를 {label_*} 토큰화, input_options 도 변수 치환 적용.
- negosium: 협상 화면 기준 단가·배송형태 라벨을 회사 설정 기준으로, 유의사항 본문과 헬프데스크 연락처를
  하드코딩에서 회사 설정으로(미등록 시 영역 숨김). 유의사항의 VAT·배송비 문구 삭제(IMK 0803 ⑥).
- negodata backend: LPS 검색 가격 힌트를 items.price 고정에서 기준가 규칙으로.

검증: 기준가 설정 3 × 숨김 4 = 12조합에서 협상 멘트·포털 표시·LPS 힌트가 전부 일치.
agent 테스트 176건 통과. 보고서 negodata/docs/nego-baseline-verification.md
2026-08-05 11:47:06 +09:00

6.4 KiB
Raw Blame History

IMK 0803 가격협상 요청 정리

원본: 0803_가격협상 우선 적용 및 논의 정리.xlsx (12건). 시트의 우선/논의 구분 대신 스펙이 확정돼 바로 착수 가능한 것 / 결정이 있어야 착수 가능한 것으로 다시 갈랐다. 시트와 다른 3건 — ⑪은 수정으로, ③은 논의로, ④는 코드가 아니라 회사설정으로.


수정 (스펙 확정)

④ 부가세 입력칸 — 코드 문제 아님

vat_yn 은 상품폼(ProductFormSheet.tsx)·업로드 양식(ExcelUploadModal.tsx) 둘 다 이미 있다. IMK 회사설정이 hidden_fields = [made_in, delivery_fee_yn, selling_price, vat_yn] 라 화면·양식에서 같이 빠진 것. 설정에서 체크 해제하면 끝.

⑥ 유의사항 문구 / 헬프데스크

  • 유의사항의 VAT·배송비 문구는 GuideContent.tsx 에서 상품별 동적 표기로 이미 바뀐 상태 → 요청대로 삭제.
  • 헬프데스크 연락처 실사용처 3곳, 전부 placeholder였음: frontend/src/features/chat/components/menu/Contact.tsx, frontend/src/features/chat/components/popup/GuideContent.tsx, frontend/src/pages/LoginPage.tsx. 초청 메일엔 없음.
  • 반영: companies.settings.branding.helpdesk: string[] 로 회사별 설정화. 아래 "헬프데스크 배선" 참조.

⑦ 공급가–매입가 일원화

목표가 후보엔 이미 공급가가 없다(internet·purchase·selling — pricing.py). 잔여 2건:

  1. 화면의 공급가(item.price, IMK 라벨 "공급가") 노출 제거
  2. 신규 견적이 아직 인터넷최저가만 씀 → 매입가 후보 추가

⚠️ items.price 를 hidden 으로 감추면 안 된다. hidden_fields 는 목록·등록폼·엑셀양식 3곳을 동시에 감추므로 신규 상품의 price 가 NULL 로 쌓이고, 협상 멘트의 인하율이 통째로 빠진다. items.price 소비처 → items-price-is-nego-baseline 메모 참조.

⑫ 시장가 산식 (상품가+배송비)/1.1

인터넷 최저가는 몰 판매가(VAT 포함·배송비 별도)로 수집되는데 우리 매입가·공급사 견적가는 VAT 별도라 축이 어긋난 채 비교 중. 배송비를 더해 실구매 총액을 만들고 1.1로 나눠 VAT를 벗긴다(약 9% 낮아짐).

미결 2개:

  • 배송비를 대부분 모른다(네이버 쇼핑 API 미제공, 실측 39/39 null). 0으로 칠지 / 미상이면 환산 스킵할지
  • 적용 범위 — 카드 멘트 인용값만인지, 목표가 후보(internet × (1−수수료율))에도 거는지. 후자면 인터넷 기준 목표가가 9% 내려간다.

⑪ 인터넷최저가 ≥ 목표가 → 시장가 카드 차단 (절반 완료)

견적생성 시 선택 게이팅은 반영됨(useCardGating.tsx). 잔여 = 협상 진행 중 런타임 차단.

⑤ 상품 일괄등록 유효성 오류

재현 케이스(어느 필드가 오탐인지) 확보 후 수정. 엑셀 원본을 받는 게 빠름.

② 목표가 초과 낙찰 허용

상한 그릇은 들어감(sessions.done_ceiling_price, 커밋 91b8dc77). 협상 엔진이 아직 안 읽는다 — agent/·루트 backend/ 어디에도 done_ceiling 참조 없음. 타결 판정에 배선하면 EST-202607-973E 케이스 해소. 조건: 기존 단가 > 견적가 > 목표가 > 앵커링가면 타결. 여기서 "기존 단가" = items.price.


논의 (결정 필요)

③ 중간값 로직

②의 짝. "목표가 초과 제시 안 함" 상한을 푸는 건 맞는데 대신 무엇을 상한으로 쓸지 — done_ceiling_price(목표가×(1+율))인지 기존 단가인지. IMK 예시(목표 39,800 / 필요 40,200)는 +1.0% 수준이고 현 기본율은 +5%.

⑧ 세팅 횟수만큼 카드 소진

목표가에 이미 근접했는데도 남은 카드를 다 태울지. 라운드가 늘면 결렬 위험·시간도 는다. "최소 사용 횟수 보장" vs "조기 타결 우선" 중 택.

⑨ 앵커 제안가 단조성

후속 카드 제안가가 앞 카드보다 낮아지는 건(16,980 → 16,810) 카드마다 고정 인하율을 쓰기 때문. 제안가를 카드가 아니라 라운드에 종속시켜 단조 상향으로 바꾸는 구조 변경 → 이 중 유일하게 범위가 큼.

⑩ 와일드카드 + 최종제안 중복

종결 국면은 현재 필수 관문이다(chat_engine.py check_iteration_limit):

  1. closing_played false → force_closing → 종결 전용 와일드카드 또는 폴백 최후통첩(목표가 제시)
  2. closing_played true → 제시가 ≤ 목표가면 타결, 초과면 결렬

스킵하면 마지막 우리 카운터가 안 나가고 공급사 마지막 제시가로 즉시 판정 → 결렬률 상승. 구현 시 closing_played = True 는 반드시 세워야 무한루프를 피한다.

단 중복 방지는 이미 양쪽에 걸려 있다 — 진입 단계(chat_engine.py)도 종결 단계(chat_service.py)도 is_played 로 쓴 카드를 건너뛴다. 코드상 같은 카드가 2회 나올 수 없으므로 IMK가 본 것이 진짜 동일 카드인지 확인이 먼저(dev DB엔 EST-202607-5947 없음 — 운영 데이터).

⑬ SG별 앵커링

SG = items.category (IMK 라벨 "SG명"). 현재 SG는 앵커링에도 목표가 산정에도 안 들어간다.

앵커링 축은 3개 — (company_id, supplier_type, price_range_index):

축 값
company_id 회사
supplier_type 1 유통 / 2 제조 / 3 총판 — 개별 협력사가 아니라 유형
price_range_index 목표가 기준 46개 자릿수 구간

SG 축을 추가하면 셀이 46×3×SG수로 쪼개져 셀당 표본 10건(SAMPLE_THRESHOLD)을 못 채우고 평가가 스킵·이월되어 학습이 사실상 멈춘다. 이게 실제 결정 포인트.


헬프데스크 배선 (⑥ 반영분)

companies.settings.branding.helpdesk: string[] — 한 줄 = 담당자 한 명, 자유 문자열.

경로 파일
편집 UI negodata/front/src/features/settings/SettingsView.tsx (브랜딩 탭)
타입 negodata/front/src/features/settings/catalog.ts
로그인 후 전달 backend/services/auth_service.py me() — branding dict 통째로 내려 자동 포함
로그인 전 전달 backend/services/auth_service.py session_branding() + protocol.py Res_SessionBranding
표시 frontend/ 의 Contact.tsx · GuideContent.tsx · LoginPage.tsx

값이 비면 각 표시부는 연락처 줄을 렌더하지 않는다(placeholder 노출 금지).