- 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>
86 lines
5.5 KiB
Markdown
86 lines
5.5 KiB
Markdown
# 고객사 사내 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=<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 돌리기 전에 반드시)
|
|
|
|
```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=<AAF_GATEWAY_KEY>`, `LLM_MODEL=339`. 게이트웨이가 헤더를 대신 얹는다. 백엔드가 떠 있어야 하고 부하가 거기 걸린다.
|