Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ LLM_PROVIDER=openai
LLM_MODEL=gpt-4.1-mini
# 챗봇만 쓰는 모델(같은 provider). 비우면 LLM_MODEL. 도구를 고르는 에이전트라 nano 로는 불안정하다.
CHATBOT_LLM_MODEL=gpt-4.1-mini
# 화물 혼재 심사(/safety/assess)만 쓰는 모델. 비우면 LLM_MODEL. 서술(화물 특성·체크리스트)만 LLM 이라 nano 로 충분.
SAFETY_LLM_MODEL=gpt-4.1-nano

# ── 공공 API ───────────────────────────────────────────────────────────────
KMA_API_KEY=
Expand Down
4 changes: 2 additions & 2 deletions app/api/v1/safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
SafetyVerdict,
)
from app.agents.safety.service import assess_safety, assess_verdict
from app.core.deps import get_llm_client, get_session
from app.core.deps import get_safety_llm_client, get_session
from app.core.exceptions import LLMGenerationError, MsdsNotFoundError, MsdsUpstreamError
from app.llm.base import LLMClient
from app.neo4j_client import neo4j_client
Expand Down Expand Up @@ -57,7 +57,7 @@
async def assess(
request: SafetyAssessmentRequest,
db: AsyncSession = Depends(get_session),
llm_client: LLMClient = Depends(get_llm_client),
llm_client: LLMClient = Depends(get_safety_llm_client),
) -> SafetyAssessmentResult:
try:
return await assess_safety(db, neo4j_client.driver, llm_client, request)
Expand Down
3 changes: 3 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ class Settings(BaseSettings):
# 고르는 에이전트라(27번 설계안 C단계) 작은 모델로는 도구 선택이 불안정하고, 판정 잡은
# 결과가 바뀔 때만 LLM 을 부르므로 작은 모델로 충분해 둘을 나눈다.
chatbot_llm_model: str | None = None
# [2026-09-29] 화물 혼재 심사(POST /safety/assess)만 쓰는 모델. 비우면 llm_model. 등급은 규칙엔진이
# 정하고 LLM 은 화물 특성·체크리스트만 쓰므로 작은 모델로 응답 시간을 줄인다.
safety_llm_model: str | None = None
# 임베딩은 llm_provider와 독립적으로 고른다. 챗봇 RAG의 벡터 공간은 적재
# 시점(scripts/embed_msds.py)과 조회 시점이 반드시 같은 모델이어야 하는데,
# llm_provider는 답변 품질/비용 사정으로 자유롭게 바뀔 수 있기 때문이다.
Expand Down
5 changes: 5 additions & 0 deletions app/core/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from app.llm.factory import get_chatbot_llm_client as _get_chatbot_llm_client
from app.llm.factory import get_embedding_client as _get_embedding_client
from app.llm.factory import get_llm_client as _get_llm_client
from app.llm.factory import get_safety_llm_client as _get_safety_llm_client


async def get_session(session: AsyncSession = Depends(get_db)) -> AsyncSession:
Expand All @@ -21,5 +22,9 @@ def get_chatbot_llm_client() -> LLMClient:
return _get_chatbot_llm_client()


def get_safety_llm_client() -> LLMClient:
return _get_safety_llm_client()


def get_embedding_client() -> EmbeddingClient:
return _get_embedding_client()
16 changes: 15 additions & 1 deletion app/llm/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,22 @@ def get_chatbot_llm_client() -> LLMClient:
provider 는 get_llm_client 와 같다 — 챗봇만 다른 provider 를 쓸 이유가 아직 없다.
"""
settings = get_settings()
model = settings.chatbot_llm_model or settings.llm_model
return _client_for(settings.chatbot_llm_model or settings.llm_model)


@lru_cache
def get_safety_llm_client() -> LLMClient:
"""화물 혼재 심사(POST /safety/assess)용 LLMClient. safety_llm_model 이 있으면 그 모델, 없으면 llm_model.

[2026-09-29] 종합 문장 품질 때문에 llm_model 을 mini 로 올리자 혼재 심사 화면(화물 특성·체크리스트만
LLM)이 느려졌다 — 등급은 규칙엔진이 정하므로 이 서술은 작은 모델로 충분하다.
"""
settings = get_settings()
return _client_for(settings.safety_llm_model or settings.llm_model)


def _client_for(model: str) -> LLMClient:
settings = get_settings()
if settings.llm_provider == "gemini":
if not settings.gemini_api_key:
raise RuntimeError("GEMINI_API_KEY가 설정되지 않았습니다 (.env 확인).")
Expand Down
Loading