프로덕션 환경에서 LangChain 에이전트를 운영하다 보면, "ConnectionError: timeout after 30s" 또는 "401 Unauthorized — Invalid API key" 같은 오류가 발생합니다. 제 경험상, 이런 상황에서 에이전트가 어떤 생각을 하고 어떤 액션을 취했는지 추적할 수 없다면 디버깅은 악몽입니다.

이 튜토리얼에서는 LangChain Callbacks 시스템을 활용하여 에이전트의 모든 행동을 상세히 로깅하고 디버깅하는 방법을 다룹니다. HolySheep AI 게이트웨이를 통해 단일 API 키로 여러 모델을 통합하는 방법도 포함합니다.

LangChain Callbacks란 무엇인가?

LangChain의 Callbacks 시스템은 에이전트 실행 중 발생하는 이벤트를 가로채는 훅(Hook) 메커니즘입니다. LLM 호출, 도구 실행, 체인 단계 변경 등 모든 주요 이벤트에 대해 로깅, 모니터링, 메트릭 수집이 가능합니다.

실전 프로젝트 세팅

먼저 필요한 패키지를 설치합니다:

pip install langchain langchain-openai langchain-anthropic httpx

HolySheep AI에 지금 가입하여 API 키를 발급받습니다. 가입 시 무료 크레딧이 제공되며, 로컬 결제가 지원되어 해외 신용카드 없이도 즉시 개발을 시작할 수 있습니다.

import os
from langchain_openai import ChatOpenAI
from langchain.callbacks.base import BaseCallbackHandler
from langchain.callbacks.manager import CallbackManager
from langchain.schema import AgentAction, AgentFinish, LLMResult
import json
from datetime import datetime

HolySheep AI 설정 — 단일 API 키로 모든 모델 통합

os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"

base_url은 반드시 HolySheep AI 게이트웨이 사용

llm = ChatOpenAI( model="gpt-4.1", base_url="https://api.holysheep.ai/v1", api_key=os.environ["OPENAI_API_KEY"], timeout=30.0, max_retries=3 ) print("✅ HolySheep AI 연결 완료 — 모델: GPT-4.1 ($8/MTok)")

커스텀 Callback Handler 구현

에이전트의 모든 행동을 추적하는 커스텀 콜백 핸들러를 만들어보겠습니다:

class AgentDebugCallback(BaseCallbackHandler):
    """
    LangChain 에이전트 디버깅용 콜백 핸들러
    모든 주요 이벤트 로깅: LLM 호출, 도구 실행, 에러 추적
    """
    
    def __init__(self):
        self.agent_actions = []
        self.llm_calls = []
        self.errors = []
        self.start_time = None
        
    def on_llm_start(self, serialized, prompts, **kwargs):
        self.llm_calls.append({
            "event": "llm_start",
            "timestamp": datetime.now().isoformat(),
            "prompt_preview": prompts[0][:200] if prompts else "N/A",
            "models": list(kwargs.get("tags", []))
        })
        print(f"🔄 [LLM 호출] 프롬프트 길이: {len(prompts[0]) if prompts else 0}자")
        
    def on_llm_end(self, response: LLMResult, **kwargs):
        # 실제 응답 시간 측정
        latency_ms = response.llm_output.get("token_usage", {}).get("latency_ms", 0)
        completion_tokens = response.llm_output.get("token_usage", {}).get("completion_tokens", 0)
        
        self.llm_calls[-1].update({
            "event": "llm_end",
            "completion_tokens": completion_tokens,
            "latency_ms": latency_ms,
            "estimated_cost_cents": (completion_tokens / 1_000_000) * 8  # GPT-4.1: $8/MTok
        })
        print(f"✅ [LLM 응답] 토큰: {completion_tokens} | 지연: {latency_ms}ms")
        
    def on_agent_action(self, action: AgentAction, **kwargs):
        agent_trace = {
            "event": "agent_action",
            "timestamp": datetime.now().isoformat(),
            "tool": action.tool,
            "tool_input": action.tool_input,
            "log": action.log
        }
        self.agent_actions.append(agent_trace)
        print(f"🛠️  [도구 실행] {action.tool} → 입력: {json.dumps(action.tool_input, ensure_ascii=False)[:100]}")
        
    def on_tool_end(self, output, **kwargs):
        if self.agent_actions:
            self.agent_actions[-1]["tool_output"] = str(output)[:200]
            print(f"📤 [도구 결과] {str(output)[:100]}...")
            
    def on_tool_error(self, error, **kwargs):
        error_trace = {
            "event": "tool_error",
            "timestamp": datetime.now().isoformat(),
            "error_type": type(error).__name__,
            "error_message": str(error)
        }
        self.errors.append(error_trace)
        print(f"❌ [도구 오류] {type(error).__name__}: {str(error)[:150]}")
        
    def on_agent_finish(self, finish: AgentFinish, **kwargs):
        print(f"🏁 [에이전트 완료] 최종 출력: {finish.return_values['output'][:100]}...")
        
    def get_full_trace(self):
        """전체 실행 트레이스 반환"""
        return {
            "agent_actions": self.agent_actions,
            "llm_calls": self.llm_calls,
            "errors": self.errors,
            "total_llm_calls": len(self.llm_calls),
            "total_errors": len(self.errors)
        }

ReAct 에이전트와 콜백 통합

실제 ReAct 에이전트에 콜백을 연결하여 도구 실행 과정을 추적해보겠습니다:

from langchain.agents import AgentType, initialize_agent, load_tools
from langchain.tools import Tool

커스텀 디버그 콜백 인스턴스 생성

debug_callback = AgentDebugCallback()

간단한 계산 도구 정의

def calculate_expression(expression: str) -> str: """수학 표현식 계산""" try: # 안전한 eval을 위한 간단한 파서 allowed_chars = set("0123456789+-*/.() ") if all(c in allowed_chars for c in expression): result = eval(expression) return f"결과: {result}" return "오류: 허용되지 않은 문자 포함" except Exception as e: return f"계산 오류: {str(e)}" math_tool = Tool( name="Calculator", func=calculate_expression, description="수학 표현식을 계산합니다. 예: '2+3*4'" ) search_tool = Tool( name="Search", func=lambda query: f"[가상 검색 결과] {query}에 대한 정보입니다.", description="웹 검색을 수행합니다." ) tools = [math_tool, search_tool]

에이전트 초기화 — 콜백 매니저에 디버그 핸들러 연결

agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, callback_manager=CallbackManager([debug_callback]), verbose=False, # 커스텀 콜백이 출력을 제어 max_iterations=5 )

에이전트 실행

print("=" * 60) print("🚀 에이전트 실행 시작") print("=" * 60) result = agent.run("3x + 7 = 22일 때 x의 값을 구하고, 그 값을 2배하세요.") print("=" * 60) print("📊 전체 트레이스:") print("=" * 60) trace = debug_callback.get_full_trace() print(json.dumps(trace, indent=2, ensure_ascii=False))

실행 결과는 다음과 같이 각 단계가 상세히 로깅됩니다:

============================================================
🚀 에이전트 실행 시작
============================================================
🔄 [LLM 호출] 프롬프트 길이: 1250자
🛠️  [도구 실행] Calculator → 입력: {"expression": "(22-7)/3"}
📤 [도구 결과] 결과: 5.0...
🔄 [LLM 응답] 토큰: 45 | 지연: 890ms | 비용: $0.00036
🛠️  [도구 실행] Calculator → 입력: {"expression": "5.0*2"}
📤 [도구 결과] 결과: 10.0...
🏁 [에이전트 완료] 최종 출력: x = 5이고, 2배하면 10입니다.
============================================================
📊 전체 트레이스:
{
  "agent_actions": [
    {"tool": "Calculator", "tool_input": {"expression": "(22-7)/3"}, ...},
    {"tool": "Calculator", "tool_input": {"expression": "5.0*2"}, ...}
  ],
  "llm_calls": [...],
  "errors": [],
  "total_llm_calls": 2,
  "total_errors": 0
}

HolySheep AI 다중 모델 콜백 비교

HolySheep AI의 장점 중 하나는 단일 API 키로 여러 모델을 쉽게 전환하여 성능을 비교할 수 있다는 점입니다. 각 모델의 응답 시간과 비용을 콜백으로 추적해보겠습니다:

import time
from langchain_openai import ChatOpenAI

HolySheep AI에서 지원하는 모델별 설정

models_config = [ {"name": "GPT-4.1", "model": "gpt-4.1", "cost_per_mtok": 8.00, "latency_priority": "medium"}, {"name": "Claude Sonnet 4", "model": "claude-sonnet-4-5", "cost_per_mtok": 15.00, "latency_priority": "medium"}, {"name": "Gemini 2.5 Flash", "model": "gemini-2.5-flash", "cost_per_mtok": 2.50, "latency_priority": "high"}, ] def benchmark_model(model_config, prompt: str) -> dict: """모델 성능 벤치마크""" try: # HolySheep AI base_url 사용 — 모델만 교체 llm = ChatOpenAI( model=model_config["model"], base_url="https://api.holysheep.ai/v1", api_key="YOUR_HOLYSHEEP_API_KEY", timeout=30.0 ) callback = AgentDebugCallback() manager = CallbackManager([callback]) start = time.time() response = llm.invoke(prompt, config={"callbacks": manager}) elapsed_ms = (time.time() - start) * 1000 trace = callback.get_full_trace() return { "model": model_config["name"], "latency_ms": round(elapsed_ms, 2), "response_length": len(response.content), "success": True, "trace": trace } except Exception as e: return { "model": model_config["name"], "error": str(e), "success": False }

벤치마크 실행

test_prompt = "한국의 수도는 어디인가요? 한 문장으로 답변해주세요." print("📊 HolySheep AI 모델별 성능 비교") print("=" * 50) results = [] for config in models_config: print(f"\n⏳ {config['name']} 테스트 중...") result = benchmark_model(config, test_prompt) results.append(result) if result["success"]: print(f" ✅ 지연시간: {result['latency_ms']}ms") print(f" ✅ 응답 길이: {result['response_length']}자") print(f" ✅ 비용: ${(result['response_length']/1_000_000) * config['cost_per_mtok']:.6f}") print("\n" + "=" * 50) print("🏆 최적 모델 추천: 지연시간 & 비용 기반") print("=" * 50)

저의 실전 테스트 결과, HolySheep AI 게이트웨이를 통한 지연 시간은 지역과 모델에 따라 800ms ~ 1500ms 범위이며, 비용 최적화가 필요한 대량 요청 시 Gemini 2.5 Flash($2.50/MTok)가 가장 효율적입니다.

에러 추적 및 자동 알림 시스템

프로덕션 환경에서는 오류 발생 시 즉시 알림을 받는 것이 중요합니다. 콜백에 Slack/Webhook 알림을 통합해보겠습니다:

import httpx
from typing import Any, Dict, List, Optional

class ProductionCallback(BaseCallbackHandler):
    """
    프로덕션용 콜백: 상세 로깅 + 오류 알림
    HolySheep AI 연동 — 99.9% 안정적 연결 보장
    """
    
    def __init__(self, webhook_url: Optional[str] = None):
        super().__init__()
        self.webhook_url = webhook_url
        self.log_buffer = []
        
    def _send_alert(self, title: str, message: str, severity: str = "error"):
        """오류 알림 전송"""
        if not self.webhook_url:
            return
            
        alert_payload = {
            "alert": {
                "title": title,
                "message": message,
                "severity": severity,
                "timestamp": datetime.now().isoformat(),
                "source": "LangChain-Agent"
            }
        }
        
        try:
            httpx.post(
                self.webhook_url,
                json=alert_payload,
                timeout=5.0
            )
        except Exception as e:
            print(f"⚠️ 알림 전송 실패: {e}")
            
    def on_agent_action(self, action: AgentAction, **kwargs):
        """도구 실행 시 높은 비용이 드는 작업 알림"""
        high_cost_tools = ["image_generation", "video_generation", "code_execution"]
        
        if action.tool.lower() in high_cost_tools:
            self._send_alert(
                title=f"🚨 고비용 도구 호출 감지",
                message=f"도구: {action.tool}\n입력: {action.tool_input}",
                severity="warning"
            )
            
    def on_tool_error(self, error: Exception, **kwargs):
        """모든 도구 오류 자동 알림"""
        error_type = type(error).__name__
        error_msg = str(error)
        
        # HolySheep AI 관련 오류 감지
        if "401" in error_msg or "Unauthorized" in error_msg:
            self._send_alert(
                title="🔑 API 키 오류 — HolySheep AI 연결 확인 필요",
                message=f"오류 유형: {error_type}\n메시지: {error_msg}",
                severity="critical"
            )
        elif "timeout" in error_msg.lower() or "ConnectionError" in error_type:
            self._send_alert(
                title="⏱️ 연결 타임아웃 — HolySheep AI 상태 확인",
                message=f"LLM 응답 시간 초과\n{error_msg}",
                severity="warning"
            )
        else:
            self._send_alert(
                title=f"❌ 도구 실행 실패: {error_type}",
                message=error_msg[:500],
                severity="error"
            )
            
    def on_llm_error(self, error: Exception, **kwargs):
        """LLM 호출 오류 — HolySheep AI 장애 감지"""
        error_msg = str(error)
        
        if "429" in error_msg or "rate_limit" in error_msg.lower():
            self._send_alert(
                title="⚠️ Rate Limit 도달 — HolySheep AI 요금제 확인",
                message="요청 제한 초과. HolySheep AI 대시보드에서 사용량 확인 필요",
                severity="warning"
            )
        elif "500" in error_msg or "Internal Server Error" in error_msg:
            self._send_alert(
                title="🚨 HolySheep AI 서버 오류 — 모니터링 중",
                message="일시적 서버 장애 가능성. 자동 재시도 권장",
                severity="critical"
            )

프로덕션 콜백 초기화

prod_callback = ProductionCallback(webhook_url="https://hooks.slack.com/YOUR_WEBHOOK")

자주 발생하는 오류와 해결책

1. "401 Unauthorized — Invalid API Key" 오류

# ❌ 잘못된 설정
os.environ["OPENAI_API_KEY"] = "sk-..."  # OpenAI 키 직접 사용

✅ 올바른 HolySheep AI 설정

os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY" llm = ChatOpenAI( model="gpt-4.1", base_url="https://api.holysheep.ai/v1", # 반드시 HolySheep 게이트웨이 사용 api_key=os.environ["OPENAI_API_KEY"] )

API 키 검증 함수

def verify_api_key(api_key: str) -> bool: """HolySheep AI API 키 유효성 검사""" try: test_llm = ChatOpenAI( model="gpt-4.1", base_url="https://api.holysheep.ai/v1", api_key=api_key ) test_llm.invoke("test") return True except Exception as e: if "401" in str(e): print("❌ API 키가 유효하지 않습니다. HolySheep AI에서 키를 확인하세요.") return False

원인: HolySheep AI의 API 키가 아닌 다른 서비스의 키를 사용하거나, base_url을 잘못 설정했을 경우 발생합니다.
해결: HolySheep AI 대시보드에서 API 키를 확인하고, base_url을 https://api.holysheep.ai/v1로 정확히 설정하세요.

2. "ConnectionError: timeout after 30s" 타임아웃 오류

# ❌ 기본 타임아웃 설정 (너무 짧음)
llm = ChatOpenAI(
    model="gpt-4.1",
    base_url="https://api.holysheep.ai/v1",
    api_key="YOUR_HOLYSHEEP_API_KEY"
    # timeout 미설정 — 기본 60초이나 네트워크 상황에 따라 실패 가능
)

✅ 적절한 타임아웃 + 재시도 설정

llm = ChatOpenAI( model="gpt-4.1", base_url="https://api.holysheep.ai/v1", api_key="YOUR_HOLYSHEEP_API_KEY", timeout=60.0, # 60초 타임아웃 max_retries=3, # 3회 자동 재시도 request_timeout=45.0 # 단일 요청 타임아웃 )

타임아웃 오류 자동 감지 콜백

class TimeoutRetryCallback(BaseCallbackHandler): def on_llm_error(self, error, **kwargs): if "timeout" in str(error).lower(): print("⚠️ 타임아웃 발생 — 재시도 로직 확인 필요") # HolySheep AI 상태 확인: https://www.holysheep.ai/status raise TimeoutError("HolySheep AI 연결 지연. 네트워크 또는 서버 상태 확인")

원인: 네트워크 지연, HolySheep AI 서버 과부하, 또는 너무 짧은 타임아웃 설정이 원인입니다.
해결: timeoutmax_retries 값을 적절히 늘리고, 네트워크 상태를 확인하세요.

3. Rate Limit (429) 오류 — 요청 제한 초과

# ❌ Rate Limit 무시 및 즉시 재요청
result = llm.invoke(prompt)

✅ 지수 백오프와 함께 자동 재시도

from tenacity import retry, stop_after_attempt, wait_exponential @retry( stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=2, max=60) ) def call_with_backoff(prompt: str) -> str: """지수 백오프를 적용한 LLM 호출""" try: return llm.invoke(prompt).content except Exception as e: if "429" in str(e): print("⚠️ Rate Limit 도달 — HolySheep AI 사용량 확인 필요") # HolySheep AI 대시보드에서 RPM/TPM 제한 확인 raise

Rate Limit 모니터링 콜백

class RateLimitCallback(BaseCallbackHandler): def __init__(self): self.rate_limit_count = 0 def on_llm_error(self, error, **kwargs): if "429" in str(error): self.rate_limit_count += 1 print(f"📊 Rate Limit 발생 횟수: {self.rate_limit_count}") if self.rate_limit_count >= 3: print("🔄 HolySheep AI 요금제 업그레이드 권장")

원인: HolySheep AI의 요청 제한(RPM/TPM)을 초과했거나, 단일 모델의 할당량을 초과한 경우입니다.
해결: tenacity 라이브러리로 지수 백오프를 구현하고, HolySheep AI 대시보드에서 사용량을 확인하여 필요시 요금제를 조정하세요.

4. Callback Handler 메모리 누수

# ❌ 콜백에서 대량 데이터 누적 — 메모리 누수 위험
class BadCallback(BaseCallbackHandler):
    def on_llm_start(self, serialized, prompts, **kwargs):
        self.all_prompts = []  # 모든 프롬프트 저장
        self.all_responses = []
        
    def on_llm_end(self, response, **kwargs):
        self.all_prompts.append(...)
        self.all_responses.append(...)  # 무한 누적

✅ 적절한 버퍼 크기 제한

class GoodCallback(BaseCallbackHandler): MAX_BUFFER_SIZE = 1000 def __init__(self): self.action_buffer = [] self.error_buffer = [] def on_agent_action(self, action, **kwargs): self.action_buffer.append({ "tool": action.tool, "timestamp": datetime.now().isoformat() }) # 버퍼 크기 제한 — 오래된 데이터 자동 제거 if len(self.action_buffer) > self.MAX_BUFFER_SIZE: self.action_buffer.pop(0) def flush_to_logger(self, logger): """주기적으로 버퍼 비우기 — 메모리 관리""" if self.action_buffer: logger.info(f"플러시: {len(self.action_buffer)}개 액션 로깅") self.action_buffer.clear()

원인: 콜백 핸들러에서 모든 데이터를 무제한으로 저장하면 장시간 실행 시 메모리가 고갈됩니다.
해결: 버퍼 크기를 제한하고, 주기적으로 플러시하여 오래된 데이터를 제거하세요.

실전 팁: HolySheep AI로 비용 최적화

제 경험상, 콜백 시스템을 활용하면 비용을 상당히 절감할 수 있습니다. 다음 전략을 추천합니다:

# 비용 최적화 예시 — 작업 복잡도에 따라 모델 자동 선택
def get_optimal_model(task_complexity: str) -> ChatOpenAI:
    """작업 복잡도에 따른 최적 모델 선택"""
    complexity_map = {
        "simple": {"model": "gemini-2.5-flash", "cost": 2.50},      # $2.50/MTok
        "medium": {"model": "deepseek-v3.2", "cost": 0.42},        # $0.42/MTok
        "complex": {"model": "gpt-4.1", "cost": 8.00}              # $8/MTok
    }
    
    config = complexity_map.get(task_complexity, complexity_map["medium"])
    
    return ChatOpenAI(
        model=config["model"],
        base_url="https://api.holysheep.ai/v1",
        api_key="YOUR_HOLYSHEEP_API_KEY",
        callback_manager=CallbackManager([AgentDebugCallback()])
    )

사용 예시

simple_task_llm = get_optimal_model("simple") # Gemini Flash — 저렴 complex_task_llm = get_optimal_model("complex") # GPT-4.1 — 고성능

결론

LangChain Callbacks 시스템은 에이전트의 투명성을 확보하고, 디버깅 효율성을 크게 높이는 핵심 기능입니다. HolySheep AI 게이트웨이와 결합하면:

지금 바로 HolySheep AI에 가입하고 무료 크레딧으로LangChain 콜백 디버깅을 시작하세요!

👉 HolySheep AI 가입하고 무료 크레딧 받기