- server-dev start/stop/deploy 및 Gitea push 자동 배포 - local-dev 로컬 개발 환경 Co-authored-by: Cursor <cursoragent@cursor.com>
24 KiB
exAiChatBot 프로세스 흐름
현재 기준: Agent 모드 운영, Qdrant 단일 벡터 스토어,
chatbotApp웹 UI,chatbotAdmin학습·통계 운영
주요 엔드포인트: RAG API:28012, Vector Admin:28013, Qdrant:6333
최종 갱신: 2026-07-14
이 문서는 exAiChatBot이 실제 운영 흐름에서 어떤 역할을 하는지 정리합니다.
상세 시스템 배치는 ../SYSTEM_ARCHITECTURE.md, Agent 구조는 ../docs/AGENT_ARCHITECTURE.md를 기준 문서로 봅니다.
1. 한눈에 보는 현재 흐름
flowchart LR
User[사용자]
Kakao[카카오톡 / 오픈빌더]
Web[kakaoChatbotSkill\n:8083]
App[chatbotApp\n/chatbot/ai/]
RAG[exAiChatBot API\n:28012]
WAS[chatbotApi\n:8086]
QD[(Qdrant\n:6333)]
LLM[LLM / Embedding / Reranker\nGateway]
ADM[exAiChatBot Admin\n:28013]
CA[chatbotAdmin\n:8087]
Mongo[(MongoDB)]
Oracle[(Oracle)]
Data[data.ex.co.kr]
User --> Kakao
Kakao -->|fallback skill| Web
Web -->|redirect-only button| App
App -->|POST /web/ask| Web
Web -->|POST /agent/chat| RAG
RAG -->|tool definitions / execute| WAS
WAS -->|WAS domain tool| Oracle
WAS -->|WEB domain tool| Web
Web --> Data
RAG -->|rag_search| QD
RAG -->|chat / embed / rerank| LLM
RAG -->|history / pending| Mongo
CA -->|학습 / FAQ CRUD| ADM
ADM --> QD
핵심 원칙
| 구분 | 현재 동작 |
|---|---|
| 카카오톡 | 답변 본문 처리보다 chatbotApp을 여는 버튼 제공 중심 |
| 웹 채팅 | /web/ask가 Agent 응답을 직접 받아 WebAskResponse로 매핑 |
| Agent | /agent/chat에서 LLM tool loop 실행 |
| FAQ 검색 | rag_search tool이 Qdrant 벡터 검색 + rerank 수행 |
| 도메인 조회 | RAG Agent가 WAS tool API를 호출, WAS/WEB fetchOwner에 따라 실행 |
| 환각 방지 | tool 또는 Qdrant 근거가 없으면 자유 답변 폐기 후 guidance 안내 |
| 학습 관리 | chatbotAdmin → exAiChatBot Admin(:28013) → Qdrant upsert/delete |
2. 실행 서비스와 책임
| 서비스 | 포트 | 주요 파일 | 책임 |
|---|---|---|---|
| RAG API | 28012 |
scripts/run_service_qa.py |
/agent/chat, /health |
| AgentService | 내부 | scripts/agent/agent_service.py |
tool loop, pending, 강제 RAG, guidance, 최종 응답 |
| ToolExecutor | 내부 | scripts/agent/tool_executor.py |
rag_search, ask_user, WAS domain tool 실행 |
| Vector Admin | 28013 |
scripts/admin_service.py |
FAQ 검색/목록/추가/수정/삭제, keyword-search |
| QdrantStore | 6333 |
scripts/vector_store.py |
Qdrant collection 연결, upsert, query, scroll, delete |
| Batch ingest | 일회성 | scripts/ingest_qa.py |
qa_raw.jsonl → embedding → Qdrant upsert |
build_index_qa.py는 FAISS 시절 단계입니다.VECTOR_STORE=qdrant이면 즉시 종료하며, 현재 Qdrant 운영에서는 별도 인덱스 파일을 만들지 않고 upsert가 곧 반영입니다.
3. 런타임 초기화
flowchart TD
Start[api 컨테이너 시작]
Config[환경변수 로드\nConfig.from_env]
Clients[LLM / Embedding / Reranker client]
Qdrant[QdrantStore 연결\ncollection qa_vectors]
Mongo[MongoDB 연결\n대화 이력 / pending]
Handlers[SearchHandler / PromptBuilder\nLLMHandler / QueryRewriter 등]
Tools[ChatbotToolClient\nWAS tool API]
Agent[AgentService 생성]
Ready[FastAPI 대기\n/agent/chat, /health]
Start --> Config --> Clients --> Qdrant --> Mongo --> Handlers --> Tools --> Agent --> Ready
주요 환경변수
| 환경변수 | 기본값 | 설명 |
|---|---|---|
VECTOR_STORE |
qdrant |
운영 벡터 스토어 |
QDRANT_COLLECTION |
qa_vectors |
FAQ collection |
FAISS_TOP_K |
30 |
이름은 레거시지만 Qdrant 검색 top-k에 사용 |
FAISS_THRESHOLD |
0.55 |
1차 벡터 검색 score threshold |
FAISS_THRESHOLD_REWRITE |
0.50 |
no-match 재검색 완화 threshold |
RERANK_CANDIDATES |
20 |
reranker 입력 후보 수 |
TOP_N_FOR_LLM |
5 |
최종 답변 프롬프트 참고 FAQ 수 |
LOW_CONFIDENCE_THRESHOLD |
0.65 |
낮은 신뢰도·제안 기준 |
HIGH_CONFIDENCE_THRESHOLD |
0.75 |
high confidence 기준 |
AGENT_MAX_ROUNDS |
6 |
Agent tool loop 최대 반복 |
CHATBOT_API_BASE_URL |
http://127.0.0.1:8086/api |
WAS tool API |
INTERNAL_TOOL_API_KEY |
빈 값 | RAG→WAS 내부 인증 키 |
CHAT_HISTORY_LIMIT |
10 |
최근 대화 이력 조회 수 |
CHAT_HISTORY_HOURS |
24 |
대화 이력 조회 시간 범위 |
4. Agent 응답 흐름 (POST /agent/chat)
운영 주 경로입니다. kakaoChatbotSkill의 RagAgentClient와 웹 채팅의 WebChatAgentService가 호출합니다.
요청
{
"query": "사용자 질문",
"botId": "conversation-id",
"pendingIntentType": "FARE_SEARCH",
"pendingParams": {
"toIc": "신갈"
}
}
pendingIntentType, pendingParams는 선택입니다. 저장된 pending이 있으면 AgentPendingStore에서 보완합니다.
처리 순서
flowchart TD
Q[사용자 질문]
Pending[저장 pending 조회]
Special{인사/종료?}
History[대화 이력 로드]
Tools[tool 목록 구성\nrag_search + ask_user + WAS tools]
Loop[LLM tool loop]
Tool{tool 호출}
RAG[rag_search\nQdrant + rerank]
Domain[WAS domain tool\n/tools/execute]
Ask[ask_user / clarify]
Ground[근거 기반 최종 답변 생성\nPromptBuilder 공통]
Safe{근거 있음?}
Guidance[guidance 안내\n콜센터/정확 확인]
Save[Mongo 이력 저장]
Response[AgentResponse 반환]
Q --> Pending --> Special
Special -->|yes| Response
Special -->|no| History --> Tools --> Loop --> Tool
Tool -->|rag_search| RAG --> Loop
Tool -->|domain tool| Domain --> Loop
Tool -->|ask_user| Ask --> Response
Loop --> Safe
Safe -->|domainData 또는 references| Ground --> Save --> Response
Safe -->|없음| Guidance --> Save --> Response
Agent 안전망
| 안전망 | 목적 |
|---|---|
| 매 턴 tool 사용 강제 프롬프트 | 대화 이력만 보고 사실 답변을 재생성하지 않도록 제한 |
강제 rag_search |
LLM이 정보성 질문에서 tool을 스킵하면 Qdrant를 강제 조회 |
| 검색어 맥락화 | 이력이 있는 후속 질문은 QueryRewriter로 완결형 검색어를 만든 뒤 검색 |
| 도메인 의도 가드 | 통행요금·미납처럼 도메인 의도가 강하면 FAQ 검색보다 되물음 우선 |
| pending 중 안전망 스킵 | 슬롯필링 중 강제 RAG가 pending을 지우는 회귀 방지 |
| 단일 system 메시지 | LLM tool-call 요청에서 pending 안내를 첫 system prompt에 병합해 게이트웨이 400 오류 방지 |
| 근거 없는 final 폐기 | tool/Qdrant 근거가 없으면 LLM 자유 답변을 버리고 guidance 반환 |
| PromptBuilder 사용 | Agent 최종 답변에서 FAQ·도메인 근거를 일관된 형식으로 사용 |
5. rag_search 상세 흐름
rag_search는 Agent의 로컬 tool입니다. FAQ/일반 안내 질문에 사용합니다.
flowchart TD
Query[검색어]
Embed[EmbeddingClient.embed\nis_query=true]
Search1[Qdrant query_points\nthreshold 0.55]
Retry{검색 결과 있음?}
Search2[Qdrant 재검색\nthreshold 0.50]
Candidates[meta 언랩\nq/a/url/category]
Rerank[Reranker\n질문+답변 passage]
HasRefs{references 있음?}
KeywordGuard{keyword retry 허용?}
Keyword[Qdrant 문자열 검색\n최대 3건]
KeywordRerank[Reranker 검증\nLOW_CONFIDENCE_THRESHOLD 이상]
Refs[references 최대 5개]
Empty[references 0건\nguidance 후보]
Query --> Embed --> Search1 --> Retry
Retry -->|yes| Candidates
Retry -->|no| Search2 --> Candidates
Candidates --> Rerank --> HasRefs
HasRefs -->|yes| Refs
HasRefs -->|no| KeywordGuard
KeywordGuard -->|yes| Keyword --> KeywordRerank
KeywordGuard -->|no| Empty
KeywordRerank -->|통과| Refs
KeywordRerank -->|미달| Empty
현재 검색 특성
| 항목 | 동작 |
|---|---|
| 저장 임베딩 | FAQ 질문 q만 document 모드로 임베딩 |
| sparse 저장 | qa_vectors_v2 또는 HYBRID_SEARCH_ENABLED=true면 q+a+category+source 기반 모델 없는 sparse vector도 저장 |
| 검색 임베딩 | 사용자 검색어를 query 모드로 임베딩 |
| 1차 검색 | Qdrant cosine score threshold 0.55 |
| hybrid 검색 | 활성화 시 dense 후보 + sparse 후보를 병합한 뒤 rerank |
| 재검색 | 0건이면 threshold 0.50으로 한 번 완화 |
| rerank 문서 | 질문: {q}\n답변: {a} 형태로 Q+A 전체 전달 |
| keyword retry | 벡터/완화 재검색 후 references가 0건일 때만 문자열 검색 최대 3건 |
| keyword 검증 | keyword 결과도 reranker 최고 점수가 LOW_CONFIDENCE_THRESHOLD 이상일 때만 채택 |
희망드림처럼 FAQ 질문에 실제 포함된 짧은 고유명사가 벡터 score threshold 아래로 누락되는 경우를 보완하기 위해, 최종 guidance 직전 보조 검색을 둡니다.
단 할인, 감면, 요금, ?처럼 너무 넓거나 비정보성인 단독 발화는 keyword retry를 실행하지 않습니다. keyword 결과가 있어도 reranker 점수가 낮으면 references를 비워 두고 기존 guidance로 전환합니다.
5.1 Agent 재검색: 강제 RAG와 검색어 맥락화
Agent 모드에서는 LLM이 정상적으로 rag_search를 호출하면 위 흐름 그대로 검색합니다.
다만 LLM이 대화 이력만 보고 tool 호출 없이 final text를 만들거나, 도메인 근거도 Qdrant 근거도 없는 경우에는 AgentService가 안전망으로 rag_search를 강제 실행합니다.
flowchart TD
Final[LLM tool loop 종료]
Domain{사용 가능한 domainData 있음?}
RagDone{rag_search 이미 실행?}
Pending{pending 진행 중?}
Guard{명백한 도메인 의도?}
History{botId + 대화 이력 있음?}
Rewrite[QueryRewriter.rewrite_query]
NoRewrite[원문 검색어 사용]
Search[rag_search 강제 실행]
Guide[근거 없으면 guidance]
Final --> Domain
Domain -->|yes| SearchDone[도메인 근거로 답변 생성]
Domain -->|no| RagDone
RagDone -->|yes| SearchDone
RagDone -->|no| Pending
Pending -->|yes| Guide
Pending -->|no| Guard
Guard -->|yes| Clarify[FAQ 대신 되물음]
Guard -->|no| History
History -->|yes| Rewrite --> Search
History -->|no| NoRewrite --> Search
Search --> SearchDone
강제 RAG가 실행되는 조건
| 조건 | 의미 |
|---|---|
| 도메인 tool 결과 없음 | domainData가 없거나 사용할 수 없는 상태 |
rag_search 미실행 |
LLM이 이번 턴에서 Qdrant를 조회하지 않음 |
| pending 진행 중 아님 | 슬롯필링 중 강제 검색이 pending을 지우는 회귀 방지 |
| 도메인 의도 가드 통과 | 차량번호/통행요금 계산처럼 명백한 도메인 조회는 FAQ 대신 되물음 |
검색어 맥락화 순서
botId가 있고CHAT_HISTORY_ENABLED=true이면 MongoDB에서 최근 대화 이력을 조회합니다.- 조회 범위는
CHAT_HISTORY_HOURS시간 이내, 최대CHAT_HISTORY_LIMIT개입니다. - 이력은 오래된 대화 → 최신 대화 순서로 정렬됩니다.
- 답변 본문에서
혹시 이런 것을 찾으셨나요?제안 블록은 제거합니다. QueryRewriter.rewrite_query()가 현재 질문이 후속 질문인지 판단합니다.- 후속 질문이면 완결형 검색어를 반환하고, 새 주제면
__NO_REWRITE__를 반환합니다. - 재작성 결과가 없거나 원문과 같으면 원문 검색어로
rag_search를 실행합니다.
예시는 다음과 같습니다.
| 이전 대화 | 현재 질문 | 검색어 |
|---|---|---|
| "하이패스 단말기가 뭔가요?" | "그럼 어디서 사나요?" | "하이패스 단말기는 어디서 구매할 수 있나요?" |
| "하이패스 단말기에 대해 알려줘" | "얼마야?" | "하이패스 단말기 가격은 얼마인가요?" |
| "통행요금 조회해줘" | "안녕" | 재작성 안 함 |
| "하이패스 설명" | "동김천 휴게소 메뉴 알려줘" | 재작성 안 함 |
5.2 QueryRewriter 판단 기준
QueryRewriter는 무조건 이전 대화에 붙이지 않습니다.
현재 질문이 아래 조건 중 하나에 해당할 때만 후속 질문 후보로 보고, 최종 판단은 LLM 재작성 프롬프트에서 한 번 더 합니다.
| 판단 기준 | 예 |
|---|---|
| 지시어 포함 | 그럼, 그거, 거기, 아까, 이어서, 해당 |
| 짧은 후속 질문 | 20자 이하의 얼마야?, 어디서 사?, 어떻게 해?, 가능해? |
| 시간/방법/이유 질문 | 언제, 기간, 왜, 방법, 절차, 신청 |
재작성 프롬프트의 핵심 규칙은 다음과 같습니다.
| 규칙 | 설명 |
|---|---|
| 후속 질문만 재작성 | 새 주제면 __NO_REWRITE__만 출력 |
| 지시어 구체화 | 그거, 거기 등을 이전 대화의 명사로 치환 |
| 의도 유지 | 사용자가 물은 범위를 넓히거나 새 정보를 추가하지 않음 |
| 한 문장 출력 | 재작성된 질문 한 문장 또는 __NO_REWRITE__만 허용 |
| thinking 제거 | <think>...</think>가 있으면 후처리에서 제거 |
6. 도메인 tool 흐름
RAG Agent가 tool을 선택하면 WAS chatbotApi가 실제 정책·검증·조회 실행을 담당합니다.
sequenceDiagram
participant Agent as exAiChatBot Agent
participant WAS as chatbotApi
participant Skill as kakaoChatbotSkill
participant DB as Oracle
participant Data as data.ex.co.kr
Agent->>WAS: GET /api/v1/tools/definitions
WAS-->>Agent: intent-definitions.yml 기반 tools
Agent->>WAS: POST /api/v1/tools/execute
alt fetchOwner=WAS
WAS->>DB: Oracle/domain handler 조회
WAS-->>Agent: domainData, uiType, intentType
else fetchOwner=WEB
WAS->>Skill: POST /internal/tools/data-portal
Skill->>Data: 외부 API 조회
Skill-->>WAS: domainData
WAS-->>Agent: domainData, uiType, intentType
else needsClarification
WAS-->>Agent: clarificationQuestion, missingParams
end
대표 tool
| 구분 | Tool |
|---|---|
| WAS/Oracle | FARE_SEARCH, FARE_UNPAID, FARE_REFUND, IC_TEL, REST_AREA_PRICE, TROAD_* |
| WEB/data.ex.co.kr | GASSTATION, BRAND_SHOP, REST_AREA_FOOD_LIST |
| RAG 로컬 | rag_search, ask_user |
WAS가 needsClarification=true를 반환하면 Agent는 pending을 저장하고 clarify 응답을 반환합니다.
7. Pending · 멀티턴 흐름
sequenceDiagram
participant User as 사용자
participant Web as kakaoChatbotSkill
participant RAG as AgentService
participant WAS as chatbotApi
participant Mongo as MongoDB
User->>Web: 신갈까지 통행요금
Web->>RAG: POST /agent/chat
RAG->>WAS: FARE_SEARCH {toIc: 신갈}
WAS-->>RAG: needsClarification, missing fromIc
RAG->>Mongo: pending 저장
RAG-->>Web: clarify + pendingIntentType
Web-->>User: 출발 IC를 알려주세요
User->>Web: 판교
Web->>RAG: POST /agent/chat
RAG->>Mongo: pending 조회
RAG->>WAS: FARE_SEARCH {fromIc: 판교, toIc: 신갈}
WAS-->>RAG: domainData
RAG->>Mongo: pending clear
RAG-->>Web: domain answer
| 저장소 | 역할 |
|---|---|
AgentPendingStore |
agent_pending Mongo collection, 실패 시 in-memory fallback |
| 대화 이력 | rag_conversations collection, 재작성·통계·문맥 참고 |
| Skill | pending을 직접 소유하지 않고 응답 필드를 UI에 반영 |
| WAS | stateless. 매 요청의 params를 기준으로 검증/조회 |
8. 최종 답변 생성
Agent loop의 final_content는 그대로 사용자에게 내보내지 않습니다.
근거가 있으면 PromptBuilder로 다시 최종 답변을 생성하고, 근거가 없으면 guidance로 전환합니다.
flowchart TD
ToolResult[tool 결과]
Domain{사용 가능한 domainData?}
RAG{Qdrant references?}
Prompt[PromptBuilder.build_answer_prompt_messages]
LLM[LLMHandler.generate_answer_from_messages]
Suggest[낮은 신뢰도면 제안 질문 추가]
Guide[PromptBuilder.build_guidance_prompt]
Save[대화 이력 저장]
Out[응답 반환]
ToolResult --> Domain
Domain -->|yes| Prompt
Domain -->|no| RAG
RAG -->|yes| Prompt
RAG -->|no| Guide
Prompt --> LLM --> Suggest --> Save --> Out
Guide --> Save --> Out
응답 주요 필드
| 필드 | 의미 |
|---|---|
answer, llmAnswer |
사용자에게 보여줄 텍스트 |
routeType |
agent, domain, web_domain, clarify, greeting 등 |
intentType |
도메인 tool 상세 (FARE_SEARCH, IC_TEL 등) |
domainData |
도메인 조회 결과 |
references |
Qdrant FAQ 근거 |
faqUrls |
HTTP/HTTPS 원문 URL 최대 3개 |
toolTrace |
호출 tool, arguments, 성공/실패 |
needsClarification |
되물음 여부 |
pendingIntentType, missingParams |
멀티턴 슬롯필링 정보 |
9. Web 채팅 흐름 (/web/ask)
현재 웹 채팅은 카카오 SkillResponse를 우회합니다.
WebChatService가 Agent 응답을 받은 뒤 WebChatResponseMapper가 웹 전용 DTO로 변환합니다.
flowchart TD
App[chatbotApp]
Controller[WebChatController]
Service[WebChatService]
AgentSvc[WebChatAgentService]
RAG[exAiChatBot /agent/chat]
Mapper[WebChatResponseMapper]
DTO[WebAskResponse]
App -->|POST /web/ask| Controller --> Service --> AgentSvc
AgentSvc -->|agent.mode=agent| RAG
RAG --> Mapper
Mapper --> DTO --> App
Web 전용 변환
| 원본 필드 | 웹 UI |
|---|---|
answer |
MarkdownBody 렌더링 |
references, faqUrls |
원문 버튼 |
domainData, uiType |
요금/주유/브랜드/음식/전화 카드 |
icCandidates, quickReplies |
빠른 선택 버튼 |
| 제안 질문 블록 | 본문 안에서 클릭 가능한 세로 버튼 |
10. FAQ 데이터 적재 흐름
10.1 Batch ingest
flowchart TD
Raw[data/qa_raw.jsonl]
Load[q/a + source_created_at 검사]
Dedup[동일 질문 최신 source_created_at만 유지]
Embed[EmbeddingClient.embed\nis_query=false]
Upsert[Qdrant upsert_vectors\nskip_if_older=true]
QD[(qa_vectors)]
Raw --> Load --> Dedup --> Embed --> Upsert --> QD
| 단계 | 내용 |
|---|---|
| 입력 | q/a 또는 question/answer 지원 |
| 필수 | 질문, 답변, source_created_at |
| 중복 | 동일 질문은 최신 source_created_at만 유지 |
| 임베딩 | 질문 q만 document 모드로 임베딩 |
| 저장 | payload에 q, a, category, url, source, source_id, 날짜 저장 |
| 갱신 | 기존 데이터가 더 최신이면 upsert skip |
10.2 Vector Admin API
scripts/admin_service.py는 chatbotAdmin과 운영자가 사용하는 Qdrant 큐레이션 API입니다.
| Method | URL | 용도 |
|---|---|---|
POST |
/api/search |
벡터 의미 검색 |
POST |
/api/keyword-search |
문자열 포함 검색 (q, a, both) |
GET |
/api/points |
FAQ 목록/페이지 조회 |
GET |
/api/points/{id} |
단건 조회 |
POST |
/api/points |
FAQ 추가 |
PUT |
/api/points/{id} |
FAQ 수정, 질문 변경 시 새 ID 생성 후 기존 ID 삭제 |
DELETE |
/api/points/{id} |
FAQ 삭제 |
POST |
/api/points/delete-batch |
일괄 삭제 |
GET |
/api/categories |
카테고리 목록/건수 |
/api/keyword-search는 Admin 검색용 API입니다. 챗봇rag_search는 같은 Qdrant 문자열 검색 메서드를 직접 호출하되, 벡터 검색 실패 후 guidance 직전에 최대 3건만 보조 검색합니다.
11. chatbotAdmin 학습 관리 흐름
flowchart TD
Excel[엑셀 업로드]
Rows[TrainingQaRow 파싱]
Similar[유사 FAQ 검색\nFaqSimilarityService]
Decision{유사 후보 있음?}
Add[Qdrant 즉시 적재]
Conflict[training_conflicts 저장\n검토 대기]
RowLog[training_job_rows 저장\n행별 결과]
Review[운영자 검토]
Action{검토 액션}
Keep[기존 유지 / 신규 폐기]
New[신규 추가]
Update[기존 업데이트\n질문 변경 시 delete + create]
Excel --> Rows --> Similar --> Decision
Decision -->|없음| Add --> RowLog
Decision -->|있음| Conflict --> RowLog --> Review --> Action
Action --> Keep
Action --> New
Action --> Update
| 저장소 | 역할 |
|---|---|
training_jobs |
업로드 작업 단위 상태·집계 |
training_job_rows |
모든 행의 결과 (ADDED, CONFLICT, SKIPPED_*, FAILED) |
training_conflicts |
유사 FAQ 충돌 검토 대상만 저장 |
| Qdrant | 최종 FAQ 원천 |
검토 액션
| 액션 | 결과 |
|---|---|
keep_existing / skip_new |
기존 FAQ 유지, 신규 데이터 폐기 |
add_new / create_new |
신규 FAQ 추가 |
update_existing |
기존 FAQ 수정. 질문이 바뀌면 기존 FAQ 삭제 후 신규 생성 |
replace_existing |
기존 FAQ 삭제 후 신규 FAQ 생성 |
삭제나 교체 전에는 FaqService가 기존 FAQ를 버전 스냅샷으로 남깁니다.
다만 현재 검토 화면에는 즉시 되돌리기 버튼은 없으므로, 삭제성 액션에는 확인 UX를 추가하는 것이 안전합니다.
12. 로깅·통계
Agent 응답은 MongoDB 대화 이력에 저장됩니다.
| 필드 | 용도 |
|---|---|
bot_id |
대화 단위 |
user_query |
사용자 발화 |
ai_response |
최종 답변 |
matched_questions |
참고 FAQ 질문 |
scores |
rerank 점수 |
metadata.routeType |
처리 경로 |
metadata.intentType |
도메인 tool 상세 |
metadata.answer_confidence |
high/medium/low |
metadata.num_references |
참고 문서 수 |
chatbotAdmin의 통계 분석은 이 실제 대화 이력을 기준으로 실패질문, 전체 로그, 많이 묻는 질문, 근거 구분(Qdrant/도메인 툴)을 보여줍니다.
13. 주요 파일 역할
| 파일 | 역할 |
|---|---|
scripts/run_service_qa.py |
FastAPI 엔트리포인트, /agent/chat, /health |
scripts/agent/agent_service.py |
Agent tool loop, pending, 강제 RAG, 도메인 가드, guidance |
scripts/agent/tool_executor.py |
rag_search, ask_user, WAS domain tool 실행 |
scripts/agent/chatbot_tool_client.py |
WAS /api/v1/tools/* HTTP client |
scripts/agent/pending_store.py |
Agent pending 저장소 |
scripts/handlers/search_handler.py |
Qdrant 검색과 rerank |
scripts/handlers/prompt_builder.py |
최종 답변/guidance 프롬프트 |
scripts/handlers/query_rewriter.py |
대화 이력 기반 검색어 재작성 |
scripts/handlers/llm_handler.py |
최종 LLM 답변 생성 |
scripts/handlers/response_handler.py |
대화 이력 저장·응답 보조 유틸 |
scripts/vector_store.py |
QdrantStore 구현 |
scripts/admin_service.py |
Vector Admin API |
scripts/ingest_qa.py |
batch FAQ 적재 |
scripts/api_clients.py |
LLM/Embedding/Reranker client |
14. 운영 확인 포인트
| 확인 항목 | 기준 |
|---|---|
| RAG API | GET :28012/health가 ok 또는 원인 확인 가능한 degraded 반환 |
| Qdrant 적재 | /health.vector_count > 0, Admin 목록에서 FAQ 조회 |
| Agent tool | /agent/chat 응답에 toolTrace 확인 |
| FAQ 근거 | FAQ 답변에 references, faqUrls 포함 |
| 도메인 근거 | 도메인 질문에 intentType, domainData, uiType 포함 |
| 멀티턴 | clarify 후 다음 발화에서 pending이 이어지는지 확인 |
| Web UI | /web/ask 응답이 WebAskResponse 형태로 카드/버튼을 포함 |
| 학습 관리 | 업로드 후 training_job_rows, training_conflicts 상태 확인 |