테스트
This commit is contained in:
11
app.py
11
app.py
@@ -105,11 +105,8 @@ def query_select_summarize_stream(results, query, ai, min_similarity: float = 0.
|
||||
else :
|
||||
print('일반', results.get('documents'))
|
||||
context_parts = [f'검색된 사용자 수는 {len(results.get('ids'))}']
|
||||
docs = [f"{d[d.find('[이름]'): d.find('[', d.find('[이름]')+1)]} {d[d.find('[부서]'): d.find('[', d.find('[부서]')+1)]}" for d in results.get('documents')]
|
||||
|
||||
|
||||
|
||||
context = "\n".join(context_parts + docs)
|
||||
docs = [f"[{d[d.find('[이름]'): d.find('[', d.find('[이름]')+1)]} {d[d.find('[부서]'): d.find('[', d.find('[부서]')+1)]}]" for d in results.get('documents')]
|
||||
context = ",".join(context_parts + docs)
|
||||
print(context)
|
||||
|
||||
# 모델 로드
|
||||
@@ -148,7 +145,7 @@ def query_select_summarize_stream(results, query, ai, min_similarity: float = 0.
|
||||
generation_kwargs = dict(
|
||||
**model_inputs,
|
||||
streamer=streamer,
|
||||
max_new_tokens=3000,
|
||||
max_new_tokens=600,
|
||||
do_sample=True,
|
||||
temperature=0.3,
|
||||
top_p=0.9,
|
||||
@@ -213,7 +210,7 @@ def query_summarize_simple(query: str) :
|
||||
# conduct text completion
|
||||
generated_ids = model.generate(
|
||||
**model_inputs,
|
||||
max_new_tokens=300,
|
||||
max_new_tokens=600,
|
||||
do_sample=True, # ✅ 샘플링 활성화
|
||||
temperature=0.3,
|
||||
top_p=0.9,
|
||||
|
||||
@@ -2,26 +2,28 @@ import json
|
||||
from typing import List, Union
|
||||
|
||||
import chromadb
|
||||
from chromadb.utils import embedding_functions
|
||||
from sentence_transformers import SentenceTransformer # ✅ 임베딩 전용 라이브러리
|
||||
|
||||
# === 경로 설정 (모두 로컬) ===
|
||||
EMBEDDING_MODEL_PATH = "./models/ko-sroberta-multitask"
|
||||
|
||||
# 1. 임베딩 모델 로드 (앱 시작 시 1회만 실행 권장)
|
||||
# NOTE: 실제 프로덕션에서는 전역 변수나 싱글톤으로 관리
|
||||
embedding_model = SentenceTransformer(
|
||||
EMBEDDING_MODEL_PATH,
|
||||
device="cpu"
|
||||
)
|
||||
# 속도 최적화: 조직도 데이터는 짧으므로 max_seq_length 줄임
|
||||
embedding_model.max_seq_length = 128
|
||||
|
||||
# 2. 벡터 DB 설정
|
||||
persist_directory = "./chroma_db"
|
||||
chroma_client = chromadb.PersistentClient(path=persist_directory)
|
||||
|
||||
# 임베딩 함수 설정
|
||||
embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
|
||||
model_name=EMBEDDING_MODEL_PATH, # 로컬 폴더 경로 가능
|
||||
device="cpu",
|
||||
normalize_embeddings=True
|
||||
)
|
||||
|
||||
# 컬렉션 생성 또는 가져오기
|
||||
# 컬렉션 생성 (임베딩 함수는 None으로 설정 - 수동 전달할 예정)
|
||||
collection = chroma_client.get_or_create_collection(
|
||||
name="orgchart",
|
||||
embedding_function=embedding_fn,
|
||||
embedding_function=None, # ✅ 수동 임베딩 전달 시 None
|
||||
metadata={"hnsw:space": "cosine"}
|
||||
)
|
||||
|
||||
@@ -36,21 +38,24 @@ def init(sessionId: str, data: List[Union[str, dict]]):
|
||||
"""
|
||||
print(f'{sessionId} init start')
|
||||
|
||||
if not data:
|
||||
print(f'{sessionId} init skipped: no data')
|
||||
return
|
||||
|
||||
# 문서 ID 생성
|
||||
doc_ids = [f"{sessionId}_{i}" for i in range(len(data))]
|
||||
|
||||
# 데이터 처리: 문자열이면 그대로, 객체면 JSON 문자열로 변환
|
||||
documents = []
|
||||
|
||||
# 데이터 처리: 간결하고 검색 친화적인 텍스트 생성
|
||||
doc_list = []
|
||||
metadatas = []
|
||||
|
||||
for q in data:
|
||||
# 각 필드를 안전하게 추출 (None 방어)
|
||||
# 필드 안전 추출
|
||||
name = q.get('name') or ""
|
||||
dept = q.get('deptNm') or ""
|
||||
grade = q.get('gradeNm') or ""
|
||||
position = q.get('ptsnNm') or ""
|
||||
office_phone = q.get('ofcePhn') or ""
|
||||
mobile_phone = q.get('mblPhn') or ""
|
||||
chief_name = q.get('chiefNm') or ""
|
||||
state_code = q.get('state') or ""
|
||||
|
||||
@@ -58,30 +63,48 @@ def init(sessionId: str, data: List[Union[str, dict]]):
|
||||
state_map = {'C': '재직', 'T': '퇴사', 'H': '휴직'}
|
||||
status = state_map.get(state_code, "정보없음")
|
||||
|
||||
# [핵심] 검색 엔진이 좋아할만한 서술형 문장 생성
|
||||
# 부서명과 이름을 앞부분에 배치하여 가중치 유도
|
||||
if name == '':
|
||||
doc = (
|
||||
f"부서: {dept}. "
|
||||
f"해당 {dept}의 부서장은 {chief_name}입니다."
|
||||
)
|
||||
# ✅ 개선: 짧고 핵심적인 문장 (토큰 수 ↓ → 속도 ↑, 정확도 ↑)
|
||||
if name:
|
||||
# 이름 있는 경우: "이름 직급 직위, 부서 소속, 상태"
|
||||
doc = f"[이름]:{name} [직급]:{grade} [직위]:{position} [부서]:{dept} [상태]: {status}"
|
||||
if office_phone:
|
||||
doc += f", 사내번호:{office_phone}"
|
||||
else :
|
||||
# 부서장 정보만 있는 경우
|
||||
doc = (
|
||||
f" [이름]:{name}"
|
||||
f" [부서]:{dept}"
|
||||
f" [소속]:{dept}"
|
||||
f" [직급]:{grade}"
|
||||
f" [직위]:{position}"
|
||||
f" 현재 {status} 중입니다. "
|
||||
f" 사내 전화번호(사선)는 {office_phone}입니다."
|
||||
f"부서: {dept}. 해당 {dept}의 [부서장][팀장]은 {chief_name}입니다."
|
||||
)
|
||||
|
||||
doc_list.append(doc)
|
||||
|
||||
# 벡터 DB에 추가
|
||||
# 메타데이터는 원본 값 유지 (검색 필터링용)
|
||||
metadatas.append({
|
||||
"sessionId": sessionId,
|
||||
"deptCd": q.get('deptCd') or "",
|
||||
"gradeCd": q.get('gradeCd') or "",
|
||||
"name": name,
|
||||
"deptNm": dept,
|
||||
"state": state_code
|
||||
})
|
||||
|
||||
# ✅ 임베딩 직접 계산 (Chroma 내부 중복 계산 방지)
|
||||
try:
|
||||
embeddings = embedding_model.encode(
|
||||
doc_list,
|
||||
normalize_embeddings=True, # cosine 유사도 사용 시 필수
|
||||
show_progress_bar=False,
|
||||
batch_size=32 # 메모리 절약
|
||||
).tolist()
|
||||
except Exception as e:
|
||||
print(f"임베딩 생성 실패: {e}")
|
||||
raise
|
||||
|
||||
# ✅ 벡터 DB에 추가 (임베딩 직접 전달)
|
||||
collection.add(
|
||||
documents=doc_list,
|
||||
ids=doc_ids,
|
||||
metadatas=[{"sessionId": sessionId, 'deptCd': d.get('deptCd') or "", 'gradeCd': d.get('gradeCd') or ""} for d in data]
|
||||
embeddings=embeddings, # ← 핵심: 수동 전달
|
||||
metadatas=metadatas
|
||||
)
|
||||
|
||||
print(f'{sessionId} init end')
|
||||
print(f'{sessionId} init end (총 {len(data)}건)')
|
||||
Reference in New Issue
Block a user