Files
CODE_ASSISTANT/docs/tech/observability-phoenix.md
T

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_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=/phoenixHTML 링크에만 접두어를 붙임. 실제 경로는 접두어 없이 받음(/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 servepkill -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). 쌓이면 정함.