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
5 changes: 5 additions & 0 deletions ai/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ EMBEDDING_DIM=1536
EMBEDDING_CHUNK_SIZE=1000
EMBEDDING_CHUNK_OVERLAP=200
EMBEDDING_BATCH_SIZE=32

# 답변별 복기(코칭) 부하 안전밸브 — 피드백의 최대 비용 지점(답변 수만큼 LLM 호출).
# 게이트웨이가 429 를 내기 시작하면 재배포 없이 이 둘을 낮춘다.
FEEDBACK_COACHING_MAX_ANSWERS=30
FEEDBACK_COACHING_CONCURRENCY=5
EMBEDDING_MAX_RETRIES=5 # 429(RESOURCE_EXHAUSTED) 지수 백오프 재시도 횟수
EMBEDDING_RETRY_BASE_DELAY_SEC=2.0 # 재시도 기본 지연(초). delay=base*2^attempt (최대 30s)

Expand Down
6 changes: 6 additions & 0 deletions ai/src/ai_server/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class Settings(BaseSettings):
ai_realtime_exchange: str = "stackup.realtime"
ai_realtime_routing_user: str = "realtime.user.notify"
feedback_rag_top_k: int = 5
# 답변별 복기(코칭)는 답변 수만큼 LLM 을 부르는 피드백 최대 비용 지점이다. 아래 둘이
# 게이트웨이 429/과부하를 막는 안전밸브인데 상수로 박혀 있어 조일 수가 없었다 —
# 다른 부하 관련 값(embedding_batch_size·llm_*_timeout_sec·*_rag_timeout_sec)과 같이
# 환경변수로 뺀다. 게이트웨이가 조이기 시작하면 재배포 없이 이 둘을 낮춘다.
feedback_coaching_max_answers: int = 30
feedback_coaching_concurrency: int = 5
# RAG 컨텍스트 구성(임베딩+검색) 전체 상한. 초과 시 (none) 으로 폴백해 첫 토큰을 막지 않는다.
followup_rag_timeout_sec: float = 1.5
# 질문 풀 생성 시 다문서 RAG 검색 상한. followup 과 대칭 — 초과 시 base_context 로 폴백.
Expand Down
2 changes: 2 additions & 0 deletions ai/src/ai_server/messaging/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ def __init__(self, settings: Settings) -> None:
core_client=core_client,
embedder=embedder,
rag_top_k=settings.feedback_rag_top_k,
coaching_max_answers=settings.feedback_coaching_max_answers,
coaching_concurrency=settings.feedback_coaching_concurrency,
# 자기소개 첫인상 평가(Flash, 경량). 종합 점수엔 미포함, 패널 '첫인상' 항목으로만 표시.
self_intro_evaluator=LlmSelfIntroEvaluator(
build_self_intro_evaluation_chain(settings, core_client=core_client)
Expand Down
22 changes: 22 additions & 0 deletions ai/tests/test_feedback_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1264,3 +1264,25 @@ async def test_no_storage_keeps_report_key_none():

payload: FeedbackCallbackPayload = publisher.publish.await_args.kwargs["payload"]
assert payload.report_s3_key is None


# 코칭 상한·동시성은 게이트웨이 429 를 막는 안전밸브다. 상수로 박혀 있으면 게이트웨이가
# 조이기 시작해도 재배포 없이는 낮출 수 없다 — runner 가 settings 값을 실제로 전달하는지 고정.
def test_runner_wires_coaching_limits_from_settings() -> None:
import inspect

from ai_server.messaging import runner as runner_module

# 공백·줄바꿈에 깨지지 않게 정규화한 뒤 본다. 런타임 조립에 필요한 의존성이 많아
# 인스턴스를 만들어 검증하기보다 배선 자체를 고정하는 쪽이 싸다.
source = "".join(inspect.getsource(runner_module).split())
assert "coaching_max_answers=settings.feedback_coaching_max_answers" in source
assert "coaching_concurrency=settings.feedback_coaching_concurrency" in source


def test_coaching_limits_are_settings_backed() -> None:
from ai_server.config.settings import Settings

fields = Settings.model_fields
assert "feedback_coaching_max_answers" in fields
assert "feedback_coaching_concurrency" in fields
2 changes: 2 additions & 0 deletions docs/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ EMBEDDING_DIM=1536 # DB 컬럼 차원과 일치 필수
EMBEDDING_CHUNK_SIZE=1000
EMBEDDING_CHUNK_OVERLAP=200
EMBEDDING_BATCH_SIZE=32 # 한 요청당 청크 수 (작을수록 429 회피, 호출 수↑)
FEEDBACK_COACHING_MAX_ANSWERS=30 # 답변별 복기 대상 상한 (초과분은 잘라내고 로그 남김)
FEEDBACK_COACHING_CONCURRENCY=5 # 복기 동시 호출 수 (429 회피용 — 낮출수록 느려지고 안전)
EMBEDDING_MAX_RETRIES=5 # 429(RESOURCE_EXHAUSTED) 지수 백오프 재시도 횟수
EMBEDDING_RETRY_BASE_DELAY_SEC=2.0 # delay = base*2^attempt + jitter (상한 30s)

Expand Down
Loading