diff --git a/project/CALLCENTER_APP/.idea/dataSources.xml b/project/CALLCENTER_APP/.idea/dataSources.xml
index 9961c60..cf3009a 100644
--- a/project/CALLCENTER_APP/.idea/dataSources.xml
+++ b/project/CALLCENTER_APP/.idea/dataSources.xml
@@ -8,5 +8,12 @@
jdbc:sqlite:$PROJECT_DIR$/db/callcenter.db
$ProjectFileDir$
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:$PROJECT_DIR$/vectordb/chroma.sqlite3
+ $ProjectFileDir$
+
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/.idea/db-forest-config.xml b/project/CALLCENTER_APP/.idea/db-forest-config.xml
new file mode 100644
index 0000000..4409ab1
--- /dev/null
+++ b/project/CALLCENTER_APP/.idea/db-forest-config.xml
@@ -0,0 +1,10 @@
+
+
+
+ .
+ ----------------------------------------
+ 1:0:91805924-e27b-4e0d-8c54-ca806a60bde7
+ 2:0:c3ead1c1-bc14-4dae-9377-ad6045897614
+ .
+
+
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/.idea/forwardedPorts.xml b/project/CALLCENTER_APP/.idea/forwardedPorts.xml
new file mode 100644
index 0000000..034e067
--- /dev/null
+++ b/project/CALLCENTER_APP/.idea/forwardedPorts.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/main.py b/project/CALLCENTER_APP/backend/main.py
index 64b0b27..2190967 100644
--- a/project/CALLCENTER_APP/backend/main.py
+++ b/project/CALLCENTER_APP/backend/main.py
@@ -3,6 +3,9 @@ from starlette.staticfiles import StaticFiles
from contextlib import asynccontextmanager
from backend.repository.db_init import Base, SessionLocal, engine
+
+from backend.routers.evalu_router import router as evalu_router
+from backend.routers.assistant_router import router as assistant_router
from backend.routers.call_router import router as call_router
from backend.repository.seed import seed_customers
@@ -31,4 +34,6 @@ app = FastAPI(title="상담 LLM", version="1.0", lifespan=lifespan)
app.mount("/static", StaticFiles(directory="backend/static"), name="static")
# 라우터 등록
-app.include_router(call_router)
\ No newline at end of file
+app.include_router(call_router)
+app.include_router(assistant_router)
+app.include_router(evalu_router)
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/prompts/all_prompt.py b/project/CALLCENTER_APP/backend/prompts/all_prompt.py
index 757b8fb..7907cfb 100644
--- a/project/CALLCENTER_APP/backend/prompts/all_prompt.py
+++ b/project/CALLCENTER_APP/backend/prompts/all_prompt.py
@@ -25,4 +25,48 @@ SUMMARY_SYSTEM_PROMPT = """
7. resolution
- 상담사가 제공한 해결 방법
+"""
+
+CALL_ASSISTANT_PROMPT = """
+당신은 콜센터 상담 지원 ai 입니다.
+
+제공된 정보만 활용하여 답변하세요.
+참고 정보가 부족하면 추측하지 말고 추가 확인이 필요하다고 답변하세요.
+
+[지식문서]
+{sim_context}
+
+[고객 상담 이력]
+{customer_text}
+
+[질문]
+{question}
+"""
+
+CALL_EVALUATION_PROMPT = """
+당신은 콜센터 QA 평가 전문가입니다.
+
+다음 항목을 평가하세요.
+1. 고객 신원 확인
+2. 공감 표현
+3. 문제 해결
+4. 설문 조사 안내
+
+각 항목에 대해
+- True / False
+- 판단 근거(reason)
+
+를 제공하세요.
+
+특히 공감 표현은 아래와 같이 명시적 표현이 있을 때만 True로 판단하세요.
+
+예시:
+- 불편을 드려 죄송합니다.
+- 많이 답답하셨겠습니다.
+- 불편을 겪으셔서 죄송합니다.
+
+단순히 문제 해결 절차를 안내하는 경우는 공감 표현으로 간주하지 마세요.
+
+상담기록:
+{transcript}
"""
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/repository/models.py b/project/CALLCENTER_APP/backend/repository/models.py
index b2c10ce..19cf05c 100644
--- a/project/CALLCENTER_APP/backend/repository/models.py
+++ b/project/CALLCENTER_APP/backend/repository/models.py
@@ -27,4 +27,27 @@ class CallHistory(Base):
customer_issue:Mapped[str]
resolution:Mapped[str]
# created_at:Mapped[datetime] = mapped_column(server_default=func.now(), nullable=False)
- created_at:Mapped[datetime] = mapped_column(default=datetime.now, nullable=False)
\ No newline at end of file
+ created_at:Mapped[datetime] = mapped_column(default=datetime.now, nullable=False)
+
+# 상담 평가 저장
+class CallEvaluation(Base):
+ __tablename__ = 'call_evaluation'
+
+ evaluation_id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
+ call_id: Mapped[int] = mapped_column(ForeignKey('call_history.call_id'))
+
+ # 평가 기준
+ # 고객 신원 확인
+ identity_verification:Mapped[bool] = mapped_column(default=False)
+ identity_verification_reason:Mapped[str]
+ # 공감 표현
+ empathy:Mapped[bool] = mapped_column(default=False)
+ empathy_reason:Mapped[str]
+ # 문제 해결
+ issue_resolution:Mapped[bool] = mapped_column(default=False)
+ issue_resolution_reason:Mapped[str]
+ # 설문 조사 안내
+ survey_guidance:Mapped[bool] = mapped_column(default=False)
+ survey_guidance_reason:Mapped[str]
+ score:Mapped[int]
+ created_at:Mapped[datetime] = mapped_column(default=datetime.now, nullable=False)
diff --git a/project/CALLCENTER_APP/backend/routers/assistant_router.py b/project/CALLCENTER_APP/backend/routers/assistant_router.py
new file mode 100644
index 0000000..309a7e3
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/routers/assistant_router.py
@@ -0,0 +1,14 @@
+from fastapi import APIRouter, Depends
+from backend.schemas.assistant_schema import AssistantResponse, AssistantRequest
+from backend.schemas.summary_schema import SummaryRequest, CallSummary, CallRequest
+from backend.services.assistant_service import answer_assistant_question
+from backend.services.call_service import summary_call, create_call_history
+from sqlalchemy.orm import Session
+from backend.repository.db_init import get_db
+
+
+router = APIRouter(prefix="/api/assistant", tags=["Assistant"])
+
+@router.post("", response_model=AssistantResponse)
+def ask_assistant(req:AssistantRequest, db:Session = Depends(get_db)):
+ return answer_assistant_question(customer_id = req.customer_id, question = req.question, db = db)
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/routers/call_router.py b/project/CALLCENTER_APP/backend/routers/call_router.py
index 3e6bd7c..9aac680 100644
--- a/project/CALLCENTER_APP/backend/routers/call_router.py
+++ b/project/CALLCENTER_APP/backend/routers/call_router.py
@@ -13,6 +13,5 @@ def generate_summary(req:SummaryRequest):
return summary_call(req.transcript)
@router.post("/save", response_model=CallSummary)
-# @router.post("/save", response_model=CallRequest)
def create_call(req: CallRequest, db:Session = Depends(get_db)):
return create_call_history(req, db)
diff --git a/project/CALLCENTER_APP/backend/routers/evalu_router.py b/project/CALLCENTER_APP/backend/routers/evalu_router.py
new file mode 100644
index 0000000..49d6bd7
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/routers/evalu_router.py
@@ -0,0 +1,11 @@
+from fastapi import APIRouter, Depends
+from backend.schemas.evaluation_schema import EvaluationResponse
+from sqlalchemy.orm import Session
+from backend.repository.db_init import get_db
+from backend.services.evalu_service import get_call_evaluation
+
+router = APIRouter(prefix="/api/evaluation", tags=["evaluation"])
+
+@router.get("/{call_id}", response_model=EvaluationResponse)
+def read_evaluation(call_id : int, db:Session = Depends(get_db)):
+ return get_call_evaluation(call_id = call_id, db = db)
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/schemas/assistant_schema.py b/project/CALLCENTER_APP/backend/schemas/assistant_schema.py
new file mode 100644
index 0000000..671cb1b
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/schemas/assistant_schema.py
@@ -0,0 +1,8 @@
+from pydantic import BaseModel
+
+class AssistantRequest(BaseModel):
+ customer_id: int
+ question: str
+
+class AssistantResponse(BaseModel):
+ answer: str
diff --git a/project/CALLCENTER_APP/backend/schemas/evaluation_schema.py b/project/CALLCENTER_APP/backend/schemas/evaluation_schema.py
new file mode 100644
index 0000000..a7350d7
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/schemas/evaluation_schema.py
@@ -0,0 +1,14 @@
+from pydantic import BaseModel
+from datetime import datetime
+
+class EvaluationResponse(BaseModel):
+ identity_verification:bool
+ identity_verification_reason:str
+ empathy:bool
+ empathy_reason:str
+ issue_resolution:bool
+ issue_resolution_reason:str
+ survey_guidance:bool
+ survey_guidance_reason:str
+ score: int
+ created_at: datetime
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/schemas/summary_schema.py b/project/CALLCENTER_APP/backend/schemas/summary_schema.py
index 77c86ee..0f3f5ef 100644
--- a/project/CALLCENTER_APP/backend/schemas/summary_schema.py
+++ b/project/CALLCENTER_APP/backend/schemas/summary_schema.py
@@ -25,4 +25,15 @@ class CallCreate(BaseModel):
category: str
sentiment: str
customer_issue: str
- resolution: str
\ No newline at end of file
+ resolution: str
+
+# 평가 스키마
+class CallEvaluationResponse(BaseModel):
+ identity_verification:bool
+ identity_verification_reason:str
+ empathy:bool
+ empathy_reason:str
+ issue_resolution:bool
+ issue_resolution_reason:str
+ survey_guidance:bool
+ survey_guidance_reason:str
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/scripts/build_vector_db.py b/project/CALLCENTER_APP/backend/scripts/build_vector_db.py
index ce11eba..02fde7e 100644
--- a/project/CALLCENTER_APP/backend/scripts/build_vector_db.py
+++ b/project/CALLCENTER_APP/backend/scripts/build_vector_db.py
@@ -1,4 +1,4 @@
-from langchain_community.vectorstores import Chroma
+from langchain_chroma import Chroma
from backend.ai.embedding import watson_embedding
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
diff --git a/project/CALLCENTER_APP/backend/services/assistant_service.py b/project/CALLCENTER_APP/backend/services/assistant_service.py
new file mode 100644
index 0000000..9b8d71e
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/services/assistant_service.py
@@ -0,0 +1,39 @@
+from backend.ai.llm import hugging_llm
+from langchain_core.prompts import ChatPromptTemplate
+from backend.prompts.all_prompt import SUMMARY_SYSTEM_PROMPT, CALL_ASSISTANT_PROMPT
+from backend.repository.models import CallHistory
+from backend.schemas.summary_schema import CallSummary, CallCreate
+from sqlalchemy.orm import Session
+from backend.schemas.assistant_schema import AssistantRequest
+from langchain_chroma import Chroma
+from backend.ai.embedding import watson_embedding
+from langchain_core.output_parsers import StrOutputParser
+
+# 질의 응답
+def answer_assistant_question(customer_id:int, question:str, db:Session):
+ # 1 단계 : 벡터 DB에서 질의
+ # 벡터db 불러오기
+ vectorstore = Chroma(embedding_function=watson_embedding, persist_directory="./vectordb")
+ # as_retriever()
+ retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
+ # invoke() = docs => page_content join
+ docs = retriever.invoke(question)
+ sim_context = "\n\n".join(doc.page_content for doc in docs)
+ # 2 단계 : DB 검색
+ # 고객이 이전에 질문한 내역을 추출
+ if customer_id:
+ histories = db.query(CallHistory).filter(CallHistory.customer_id == customer_id).order_by(CallHistory.created_at.desc()).limit(5).all()
+
+ # 문제, 해결 컬럼만 문자열로 추출
+ customer_text ="\n".join([f"""
+ 문제: {h.customer_issue}\n
+ 해결: {h.resolution}
+""" for h in histories])
+
+ # 1, 2 단계 => LLM => 답변 생성
+ prompt = ChatPromptTemplate.from_template(CALL_ASSISTANT_PROMPT)
+ chain = prompt | hugging_llm | StrOutputParser()
+ result = chain.invoke({"sim_context": sim_context, "customer_text": customer_text, "question": question})
+
+ return {"answer" : result}
+ # return AssistantRequest(answer=result)
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/services/call_service.py b/project/CALLCENTER_APP/backend/services/call_service.py
index 614d4f2..865f529 100644
--- a/project/CALLCENTER_APP/backend/services/call_service.py
+++ b/project/CALLCENTER_APP/backend/services/call_service.py
@@ -1,8 +1,8 @@
from backend.ai.llm import hugging_llm
from langchain_core.prompts import ChatPromptTemplate
-from backend.prompts.all_prompt import SUMMARY_SYSTEM_PROMPT
-from backend.repository.models import CallHistory
-from backend.schemas.summary_schema import CallSummary, CallCreate
+from backend.prompts.all_prompt import SUMMARY_SYSTEM_PROMPT, CALL_EVALUATION_PROMPT
+from backend.repository.models import CallHistory, CallEvaluation
+from backend.schemas.summary_schema import CallSummary, CallCreate, CallEvaluationResponse
from sqlalchemy.orm import Session
from backend.schemas.summary_schema import CallRequest
@@ -41,15 +41,51 @@ def save_call_history(db, data):
return history
-def evaluate_call():
+def evaluate_call(transcript:str):
"""상담 평가"""
+ structured_llm = hugging_llm.with_structured_output(CallEvaluationResponse)
+ prompt = ChatPromptTemplate.from_template(CALL_EVALUATION_PROMPT)
- pass
+ chain = prompt | structured_llm
+ return chain.invoke({"transcript":transcript})
-def save_call_evaluation():
+def calculate_score(evaluation):
+ score = 0
+
+ if evaluation.identity_verification:
+ score += 25
+
+ if evaluation.empathy:
+ score += 25
+
+ if evaluation.issue_resolution:
+ score += 25
+
+ if evaluation.survey_guidance:
+ score += 25
+
+ return score
+
+def save_call_evaluation(db:Session, call_id:int, evaluation:CallEvaluationResponse):
"""상담 평가 내용 저장"""
+ score = calculate_score(evaluation)
+
+ entity = CallEvaluation(
+ call_id = call_id,
+ identity_verification = evaluation.identity_verification,
+ identity_verification_reason = evaluation.identity_verification_reason,
+ empathy = evaluation.empathy,
+ empathy_reason = evaluation.empathy_reason,
+ issue_resolution = evaluation.issue_resolution,
+ issue_resolution_reason = evaluation.issue_resolution_reason,
+ survey_guidance = evaluation.survey_guidance,
+ survey_guidance_reason = evaluation.survey_guidance_reason,
+ score = score,
+ )
+ db.add(entity)
+ db.commit()
+ db.refresh(entity)
- pass
def create_call_history(req: CallRequest, db:Session):
# 요약
@@ -66,7 +102,19 @@ def create_call_history(req: CallRequest, db:Session):
customer_issue = summary.customer_issue,
resolution = summary.resolution
)
+ history = save_call_history(db = db, data = call_data)
- return save_call_history(db = db, data = call_data)
-
+ # 상담 평가
+ evaluation = evaluate_call(req.transcript)
+ # 평가 저장
+ save_call_evaluation(db = db, call_id=history.call_id, evaluation=evaluation)
+ return CallSummary(
+ summary=summary.summary,
+ keywords=summary.keywords,
+ category=summary.category,
+ sentiment=summary.sentiment,
+ action_items=summary.action_items,
+ customer_issue=summary.customer_issue,
+ resolution=summary.resolution,
+ )
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/backend/services/evalu_service.py b/project/CALLCENTER_APP/backend/services/evalu_service.py
new file mode 100644
index 0000000..a04d043
--- /dev/null
+++ b/project/CALLCENTER_APP/backend/services/evalu_service.py
@@ -0,0 +1,23 @@
+from sqlalchemy.orm import Session
+from backend.repository.models import CallEvaluation
+from backend.schemas.evaluation_schema import EvaluationResponse
+
+
+# 질의 응답
+def get_call_evaluation(call_id:int, db:Session):
+ # DB 검색 - call_id와 일치하는 정보 추출
+ if call_id:
+ evaluation = db.query(CallEvaluation).filter(CallEvaluation.call_id == call_id).first()
+
+ return EvaluationResponse(
+ identity_verification=evaluation.identity_verification,
+ identity_verification_reason=evaluation.identity_verification_reason,
+ empathy=evaluation.empathy,
+ empathy_reason=evaluation.empathy_reason,
+ issue_resolution=evaluation.issue_resolution,
+ issue_resolution_reason=evaluation.issue_resolution_reason,
+ survey_guidance=evaluation.survey_guidance,
+ survey_guidance_reason=evaluation.survey_guidance_reason,
+ score=evaluation.score,
+ created_at=evaluation.created_at,
+ )
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/data/transcript.txt b/project/CALLCENTER_APP/data/transcript.txt
index 17eb993..8286592 100644
--- a/project/CALLCENTER_APP/data/transcript.txt
+++ b/project/CALLCENTER_APP/data/transcript.txt
@@ -2,4 +2,8 @@
----
-상담사: 안녕하세요. 인터넷 서비스 고객센터입니다. 성함과 PIN 번호를 알려주시겠습니까?\n고객: 홍길동이고 PIN은 1234입니다.\n상담사: 감사합니다. 어떤 문제로 연락주셨나요?\n고객: 어제부터 인터넷 속도가 너무 느립니다.\n상담사: 불편을 드려 죄송합니다. 연결 상태를 확인해보겠습니다.\n(확인 중)\n상담사: 현재 고객님 지역의 회선 장애가 의심됩니다. 관련 부서에 장애 내용을 접수하겠습니다.\n고객: 네, 빨리 해결되면 좋겠네요.\n상담사: 최대한 신속히 처리하겠습니다. 신고해 주셔서 감사합니다.
\ No newline at end of file
+상담사: 안녕하세요. 인터넷 서비스 고객센터입니다. 성함과 PIN 번호를 알려주시겠습니까?\n고객: 홍길동이고 PIN은 1234입니다.\n상담사: 감사합니다. 어떤 문제로 연락주셨나요?\n고객: 어제부터 인터넷 속도가 너무 느립니다.\n상담사: 불편을 드려 죄송합니다. 연결 상태를 확인해보겠습니다.\n(확인 중)\n상담사: 현재 고객님 지역의 회선 장애가 의심됩니다. 관련 부서에 장애 내용을 접수하겠습니다.\n고객: 네, 빨리 해결되면 좋겠네요.\n상담사: 최대한 신속히 처리하겠습니다. 신고해 주셔서 감사합니다.
+
+----
+
+"상담사: 안녕하세요. 무엇을 도와드릴까요?\n고객: 어제부터 인터넷이 계속 끊어집니다./n상담사: 공유기를 재부팅해 보셨나요?/n고객: 네. 이미 해봤는데 똑같습니다.\n상담사: 현재 공유기 상태 표/n등은 어떻게 되어 있나요?/n고객: 전원등은 켜져 있고 인터넷 표시등은 깜빡입니다./n상담사: 공유기 케이블 연결 상태를 확인해 주시겠습니까?/n고객: 네. 확인했는데 이상 없습니다./n상담사: 그렇다면 공유기 초기화를 진행해보겠습니다. 초기화 방법을 알고 계신가요?/n고객: 아니요./n상담사: 공유기 뒷면의 리셋 버튼을 10~15초 정도 눌러주세요./n고객: 네. 지금 재시작 중입니다./n(몇 분 후)/n고객: 이제 정상적으로 작동하는 것 같습니다./상담사: 다행입니다. 다른 문제가 있으시면 언제든 연락 주세요."
\ No newline at end of file
diff --git a/project/CALLCENTER_APP/db/callcenter.db b/project/CALLCENTER_APP/db/callcenter.db
index aea8cd2..6ad87d6 100644
Binary files a/project/CALLCENTER_APP/db/callcenter.db and b/project/CALLCENTER_APP/db/callcenter.db differ
diff --git a/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/length.bin b/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/length.bin
index 0307c63..cb3e162 100644
Binary files a/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/length.bin and b/project/CALLCENTER_APP/vectordb/8aa1a33e-b040-4aa6-8954-320af0dc9c67/length.bin differ
diff --git a/project/CALLCENTER_APP/vectordb/chroma.sqlite3 b/project/CALLCENTER_APP/vectordb/chroma.sqlite3
index 0380404..444a1b5 100644
Binary files a/project/CALLCENTER_APP/vectordb/chroma.sqlite3 and b/project/CALLCENTER_APP/vectordb/chroma.sqlite3 differ