
Strands Agents + Langfuse 평가 (Evaluations)
요약
Strands Agents와 Langfuse를 활용하여 Python 기반 뱅킹 어시스턴트 에이전트를 구축하고 평가하는 PoC 과정을 소개합니다. AI 애플리케이션의 비결정론적 특성을 관리하기 위해 트레이싱과 지속적인 평가의 중요성을 강조합니다.
핵심 포인트
- Strands Agents를 이용한 경량 Python 기반 에이전트 구축 방법
- Langfuse를 활용한 에이전트의 관찰 가능성(Observability) 확보
- 전통적 테스트 방식의 한계를 극복하기 위한 AI 평가(Evaluations)의 필요성
- 트레이싱과 평가를 통한 AI 애플리케이션 품질 관리 전략
시작하기 전에, 이 PoC (Proof of Concept) 과정을 설명하는 발표 자료인 **AI was supposed to take my job — instead it gave me a new one: Evaluations**를 확인해 보세요.
이 프로젝트에서는 Strands Agents를 사용하여 Python 기반의 뱅킹 어시스턴트 에이전트 (agent)를 구축하고, Langfuse를 사용하여 이를 단계별로 관찰 가능하게(observable) 만들고 지속적으로 평가(evaluated)하는 과정을 진행합니다.
Strands Agents는 도구 사용 (tool use) 및 세션 메모리 (session memory) 기능을 갖춘 LLM 기반 에이전트를 구축하기 위한 경량 Python SDK로, 2025년 5월 AWS에 의해 오픈 소스로 공개되었습니다. 이는 Python 네이티브 (Python-native) 방식이라 Langfuse Python SDK와 잘 어우러지며, 탐구해 볼 가치가 있을 만큼 최신 기술입니다. 이 PoC를 위해서는 다른 어떤 Python 에이전트 프레임워크를 사용해도 무방합니다.
**전통적인 애플리케이션 (classic applications)**의 경우, 품질은 유닛 테스트 (unit tests), 통합 테스트 (integration tests), 그리고 정적 분석 (static analysis)을 통해 강제됩니다. 모든 함수는 정의된 계약 (contract)을 가지며, 단언 (assert)할 수 있는 결정론적 (deterministic) 출력을 가집니다. 운영 환경에서는 지표 (metrics) (오류율, 지연 시간, 메모리)를 통해 실패를 안정적으로 파악할 수 있습니다.
AI 애플리케이션은 이 두 가지 모두를 깨뜨립니다. 동일한 입력이라도 실행할 때마다 다른 출력을 생성할 수 있습니다. 문구가 바뀌거나, 도구가 호출되는 순서가 달라지거나, 예외 상황 (edge cases)이 예측 불가능하게 나타날 수 있습니다. 또한 운영 환경에서 요청이 300ms 만에 200 OK를 반환하더라도, 매우 확신에 찬 태도로 완전히 틀린 답변을 내놓을 수 있습니다. 전통적인 지표로는 이를 잡아낼 수 없습니다. 더 많은 것이 필요합니다!
바로 여기서 **트레이스(traces)**와 **평가(evaluations)**가 등장합니다. 이 기능은 점점 더 많은 플랫폼에서 지원하고 있습니다: Langfuse, Arize Phoenix, MLflow, LangSmith, W&B Weave, Datadog, AWS AgentCore, Azure AI Foundry, Google Vertex AI
이 PoC는 Langfuse를 사용하는데, 이는 오픈 소스이며 단일 docker compose up 명령으로 자체 호스팅(self-hostable)할 수 있기 때문입니다. Langfuse가 제공하는 기능은 다음과 같습니다:
- 트레이싱 (Tracing) — 모든 LLM 호출, 도구 호출, 서브 에이전트 단계의 구조화된 트리(structured tree)를 기록합니다: 입력값(inputs), 출력값(outputs), 지연 시간(latency), 비용(cost).
- 평가 (Evaluations) — 에이전트 출력에 대한 점수화된 평가를 실행합니다:
- 오프라인 (Offline) — 변경 사항 발생 전이나 후에 고정되고 선별된 데이터셋을 대상으로 실행하며, CI 품질 게이트(CI quality gate)로 재실행 가능합니다.
- 온라인 (Online) — 실시간 트레이스에 의해 비동기적으로 트리거되며, 고정된 데이터셋에서는 나타나지 않았던 문제를 포착합니다.
- 외부 평가 (External Evaluations) — 자체 코드에서 라이브 트레이스에 점수를 첨부합니다.
- 주석 큐 (Annotation Queues) — 명시적인 프로그래밍 호출을 통해 트레이스를 인간 검토자에게 라우팅합니다.
- 프롬프트 관리 (Prompt Management) — 프롬프트 템플릿의 버전 관리를 수행하고 SDK를 통해 런타임에 가져옵니다.
평가 대상 애플리케이션은 **뱅킹 센티넬(banking sentinel)**입니다. 이 애플리케이션은 ROGERVINAS 은행을 위한 고객 지원 에이전트로, Strands Agents를 사용하여 구축되었으며, 5개의 거래가 있는 3개의 모의 계좌와 카드를 동결/해제(freeze/unfreeze), 거래 조회(look up transactions), 분쟁 개설 또는 추적(open or track disputes)을 위한 도구들로 구성되어 있습니다.
준비되셨나요? 에이전트(agent)와 Langfuse를 실행하고 (Configuration → Run), 채팅 UI를 열어 직접 사용해 본 다음, 각 구성 요소가 어떻게 작동하는지 확인하기 위해 구현 (Implementation) 단계를 살펴보세요.
- Configuration (설정)
- Run (실행)
- Implementation (구현)
- Step 1: Banking Agent (뱅킹 에이전트)
- Step 2: Tracing (트레이싱)
- Step 3: Strands Native Evaluations (Strands 네이티브 평가)
- Step 4: Experiments (실험)
- Step 5: Online Evaluations (LLM-as-judge, 온라인 평가)
- Step 6: External Evaluations (외부 평가)
- Step 7: Annotation Queues (어노테이션 큐)
- Step 8: Prompt Management (프롬프트 관리)
- CI/CD
- Documentation (문서)
Configuration (설정)
Prerequisites (사전 요구 사항)
- Python (
.python-version에 고정된 버전)) - uv
- Docker + Docker Compose
- 모델 제공자 (Model provider, 아래 참조)
Install dependencies (의존성 설치)
uv sync --extra dev --extra evals
dev— 단위 테스트 (unit tests)를 위한 의존성evals— 평가 (evaluations)를 위한 의존성
Environment (환경 설정)
cp .env.example .env
.env 파일을 편집하여 모델 제공자 및 Langfuse 자격 증명 (credentials)을 설정하세요.
Model providers (모델 제공자)
.env 파일에서 MODEL_PROVIDER를 설정하세요:
| Provider (제공자) | MODEL_PROVIDER | Requirements (요구 사항) |
|---|---|---|
| Ollama (기본값) | ollama | ollama serve + ollama pull llama3.1:8b |
| ... |
Langfuse
저희는 Docker Compose를 이용한 셀프 호스팅 (self-hosted with Docker Compose) 배포 방식을 사용합니다. 이 PoC의 docker-compose-langfuse.yml은 공식 docker-compose를 복사한 것입니다.
시작하려면:
docker compose -f docker-compose-langfuse.yml up -d
http://localhost:3000에서 Langfuse UI를 엽니다 → 프로젝트 banking-sentinel을 선택하세요. 미리 설정된 자격 증명은 다음과 같습니다:
Email: admin@local.dev
Password: password
중지하려면:
docker compose -f docker-compose-langfuse.yml down
중지하고 모든 데이터를 삭제하려면:
docker compose -f docker-compose-langfuse.yml down -v
실행 (Run)
uv run uvicorn banking_sentinel.api:app --reload
http://localhost:8000을 열고 테스트해 보세요:
- 텍스트 박스를 사용하여 서로 다른 User ID, Account ID, Tier, Session ID를 시뮬레이션합니다.
- 에이전트와 채팅하세요. 예를 들어, _"도와주세요, 넷플릭스가 안 돼요!"_라고 말해 보세요.
- 각 답변에는 직접 클릭할 수 있는 **제안된 작업 (suggested actions)**과 👍 / 👎 피드백 버튼이 포함될 수 있습니다.
.env 파일에 LANGFUSE_* 및 OTEL_* 환경 변수 (env vars)가 설정되어 있으므로, 에이전트 트레이스 (Agent traces)는 OpenTelemetry를 통해 Langfuse로 자동 전송됩니다.
구현 (Implementation)
에이전트와 Langfuse가 실행 중인 상태에서, 각 구성 요소가 어떻게 작동하는지 단계별로 살펴보겠습니다.
1단계: 뱅킹 에이전트 (The Banking Agent)
도메인: ROGERVINAS 은행을 위한 뱅킹 고객 지원 에이전트인 Sentinel입니다. 세 개의 모의 계정 (ACC-1001, ACC-1002, ACC-1003), 각 계정당 5개의 트랜잭션 (transactions), 그리고 7개의 도구 (tools)가 있습니다:
freeze_card— 카드 정지unfreeze_card— 카드 정지 해제is_card_frozen— 정지 상태 확인get_transactions— 두 날짜 사이의 트랜잭션 목록 조회open_dispute— 트랜잭션에 대한 분쟁 (dispute) 제기get_dispute_status— 분쟁 상태 확인list_disputes— 계정의 모든 분쟁 목록 조회
구현은 의도적으로 최소화되었습니다 — 단일 프로세스, 파일 기반 세션 저장소, RAG(Retrieval-Augmented Generation) 미사용(지식 베이스가 시스템 프롬프트에 직접 포함할 수 있을 만큼 충분히 짧음), MCP(Model Context Protocol) 미사용, 외부 서비스 호출 없음. 여기서의 목표는 에이전트를 단순하게 유지하여 관찰 가능성(Observability)과 평가(Evaluations)에 집중하는 것입니다. 전체 구현은 src/banking_sentinel 아래에 있습니다. Strands가 처음이라면 먼저 Strands Agents 튜토리얼을 따라가세요.
프로덕션 환경에서 에이전트는 로드 밸런서(Load Balancer) 뒤에서 여러 개의 복제본(Replicas)으로 실행되며, MCP 또는 직접적인 API 호출을 통해 실제 외부 서비스에 연결되고, 로컬 파일 기반 세션 저장소를 영구 저장소(Persistent Store)로 교체하게 됩니다 — Strands 세션 관리 문서를 참조하세요.
2단계: 트레이싱 (Tracing)
트레이스(Traces)는 표준화되어 있지 않습니다 — 얻게 되는 결과는 전적으로 프레임워크와 계측(Instrumentation) 방식에 따라 달라집니다. GenAI OTel(OpenTelemetry) 시맨틱 컨벤션(Semantic Conventions)은 아직 실험적인 단계이므로, SDK와 플랫폼마다 이를 일관되지 않게 구현합니다. 따라서 트레이스에 의존하기 전에 항상 UI에서 트레이스를 확인하십시오.
예를 들어, 이 PoC(Proof of Concept)에서 Strands의 [otel] 확장은 스팬(Spans)을 방출하지만 루트 스팬(Root Span)에 트레이스 수준의 input/output을 설정하지 않습니다 — 그리고 우리는 이후 단계에서 이것들이 필요합니다. 따라서 api.py는 전체 /chat 요청을 langfuse.start_as_current_observation()으로 감싸서, Strands OTel 스팬을 자식(Children)으로 캡처하는 동시에 루트 스팬에 input/output을 설정합니다:
with langfuse.start_as_current_observation(name="banking-sentinel-chat", as_type="generation") as span:
# ... 에이전트 실행 ...
span.update(input=request.message, output=response.answer, prompt=prompt_obj)
이 작업은 Langfuse에서 다음과 같은 span 계층 구조를 생성합니다:
banking-sentinel-chat ← Langfuse 네이티브 루트 span (input/output/user_id)
└── Strands OTel spans ← 자동으로 캡처됨
http://localhost:3000을 열고 → Traces로 이동하여 확인하세요:
Step 3: Strands 네이티브 평가 (Native Evaluations)
Strands Evals SDK는 Case, Experiment, 그리고 OutputEvaluator를 제공합니다. Langfuse가 필요하지 않으며, 완전히 오프라인으로 실행되어 로컬 보고서를 생성합니다. 이 단계의 전체 코드는 evals/strands/run_evaluations.py에 있습니다.
각 Case는 입력(input)과 예상 출력(expected output)을 하나로 묶습니다:
CASES = [
Case(
name="unauthorized-netflix-charge",
...
두 개의 평가기(evaluator)가 각 결과를 점수화합니다 — 하나는 결정론적(deterministic) 방식이고, 다른 하나는 LLM-as-judge 방식입니다:
class CorrectnessEvaluator(OutputEvaluator):
pass
...
참고: Strands Evals는 모든 Python 호출 가능 객체(callable)를 평가할 수 있으며, Strands 에이전트 자체와 결합되어 있지 않습니다. 다른 평가 프레임워크를 선호한다면, DeepEval, Ragas (RAG 중심), Braintrust autoevals, 또는 사용자 정의 단언(custom assertions)을 포함한 일반적인 pytest 등이 대안이 될 수 있습니다.
작업을 실행하는 방법에는 두 가지가 있습니다:
- Embedded (임베디드) — 에이전트가 프로세스 내에서 인스턴스화됩니다. 외부 서비스는 모킹(mocked)되며, 내부 상태를 검사할 수 있습니다 (화이트박스 (white-box)). 빠르고 서버가 필요하지 않아 CI에 이상적입니다. 빠르고 격리된, 재현 가능한 실행을 원할 때 사용하세요.
- API — 작업이 실제 외부 서비스가 포함된 실행 중인 서버에 요청을 보냅니다 (블랙박스 (black-box)). 실제 배포 환경에 대해 검증하거나 모킹이 실용적이지 않을 때 사용하세요.
서버 없이 임베디드 실행:
uv run python -m evals.strands.run_evaluations embedded
실행 중인 서버를 대상으로 실행:
uv run python -m evals.strands.run_evaluations api --url http://localhost:8000
리포트는 다음과 같이 나타납니다:
============================================================
Evaluator: CorrectnessEvaluator
Overall score: 1.00
...
Step 4: Experiments (실험)
Langfuse **experiment (실험)**는 오프라인 평가 실행입니다. 에이전트가 큐레이션된 데이터셋을 대상으로 실행되며, 각 출력은 자동으로 점수가 매겨집니다. 결과는 저장되어 코드 버전, 프롬프트 변경, 모델 업그레이드에 따른 변화를 시간에 걸쳐 비교할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
