Repository navigation
feat(observability): LLM 호출·도구 실행 OpenTelemetry span - #162
Merged
Merged
Conversation
## 배경
dev Tempo 의 Geny 턴 trace 에 `chat` span 이 하나도 없었다. LLM 의
`POST /v1/chat/completions` 5건과 도구가 부른 HTTP·MCP 호출이 에이전트 턴이 아니라
워크플로우 span 바로 아래에 흩어져, 어느 호출이 병목인지 trace 에서 읽을 수 없었다.
호스트는 턴 끝의 사용량 합계와 도구 사건만 받으므로 호출 구간도 부모 관계도 되살릴 수 없다.
그래서 호출과 실행이 실제로 일어나는 런타임이 span 을 연다.
## 변경
- `observability/` 패키지 추가 (`spans` 수명 / `attributes` 키 / `genai` 계측)
- `APIStage._traced_call`: Stage 6 호출 1건(재시도 포함) → `chat {model}`
- `RegistryRouter.route`: 도구 실행 1건 → `execute_tool {tool}` (모든 실행기와 CLI 도구 표면이 지나는 한 곳)
- 4.91.0, CHANGELOG
## 동작 규칙
- span 은 호출 시점의 현재 context 의 자식이다. 호스트가 턴 span 을 현재 context 로
두고 `run_turn`/`stream_turn` 을 부르면 그 아래에 달린다.
- 토큰(`gen_ai.usage.*`)은 호출 span 에만 둔다. 호스트는 `spans_enabled()` 가 참이면 자기 쪽
도구 span 과 턴 span 의 토큰을 끈다 (중복 span · 이중 합산 방지).
- 프롬프트·응답·도구 입출력 본문은 남기지 않는다. 취소는 오류로 표시하지 않는다.
- `opentelemetry-api` 는 선택 의존성이다. 없거나 `XGEN_AGENT_RUNTIME_OTEL=0` 이면 no-op,
span 조작 실패는 실행을 깨지 않는다.
## 테스트
- `tests/test_otel_spans.py` 6건: chat/execute_tool 이 호스트 턴 아래에 달림, HTTP span 이
각 호출 아래에 중첩, 실패 도구의 오류 표시와 본문 미기록, 스위치 off, tracer 고장 시 턴 유지
- 단위 테스트 전체: 변경 전후 실패 목록 동일 (Windows 에서 셸·서브프로세스를 실행하는 기존 실패)
- 실제 Tempo trace 는 워크플로우 쪽 변경과 함께 배포한 뒤 확인 필요
## 함께 필요한 변경
xgen-workflow `fix/geny-otel-trace`: 턴을 노드 실행 시작에서 열고 턴 context 를 전파한다.
이 PR 의 릴리스 후 워크플로우의 런타임 핀(`pyproject.toml`, `requirements.lock`)을 v4.91.0 으로 올린다.
워크플로우 변경은 옛 런타임에서도 기존 방식으로 동작한다.
CI 의 `ruff format --check` 를 통과하도록 observability/genai.py 의 긴 `with` 줄과 dict 인자 줄바꿈을 맞춘다. 동작 변경 없음.
test(observability): dev extra 에 opentelemetry-sdk 추가 CI 는 `pip install -e ".[dev]"` 만 하므로 SDK 가 없어 tests/test_otel_spans.py 가 importorskip 으로 통째로 skip 되었다. 테스트 전용으로 dev extra 에만 넣는다. 런타임 의존성은 그대로다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
변경
observability/패키지 추가 —spans(span 수명) /attributes(속성 키) /genai(계측)APIStage._traced_call: Stage 6 호출 1건(재시도 포함) →chat {model}RegistryRouter.route: 도구 실행 1건 →execute_tool {tool}(모든 실행기와 CLI 도구 표면이 지나는 한 곳. 실제 라우팅은
_route로 옮겼고 시그니처·반환값은 그대로)공개 API 변경은 없다(새 모듈과 내부 헬퍼만 추가).
동작 규칙
run_turn/stream_turn을 부르면 그 아래에 달리고, 안의 HTTP·MCP 호출은 각 span 아래에 중첩된다.gen_ai.usage.*)은 호출 span 에만 둔다. 호스트는spans_enabled()가 참이면 자기 쪽도구 span 과 턴 span 의 토큰을 끈다 (중복 span · 이중 합산 방지).
opentelemetry-api는 의존성으로 선언하지 않는다. 설치된 환경(xgen-workflow)에서만 켜지고,없거나
XGEN_AGENT_RUNTIME_OTEL=0이면 no-op 이다. span 조작 실패는 실행을 깨지 않는다.도구 1건당 1개 늘어난다.
테스트
tests/test_otel_spans.py6건 — chat/execute_tool 이 호스트 턴 아래에 달림, HTTP span 이 각 호출아래에 중첩, 실패 도구의 오류 표시와 본문 미기록, 스위치 off, tracer 고장 시에도 턴 유지
opentelemetry-sdk가 없는 환경에서는 이 테스트가 skip 된다[dev extra 에 sdk 를 추가했다면 삭제]tests/unit실패 목록 비교: 동일 (셸·서브프로세스를 실행하는 기존 실패)ruff check,ruff format --check,gen_event_docs --check통과. CI 결과는 이 PR 에서 확인함께 필요한 변경
xgen-workflow
fix/geny-otel-trace[MR 링크로 교체]: 턴을 노드 실행 시작에서 열고 턴 context 를전파한다. 이 PR 이 v4.91.0 으로 릴리스되면 워크플로우의 런타임 핀(
pyproject.toml,requirements.lock)을 올린다. 워크플로우 변경은 옛 런타임에서도 기존 방식으로 동작한다.