docs(tech): LLM 관측(Phoenix) — 환경 무관 결정·실측·설치 요령 정리
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
5d1ef6ee30
commit
f55e673cb5
@@ -0,0 +1,47 @@
|
|||||||
|
# LLM 관측(Phoenix) — 어디 옮겨 깔아도 그대로 쓰는 정보 (2026-09-22)
|
||||||
|
|
||||||
|
고객사 -12/-13 에 처음 올리며 정한 것들. 서버 구조가 바뀌어도 유지되는 결정과 실측만 적음.
|
||||||
|
환경별 명령은 `5_django_backend/deploy/phoenix/README.md`, 반입 함정은 `docs/tech/customer-repo-layout.md`.
|
||||||
|
|
||||||
|
## 결정
|
||||||
|
|
||||||
|
- **Phoenix(Arize) 하나에 두 제품 다 모음.** 프로젝트로 나눔: `codeassist`(Code Assistant), `abap-specgen`(ABAP OpenCode). 이름은 각 백엔드 `TRACE_PROJECT` env(기본값이 그것).
|
||||||
|
- **Langfuse 아님.** Langfuse 는 docker 컨테이너 6개가 전제인데 고객사는 컨테이너 안이라 docker 를 못 띄움(권한). Phoenix 는 `pip install` 하나 = 파이썬 프로세스 하나라 어디든 뜸. 코드는 둘 다 지원하게 남겨둠(`LANGFUSE_HOST` 주면 Langfuse 로도 보냄) — 나중에 docker 되는 환경이면 갈아탈 수 있음.
|
||||||
|
- **전송은 SDK 없이 OTLP/HTTP 직접.** 오프라인 반입 패키지 줄이려고. 필요한 건 `httpx`(이미 있음) + `opentelemetry-proto`(+protobuf) 둘.
|
||||||
|
- **전송 모듈은 두 레포에 같은 파일.** `CODE_ASSISTANT/5_django_backend/apps/gateway/langfuse.py` = `ABAP_OPENCODE/web/BE/code/common/langfuse.py`. 고치면 양쪽 같이.
|
||||||
|
- **관측이 죽어도 제품은 멀쩡.** 전송은 fire-and-forget, 실패는 warning 로그만. `PHOENIX_HOST` 비우면 아예 안 보냄.
|
||||||
|
|
||||||
|
## 트레이스 모양 (둘 다 동일)
|
||||||
|
|
||||||
|
- 목록 한 줄 = 질문 하나. 루트 span 이 곧 답변(type LLM): 질문·답·모델·토큰(입력/출력/합)·걸린 시간·사용자(email)·세션 id.
|
||||||
|
- 루트 밑 트리 = 답 만들며 부른 도구들 순서대로(type TOOL): 도구 이름·입력·출력(앞 4KB)·시작/끝 시각·실패 여부. OpenCode 가 기록한 tool 파트를 그대로 옮김.
|
||||||
|
- 게이트웨이(FabriX 호출 원문) 트레이스는 기본 꺼짐(`LANGFUSE_TRACE_GATEWAY=1` 로 켬) — 켜면 질문 하나에 행이 둘 생김.
|
||||||
|
- 어느 시점에 보내나: Code Assistant 는 백엔드가 턴을 마무리(`_finalize`)할 때, ABAP OpenCode 는 워처 프로세스가 OpenCode `session.idle` 을 받을 때. 둘 다 그 턴의 메시지를 OpenCode 에서 다시 읽어 만듦.
|
||||||
|
|
||||||
|
## Phoenix 실측 (문서에 없거나 헷갈리는 것)
|
||||||
|
|
||||||
|
- 수신 엔드포인트 `POST /v1/traces`, **protobuf 만 받음**(JSON 은 415). 인증 기본 없음.
|
||||||
|
- `PHOENIX_HOST_ROOT_PATH=/phoenix` 는 **HTML 링크에만 접두어를 붙임.** 실제 경로는 접두어 없이 받음(`/phoenix/assets/…` 는 SPA 폴백 HTML). 즉 리버스 프록시가 접두어를 **떼고** 넘겨야 함. `/v1/traces` 도 접두어 없음.
|
||||||
|
- 속성 이름은 OpenInference: `openinference.span.kind`, `input.value`/`output.value`, `llm.model_name`, `llm.token_count.*`, `session.id`, `user.id`, `tool.name`. 프로젝트는 리소스 속성 `openinference.project.name`. 우리 모듈은 Langfuse 용 `langfuse.*` 속성도 같은 span 에 같이 실음(어느 쪽으로 보내도 읽히게).
|
||||||
|
- 저장은 기본 SQLite(`PHOENIX_WORKING_DIR`). PostgreSQL 은 `PHOENIX_SQL_DATABASE_URL` + `PHOENIX_SQL_DATABASE_SCHEMA` 로 스키마 분리 가능.
|
||||||
|
- 폐쇄망: `PHOENIX_ALLOW_EXTERNAL_RESOURCES=false`(구글 폰트 안 부름), `PHOENIX_TELEMETRY_ENABLED=false`.
|
||||||
|
- 프로세스 이름은 `python -m phoenix.server.main serve` — `pkill -f phoenix.server.main`.
|
||||||
|
|
||||||
|
## 접근·보안
|
||||||
|
|
||||||
|
- Phoenix 화면은 관리자용(트레이스·토큰). 일반 사용자는 볼 일 없음.
|
||||||
|
- 바깥 포트를 새로 못 받으면 백엔드 뒤 `/phoenix/` 로 중계(`apps/gateway/phoenix_proxy.py`): HTTP Basic 잠금(`PHOENIX_UI_PASSWORD`), 접두어 떼서 전달, `POST /phoenix/v1/traces` 만 무인증(다른 서버가 trace 보내는 길). 웹소켓은 안 넘김 — 트레이스 화면은 HTTP 로 충분.
|
||||||
|
- 바깥 포트를 바로 받을 수 있는 환경이면 중계 없이 Phoenix 포트를 열고 `PHOENIX_ENABLE_AUTH=true` 로 자체 로그인 쓰면 됨(그땐 수신에 API 키 필요 — 모듈에 헤더 한 줄 추가).
|
||||||
|
|
||||||
|
## 오프라인 설치 요령
|
||||||
|
|
||||||
|
- wheel 은 **서버와 같은 파이썬 버전·리눅스 glibc** 로 받아야 함. `docker run python:<버전>-slim … pip download 'arize-phoenix[pg]'` 로 155개(220MB). 서버 glibc 가 낮으면(데비안 11 = 2.31) `manylinux_2_34` wheel 이 안 깔림 → 그 패키지만 `--platform manylinux_2_28_x86_64` 로 다시(실제로 cryptography·caio 둘).
|
||||||
|
- 백엔드 venv 엔 `opentelemetry-proto` 만 추가 설치(같은 wheel 묶음에서). trace 보내기만 하는 서버(-13 같은)는 wheel 2개(`opentelemetry_proto`, `protobuf`)만 있으면 됨.
|
||||||
|
- 서버에 systemd 없으면 `nohup … &` 로 띄우고 재부팅 후 다시 — `deploy/phoenix/run.sh`.
|
||||||
|
|
||||||
|
## 안 한 것 / 다음
|
||||||
|
|
||||||
|
- 사용자별 비용 계산 없음(FabriX 단가 없음). 토큰까지만.
|
||||||
|
- Phoenix 자체 로그인 안 켬(중계 Basic 으로 대신). 사용자 늘면 켜기.
|
||||||
|
- OpenCode 의 reasoning(생각) 텍스트는 트리에 안 넣음 — 필요하면 tool 처럼 자식 span 하나 더.
|
||||||
|
- 데이터 보존 기간 무제한(`PHOENIX_DEFAULT_RETENTION_POLICY_DAYS=0`). 쌓이면 정함.
|
||||||
Reference in New Issue
Block a user