- settings: LLM_PROVIDER/LLM_CHAT_PATH/LLM_BODY_MODEL/LLM_JSON_MODE/LLM_MAX_TOKENS, FABRIX_* 3종, llm_enabled() - llm_client: headers()/body() 를 규격별로 구성. fabrix 는 x-openapi-token(Bearer)/x-generative-ai-client/ x-llm-model-id/x-generative-ai-user-email, body model 은 LLM_BODY_MODEL - summarize/ping: 접속 점검 명령 (--show 로 요청만 확인) - docs/llm-provider-plan.md: 계획·.env 값·오류별 조치. 기본값은 openai 라 기존 동작 불변 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.5 KiB
5.5 KiB
고객사 사내 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 에 추가
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=<YOUR_CLIENT_KEY 값>
FABRIX_OPENAPI_TOKEN=<YOUR_PASS_KEY 값. "Bearer " 는 있어도 없어도 됨>
FABRIX_USER_EMAIL=<YOUR_EMAIL 값>
LLM_CONCURRENCY=2
LLM_API_KEY= # 안 씀. 비워도 됨
3.2 점검 (Stage 3 돌리기 전에 반드시)
.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 실행
.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. 확인이 필요한 것 (고객사 파일 원본을 보면 바로 답이 나온다)
requests.post(...)에 넣는 URL 이 ENDPOINT_URL 그대로인지, 뒤에 경로가 붙는지 →LLM_CHAT_PATH- body 의
model값 — 예시 파일이"model": ...을 뭐로 보내는지 →LLM_BODY_MODEL - 응답 JSON 모양 —
choices[0].message.content인지 - 동시 호출·일일 토큰 한도 — 55본에 조각 3,212개(LLM 호출 약 5,600회)였다. 1만 본이면 호출 수십만 회라 한도를 미리 물어야 한다
tools(function calling) 는 예시에 있지만 우리는 안 쓴다 — 무시해도 된다
5. 대안 (직결이 안 될 때)
팀원 가이드의 -12 컨테이너 게이트웨이(/api/ito) 경유: LLM_PROVIDER=openai, LLM_BASE_URL=http://10.196.81.34:8914/api/ito,
LLM_API_KEY=<AAF_GATEWAY_KEY>, LLM_MODEL=339. 게이트웨이가 헤더를 대신 얹는다. 백엔드가 떠 있어야 하고 부하가 거기 걸린다.