# 고객사 사내 LLM(FabriX/Gauss) 호출 규격 맞추기 — 계획과 적용 (2026-09-21) 고객사 PC 에서는 외부 LLM(OpenRouter) 을 못 쓴다(보안). Stage 3(로직 조각 추출)을 고객사 전용 LLM 으로 돌리려면 호출 규격을 바꿔야 한다. 코드는 이미 바꿨고, **고객사 PC 에서는 `.env` 만 채우면 된다.** ## 1. 무엇이 다른가 | | 지금까지 (OpenRouter) | 고객사 LLM (serving_llm.py 예시 + 팀원 실측) | |---|---|---| | 인증 | `Authorization: Bearer <키>` 헤더 1개 | 커스텀 헤더 4개: `x-openapi-token`(Bearer 접두 필수) · `x-generative-ai-client` · `x-llm-model-id` · `x-generative-ai-user-email`(신원) | | 주소 | `https://openrouter.ai/api/v1` + `/chat/completions` | 예시는 `https://genai-openapi.sec.samsung.net/dxhq/prod/api-llm` (완성 주소인지, 뒤에 `/chat/completions` 가 붙는지 **미확인**) | | 모델 지정 | body `model: "z-ai/glm-5.2"` | 헤더 `x-llm-model-id: 581`(예시) 또는 `339`(팀원). body 의 `model` 은 팀원 실측상 `/mnt/models` 고정 | | 응답 형식 강제 | `response_format: json_object` | 받는지 **미확인** — 400 이면 빼야 함 | | 응답 모양 | OpenAI 규격 `choices[0].message.content` | 같다고 가정 (팀원 가이드도 같은 전제). 다르면 파서 추가 | 바뀌는 건 **헤더와 body 의 model 값**뿐이고 프롬프트·파이프라인은 그대로다. ## 2. 적용한 코드 변경 | 파일 | 내용 | |---|---| | `config/settings.py` | `LLM_PROVIDER`(openai/fabrix), `LLM_CHAT_PATH`, `LLM_BODY_MODEL`, `LLM_JSON_MODE`, `LLM_MAX_TOKENS`, `FABRIX_CLIENT_KEY/OPENAPI_TOKEN/USER_EMAIL`. `llm_enabled()` 가 규격별 필수값을 본다 | | `summarize/llm_client.py` | `OpenAICompatClient` 가 `headers()`/`body()` 를 규격에 따라 만든다. fabrix 면 Bearer 접두를 코드가 붙인다 | | `summarize/ping.py` | **접속 점검 명령.** `python -m summarize.ping` 한 번으로 주소·헤더·body 와 서버 응답을 확인 | | `query/api.py` | "LLM 켜짐" 판정을 `settings.llm_enabled()` 로 | | `.env.example` | fabrix 블록(주석) | 기본값은 전부 openai 라 기존 OpenRouter 동작은 그대로다. ## 3. 고객사 PC 에서 할 일 (순서대로) ### 3.1 `.env` 에 추가 ```ini LLM_PROVIDER=fabrix LLM_BASE_URL=https://genai-openapi.sec.samsung.net/dxhq/prod/api-llm # 고객사 파일(serving_llm.py)의 ENDPOINT_URL 그대로 LLM_CHAT_PATH= # 위 주소가 완성 주소면 빈값. 404 나면 /chat/completions 로 LLM_MODEL=581 # 고객사 파일의 YOUR_MODEL. 팀원은 339(GaussO Flash) 를 씀 — 텍스트 모델이면 됨 LLM_BODY_MODEL=/mnt/models # 400 나면 LLM_MODEL 과 같은 값으로 바꿔 본다 LLM_JSON_MODE=1 # 400 나면 0 FABRIX_CLIENT_KEY= FABRIX_OPENAPI_TOKEN= FABRIX_USER_EMAIL= LLM_CONCURRENCY=2 LLM_API_KEY= # 안 씀. 비워도 됨 ``` ### 3.2 점검 (Stage 3 돌리기 전에 반드시) ```powershell .venv\Scripts\python -m summarize.ping --show # 보낼 내용만 출력 (비밀값 가림) .venv\Scripts\python -m summarize.ping # 실제 한 번 호출 ``` | 결과 | 뜻 | 조치 | |---|---|---| | `성공` + `JSON 파싱: OK` | 규격 맞음 | 3.3 으로 | | HTTP 401 / 403 | 헤더 문제 | 토큰·클라이언트키·이메일 값 확인. 토큰이 `Bearer ` 로 시작하는지 서버 응답 본문을 본다 | | HTTP 404 | 주소 문제 | `LLM_CHAT_PATH=/chat/completions` 로 바꾸거나, 고객사 파일의 실제 `requests.post(...)` URL 을 확인 | | HTTP 400 | body 문제 | 먼저 `LLM_JSON_MODE=0`, 그래도면 `LLM_BODY_MODEL` 을 비우거나 `LLM_MODEL` 값으로 | | `응답 형태가 OpenAI 규격이 아닙니다` | 응답 모양이 다름 | 출력된 JSON 을 가져오면 파서를 맞춘다 (코드 수정 필요) | | `JSON 파싱: 실패` | 모델이 JSON 을 안 지킴 | `LLM_JSON_MODE=1` 로 켜 보고, 안 되면 모델 번호를 바꾼다 | ### 3.3 Stage 3 실행 ```powershell .venv\Scripts\python -m summarize.runner --llm api --program <프로그램> --limit 3 # 작게 .venv\Scripts\python -m summarize.jobs stats # failed 0 인지 .venv\Scripts\python -m summarize.runner --llm api --program <프로그램> # 한 본 .venv\Scripts\python -m summarize.runner --llm api # 전체 .venv\Scripts\python -m wiki_out.run --all ``` ## 4. 확인이 필요한 것 (고객사 파일 원본을 보면 바로 답이 나온다) 1. `requests.post(...)` 에 넣는 **URL 이 ENDPOINT_URL 그대로인지, 뒤에 경로가 붙는지** → `LLM_CHAT_PATH` 2. **body 의 `model` 값** — 예시 파일이 `"model": ...` 을 뭐로 보내는지 → `LLM_BODY_MODEL` 3. **응답 JSON 모양** — `choices[0].message.content` 인지 4. **동시 호출·일일 토큰 한도** — 55본에 조각 3,212개(LLM 호출 약 5,600회)였다. 1만 본이면 호출 수십만 회라 한도를 미리 물어야 한다 5. `tools`(function calling) 는 예시에 있지만 **우리는 안 쓴다** — 무시해도 된다 ## 5. 대안 (직결이 안 될 때) 팀원 가이드의 -12 컨테이너 게이트웨이(`/api/ito`) 경유: `LLM_PROVIDER=openai`, `LLM_BASE_URL=http://10.196.81.34:8914/api/ito`, `LLM_API_KEY=`, `LLM_MODEL=339`. 게이트웨이가 헤더를 대신 얹는다. 백엔드가 떠 있어야 하고 부하가 거기 걸린다.