- feature_dqn_policy: ScoreNet(상태+카드특징 → 점수) 학습 정책 (replay+타깃넷) - feature_builder: 이산화 없는 연속 상태 벡터(9) + 테넌트 성향 벡터(5) - dqn_store: numpy 전용 서빙(컨테이너 PyTorch 불필요), DQN_SERVING 플래그, 미지원 테넌트는 Q-table 자동 폴백 - 파이프라인: build_card_embeddings -> train_feature_dqn -> export_dqn_serving(npz) - retrain_from_logs: 실로그 재학습 + OPE(SNIPS) 게이트, 통과 시에만 번들 교체(.prev 백업) - probe_serving_dqn / compare_qtable_vs_dqn: 배포 전 행동 점검 도구 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64 lines
2.3 KiB
Python
64 lines
2.3 KiB
Python
"""카드 스크립트 → 임베딩 캐시 생성 (action-as-feature 준비, 1회 실행).
|
|
|
|
card.nego_cards(11장)의 name+script 를 문장 임베딩으로 변환해 artifacts/card_embeddings.npz 에 저장.
|
|
새 카드가 추가되면 이 스크립트를 다시 돌리면 된다(그 카드만 임베딩돼 캐시에 합류).
|
|
|
|
실행:
|
|
APP_ENV=local python -m tools.build_card_embeddings
|
|
출력:
|
|
artifacts/card_embeddings.npz (numbers, names, strategy, tone, embeddings[N,384])
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
|
|
import numpy as np
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ARTIFACTS = os.path.join(_HERE, "..", "artifacts")
|
|
OUT_PATH = os.path.join(ARTIFACTS, "card_embeddings.npz")
|
|
|
|
MODEL_NAME = "paraphrase-multilingual-MiniLM-L12-v2" # 384차원, 한국어 지원, 로컬/무료
|
|
|
|
|
|
async def load_cards():
|
|
"""card.nego_cards 에서 (number, name, script, strategy_type, tone) 로드."""
|
|
import asyncpg
|
|
conn = await asyncpg.connect(
|
|
host="127.0.0.1", port=5432, user="postgres", password="password", database="negosium_db")
|
|
try:
|
|
rows = await conn.fetch(
|
|
"SELECT number, name, script, strategy_type, tone FROM card.nego_cards "
|
|
"WHERE deleted = FALSE ORDER BY number")
|
|
return [(r["number"], r["name"], r["script"], r["strategy_type"], r["tone"]) for r in rows]
|
|
finally:
|
|
await conn.close()
|
|
|
|
|
|
def main():
|
|
cards = asyncio.run(load_cards())
|
|
if not cards:
|
|
raise SystemExit("card.nego_cards 가 비어있음 — DB 시드 확인 (docker start negosium-pg)")
|
|
print(f"카드 {len(cards)}장 로드: {[c[0] for c in cards]}")
|
|
|
|
from sentence_transformers import SentenceTransformer
|
|
model = SentenceTransformer(MODEL_NAME)
|
|
texts = [f"{name}. {script}" for _, name, script, _, _ in cards]
|
|
emb = model.encode(texts, normalize_embeddings=True) # [N, 384], 단위벡터
|
|
print(f"임베딩 shape: {emb.shape}")
|
|
|
|
os.makedirs(ARTIFACTS, exist_ok=True)
|
|
np.savez(
|
|
OUT_PATH,
|
|
numbers=np.array([c[0] for c in cards]),
|
|
names=np.array([c[1] for c in cards]),
|
|
strategy=np.array([c[3] for c in cards], dtype=np.int64),
|
|
tone=np.array([c[4] for c in cards], dtype=np.int64),
|
|
embeddings=emb.astype(np.float32),
|
|
)
|
|
print(f"저장: {OUT_PATH}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|