tags: [worklog, llm, gpt-5-mini, provider-agnostic, skill-news, rb8001, runtime]

# 260316 gpt-5-mini 전환과 skill-news provider-agnostic 정리

## 상위 원칙

- [LLM 모델 SSOT 전환 계획](../plans/260316_llm_model_ssot_transition_계획.md)
- [모델 SSOT 하드코딩 분산과 workspace-config 로컬이식 통합 리서치](../research/260315_모델SSOT_하드코딩_분산과_workspace_config_로컬이식_통합리서치.md)
- [260316 LLM 모델 SSOT 1차구현 및 로컬검증](./260316_llm_model_ssot_1차구현_및_로컬검증.md)

## 상태

- completed
- server-runtime-verified

## 이번 작업에서 확인한 추가 사실

- 기존 닫힘 검증은 `DEFAULT_LLM_MODEL` 키 수렴까지는 맞았지만, provider 전환까지 설정 1회 변경으로 닫히는 상태는 아니었습니다.
- `skill-news`에는 `google.generativeai` 직접 호출이 남아 있어 `DEFAULT_LLM_MODEL=gpt-5-mini`만 바꾸면 실제 런타임이 깨지는 상태였습니다.
- `rb8001`은 `gpt-5-mini` 핸들러 선택까지는 가능했지만, compose 오버라이드와 OpenAI 파라미터 호환성 때문에 실제 호출은 실패했습니다.

## 구현한 것

- `skill-news`
  - `news_summarizer.py`, `companyx_news_summarizer.py`, `sea_news_filter.py`의 Gemini SDK 직접 호출을 제거했습니다.
  - `llm_gateway_client.py`를 추가해 `skill-news -> rb8001 /api/llm/generate` 공용 경로만 사용하도록 정리했습니다.
  - `test_sea_news_filter.py` 모킹 대상을 새 공용 호출 메서드명에 맞게 갱신했습니다.
- `rb8001`
  - `docker-compose.yml`에서 `OPENAI_API_KEY`, `GEMINI_API_KEY`, `DEFAULT_LLM_MODEL` 직접 오버라이드 줄을 제거해 `workspace-config` 우선순위가 실제 런타임에 반영되게 맞췄습니다.
  - `openai_handler.py`에서 `gpt-5-mini` 호출 시 `max_completion_tokens`를 사용하고 `temperature`를 보내지 않도록 모델별 파라미터 분기를 추가했습니다.
- `skill-slack`
  - 기본 모델 설정 기본값을 `gpt-5-mini`로 맞췄습니다.
- 공용 런타임
  - 51124 서버 `/home/admin/workspace-config/runtime.env`의 `DEFAULT_LLM_MODEL`을 `gpt-5-mini`로 변경했습니다.

## 검증

- `python3 -m compileall`
  - `rb8001`, `skill-slack`, `skill-news` 변경 파일 기준 통과
- `pytest /home/admin/robeing/skill_news/tests/test_sea_news_filter.py -q`
  - `5 passed`
- 51124 런타임 검증
  - `rb8001`, `skill-slack`, `robeing-skill-news` 컨테이너 모두 healthy 확인
  - 세 컨테이너 내부 `settings.DEFAULT_LLM_MODEL`이 모두 `gpt-5-mini`로 읽히는 것 확인
  - `rb8001` 컨테이너 내부 `OPENAI_API_KEY`가 `workspace-config/secrets.env`의 실제 키를 읽는 것 확인
  - `http://localhost:8001/api/llm/generate`에 `model=gpt-5-mini` 요청 시 `200` 응답 확인
  - `skill-news` 컨테이너에서 `http://192.168.0.106:8001/api/llm/generate`로 같은 요청을 보냈을 때 `200` 응답 확인
  - 실제 응답 본문에서 `model_used: gpt-5-mini` 확인

## 트러블슈팅으로 확인한 런타임 함정

- `rb8001/.env`의 placeholder `OPENAI_API_KEY`가 compose `environment` 오버라이드와 결합되면 `workspace-config/secrets.env`의 실제 키를 덮어쓸 수 있었습니다.
- `gpt-5-mini`는 기존 OpenAI 경로의 `max_tokens`, `temperature` 파라미터를 그대로 받지 않아, handler 레벨 호환성 보정이 필요했습니다.
- 따라서 이번 전환의 실제 닫힘 조건은 단순 모델명 변경이 아니라, provider 직접 호출 제거 + secrets/runtime 우선순위 정리 + 모델별 API 파라미터 호환성 정리까지 포함해야 했습니다.

## 결과

- 이제 `skill-news`는 provider SDK를 직접 선택하지 않고 공용 LLM 경로만 사용합니다.
- `rb8001`, `skill-slack`, `skill-news`의 기본 모델은 51124 런타임에서 모두 `gpt-5-mini`로 수렴했습니다.
- 최소 현재 운영 범위 기준으로 `gpt-5-mini` 전환은 실제 호출 성공까지 검증됐습니다.

## 짧은 운영 메모

- 모델 SSOT 닫힘과 provider 전환 가능 상태는 다릅니다. 다음 전환 때는 직접 SDK 호출 잔존 여부를 먼저 전수 확인합니다.
- compose `environment: KEY=${KEY}`는 `env_file` 값을 placeholder `.env`로 덮어쓸 수 있으므로, 공용 secrets/runtime를 SSOT로 쓸 때는 직접 오버라이드 줄을 먼저 의심합니다.
- 새 OpenAI 계열 모델은 `max_tokens`, `temperature` 같은 파라미터 호환성이 다를 수 있으므로, 첫 실패 시 앱 로그의 upstream 400 메시지를 바로 확인합니다.
