backend/.../properties_agent/ 16,201 LOC + 본 어시스턴트 use_case 6,800 LOC = 23K LOC 폐기.poc-chat-search/src/{ports,application,adapters}/ 를 본 backend 의 domains/ai/ 로 흡수 후 poc-chat-search/ 디렉토리 자체 삭제 (별도 docker-compose·api·tests 까지 전부)./v1/assistant/message 엔드포인트 유지 → 본 AIAssistantBottomSheet 그대로 사용, POC 전용 /chat-v2·/poc-v2 라우트 + PoCV2AIAssistantBottomSheet 삭제.| 분류 | 경로 | LOC | 비고 |
|---|---|---|---|
| SSE 진입 | backend/src/app/api/routers/assistant.py | 734 | POST /v1/assistant/message — 유지 (어댑터 교체) |
| Port | core/ports/search_assistant_port.py:97 | ~100 | search_stream 시그니처 유지 (이벤트 매핑) |
| 스레드 영속 API | api/routers/ai_chat.py + domains/ai/use_cases/* | ~6,800 | 스레드 CRUD·HMAC 검증 유지, 안쪽 비즈니스 로직만 폐기 |
| 폐기 대상 | adapters/properties_agent/** | 16,201 | assistant_adapter / pseudo_tool_agent / agent / slot_rules / persona_yeolmu / gates / response_diversity |
| 기존 Qdrant | adapters/qdrant/bootstrap.py | ~80 | collection properties_v1 1536d → properties_v2_3072 신규 (1536 collection 은 즉시 삭제) |
| 분류 | 경로 | 비고 |
|---|---|---|
| tenant 랜딩 진입 | router.tsx:198 → SubdomainHome → pages/tenant/Home.tsx:42 | AIAssistantBottomSheet 임베드 (모바일·로그인) |
| 데스크톱 랜딩 | pages/tenant/landing/TenantLandingPhoneFrame.tsx:50 | 동일 AIAssistantBottomSheet 임베드 |
| 핵심 컴포넌트 | components/chat/AIAssistantBottomSheet.tsx | 115 KB — 유지, SSE 이벤트 분기는 어댑터에서 호환 |
| 훅 | hooks/api/useAIChatSender.ts 등 4종 | 유지 |
| 삭제 대상 | pages/poc-v2/**, PoCV2AIAssistantBottomSheet, usePoCChatV2, /chat-v2 라우트 | POC 직결 mirror — 흡수 후 잔재 0 |
| 레이어 | 경로 | LOC |
|---|---|---|
| Composition | poc-chat-search/src/composition.py | ~200 |
| Ports (14종) | poc-chat-search/src/ports/* | ~600 |
| Application | application/{agent,chat_service,search_service,listing_registrar,tools}.py | 1,431 |
| Adapters | adapters/{llm/groq, embedding/openai, vector/qdrant, reranker/cohere, bm25, pg/*}/** | 5,948 |
| Eval (재사용) | tests/{scenarios_65, llm_judge, eval_split}.py | 553 |
가능 — retrieval pipeline·tool calling·suggestion 4 카테고리 전부 호환. 단 3가지 차이가 어댑터 레이어에서 해소되어야 함.
| # | 차이 | 해소 방법 |
|---|---|---|
| 1 | DB·임베딩 차원: POC PgListingsAdapter 별도 PG + 3072d / 본 backend SQLAlchemy async + 1536d | 본 Property 모델 read API 를 ListingsIndexPort 로 어댑팅 + Qdrant 신규 collection properties_v2_3072. 기존 1536 collection 폐기. |
| 2 | 인증·thread 영속: POC stateless / 본 get_current_user_optional + autosave + HMAC | composition root 에서 user_id 매핑 (member-{uid}/anon-{ip-hash}) + _autosave_conversation 은 그대로 유지. |
| 3 | SSE 이벤트 스키마: POC 6종 vs 본 SearchStreamEvent 8종 (token/properties/suggestions/end 외 4종) | 신규 어댑터에서 POC SSE → SearchStreamEvent 매핑. 프론트 useAIChatSender 8종 분기 그대로 호환. |
Layer 0 Layer 1 Layer 2 Layer 3 Layer 4 Layer 5 Layer 6
───────── ───────── ───────── ───────── ───────── ───────── ─────────
┌──────────┐ ┌──────────────┐ ┌──────────────────┐ ┌────────────────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐
│N0.1* S ⏸│═══▶│N1.1* M ⏸ │════════▶│N2.1* L ⏸ │═══▶│N3.1* M ⏸ │══▶│N4.1 M ⏸│══▶│N6.1* L ⏸│══▶│N7.1* L ⏸│
│Qdrant │ │Port 14종 │ │App: Agent/Chat/ │ │auth+tenant │ │worker │ │real DB │ │폐기 │
│v2 3072d │ │이식 (ABC) │ │Search 1,431 LOC │ │assistant.py │ │인덱싱 │ │65+holdout│ │16K LOC │
└──────────┘ └──────┬───────┘ └────────▲─────────┘ └──────┬───────┘ └─────┬──┘ └────┬─────┘ └──────────┘
│ │ │ │ │
▼ │ ▼ │ │
┌──────────────┐ │ ┌────────────────┐ │ │
│N1.2 M ⏸ │──────────────────┤ │N3.2 S ⏸ │ │ │
│LLM/Embed/Vec │ │ │HMAC signer │ │ │
│/Rerank/BM25 │ │ │통합 │ │ │
└──────┬───────┘ │ └────────────────┘ │ │
│ │ │ │
▼ │ ┌────────────────┐ │ │
┌──────────────┐ │ │N3.3 S ⏸ │ │ │
│N1.3* L ⏸ │──────────────────┘ │thread autosave│ │ │
│Listings 결합 │ │호환 │ │ │
│(Property→idx)│ └────────────────┘ │ │
└──────────────┘ │ │
┌────────────────┐ │ │
│N5.1 S ⏸ │───────┤ │
│FE endpoint │ │
│변경 0 │ │
└──────┬─────────┘ │
│ │
▼ │
┌────────────────┐ │
│N5.2 M ⏸ │───────────────────┘
│PoCV2 라우트 │
│+ 컴포넌트 제거│
└────────────────┘
범례: ═ Critical Path / ─ non-CP / * CP / [S]≤50 [M]≤150 [L]≤300 LOC / ⏸ wait ▶ run ✓ done ✗ fail
| 노드 | 경로 | 변경 요지 | 의존 | LOC | 위험 |
|---|---|---|---|---|---|
| N0.1CP | adapters/qdrant/bootstrap.py | collection properties_v2_3072 3072d 신규. 기존 properties_v1 1536d 는 N7.1 에서 drop. | — | +30 | 낮음 |
| N1.1CP | domains/ai/ports/*.py 14종 | POC 14 port 그대로 이식. SearchAssistantPort 와 공존. | N0.1 | +600 | 낮음 |
| N1.2 | adapters/{llm/groq,embedding/openai_3large,qdrant/v2_store,reranker/cohere,bm25/kiwi}/ | Groq/OpenAI/Qdrant/Cohere/BM25 adapter 이식 + httpx async 화. | N1.1 | +1,400 | 중 |
| N1.3CP | domains/properties/use_cases/index_for_chatv2.py + adapters/database/properties_repository.py | ListingRegistrar 가 본 SQLAlchemy Property 모델 → 인덱싱 텍스트로 변환. tenant 격리 컬럼 (owner_id) payload. | N1.1,N1.2 | +500 | 높음 |
| N2.1CP | domains/ai/use_cases/{chat_agent,chat_service_v2,search_service_v2,agent_tools,agent_config}.py | POC Application 3종 + tools + config 이식 (723+106+103+376=1,431 LOC). domain → port 의존만. | N1.1,N1.2 | +1,500 | 높음 |
| N2.2 | adapters/assistant_v2/assistant_adapter.py | Agent 를 SearchAssistantPort 로 래핑. POC SSE 6종 → SearchStreamEvent 8종 매핑. | N2.1 | +400 | 중 |
| N3.1CP | api/routers/assistant.py | DI 를 신규 어댑터로 완전 교체 (feature flag 없이 직접 교체 — 중복 코드 0 원칙). SearchContext.tenant_request_id 유지. | N2.2 | ~80 | 중 |
| N3.2 | core/message_signer.py | POC issue_signature 를 본 ai_message_signing_secret 으로 발행. ai_chat.py 검증 그대로. | N3.1 | +50 | 낮음 |
| N3.3 | api/routers/assistant.py _autosave_conversation | tools_used/property_ids/turn_count metadata 를 assistant_message.metadata 에 echo. | N3.1 | +40 | 낮음 |
| N4.1 | workers/index_properties_chatv2.py | Dramatiq actor — 신규 매물 → ListingRegistrar → Qdrant upsert + BM25 in-process rebuild. | N1.3 | +250 | 중 |
| N5.1 | frontend 변경 0 | /v1/assistant/message 가 새 어댑터로 향함 — useAIChatSender 8종 분기 그대로 작동. | N3.1 | 0 | 낮음 |
| N5.2 | frontend/src/{pages/poc-v2,components/chat/PoCV2AIAssistantBottomSheet.tsx,hooks/usePoCChatV2.ts} 전부 | POC 전용 mirror 라우트·컴포넌트 완전 삭제. 신규 4 카테고리 suggestion chip / tool 진행 UI 가 본 AIAssistantBottomSheet 에 흡수. | N5.1 | -300 | 중 |
| N6.1CP | backend/tests/integration/chat_v2/eval_with_real_listings.py | POC scenarios_65 + llm_judge + eval_split 재사용. 본 DB holdout 매물 fixture. LLM Judge ≥ 4.59 게이트. | N4.1,N3.1 | +300 | 중 |
| N7.1CP | adapters/properties_agent/ + domains/ai/use_cases/* (구) + poc-chat-search/ 전체 + properties_v1 Qdrant collection + frontend /chat-v2 라우트 | 중복 0 원칙 — 완전 폐기. 23K+ LOC + POC 디렉토리 + 1536d collection + mirror 라우트. | N6.1 통과 | -23,000+ | 높음 |
Critical Path: N0.1 → N1.1 → N1.3 → N2.1 → N3.1 → N6.1 → N7.1 (총 7개 CP 노드, 단일 PR/노드 = 7 PR 직렬)
| 단계 | 내용 |
|---|---|
| 1. 재사용 | poc-chat-search/tests/{scenarios_65,llm_judge,eval_split}.py 553 LOC 를 backend/tests/integration/chat_v2/ 로 그대로 이식 (이후 POC 디렉토리 제거 시 본 backend 가 단일 SSOT). |
| 2. holdout 분할 | POC_EVAL_SEED=42 유지. holdout 매물 ID 는 production property allowlist (frozen) — 신규 매물 유입 영향 X. |
| 3. 본 DB 매물 색인 | N4.1 Dramatiq worker → ListingRegistrar.register_many → properties UPSERT + embed + Qdrant properties_v2_3072 upsert + BM25 in-process rebuild. |
| 4. 평가 흐름 | pytest fixture 에서 build_container() → Agent.run(query) ×65 → LLMJudge.score() 4축 (relevance / persona_fit / anti_pattern / suggestions_quality). |
| 5. 게이트 | train 60 평균 ≥ 4.59 + holdout 5 평균 ≥ POC 기준선 (overfitting 검출). 통과 시에만 N7.1 폐기. |
| 6. CI 분리 | nightly cron = deep eval (judge budget $10) / PR-CI = smoke 10 시나리오만. |
| # | 제거 대상 | 완료 조건 |
|---|---|---|
| 1 | backend/src/app/adapters/properties_agent/** (16,201 LOC) | N7.1 PR 에서 rm -rf + import 그래프 0 검증 |
| 2 | backend/src/app/domains/ai/use_cases/* (구 ~6,800 LOC, 스레드 CRUD 제외) | N7.1 PR 에서 신규 use_case 와 충돌 0 확인 후 폐기 |
| 3 | poc-chat-search/ 디렉토리 전체 | 본 backend 이식 후 worktree 에서 디렉토리 삭제 + git rm |
| 4 | frontend/src/pages/poc-v2/** + PoCV2AIAssistantBottomSheet.tsx + usePoCChatV2.ts + /chat-v2 라우트 | N5.2 PR 에서 전부 삭제 |
| 5 | Qdrant properties_v1 collection (1536d) | N7.1 PR 에서 client.delete_collection("properties_v1") 호출 + bootstrap 코드 삭제 |
| 6 | 중복 settings 키 (POC 의 POC_*) | 본 backend config/settings.py 의 일반 키로 통합 — POC prefix 제거 |
| 위험 | 완화 | 롤백 |
|---|---|---|
| N3.1 어댑터 교체 직후 production traffic 회귀 | N6.1 통과 + alpha 환경 24h staging | git revert + Qdrant properties_v1 보존 유지 |
본 DB property 스키마 ↔ POC ListingsIndexPort 불일치 | N1.3 단독 PR 으로 read API 호환성 사전 검증 | — |
| holdout 점수 회귀 (≥ 4.59 미만) | N6.1 차단 — N7.1 진행 X | 이전 PR head 로 되돌림 |
| 16K LOC 폐기 후 의존 import 누락 | N7.1 직전 ruff --select F + pytest 전체 통과 | git revert N7.1 |
PR-1 [N0.1] Qdrant v2 collection 추가 +30 LOC 1h
PR-2 [N1.1+N1.2] Port 14종 + 5 adapter 이식 (병렬 작업) +2,000 LOC 6h
PR-3 [N1.3] Property → ListingsIndex 결합 +500 LOC 3h
PR-4 [N2.1+N2.2] Application + SearchAssistantPort 어댑터 +1,900 LOC 8h
PR-5 [N3.1+3.2+3.3] auth router DI 교체 + signer + autosave +170 LOC 2h
PR-6 [N4.1] Dramatiq 인덱싱 worker +250 LOC 3h
PR-7 [N5.1+N5.2] frontend mirror 라우트·컴포넌트 삭제 -300 LOC 2h
PR-8 [N6.1] 65 시나리오 + holdout 통합 평가 +300 LOC 4h
(게이트 통과 시 PR-9 진행)
PR-9 [N7.1] properties_agent 16K + poc-chat-search/ -23,000+ 1h
+ Qdrant v1 collection + use_case (구) 전부 폐기
총 예상 LOC: +5,180 / -23,300 = 순 −18,120 LOC. wall-time ≈ 30h (직렬, 단독 작업 기준).