LangGraph, LangChain 및 Watsonx.ai를 사용한 오류 로그 조사
Source: Dev.to
소개
프로덕션 시스템을 다룰 때 관측성(observability)은 핵심적인 역할을 합니다. 이는 사고 조사에 필수적인 요소이며, 모니터링 및 알림의 기반이 되고, 새 기능, 개선 사항 또는 버그 수정이 배포될 때 검증에 매우 유용합니다. 애플리케이션 로그는 관측성의 큰 부분을 차지합니다.1 로그를 통해 시스템이 특정 시점에 무엇을 하고 있었는지를 높은 수준의 세분성으로 이해할 수 있습니다.
하지만 애플리케이션 로그를 이해하는 것은 쉽지 않을 수 있습니다. 로그가 방대하고, 관련 로그를 찾는 것이 어려울 수 있습니다. 로그를 색인하고 검색 엔진으로 쿼리하는 것이 도움이 되지만, 조사 중인 이슈와 관련된 로그인지 여부는 알려주지 못합니다. 사고 시점과 일치하는 오류 로그를 보면 보통 다음과 같은 질문을 스스로에게 던집니다:
- 이 오류가 이슈와 관련이 있거나 근본 원인일 가능성이 있나요?
- 이 오류는 이미 알려진 문제인가요?
- 알려진 문제라면, 올바른 팀에 보고되었나요?
- 보고되었다면, 현재 작업 중이거나 수정되고 있나요?
- 이미 수정되었다면, 내가 조사 중인 환경에 적용되었나요?
- 적용되었음에도 오류가 계속 발생한다면, 회귀가 있나요?
- 회귀가 있다면, 올바른 팀에 보고되었나요?
사고 중에 이러한 질문에 답하는 데는 귀중한 시간이 소요됩니다. 그러나 답변에는 버그 티켓에 있는 워크어라운드나 즉시 적용 가능한 핫픽스 릴리스와 같은 중요한 정보가 담겨 있는 경우가 많습니다. 그래서 사고 시점에 로그를 깊이 조사하는 것이 가치가 있으며, GenAI가 이러한 질문에 빠르게 답하는 데 도움이 될 수 있다고 생각합니다.
이 글에서는 IBM Watsonx.ai, LangGraph, LangChain을 사용해 파이썬으로 오류 로그를 조사하는 방법을 살펴봅니다. 글의 구성은 다음과 같습니다:
- 기술적 기반 – LangGraph, LangChain, Watsonx.ai 소개
- 로그 조사 에이전트 설계 및 구현
- 결과 요약 및 향후 과제
LangGraph, LangChain 및 Watsonx.ai
LangGraph
LangGraph는 상태를 갖는 AI 워크플로(예: 에이전트)를 구축하기 위한 그래프 기반 오케스트레이션 프레임워크입니다. AI 애플리케이션을 다음과 같은 방향 그래프로 모델링할 수 있습니다:
- 노드는 공유 상태를 조작하는 함수(LLM 호출, 툴 호출, 사용자 정의 로직)입니다.
- 엣지는 조건 분기와 루프를 포함한 제어 흐름을 정의합니다.
- 상태는
dict혹은TypedDict와 같은 명시적인 공유 데이터 구조이며, 모든 노드가 읽고 업데이트할 수 있어 장기 실행, 상태 기반 에이전트를 쉽게 만들 수 있습니다.
LangChain
LangChain은 주로 LangGraph 노드 내부에서 빌딩 블록으로 사용됩니다. LLM을 데이터와 툴에 연결하는 유틸리티를 제공하며, LangChain과 LangGraph를 결합하면 인간이 개입하는 루프를 포함해 사이클 방식으로 사고하고 행동하는 AI 에이전트를 구축할 수 있습니다.
Watsonx.ai
Watsonx.ai는 IBM의 엔터프라이즈 AI 플랫폼으로, 관리형 LLM을 비롯한 다양한 기능을 제공합니다. 우리는 이 세 가지 도구를 결합해 로그를 조사하는 AI 에이전트를 만들 것입니다. 필요한 파이썬 패키지는 다음과 같습니다:
간단한 예시
아래 코드는 날씨 조회 툴에 접근할 수 있는 기본 에이전트를 보여줍니다. create_agent 헬퍼(create_react_agent의 후속)를 사용해 전역 상태에 메시지 히스토리를 유지하는 사전 구성된 그래프를 만들었습니다.
from ibm_watsonx_ai import ChatWatsonx
from langchain_ibm import create_agent
import os
llm = ChatWatsonx(
model_id="meta-llama/llama-3-70b-instruct",
url=os.getenv("WATSONX_URL"),
)
def get_weather(city: str) -> str:
return f"Weather in {city}: 30°C and sunny."
agent = create_agent(llm, tools=[get_weather])
response = agent.invoke({
"messages": [
{"role": "user", "content": "What is the weather in Berlin?"}
]
})
print(response)
보다 복잡한 애플리케이션을 만들고 싶다면 Graph API를 사용해 직접 그래프를 구성할 수 있습니다.
로그 조사 에이전트
범위
에이전트는 앞서 소개한 고수준 질문들을 다룹니다. 여기서는 세 가지 핵심 기능에 집중합니다:
- 관련 작업/티켓/대화 검색 – Jira와 GitHub을 동시에 검색( LangGraph의 병렬 처리 시연). 설계는 Slack, 사고 추적 도구, 사후 보고서, 혹은 Glean과 같은 전사 검색 엔진으로 확장 가능.
- 운영 컨텍스트 수집 – 파드/컨테이너 이름과 배포 버전을 가져와 검색 결과의 연관성을 평가.
- 티켓 및 대화 조사 – 워크어라운드나 수정 사항을 찾아냄.
에이전트는 가벼운 Dash UI에 래핑됩니다(아래 그림 참고).

상태 정의
LangGraph에서 상태는 모든 노드가 공유하며 엣지를 따라 전달됩니다. 여러 노드가 같은 속성을 동시에 수정할 경우 사용자 정의 병합 함수가 필요합니다. 여기서는 중간 결과를 모두 상태에 저장해 그래프가 끝난 뒤 사용자에게 보여줄 수 있도록 하여 에이전트의 추론에 대한 신뢰를 구축합니다.
from pydantic import BaseModel
from typing import Optional
class LogInvestigationState(BaseModel):
# 사용자가 제공한 원시 로그 텍스트
log_text: Optional[str] = None
# 이후에 추가될 필드(예: 검색 쿼리, 결과, 컨텍스트 등)
그래프 정의
고수준 그래프 아키텍처는 다음 단계로 구성됩니다:
- 로그 검사 및 쿼리 도출 – 첫 번째 노드가 로그를 파싱하고 각 외부 시스템(Jira, GitHub)용 검색 쿼리를 생성합니다.
- 병렬 검색 – 각각의 시스템에 API 호출을 병렬로 수행합니다; 이 단계에서는 LLM이 필요하지 않습니다.
- 티켓 노드 생성 –
Send기능을 사용해 발견된 티켓/대화마다 노드를 스폰합니다. - 티켓 연관성 평가 – LLM이 티켓의 제목, 설명, 전체 로그, 운영 컨텍스트를 바탕으로 연관성을 평가합니다.
- 결과 요약 – 최종 노드가 가장 연관성이 높은 티켓을 모아 워크어라운드를 추출하고 사용자에게 간결한 답변을 제공합니다.
아래는 그래프 정의의 골격이며, API 래퍼와 병합 로직 등 상세 구현은 생략했습니다.
from langgraph.graph import StateGraph, Send
from langchain_ibm import ChatWatsonx
from typing import List, Dict
import os
# LLM 초기화
llm = ChatWatsonx(
model_id="meta-llama/llama-3-70b-instruct",
url=os.getenv("WATSONX_URL"),
)
def inspect_log(state: LogInvestigationState) -> Dict:
"""원시 로그에서 검색 쿼리를 생성합니다."""
queries = llm.invoke(
f"Extract concise search terms from the following log:\n{state.log_text}"
)
return {"queries": queries.split(",")}
def search_jira(state: Dict) -> Dict:
"""생성된 쿼리를 사용해 Jira를 검색합니다."""
# 실제 Jira API 호출은 여기서 구현
results = [] # 티켓 딕셔너리 리스트
return {"jira_results": results}
def search_github(state: Dict) -> Dict:
"""생성된 쿼리를 사용해 GitHub 이슈/PR을 검색합니다."""
results = [] # 이슈 딕셔너리 리스트
return {"github_results": results}
def grade_ticket(state: Dict, ticket: Dict) -> Dict:
"""단일 티켓의 연관성을 LLM으로 평가합니다."""
prompt = f"""
Log: {state['log_text']}
Operational context: {state.get('context', '')}
Ticket title: {ticket['title']}
Ticket description: {ticket['description']}
Rate the relevance of this ticket to the log on a scale of 0‑10 and provide a short justification.
"""
rating = llm.invoke(prompt)
ticket["rating"] = rating
return {"graded_ticket": ticket}
def summarize(state: Dict) -> Dict:
"""사용자를 위한 최종 요약을 생성합니다."""
relevant = [
t for t in state.get("graded_tickets", [])
if int(t["rating"].split()[0]) >= 7
]
summary = llm.invoke(
f"Summarize the following relevant tickets and any suggested workarounds:\n{relevant}"
)
return {"summary": summary}
# 그래프 구축
graph = StateGraph(LogInvestigationState)
graph.add_node("inspect_log", inspect_log)
graph.add_node("search_jira", search_jira)
graph.add_node("search_github", search_github)
graph.add_node("grade_ticket", grade_ticket)
graph.add_node("summarize", summarize)
# 엣지 정의
graph.add_edge("inspect_log", "search_jira")
graph.add_edge("inspect_log", "search_github")
graph.add_conditional_edges(
"search_jira",
lambda s: Send("grade_ticket", s["jira_results"]),
)
graph.add_conditional_edges(
"search_github",
lambda s: Send("grade_ticket", s["github_results"]),
)
graph.add_edge("grade_ticket", "summarize")
graph.set_entry_point("inspect_log")
log_investigation_app = graph.compile()
컴파일된 그래프(log_investigation_app)는 원시 로그 텍스트를 포함한 딕셔너리로 호출할 수 있습니다. 최종 summary 필드는 Dash UI에 표시됩니다.
결론
LangGraph를 이용해 병렬 검색을 오케스트레이션하고, LangChain으로 LLM 기반 추론을 수행하며, Watsonx.ai의 강력한 모델 추론을 결합하면 사고 조사 시 발생하는 핵심 질문에 신속히 답변하는 에이전트를 만들 수 있습니다. 모듈형 그래프 구조 덕분에 Slack, 사내 지식 베이스 등 추가 데이터 소스나 더 풍부한 운영 컨텍스트를 손쉽게 확장할 수 있습니다.
향후 과제로는 다음을 고려해볼 수 있습니다:
- 로그 내용에 따라 동적 툴 선택 구현
- 인간이 제안된 워크어라운드를 승인·거부할 수 있는 피드백 루프
- 반복 조사 시 지연을 줄이기 위한 검색 결과 캐싱
Footnotes
-
관측성 기본 개념 – 표준 관측성 참고 문헌을 확인하세요. ↩