Jev와 LangGraph를 사용한 AI 에이전트 의사 결정 계층 구축
요약
본 글은 AI 에이전트가 수행하는 복잡한 의사 결정 과정 중, 분류 및 점수화 작업에 LLM 호출을 사용하는 비효율성을 지적합니다. 이를 해결하기 위해 시스템 원(System One) 기반의 의사결정 모델인 Jev를 소개하며, LangGraph.js와 결합하여 라우팅 및 위험 평가 등 구조화된 결정 처리에 활용하는 방법을 설명합니다.
핵심 포인트
- Jev는 LLM 대신 System One 방식으로 작동하는 의사결정 모델입니다.
- Jev는 텍스트 생성 없이 보정된 확률과 구조화된 답변을 반환합니다.
- LangGraph.js와 Jev를 결합하여 에이전트의 라우팅 및 위험 평가 기능을 구현할 수 있습니다.
- Choice, Score, Noul 등 세 가지 질문 유형으로 구조화된 결정 처리가 가능합니다.
AI 에이전트는 점점 더 복잡해지고 있습니다. 이들은 메시지가 긴급한지 여부를 판단하거나, 작업을 처리할 모델을 선택하거나, 작업의 위험도를 평가해야 할 필요가 자주 생기는데, 이 모든 것이 본질적으로는 분류(classification) 및 점수화(scoring) 작업입니다. 하지만 이러한 작업들은 종종 비용이 많이 드는 대규모 언어 모델(LLM) 호출로 처리됩니다.
TypeSafe AI의 Jev는 바로 이 문제를 해결하기 위해 설계되었습니다. 이것은 전통적인 LLM이 아니라, 시스템 원(System One) 의사 결정 모델입니다. 사용자는 상태(state)와 일련의 타입 지정된 질문을 보내면, 텍스트를 생성하지 않고도 보정된 확률(calibrated probabilities)과 함께 구조화된 답변을 반환받습니다.
하지만 실제로 에이전트 내부에서 이것이 어떻게 작동하는지 궁금할 수 있습니다. 본 게시물에서는 구체적인 오픈 소스 예제인 JevSample을 통해 Jev가 LangGraph.js 에이전트에 어떻게 통합되어 라우팅 및 위험 평가를 처리하는지 살펴보겠습니다.
LLM에 모든 것을 결정하게 하는 것의 문제점
일반적인 AI 에이전트는 다음과 같은 모습을 할 수 있습니다:
LLM이 거의 모든 것을 책임집니다.
콘텐츠를 생성하고, 어떤 도구를 호출할지 결정하며, 결과를 평가하고, 계속 진행해야 할지 여부를 결정하며, 때로는 안전성 결정까지 내립니다.
이것이 작동할 수는 있지만, 동시에 문제를 야기하기도 합니다.
이러한 결정 중 일부는 비교적 작고 잘 정의된 답변 공간을 가집니다.
예를 들어:
요청을 허용해야 할까요? 예(YES) / 아니오(NO)
또는:
다음으로 무엇이 일어나야 할까요? 실행(EXECUTE) / 검토(REVIEW) / 확인(VERIFY) / 재시도(RETRY)
또는:
이 작업은 얼마나 위험한가요? 낮음(LOW) / 중간(MEDIUM) / 높음(HIGH)
이들은 구조화된 결정입니다.
우리가 이 모든 것에 대해 반드시 LLM의 생성적 응답을 필요로 하는 것은 아닙니다.
Jev는 이러한 종류의 문제에 맞춰 설계되었습니다. 단순히 단락을 생성하도록 요청하는 대신, 상태(state)를 제공하고 유형이 지정된 질문(typed question)을 합니다. Jev는 세 가지 주요 질문 유형을 지원합니다: Choice, Score, 그리고 Noul.
Choice는 미리 정의된 옵션 중에서 선택하며, Score는 순서가 매겨진 척도(ordered scale)를 평가하고, Noul은 예/아니오 진술에 대한 확률을 산출합니다.
이로 인해 애플리케이션 코드가 소비하기 훨씬 쉬운 출력을 얻게 됩니다.
JevSample 소개
JevSample은 LangGraph를 사용하여 구축된 AI 에이전트의 작은 예제입니다.
흥미로운 부분은 프로젝트의 크기가 아닙니다. 아키텍처(architecture)입니다.
이 샘플은 세 가지 다른 책임을 분리합니다:

에이전트 그래프는 여러 노드(nodes)를 포함합니다:

이를 통해 에이전트는 훨씬 더 명시적인 제어 흐름(control flow)을 갖게 됩니다.
그래프 구축하기
JevSample의 핵심 그래프 구성은 다음과 같습니다:
export function buildGraph(deps) {
return new StateGraph(GraphState)
.addNode('router', routerNode(deps))
...
여기서 중요한 점은 이 그래프가 단순한 선형 파이프라인을 나타내지 않는다는 것입니다. 이는 **상태 기계(state machine)**를 나타냅니다.
에이전트는 앞으로 나아갈 수도 있고, 멈출 수도 있고, 인간의 입력을 요청할 수도 있고, 액션(action)을 실행할 수도 있고, 결과를 검증할 수도 있으며, 생성기(generator)로 돌아가 다시 시도할 수도 있습니다.
이것이 실제 세계 에이전트의 중요한 특징입니다.
Jev가 들어갈 곳
이 샘플에서 Jev를 사용할 수 있는 특히 흥미로운 장소가 두 군데 있습니다.
첫 번째는 router입니다.
라우터는 에이전트가 어떤 경로를 따라야 할지에 대한 구조화된 결정(structured decision)을 내릴 수 있습니다.
개념적으로:

<br>LLM에게 다음과 같은 내용을 생성하도록 요청하는 대신:
"I think we should probably execute the request..."
애플리케이션은 Jev에게 가능한 결과가 이미 정의된 'Choice' 질문을 할 수 있습니다.
예를 들어:
어떤 워크플로우가 이 요청을 처리해야 하나요?
- generate
...
<br>애플리케이션은 반환된 선택지를 직접 사용하여 그래프 전환(graph transition)을 선택할 수 있습니다.
이것이 훨씬 더 깔끔한 계약(contract)입니다.
안전성 결정에 사용되는 Jev
두 번째 흥미로운 용도는 safety 노드입니다.
<br><br>흐름은 다음과 같습니다:

생성기(generator)가 후보 결과(candidate result)를 생성합니다.
<br>프리필터(prefilter)는 애플리케이션 코드에서 결정론적 검사(deterministic checks)를 수행합니다.
<br>그런 다음 Jev가 더 높은 수준의 안전성 결정(higher-level safety decision)을 평가합니다.
<br><br>이러한 분리는 중요합니다.
<br>프리필터는 아예 AI 모델일 필요가 없습니다.
<br>예를 들어, 결정론적 검사는 다음을 처리할 수 있습니다:
if (!result) {
return "blocked";
}
...
<br>Jev는 간단한 규칙으로는 표현하기 어려운 판단(judgment)을 처리할 수 있습니다.
<br>Noul 질문은 이러한 종류의 결정에 자연스럽게 적합합니다:
이 생성된 결과는 실행해도 안전한가요?
<br>결과는 해당 진술이 참일 확률을 나타내는 0과 1 사이의 값입니다.
<br>애플리케이션은 그 후 자체 정책(policy)을 정의할 수 있습니다.
<br>예를 들어:
if (safety.noul >= 0.9) {
return "execute";
}
...
<br>중요한 점은 Jev가 아무것도 실행하지 않는다는 것입니다.
<br>Jev는 판단(judgment)을 제공할 뿐입니다.
<br>애플리케이션이 그 판단이 운영적으로 무엇을 의미하는지 결정할 책임이 남아 있습니다.
프리필터가 여전히 중요한 이유
모든 것을 Jev에 넣고 싶은 유혹을 느낄 수 있습니다.
하지만 그렇게 하는 것은 좋은 생각이 아니라고 생각합니다.
일반적인 코드가 더 잘 처리하는 많은 것들이 존재하기 때문입니다.
예를 들어:
if (!input) {
return "invalid";
}
여기에 AI 모델을 사용할 이유는 없습니다.
마찬가지로:
if (result.length > MAX_LENGTH) {
return "blocked";
}
은 결정론적(deterministic) 규칙입니다.
따라서 아키텍처는 다음과 같이 구성됩니다:

각 구성 요소는 각기 다른 역할을 가지고 있습니다.
이것은 JevSample에서 보여주는 주요 아이디어 중 하나입니다.
디스패치 단계 (The dispatch stage)
안전성 결정(safety decision) 이후, dispatch 노드는 다음에 무슨 일이 일어날지 결정합니다.
가능한 경로는 다음과 같습니다:

이곳에서 구조화된 결정(structured decisions)의 가치가 특히 명확해집니다.
디스패처는 모델이 생성한 단락을 해석할 필요가 없습니다.
명시적인 상태(explicit states)로 작업할 수 있습니다.
예를 들어:
switch (decision) {
case "execute":
return "executor";
...
AI 모델은 판단(judgment)을 담당합니다.
애플리케이션이 제어 흐름(control flow)을 담당합니다.
인간 검토는 여전히 아키텍처의 일부입니다 (Human review is still part of the architecture)
샘플에서 또 다른 중요한 부분은 human_review입니다.
AI 에이전트가 모든 결정을 반드시 자동으로 내릴 필요는 없습니다.
어떤 상황들은 다음과 같이 처리되어야 합니다:

이는 특히 결정에 완전히 자동화하기에는 너무 중요한 결과가 따르는 경우에 유용합니다.
따라서 이 아키텍처는 Jev를 권위자로 취급하지 않습니다.
대신:

이러한 구분은 프로덕션 에이전트를 설계할 때 중요합니다.
검증(Verification)을 통한 피드백 루프 생성
실행 후, 샘플은 모든 것이 작동했다고 단순히 가정하지 않습니다.
verifier(검증기)가 결과를 확인합니다.
개념적으로:

만약 작업이 완료되면, 그래프는 종료됩니다.
완료되지 않았다면, 에이전트는 generator(생성기)로 돌아가 다른 접근 방식을 시도할 수 있습니다.
이는 익숙한 에이전트 루프를 만듭니다:

중요한 차이점은 이 루프 내의 결정들이 모두 동일한 모델에 의해 내려질 필요는 없다는 것입니다.
Jev는 LLM을 대체하는 것이 아니다
이것이 아마도 예시에서 가장 중요한 지점일 것입니다.
Jev와 LLM은 서로 다른 역할을 합니다.
LLM은 다음이 필요할 때 유용합니다:
- 텍스트 생성(text generation)
- 추론(reasoning)
- 요약(summarization)
- 코드 생성(code generation)
- 설명(explanations)
- 개방형 문제 해결(open-ended problem solving)
Jev는 다음과 같이 표현될 수 있는 결정이 있을 때 유용합니다:
어떤 옵션?
얼마나 심각한가?
...
이는 우리에게 유용한 아키텍처를 제공합니다:

하나의 모델에게 모든 것을 처리하도록 요청하는 대신, 워크플로우의 각 부분에 적합한 도구를 선택할 수 있습니다.
선택(Choice), 점수(Score) 및 노울(Noul)
Jev의 세 가지 질문 유형은 에이전트의 여러 부분에 잘 매핑됩니다.
선택 (Choice)
답변이 몇 가지 미리 정의된 옵션 중 하나일 때 Choice를 사용합니다.
예시:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기