본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 29. 17:15

Hindsight와 Cascadeflow를 사용하여 AI 메모리를 재설계했습니다

요약

SentinelAI는 사고 인텔리전스, 문서 메모리, 엔터프라이즈 AI 코파일럿을 결합한 운영 플랫폼입니다. FastAPI 백엔드와 React 프론트엔드를 사용하여 사고 관리, 지식 검색, RCA 생성을 지원하는 시스템 구조를 설명합니다.

핵심 포인트

  • 사고 관리, 엔터프라이즈 메모리, 코파일럿의 3가지 주요 워크플로우 제공
  • FastAPI 기반의 백엔드와 Mongo 스타일의 메모리 레이어 활용
  • 사고 분류(Triage) 및 근본 원인 분석(RCA) 자동화 기능
  • 문서 요약 및 지식 베이스 기반의 AI 채팅 응답 스트리밍 지원

이 시스템을 처음 연결하기 시작했을 때, 가장 어려웠던 문제는 대시보드나 React 애니메이션이 아니었습니다. 바로 사고(incident) 스토리가 유용하게 느껴지도록 만드는 것이었습니다.

이 저장소(repo)는 사고 인텔리전스(incident intelligence), 문서 메모리(document memory), 그리고 엔터프라이즈 AI 코파일럿(enterprise AI copilot)을 중심으로 구축된 운영 플랫폼입니다. 백엔드는 사고(incidents), 지식 베이스(KB) 문서, 문서, 커넥터 상태(connector state), 그리고 채팅 기록을 Mongo 스타일의 메모리 레이어에 유지하는 FastAPI 서비스입니다. 프론트엔드는 사고를 분류(triage)하고, 근본 원인 분석(RCA)을 생성하며, 지식을 검색하고, 문서를 업로드하며, 코파일럿에게 질문할 수 있는 React 앱입니다.

시스템의 기능 및 구성 방식

높은 수준에서 살펴보면, SentinelAI는 세 가지 주요 흐름을 가진 사고 분류(incident triage) 워크스페이스입니다:

  • 사고 관리(Incident management): 사고 생성 및 업데이트, 타임라인 추적, AI 신뢰도 및 리스크 점수 표시.
  • 엔터프라이즈 메모리(Enterprise memory): 유사한 사고, 관련 지식 베이스(KB) 문서, 문서 및 동일한 사고 컨텍스트로부터 권장되는 해결책을 가져옴.
  • 코파일럿 및 문서 AI(Copilot and document AI): 채팅 응답 스트리밍, 문서 요약, 문서 질문 답변 및 구조화된 RCA 생성.

백엔드는 backend/server.py에 있는 단일 FastAPI 앱입니다. 또한 로컬 개발을 위한 경량 인메모리(in-memory) Mongo API 심(shim)을 가지고 있지만, 주요 기능 로직은 /incidents/{iid}/memory, /incidents/{iid}/rca-structured, /copilot/chat/stream, 그리고 /documents/{did}/insights와 같은 라우트(routes)에 포함되어 있습니다.

프론트엔드에는 Incidents 페이지, Copilot 페이지, 그리고 Documents 페이지가 있습니다. Copilot UI는 단순한 채팅창이 아닙니다. 문서를 첨부하고, 응답을 스트리밍(stream)하며, 제안된 후속 조치(follow-up actions)를 보여줄 수 있습니다. 인시던트 상세 화면(incident detail screen)은 개요, 타임라인, AI 신뢰도(AI confidence), 그리고 구조화된 RCA(Root Cause Analysis, 근본 원인 분석) 컴포넌트를 결합하여 보여줍니다.

핵심 기술 이야기: 메모리로서의 인시던트 컨텍스트

결국 제가 집중하게 된 것은 인시던트 메모리 모델(incident memory model)이었습니다. 운영 시스템(operations system)에서 AI 어시스턴트의 가치는 "이전에 어떤 일이 일어났으며, 이전에 무엇이 이를 해결했는가?"라는 질문에 답할 수 없다면 빠르게 하락합니다.


이 저장소(repo)에서 그 이야기는 두 부분으로 나뉩니다:

  1. 과거 인시던트 유사성 엔진(historical incident similarity engine).
  2. 인시던트, KB(Knowledge Base, 지식 베이스), 그리고 문서를 혼합하는 엔터프라이즈 메모리 엔드포인트(enterprise memory endpoint).

/incidents/{iid}/memory 엔드포인트가 그 이야기의 핵심입니다. 이는 다음과 같은 단순한 질문에 답하기 위해 설계된 집계된 기능(aggregated feature)입니다: "이 활성 인시던트에 대해, 어떤 과거 컨텍스트(context)가 중요한가?"

backend/server.py에서:

@api.get("/incidents/{iid}/memory")
async def enterprise_memory(iid: str, user=Depends(current_user)):
    inc = await db.incidents.find_one({"id": iid}, {"_id": 0})
...

이 코드는 의도적으로 단순합니다. 이것은 프로덕션용 시맨틱 검색 엔진(semantic search engine)이 아닙니다. 이는 의도적인 트레이드오프(tradeoff)입니다. 서비스, 심각도(severity), 부서, 태그, 그리고 공유된 제목 토큰(title tokens)에 대해 결정론적 매칭(deterministic matching)을 사용하여 최적의 후보를 노출하는 방식입니다. 저는 설명하기 쉽고, 디버깅하기 쉬우며, "메모리" 신호가 블랙박스(black box)가 되지 않도록 유지할 수 있다는 점 때문에 이 방식에 집중했습니다.

유사한 사례들이 확보되면, 엔드포인트(endpoint)는 예상 해결 시간(estimated time to resolve)과 신뢰도 점수(confidence score)를 합성합니다:

resolved_similar = [s for s in similar if s.get("status") == "resolved"]
if resolved_similar:
    eta_min = int(sum(max(15, 90 - (s["match_score"] // 2)) for s in resolved_similar) / len(resolved_similar))
...

이것은 실제 SLA(Service Level Agreement) 모델은 아니지만, 유사성(similarity)을 운영적 예측(operational prediction)으로 전환한다는 점에서 유용합니다. 이는 사고 대응자(incident responders)들이 실제로 관심을 갖는 실용적인 "종결(closure)"의 형태입니다.

이 설계에서 Hindsight와 Cascadeflow가 중요한 이유

저는 두 가지 개념, 즉 '컨텍스트(context)로서의 메모리'와 '의사결정 경로(decision path)로서의 흐름(flow)'을 고려하지 않았다면 이를 구축할 수 없었을 것입니다.

Hindsight는 사고 복구(incident recovery)를 검색 문제(retrieval problem)로 프레임화하기 때문에 여기서 유용합니다. 좋은 메모리 시스템은 다음과 같은 질문에 답할 수 있어야 합니다: "어떤 과거의 사고, 문서, 그리고 지식 베이스(KB) 문서들이 현재의 장애와 관련이 있는가?" 이것이 바로 /incidents/{iid}/memory 엔드포인트가 수행하고 있는 작업입니다. 이는 작고 운영적인 메모리 계층(memory layer)입니다.

Cascadeflow는 UI와 에이전트(agent)가 무작위적인 채팅이 아닌 하나의 흐름(flow)처럼 느껴져야 하기 때문에 관련이 있습니다. Copilot 페이지는 단순히 LLM(Large Language Model)에 텍스트를 보내는 것이 아닙니다. 세션 상태(session state)를 관리하고 제안된 작업(suggested actions)을 인터페이스에 다시 연결(wiring)합니다. 다음은 작업 제안(action suggestion) 라우트(route)입니다:

@api.post("/copilot/suggest-actions")
async def suggest_actions(payload: Dict[str, str], user=Depends(current_user)):
    prompt = (
...

이것은 일종의 흐름 오케스트레이션(flow orchestration)입니다. 시스템은 사용자의 마지막 쿼리(query)를 읽고, 단순히 한 단락의 텍스트를 반환하는 대신 구조화된 다음 단계(next steps)를 반환합니다. 이를 의사결정의 폭포(cascade of decisions)로 상상할 수 있습니다: 사용자가 질문을 하면, 에이전트가 답변하고, 시스템이 다음 작업을 제안하며, UI가 이를 버튼으로 변환합니다.

코드 기반 동작: UI에서 구현되는 방식

제가 짚어보고 싶은 세 가지 구체적인 상호작용이 있습니다.

1. 메모리를 활용한 사고 분류(Incident triage)

인시던트(incident) 목록 페이지에는 “Triage with AI memory”라는 라벨이 붙어 있습니다. 이 페이지는 /incidents에서 인시던트를 불러오며, 특정 인시던트를 열면 인시던트 상세 페이지에서 구조화된 RCA (Root Cause Analysis, 근본 원인 분석) 버튼과 엔터프라이즈 메모리 요약(enterprise memory summary)을 제공합니다.

frontend/src/pages/Incidents.jsx에 있는 코드는 그러한 기대 사항을 매우 명시적으로 보여줍니다:

<SectionLabel>Incident Intelligence</SectionLabel>
<h1 className="mt-1 text-3xl font-bold tracking-tight">Triage with <span className="gradient-text">AI memory</span></h1>

그 후 상세 페이지에서는 동일한 인시던트 객체가 AI로 생성된 분석 및 타임라인 이벤트(timeline events)와 함께 강화됩니다.

2. 스트리밍 응답을 지원하는 Copilot

Copilot 페이지는 /copilot/chat/stream으로부터 SSE (Server-Sent Events)를 사용합니다. 이는 이벤트를 파싱하고, 텍스트를 누적하며, 거의 실시간으로 UI를 업데이트합니다. 이를 통해 마치 살아있는 어시스턴트와 대화하는 듯한 경험을 제공합니다.

프론트엔드 구현은 여기에 있습니다:

const resp = await fetch(url, {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
...

이러한 흐름은 어시스턴트가 단순히 정적인 제출 버튼처럼 느껴지지 않게 한다는 점에서 매우 중요합니다.

3. 문서 인텔리전스(Document intelligence) 및 관련 인시던트

업로드된 문서는 요약하거나 질의할 수 있습니다. 백엔드는 문서 인사이트(document insights)와 문서 Q&A를 위해 동일한 llm_chat 래퍼(wrapper)를 사용하여 경험의 일관성을 유지합니다.

backend/server.py 코드:

@api.post("/documents/{did}/insights")
async def document_insights(did: str, user=Depends(current_user)):
    excerpt = (doc.get("excerpt") or "")[:3000]
...

그 다음 프론트엔드는 문서 인사이트 패널에서 연결된 관련 인시던트들을 보여줍니다.

구체적인 상호작용 예시

만약 제가 실제 인시던트 상황에서 이 시스템을 사용한다면, 워크플로우는 다음과 같을 것입니다:

  1. payments-api에 대해 critical 심각도와 latency, db와 같은 태그가 포함된 새로운 인시던트(incident)가 생성됩니다.
  2. 인시던트 상세 페이지를 열면 “AI 신뢰도 (AI Confidence)”와 이벤트 타임라인이 보입니다.
  3. “RCA 생성 (Generate RCA)”을 클릭합니다. 백엔드는 “근본 원인 (Root Cause), 기여 요인 (Contributing Factors), 권장 해결책 (Suggested Resolution), 예방 조치 (Preventive Actions)”를 요청하는 프롬프트와 함께 Gemini를 호출합니다.
  4. /incidents/{iid}/memory를 사용하여 유사한 에피소드, 관련 지식 베이스 (KB), 그리고 과거에 해결된 인시던트에서 추출된 권장 해결책을 확인합니다.
  5. Copilot으로 전환하여 인시던트 상세 내용을 붙여넣거나 지원 문서를 첨부하면, 제안된 후속 조치가 포함된 스트리밍 어시스턴트 (streaming assistant) 응답을 받습니다.

이것이 제가 원하는 패턴입니다: 인시던트, 메모리, 그리고 가이드된 AI 흐름 (AI flow).

엔지니어로서 이것이 중요한 이유

여기에는 언급할 가치가 있는 몇 가지 설계 결정 사항이 있습니다.

  • 유사성 (similarity)을 과도하게 복잡하게 만들지 마십시오. 메모리 엔진은 시맨틱 검색 (semantic search)인 척하지 않습니다. 결과의 예측 가능성을 높이기 위해 구조화된 필드 (structured fields)와 토큰 중첩 (token overlap)을 사용합니다.
  • 프롬프트 레이어 (prompt layer)를 일관되게 유지하십시오. 모든 AI 기능은 동일한 llm_chat 가드레일 (guardrail)을 공유하며, 구조화된 출력이 필요한 경우 동일한 “엄격한 JSON (strict JSON)” 스타일을 사용합니다.
  • 문서를 일급 객체 컨텍스트 (first-class context)로 취급하십시오. 문서 업로드는 단순한 사이드바 기능이 아닙니다. 이는 실제로 Copilot 세션과 인시던트 메모리에서 사용할 수 있습니다.
  • 어시스턴트를 실행 가능하게 (actionable) 만드십시오. /copilot/suggest-actions 엔드포인트는 채팅을 “이것에 답해줘”에서 “다음에 무엇을 해야 하지?”로 바꾸는 가장 작은 단위의 장치입니다.

제가 가장 먼저 수정하고 싶은 한계점들

이 리포지토리 (repo)는 유망하지만, 명확한 트레이드오프 (tradeoff)를 가지고 있습니다: 현실성이 합성된(synthetic) 상태라는 점입니다. 백엔드의 많은 부분이 의도적으로 단순하게 설계되었으며, “엔터프라이즈 메모리 (enterprise memory)” 엔드포인트는 실제 인시던트 데이터와 폴백 휴리스틱 (fallback heuristics) 및 합성된 신뢰도 (synthetic confidence)를 혼합하여 사용합니다.

예를 들어:

  • 유사도 모델 (similarity model)은 직접적인 필드 매칭과 제목 토큰 (title tokens)만을 사용합니다.
  • ETA 계산은 실제 과거 분포 (historical distribution)가 아니라, 매칭 점수 (match score)의 결정론적 함수 (deterministic function)입니다.
  • 문서 추출 레이어 (document extraction layer)는 LLM 출력이 잘못된 형식일 경우 단순 키워드 (naive keywords)로 대체 (fallback)될 수 있습니다.

이러한 방식은 최소한의 시스템 (minimal system)으로서는 나쁜 결정이 아니지만, 만약 이 시스템을 실제 SRE 워크플로에서 실행하고 싶다면 제가 다음에 강화할 부분들입니다.

교훈 (Lessons learned)

  1. 장애 메모리 (Incident memory)는 범위가 좁을수록 더 유용합니다. "유사한 장애 + 관련 KB + 문서"와 같은 작은 기능은 전체 검색 엔진 (search engine)을 만드는 것보다 출시하기가 더 쉽습니다.
  2. 구조화된 LLM 출력 (Structured LLM output)은 취약성 (brittleness)을 줄여줍니다. 프롬프트에서 엄격한 JSON을 요구하면, 백엔드는 완전히 실패하는 대신 대체 파서 (fallback parser)를 통해 복구할 수 있습니다.
  3. 스트리밍 채팅 (Streaming chat)은 사용자의 기대치를 변화시킵니다. 어시스턴트가 토큰 단위 (token-by-token)로 응답하고 UI가 다음 단계로 제안되는 내용을 보여준다면, 제품은 양식 (form)이 아닌 흐름 (flow)처럼 느껴집니다.
  4. 높은 신뢰도로 추측하지 마세요. 백엔드는 신뢰도 점수 (confidence score)를 노출하고 이를 보수적으로 유지하는데, 이는 AI가 항상 옳다고 가장하는 것보다 더 나은 태도입니다.
  5. UI 언어를 아키텍처 (architecture)와 일치시키세요. 프론트엔드에서 이 페이지를 "AI 메모리"라고 부르는 이유는, 여기서 가장 강력한 핵심 스토리가 단순한 "AI 지원"이 아니라 컨텍스트 검색 (context retrieval)이기 때문입니다.

마지막 생각 (Final thought)

저는 하나의 아이디어를 중심으로 이 리포지토리 (repo)를 구축했습니다: AI는 장애 프로세스 (incident process)를 대체하는 것이 아니라, 장애 컨텍스트 (incident context)를 가시화해야 한다는 것입니다.

이는 메모리 엔진 (memory engine)에서 몇 가지 의도적인 단순화 (simplifications)를 수용하고, 코파일럿 (copilot)이 가이드된 흐름 (guided flow)처럼 동작하도록 UI를 구축하는 것을 의미했습니다. 만약 제가 이 프로젝트를 계속한다면, 저는 동일한 긴장 관계를 유지할 것입니다: 컨텍스트를 위해서는 메모리를 사용하고, 의사결정을 위해서는 흐름 (flows)을 사용하며, 시스템을 설명 가능하게 (explainable) 유지하는 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0