용어 체계: 값=anchoring_value(정수‰)·가격=anchoring_price·조정=adjustment·구간=price_range·표본=sample - DB: rate_adjustments→anchoring.adjustments (id→adjustment_id, price_bracket_index→price_range_index, nego_count→sample_count, anchor_rate_before/after→anchoring_value_before/after, consumed_session_ids→used_session_ids) - sessions: target_anchoring_price→anchoring_price, anchor_rate_permille→anchoring_value, last_offered_price→last_offer_price, anchoring_adjustment_id→used_by_adjustment_id - 뷰: rate_history/current_rates→value_history/current_values, delta_permille→value_change - 코드: calc_price_range_index·calc_anchoring_price·evaluate_samples·get_current_value· get_latest_adjusted_value·get_current_anchoring_value·fetch_current_values·get_base_anchoring_value· Adjustment(ORM)·update_last_offer_price, 상수 ANCHORING_VALUE_MIN/MAX·ADJUSTMENT_STEP· PRICE_RANGE_COUNT/INDEX_MAX, 배치 로그 키 bracket=→price_range= - API: negodata protocol 필드 target_anchoring_price→anchoring_price (front 생성 모델·컴포넌트 동반) - 기존 DB 마이그레이션 신설: schedules/anchoring/migrations/20260706_rename_anchoring.sql (멱등 DO 블록 — 테이블·컬럼·뷰·인덱스·PK 제약. 코드 배포와 동시 적용 필요) - postgres-init 01·04, 문서 6종 동기화 - 실배포 전 수정 포함: main.py argparse 화(--dry-run 단독·오타 플래그 기동 전 차단), 박제 정합식 calc_anchoring_price 재사용, clamped 지표가 실제 포화만 집계(경계값 유지 제외) 주의: sessions.anchoring_value(정수‰)와 quotation_settings.anchoring_value(구 float 비율)는 같은 이름·다른 단위 — 구 컬럼은 미변경. 검증: 모듈 20·negodata 50·backend 57 테스트 통과, front tsc·vite build 통과, 로컬 DB 마이그레이션 적용 후 배치 dry-run·상주 기동·양 서버 부팅 확인. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
121 lines
8.6 KiB
Markdown
121 lines
8.6 KiB
Markdown
# 앵커링 시스템 워크플로우 설명 (비개발자용)
|
||
|
||
> **문서 성격**: `개발용.md`의 워크플로우를 비개발자도 이해할 수 있게 풀어 쓴 안내서.
|
||
> **버전**: v1.2 기준 (2026-07-02) — 정책 배경은 `기획용.md`, 기술 상세는 `개발용.md` 참조.
|
||
|
||
---
|
||
|
||
## 한 줄 요약
|
||
|
||
**협상마다 "이 가격 이하면 합의한다"는 기준선을 장부에서 찾아 정하고, 협상이 끝날 때마다 결과가 쌓이고, 2주에 한 번 시스템이 그 결과를 채점해서 장부의 숫자를 한 칸씩 조정한다** — 이 순환 구조입니다.
|
||
|
||
---
|
||
|
||
## 등장하는 것 4가지
|
||
|
||
| 이름 | 비유 | 역할 |
|
||
|---|---|---|
|
||
| **기준표** (정적 기본 테이블) | 공장 출하 시 기본 설정값 | 모든 칸의 출발점(전부 1%). 절대 안 바뀌는 내장 표 |
|
||
| **협상 기록** (`negotiation.sessions`) | 협상 한 건 한 건의 계약서 철 | "그때 기준가가 얼마였고, 상대가 얼마를 써냈고, 얼마에 끝났는지"가 적힘 |
|
||
| **조정 장부** (`anchoring.adjustments`) | 가격 정책 변경 대장 | "언제, 어떤 근거로, 몇 %에서 몇 %로 바꿨다"가 한 줄씩만 추가됨 |
|
||
| **빠른 조회판** (Redis) | 벽에 붙여둔 최신 가격표 | 협상 시작할 때 즉시 참조하는 사본. 원본은 항상 조정 장부 |
|
||
|
||
여기서 **칸(cell)** 이란 값을 관리하는 최소 단위로, **어느 회사 × 어떤 협력사 유형(유통/제조/총판) × 어떤 가격대** 조합입니다. 가격대는 자릿수 단위 사다리(1천 원대·2천 원대 … 1만 원대·2만 원대 … 9천만 원대, 총 46칸)로 나뉘고, 1억을 넘는 금액은 전부 마지막 가격대 칸으로 들어갑니다.
|
||
|
||
---
|
||
|
||
## 워크플로우 — 시간 순서대로
|
||
|
||
### ① 서버가 켜질 때
|
||
|
||
기준표(46개 가격구간 × 시작값 1%)를 메모리에 올리고, 표가 손상됐으면 아예 서버를 켜지 않습니다. 잘못된 가격으로 협상하는 것보다 안 켜지는 게 낫다는 안전장치입니다.
|
||
|
||
### ② 견적(협상 건)이 만들어질 때 — "기준선을 정한다"
|
||
|
||
재협상 견적이 생성되는 시점에 견적 시스템(negodata)이 이 협상의 칸을 찾습니다. 그 칸의 현재 앵커링 값(깎는 비율)을 빠른 조회판에서 읽고 — 없으면 조정 장부, 그것도 없으면 기준표 순서로 —
|
||
|
||
> **기준가(앵커링가) = 목표가 × (1 − 앵커링 값)**, 1원 단위 내림
|
||
>
|
||
> 예) 목표가 30,000원, 앵커링 값 3% → 기준가 29,100원 — 협력사가 이 이하를 써내면 그 가격으로 합의
|
||
>
|
||
> 이 기준가는 협력사 화면에 **표시되지 않고**, 챗봇이 합의 가능 여부를 판단하는 내부 기준선으로만 동작합니다(정보 비대칭 유지 전략).
|
||
|
||
을 계산합니다. 그리고 **이 협상에 쓴 비율과 기준가를 협상 기록에 도장 찍듯 고정(박제)** 합니다. 이후 2주 정산이 지나가서 칸의 값이 바뀌어도, 이미 만들어진 협상의 기준가는 절대 흔들리지 않습니다. 협상이 다음 라운드로 재생성될 때도 옛 값을 물려받지 않고 그 시점의 칸 값으로 새로 계산합니다.
|
||
|
||
### ③ 협력사가 가격을 써낼 때 — "가격 흔적"
|
||
|
||
협력사가 협상 채팅에서 가격을 입력할 때마다, 그 **마지막 제시가가 협상 기록에 남습니다**(`last_offer_price`).
|
||
|
||
이게 중요한 이유: **가격을 써낸** 협상과 **한 번도 안 써낸** 협상은 정책적으로 완전히 다르게 취급하기 때문입니다.
|
||
|
||
- 가격을 써냈는데 기준가 아래로 합의가 안 됨(결렬·중간 이탈 포함) = **"기준이 시장보다 세다"는 신호** = 실패로 카운트
|
||
- 가격을 한 번도 안 써내고 끝남(미참여·무응답, 담당자 부재 등) = 기준가는 화면에 안 보이므로 우리 기준과 무관 = 판단 재료에서 제외
|
||
|
||
### ④ 협상이 끝나면
|
||
|
||
따로 하는 일이 없습니다. 계약서 철(협상 기록)에 결과가 이미 다 남아 있으니까요.
|
||
|
||
이게 이번 설계(v1.2)의 특징입니다 — 별도 표본 장부를 만들지 않고, **협상 기록 자체를 나중에 채점 근거로** 씁니다. 기준가·마지막 제시가·투찰가·종료 상태가 전부 확정된 값이라, 언제 채점해도 같은 결과가 나옵니다.
|
||
|
||
### ⑤ 격주 토요일 자정 — "정산"
|
||
|
||
2주에 한 번 시스템이 깨어나 **아직 채점 안 된 종료 협상들을 전부** 꺼내 채점합니다:
|
||
|
||
| 상황 | 채점 |
|
||
|---|---|
|
||
| 기준가 이하로 합의 성사 | **성공** |
|
||
| 기준가보다 높게 합의(예외적) | **실패** |
|
||
| 가격을 써냈지만 합의 못 함 — 결렬·중간 이탈 포함 | **실패** |
|
||
| 가격을 한 번도 안 써내고 끝남 | **제외** (셈에서 뺌) |
|
||
|
||
칸별로 모아서 **유효 결과가 10건 이상인 칸만** 성공률을 내고, 3단계 규칙으로 **딱 한 계단**만 조정합니다:
|
||
|
||
| 성공률 | 판단 | 조치 |
|
||
|---|---|---|
|
||
| 60% 이상 | 잘 통하고 있다 | 올림 (유통 ±2%p / 총판 ±1.5%p / 제조 ±1%p) |
|
||
| 30% ~ 60% | 적당하다 | 유지 |
|
||
| 30% 미만 | 너무 셌다 | 내림 (같은 폭) |
|
||
|
||
값은 어떤 경우에도 1% 아래로 내려가지 않고 20% 위로 올라가지 않습니다.
|
||
|
||
정산이 끝나면:
|
||
|
||
1. 조정 장부에 한 줄 추가 — "몇 건 중 몇 건 성공, 1% → 3%, 어떤 협상들을 근거로"
|
||
2. 채점에 쓴 협상들에 **"이 조정에 사용됨" 스탬프**를 찍음 (같은 협상이 두 번 채점되는 일 방지)
|
||
3. 벽의 가격표(빠른 조회판)를 새 값으로 갱신
|
||
|
||
**10건이 안 되는 칸은 스탬프를 안 찍고 그대로 둡니다** — 그게 이월입니다. 다음 정산 때 4주치가 함께 채점되고, 그래도 부족하면 6주, 8주… 로 자연스럽게 기간이 늘어납니다.
|
||
|
||
### ⑥ 그리고 다시 ②로
|
||
|
||
다음 재협상은 조정된 값으로 시작합니다. 이 순환이 반복되면서 각 칸의 값은 "시장이 받아주는 한계선" 근처에서 자연스럽게 안정됩니다.
|
||
|
||
---
|
||
|
||
## 이 구조가 주는 안전장치
|
||
|
||
- **같은 협상이 두 번 채점될 수 없음** — 스탬프 찍힌 협상은 다음 정산에서 자동으로 빠집니다. 정산이 실수로 두 번 돌아도 결과가 같습니다.
|
||
- **정산이 한 번 빠져도 문제 없음** — 다음 정산이 4주치를 한 번에 채점하고, 그래도 조정은 한 계단만 하므로 값이 튀지 않습니다.
|
||
- **진행 중 협상은 절대 안 흔들림** — 기준가는 협상 생성 시점에 고정되므로, 정산이 값을 바꿔도 이미 시작한 협상에는 영향이 없습니다.
|
||
- **"왜 이 칸이 7%야?"에 항상 답할 수 있음** — 조정 장부에 모든 변경이 근거(어떤 협상들, 성공률)와 함께 영구 보존됩니다. 장부는 수정·삭제가 금지돼 있습니다.
|
||
- **빠른 조회판이 날아가도 무사** — 어차피 사본이라 조정 장부에서 언제든 다시 만들 수 있습니다. 조회판(Redis)이 아예 꺼져 있어도 협상은 원본 장부를 직접 읽어 계속 동작하고, 조회판에 옛 값이 남아 있더라도 매주 정산 시각에 최신 값으로 전부 다시 붙입니다.
|
||
- **회사 간 칸막이** — A사의 협상 결과는 A사의 칸에만 반영됩니다. 다른 회사의 값과 기록은 완전히 분리됩니다.
|
||
- **예행 연습이 가능** — 실제로 아무것도 바꾸지 않고 "이번 정산에서 무엇이 어떻게 바뀔지"만 미리 보는 모드(dry-run)가 있어, 첫 가동처럼 조심스러운 순간에 결과를 눈으로 확인한 뒤 진행할 수 있습니다.
|
||
- **잘못 찍힌 기준가를 자동 감지** — 정산 때마다 각 협상에 도장 찍힌 기준가가 규칙대로 계산된 값인지 대조해서, 견적 시스템 쪽 계산 실수를 경고로 잡아냅니다.
|
||
|
||
---
|
||
|
||
## 자주 나올 질문
|
||
|
||
**Q. 값이 조정되면 기준표가 바뀌는 건가요?**
|
||
아닙니다. 기준표는 출발선이고 절대 바뀌지 않습니다. 조정 장부에 이력이 한 줄 쌓여서 "현재 위치"가 바뀌는 것입니다.
|
||
|
||
**Q. 결과가 많이 쌓이면 값이 크게 움직이나요?**
|
||
아닙니다. 13건이 쌓였든 30건이 쌓였든 성공률만 계산하고, 조정은 정산일당 딱 한 계단입니다.
|
||
|
||
**Q. 거래가 거의 없는 칸은요?**
|
||
10건이 찰 때까지 정산일마다 기다립니다(2주 → 4주 → 6주…). 그동안은 시작값(1%) 근처에 머무는데, 거래가 없는 칸이니 사업 영향도 작습니다.
|
||
|
||
**Q. 사람이 수동으로 값을 바꿀 수 있나요?**
|
||
현재 정책에서는 없습니다. 값은 오직 협상 결과 데이터로만 움직이고, 모든 변경은 이력으로 추적 가능합니다.
|