PROD · OBSERVABILITY
관찰 가능성
Agent의 모든 단계를 추적하고, 지연·오류·비용을 실시간으로 모니터링합니다. LangSmith와 OpenTelemetry로 분산 추적 파이프라인을 구축합니다.
관찰 가능성 3 기둥
| 기둥 | 수집 대상 | 도구 | 용도 |
|---|---|---|---|
| 📝 Logs | 이벤트, 오류, 도구 호출 결과 | Python logging, CloudWatch | 디버깅, 감사 |
| 📊 Metrics | 지연, 토큰 수, 성공률, 비용 | Prometheus, Grafana | 성능 모니터링 |
| 🔭 Traces | 요청→Agent→도구 전체 경로 | LangSmith, OpenTelemetry | 병목 분석, 재현 |
LangSmith 트레이싱 설정
pythonlangsmith_setup.py — 자동 추적 설정
import os
# 환경변수로 자동 활성화 (코드 변경 불필요)
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGSMITH_API_KEY")
os.environ["LANGCHAIN_PROJECT"] = "production-agent"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
# 특정 실행에만 메타데이터 추가
from langsmith import traceable
@traceable(name="research-pipeline", tags=["production", "v2"])
async def run_research_agent(query: str, user_id: str):
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": query}]},
config={
"metadata": {
"user_id": user_id,
"session_id": str(uuid.uuid4()),
"env": "production"
}
}
)
return result
# 수동 트레이스 제어
from langsmith import Client
client = Client()
def log_feedback(run_id: str, score: float, comment: str = ""):
"""사용자 피드백을 LangSmith 트레이스에 기록"""
client.create_feedback(
run_id=run_id,
key="user_rating",
score=score,
comment=comment
)
OpenTelemetry 분산 추적
pythonotel_tracing.py — OTel + LangChain 통합
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
# OTel 초기화 (Jaeger/Tempo에 전송)
provider = TracerProvider()
exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317")
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("agent-service")
# FastAPI 자동 계측
FastAPIInstrumentor.instrument_app(app)
# LangGraph 노드별 수동 스팬
async def instrumented_agent_node(state) -> dict:
with tracer.start_as_current_span("agent.reasoning") as span:
query = state["messages"][-1].content
span.set_attribute("query.length", len(query))
span.set_attribute("messages.count", len(state["messages"]))
import time
start = time.time()
response = await llm.ainvoke(state["messages"])
latency = time.time() - start
span.set_attribute("llm.latency_ms", round(latency * 1000))
span.set_attribute("llm.input_tokens", response.usage_metadata.get("input_tokens", 0))
span.set_attribute("llm.output_tokens", response.usage_metadata.get("output_tokens", 0))
span.set_attribute("tool_calls.count", len(response.tool_calls))
return {"messages": [response]}
핵심 메트릭 & 알림 설정
pythonmetrics.py — Prometheus 메트릭 수집
from prometheus_client import Counter, Histogram, Gauge, start_http_server
# 메트릭 정의
agent_requests_total = Counter(
"agent_requests_total",
"Total agent requests",
["status", "agent_type"]
)
agent_latency = Histogram(
"agent_request_duration_seconds",
"Agent request latency",
["agent_type"],
buckets=[0.5, 1, 2, 5, 10, 30, 60]
)
token_usage = Counter(
"llm_tokens_total",
"Total LLM tokens used",
["model", "type"] # type: input | output
)
active_tasks = Gauge(
"agent_active_tasks",
"Currently running agent tasks"
)
# 메트릭 서버 시작 (Prometheus 스크랩용)
start_http_server(9090)
# 미들웨어로 자동 수집
from fastapi import Request
import time
@app.middleware("http")
async def metrics_middleware(request: Request, call_next):
start = time.time()
active_tasks.inc()
try:
response = await call_next(request)
status = "success" if response.status_code < 400 else "error"
agent_requests_total.labels(status=status, agent_type="default").inc()
agent_latency.labels(agent_type="default").observe(time.time() - start)
return response
finally:
active_tasks.dec()
알림 임계값 권장 설정
| 메트릭 | 경고 임계값 | 위험 임계값 | 조치 |
|---|---|---|---|
| P95 응답 지연 | > 10초 | > 30초 | 오토스케일 트리거 |
| 오류율 | > 2% | > 5% | 온콜 알림, Circuit Breaker |
| 시간당 토큰 비용 | > $50 | > $100 | Rate Limiting 강화 |
| Guardrail 차단율 | > 2% | > 10% | 규칙 재검토 또는 공격 감지 |
| LLM API 오류율 | > 1% | > 3% | Fallback 모델 전환 |