Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
5.0 KiB
5.0 KiB
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_PROJECTenv(기본값이 그것). - 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 는 워처 프로세스가 OpenCodesession.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_34wheel 이 안 깔림 → 그 패키지만--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). 쌓이면 정함.