Files
exAichatbot_agent/exAiChatBot-chatbot2.0-agent/PROCESS_FLOW.md
T
Macbook 4b86b2a660 Agent 2.0 exdev 서버 배포 스택
- server-dev start/stop/deploy 및 Gitea push 자동 배포
- local-dev 로컬 개발 환경

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 22:57:30 +09:00

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 대신 되물음

검색어 맥락화 순서

  1. botId가 있고 CHAT_HISTORY_ENABLED=true이면 MongoDB에서 최근 대화 이력을 조회합니다.
  2. 조회 범위는 CHAT_HISTORY_HOURS 시간 이내, 최대 CHAT_HISTORY_LIMIT개입니다.
  3. 이력은 오래된 대화 → 최신 대화 순서로 정렬됩니다.
  4. 답변 본문에서 혹시 이런 것을 찾으셨나요? 제안 블록은 제거합니다.
  5. QueryRewriter.rewrite_query()가 현재 질문이 후속 질문인지 판단합니다.
  6. 후속 질문이면 완결형 검색어를 반환하고, 새 주제면 __NO_REWRITE__를 반환합니다.
  7. 재작성 결과가 없거나 원문과 같으면 원문 검색어로 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 상태 확인