Chapter 13 · Use Case: A Single Agent for Loan Processing

한 몸으로 다 해보다

『Agentic Architectural Patterns for Building Multi-Agent Systems』 13장 — Ali Arsanjani, Juan Pablo Bustos, Thomas Kurian

이제 이론에서 실무로 넘어갈 때다. 실제 사업 문제 하나 — 대출 실행 파이프라인 자동화 — 를 풀 완결된 에이전틱 시스템을 짓는다. 그런데 이 장의 목적은 성공만이 아니다. 먼저 단일체 에이전트 하나로 전 과정을 만들어 성공시킨 뒤, 바로 그 구현으로 단일 에이전트 설계에 내재한 아키텍처적 부담을 일부러 드러낸다. 그 “기초의 균열”을 짚어내는 것이 다음 장의 발판이다.

01

The challenge · a high-stakes workflow

과제 — 하나의 과업이 아니라 다섯 개의 국면

대출 실행 파이프라인이 에이전틱 시스템의 이상적 용례인 이유는 분명하다. 이것이 하나의 과업이 아니라, 저마다 고유한 논리와 데이터 요구와 실패 가능성을 가진 복잡한 국면들의 연쇄이기 때문이다.

1

서류 접수와 검증

신청서를 받고 소득 증빙 같은 모든 보조 서류가 완전하고 유효한지 확인한다.

다양한 서류 형식 처리, 누락 정보 식별, 완전성에 대한 업무 규칙 적용
※ 이 예제는 서류가 모두 제출됐다고 전제한다. 실제 접수와 OCR은 다루지 않는다.
2

신용 조회

외부 신용평가기관 API와 상호작용해 차주의 신용 이력과 점수를 가져온다.

자격증명의 안전한 처리, 제각각인 API 응답 파싱, 네트워크 오류나 API 장애의 우아한 처리
3

위험 평가

수집한 모든 금융 데이터에 근거해 내부 업무 로직과 동적 위험 점수 모델을 적용한다.

단순한 if/then 규칙이 아니라 여러 데이터 지점을 종합하는 복잡하고 비선형인 로직의 실행
4

규제 준수 검토

평등신용기회법(ECOA) 같은 모든 관련 규제를 지켰는지 절차를 감사한다.

의사결정 과정의 감사 가능한 추적 유지, 보호 속성이 결과에 영향을 주지 않았음의 보장
5

최종 결정과 문서 생성

모든 정보를 종합해 최종 승인·거절 결정을 내리고 필요한 문서를 생성한다.

앞선 워크플로 전체에 근거한, 일관되고 사람이 읽을 수 있는 결정 근거의 작성

〈표 13.1〉 대출 실행 워크플로의 다섯 국면

이것을 전통적 스크립트로 자동화하려 들면 부서지기 쉽고 유지가 어려운 시스템이 된다. 전통적 자동화는 경직되고 사전 정의된 규칙에 기대는데, 제각각인 서류 형식이나 모호한 신청인 정보 같은 비정형 데이터 앞에서 쉽게 무너진다. 가능한 모든 예외를 처리하려면 개발자가 끝없는 if/then 로직의 그물을 짜고 유지해야 하고, 그것은 금세 감당할 수 없게 된다.

이 워크플로가 요구하는 것은 다르다. 예외를 우아하게 다룰 동적 추론, 맥락에 따라 실행 단계를 조정할 구조화된 계획, 그리고 결정에 대한 명확하고 감사 가능한 추적을 생성하는 능력이다. 에이전틱 AI가 빛나는 지점이 바로 여기다.

02

The FCoT framework

FCoT — 에이전트를 다스리는 헌법

이 금융 과업에 필요한 엄밀함으로 에이전트가 작동하게 하려면 프랙탈 사고사슬(FCoT) 위에 세운 정교한 인지 틀을 갖춰 줘야 한다. FCoT 원칙을 담은 프롬프트를 먼저 짓는다. 이 프롬프트가 에이전트의 내부 “운영체제” 노릇을 하며, 임무와 제약과 추론 과정에 정식 구조를 준다. 에이전트의 행동을 다스릴 헌법이다.

The enemy

목표 표류 · goal drift

대출 실행 같은 복잡한 다단계 업무 프로세스에서 단순한 에이전트는 흔히 목표 표류를 겪는다. 과업이 진행될수록 원래 목적에서 점차 벗어나는 행동 실패다. 이 표류는 잘 알려진 기술적 한계, 곧 “한가운데서 잃어버리는(lost in the middle)” 현상에서 자주 비롯된다. LLM이 긴 컨텍스트 창 깊숙이 파묻힌 결정적 제약이나 지시를 되살리지 못하는 것이다. FCoT는 이 둘 모두를 겨냥해, 에이전트의 추론에 엄격하고 자기교정적인 구조를 강제한다. 맥락이 아무리 길어져도 핵심 임무와 제약을 끊임없이 되짚게 만든다.

Element 01

지시 계약 · IC

에이전트의 불변하는 진리원 노릇을 한다. 임무, 반드시 생산해야 할 정확한 산출물, 그리고 결코 위반해서는 안 될 안전·준수 가드레일을 정식으로 정의한다.

Element 02

재귀 루프

에이전트의 능동적 사고 과정을 정의한다. 행동을 계획하고, 실행하고, 무엇보다 매 단계마다 자기 작업과 추론을 IC에 비추어 검증하는 주기를 강제한다.

실제 지시 계약

INSTRUCTION CONTRACT (IC)

• Mission: 완전한 정책 준수, 사실적 접지, 공정성을 갖추고 대출을 실행·평가·승인한다. • Deliverables: JSON + 서술 요약 (a) 차주 프로필 (b) 신용도 판정 (c) 검증된 데이터를 인용한 근거 (d) 규제 준수 감사 기록 (e) 설명 가능성 보고서 • Success Criteria - 정확도 ≥ 95% (금융 데이터 정답 대비) - 정책 준수 = 100% - 설명 가능성 포괄도 ≥ 90% - 종단 간 지연 < 5분 • Hard Constraints - 로그에 개인 식별 정보 금지 - 공정대출법 및 ECOA 규정 준수 필수 - 모든 수치 필드는 권위 있는 출처에서 검증 • Safety Policy - 추측성·환각 데이터 거부 - 차주 정보를 결코 날조하지 않음 - 모호한 사례는 Human-in-the-Loop 에이전트로 이관 • IC-Fingerprint: LOAN-FCoT-v3-Δ0710

재귀 루프 세 바퀴 · N = 3

Iteration 1 — Planning

RECAP — IC 지문을 되뇌고 하위 과업(데이터 수집, 신용 점수, 준수, 문서)을 매핑한다.
REASON — 행동의 DAG를 설계하고, 검색 출처를 고르고, PoF 원장을 초기화한다.
VERIFY — 모든 하위 과업이 IC 조항을 보존하는지 확인한다.

Iteration 2 — Execution

RECAP — IC 지문. 신용 점수와 데이터 검증을 위한 도구를 실행한다.
REASON — 위험 점수를 계산하고, 데이터 출처를 정책에 비추어 검증한다.
VERIFY — 차주 속성과 결정 로직 사이의 인과적 정렬을 점검한다.

Iteration 3 — Verification & Explainability

RECAP — IC 지문. 산출물을 모으고 RAG 검증기를 돌린다.
REASON — SHAP 값을 요약하고 서술적 근거를 작성한다.
VERIFY — IC 및 이중 목적함수에 비추어 일관성을 평가한다.

Pattern insight · 의미적 가드레일 대 프로그램적 평가

프롬프트에 적힌 성공 기준(예: 지연 < 5분)을 보고 흔히 묻는다. “파이썬 코드가 이것을 강제하는가?”

이 단일체 설계에서 그것들은 의미적 가드레일이다. 외부의 파이썬 단언문이 아니라 LLM의 내부 추론 엔진에 주는 지시다. 정확도와 정책 준수를 성공 기준으로 명시함으로써, FCoT 루프의 VERIFY 단계에서 모델이 그 요인들을 고려하도록 강제한다.

이상적으로 모델은 이 지시로 자기교정한다(“지연을 낮추려면 간결해야 한다”). 운영 시스템이라면 DeepEval이나 Ragas 같은 실제 외부 평가 하니스와 짝지어, 맥락 내 검증에서 시스템 수준 거버넌스로 옮겨가야 한다.

Pattern insight · 인지적 통제 대 코드 통제

FCoT RECURSIVE LOOP (N = 3)인지 아키텍처의 대표적 예다.

여기서 N=3은 BuiltInPlanner에 넘기는 파이썬 매개변수가 아니다. 세 번 반복하라는 의미적 지시다. 사실상 우리는 영어로 모델을 프로그래밍하고 있다. 내부 추론 과정을 계획·실행·검증이라는 세 반복으로 구조화하라고 지시하는 것이다.

파이썬 코드(thinking_budget=1024)가 자원 한도를 정한다면, 프롬프트는 알고리즘을 정의한다. 얼마나 쓸 수 있는지와 어떻게 쓸지의 차이다.

Concept note · XAI와 SHAP

Iteration 3의 SHAP 값을 요약하라는 지시는 SHapley Additive exPlanations를 가리킨다. 각 특성(신용점수, 부채상환비율 등)에 수치를 배정해, 그것이 특정 예측에 얼마나 기여했는지 정량화하는 표준 기법이다.

혼합 에이전틱 아키텍처에서는 강력한 패턴이 나타난다. 전통적 ML(도구)이 정밀한 위험 계산을 수행해 원시 SHAP 값을 낸다(예: 신용점수: -0.45 기여). 생성형 AI(에이전트)는 해설자 노릇을 한다. 이 건조한 수학적 값을 고객이 읽을 수 있는 일관된 근거로 옮긴다(“귀하의 신청은 주로 신용점수의 영향을 받았습니다…”).

03

Designing a monolithic agent

단일체 설계 — 하나가 처음부터 끝까지

이번 첫 구현은 단일체 접근을 택한다. 고도로 유능한 에이전트 하나가 대출 실행 워크플로 전체를 처음부터 끝까지 책임진다는 뜻이다. 모든 로직과 도구와 상태 관리가 하나의 중앙 구성요소에 집중된다.

에이전트의 주된 과업은 내부 FCoT 프롬프트를 따르며 올바른 도구를 올바른 시점에 순차적으로 호출해 대출 신청을 파이프라인 끝까지 밀어 보내는 것이다.

사용자 요청 “Borrower-789를 처리하라” 단일체 에이전트 · LlmAgent FCoT 추론 핵심 IC + 재귀 루프 N=3 “에이전트의 뇌” 상태 관리 Runner + SessionService 대화 이력과 맥락 도구 호출 도구벨트 · TOOLBELT validate_document run_credit_check assess_risk check_compliance 순차 호출 — 하나라도 실패하면 전체가 멈춘다

〈그림 13.1〉 단일체 대출 처리 에이전트의 아키텍처

Part 01

FCoT 추론 핵심

앞서 만든 FCoT 프롬프트가 에이전트의 “뇌” 노릇을 한다.

Part 02

상태 관리

대출 신청의 진행 상태를 추적할 방법이 필요하다. ADK의 RunnerSessionService가 처리한다.

Part 03

도구벨트

바깥세상과 상호작용하려면 호출할 수 있는 함수의 모음이 필요하다.

Environment

실습은 Google Colab 노트북에서 Google ADK로 진행한다. 모델은 속도와 비용 효율이 좋은 gemini-3-flash를 쓴다. 학습과 실험에 이상적이다. 다만 LLM 지형은 빠르게 바뀌므로, 직접 만들 때는 최신 모델 목록을 확인하기를 권한다.

04

Defining the tools

네 개의 도구 — 추론을 세상에 잇는 다리

에이전트의 추론 능력은 그 행동 능력만큼만 강하다. 도구벨트는 에이전트의 인지 과정을 실제 세계에 잇는 결정적 구성요소다. 여기서는 대출 실행 워크플로의 핵심 국면을 흉내 내는 네 개의 전문 도구를 쥐어 준다.

이것들을 모의 데이터를 쓰는 단순한 파이썬 함수로 구현함으로써 완전히 기능하고 자족적인 예제를 만든다. 실제 API 키나 외부 서비스 의존성을 관리하는 복잡성 없이 에이전트의 조율과 추론 로직에만 온전히 집중할 수 있다.

① validate_document

필요한 신청 서류가 존재하고 완전한지 검증한다. 가장 먼저 써서 신청이 처리 준비가 됐는지 확인한다.

서류가 2건 미만 → incomplete
그 외 → validated

② run_credit_check

신용평가기관 API를 호출해 차주의 신용점수를 가져온다. 서류 검증 이후에 수행해야 한다.

Borrower-400 → 450점, 이력 손상
그 외 → 750~850점 무작위

③ assess_risk

차주의 신용점수에 근거해 대출 신청의 위험을 평가한다.

740점 초과 → low
그 외 → high

④ check_compliance

최종 결정 전에 절차가 공정대출 지침을 지켰는지 최종 준수 점검을 수행한다.

→ compliance_status: pass
구현 예시 · 도구 정의
# --- Tool 1: Document Validation ---
def validate_document(document_ids: list[str]) -> dict:
    """
    필요한 신청 서류가 존재하고 완전한지 검증한다.
    처리 준비 여부를 확인하려면 이것을 가장 먼저 쓴다.
    'validated' 또는 'incomplete' 상태를 반환한다.
    """
    print("--- Tool Called: validate_document() ---")
    time.sleep(1)
    if not document_ids or len(document_ids) < 2:
        return {"status": "incomplete",
                "missing_docs": ["income_proof", "id_proof"]}
    return {"status": "validated"}

# --- Tool 2: Credit Check ---
def run_credit_check(borrower_id: str) -> dict:
    """신용평가기관 API를 호출해 차주의 신용점수를 가져온다."""
    print(f"--- Tool Called: run_credit_check(borrower_id='{borrower_id}') ---")
    time.sleep(2)
    if borrower_id == "Borrower-400":
        score = 450
        report_summary = "Credit history is compromised."
    else:
        score = random.randint(750, 850)
        report_summary = "Credit history is clean."
    return {"credit_score": score, "report_summary": report_summary}

# --- Tool 3: Risk Assessment ---
def assess_risk(credit_score: int, loan_amount: float) -> dict:
    """신용점수에 근거해 위험을 'low' 또는 'high'로 평가한다."""
    time.sleep(1.5)
    if credit_score > 740:
        return {"risk_level": "low",
                "details": "High credit score indicates low risk."}
    return {"risk_level": "high",
            "details": "Low credit score indicates high risk."}

# --- Tool 4: Compliance Check ---
def check_compliance(risk_level: str) -> dict:
    """최종 결정 전 공정대출 지침 준수를 점검한다."""
    time.sleep(1)
    return {"compliance_status": "pass",
            "details": "Process adheres to guidelines."}

# ADK의 FunctionTool로 감싸 에이전트가 이해할 수 있게 한다
validate_document_tool = FunctionTool(func=validate_document)
run_credit_check_tool  = FunctionTool(func=run_credit_check)
assess_risk_tool       = FunctionTool(func=assess_risk)
check_compliance_tool  = FunctionTool(func=check_compliance)

Production tip · 데이터 계약을 위한 강한 타입

이 예제는 코드를 읽기 쉽게 하려고 도구 출력에 단순한 파이썬 딕셔너리를 쓴다. 그러나 실제 금융 시스템에서는 강하게 타입이 정해진 데이터 구조가 신뢰성에 필수다. 운영을 위해서는 Pydantic 같은 라이브러리로 엄밀한 데이터 모델을 정의하라(예: class RiskAssessmentResult(BaseModel)). 스키마 검증을 강제하고 타입 오류를 막으며, 에이전트 사이의 엄격한 “데이터 계약” 노릇을 해 하류 구성요소가 언제나 기대한 구조를 받게 한다.

Production note

실제 배포에서는 함수 시그니처는 대체로 그대로 두되 내부 로직이 바뀐다. 모의 문자열을 반환하는 대신, 내부 문서 관리 시스템을 호출하거나 제3자 API를 부르거나 MCP(Model Context Protocol)를 통해 다른 서비스와 통신하는 래퍼가 된다.

05

Execution and analysis

실행과 분석 — 두 개의 경로

여기서의 일차 목표는 관찰 가능성이다. 에이전트가 대출을 승인하는지 거절하는지만 알고 싶은 것이 아니다. 어떻게 그 결론에 이르는지를 보고 싶다.

그래서 ADK Runner로 세션 생애주기를 관리하고, call_agent라는 보조 함수를 정의해 에이전트의 “생각”과 도구 상호작용을 깨끗하게 걸러 출력한다. 이 함수가 관찰 계층 노릇을 한다.

Note · 오류 처리도 진단이어야 한다

call_agent 구현에는 전문적인 오류 처리 블록이 들어 있다. 이것이 결정적인 이유가 있다. 실제 세계에서 429 Rate Limit 오류는 알쏭달쏭한 파이썬 트레이스백이어서는 안 된다. 무슨 일이 일어났고 어떻게 고칠지를 운영자에게 정확히 알려 주는 진단 메시지여야 한다(예: “24시간 대기” 또는 “결제 활성화”).

Scenario A · Happy path

Borrower-789 — 승인

기준 기능을 검증한다. 에이전트가 도구를 올바른 순서로 호출하고, 없는 장애물을 환각하지 않고 승인 결정에 이르는지 확인한다.

🧠 THOUGHT * 먼저 validate_document를 호출해 서류를 확인한다. * 검증이 'validated'를 반환하면, 차주 ID Borrower-789로 run_credit_check를 호출한다. --- Tool Called: validate_document() --- --- Tool Called: run_credit_check(borrower_id='Borrower-789') --- 🧠 THOUGHT: Borrower-789의 신용도로 넘어간다 이제 다음 결정적 단계로 넘어갈 시간이다… → 결정: 승인 (Approved)

결과 — 단일체 에이전트가 기능한다. 단계를 추론하고, 계획을 실행하고, 승인 결정에 이르렀다. 무엇보다 도구를 만지기 전에 THOUGHT 블록에서 접근법을 명시적으로 계획한다. 이것이 FCoT의 추론 국면이며, 성급한 결론으로 달려가는 것을 막는다.

Scenario B · Exception path

Borrower-400 — 거절

시스템의 견고성을 시험한다. 대출 에이전트는 모두를 승인한다면 쓸모가 없다. 위험을 감지하고 어려운 결정을 내리면서도 준수를 유지해야 한다.

--- Tool Called: run_credit_check(borrower_id='Borrower-400') --- → credit_score: 450 → "Credit history is compromised." Iteration 3 (Verification & Explainability) 설명 가능성 보고서: 결정에 이른 구체적 데이터 지점 — Score 450 위험 수준 — high --- Tool Called: check_compliance() --- → compliance_status: pass → 결정: 거절 (Denied)

결과 — 이 시험이 확인해 준 것은 우리 에이전트가 “예스맨”이 아니라는 사실이다. 신용조회 도구에서 데이터를 받아 450점이 고위험을 뜻한다고 추론하고 올바르게 거절했다. 결정적으로, 거절임에도 준수 점검 도구를 그대로 실행했다. 거절의 절차가 승인의 절차만큼 엄정하게 유지된 것이다.

대출이 거절되었음에도 에이전트는 그 결정에 이른 구체적 데이터 지점을 인용한 상세한 설명 가능성 보고서를 생산한다. 이것이 감사 추적 패턴이 작동하는 모습이다.

06

A roadmap for improvement

기초의 균열 — 성공했기에 보이는 것들

우리가 지은 단일 에이전트 시스템은 훌륭한 성공이다. 복잡한 지시 집합을 올바로 따라 사업 문제를 풀고, 도구벨트로 환경에 행동하는 완결되고 자율적인 해법이다. 이 단계에 이른 것은 성숙도에서 유의미한 진전이며 실질적 사업 가치를 낼 수 있다.

그러나 성공한 설계를 에이전틱 AI 성숙도의 렌즈로 분석하면 더 견고하고 확장 가능한 시스템으로 진화할 기회가 보인다. 아래는 현재 설계의 약점이 아니라, 다음 단계로 나아가는 데 필요한 구체적 아키텍처 강화다.

균열 1 · 에이전트 자신이 단일 실패 지점이다

“행복 경로” 시험에서 에이전트의 로직은 건전했고 실행은 흠잡을 데 없었다. 그러나 실제 운영 환경에서 외부 시스템은 믿을 수 없다. 예컨대 run_credit_check 도구 안의 API 호출이 일시적 네트워크 문제로 실패하면, 에이전트 전체 실행이 처리되지 않은 오류와 함께 멈춘다. 이 설계는 의존성의 일시적 실패를 고려하지 않는다.

→ 나아갈 길: 장애 격리
전담 CreditCheckAgent가 이 상호작용만 책임진다. 이 전문 에이전트는 지수 백오프를 동반한 자동 재시도 같은 자체 오류 처리 로직을 갖출 수 있다. 끝내 실패하더라도 오류가 그 하위 시스템 안에 봉쇄되어, 주 오케스트레이터가 전체 워크플로를 무너뜨리는 대신 폴백 계획을 결정할 수 있다. 백업 신용평가기관 도구를 부르거나, Human-in-the-Loop 패턴으로 사건 전체를 에스컬레이션하는 식이다.

균열 2 · 하나의 프롬프트가 여러 업무 관심사를 뒤섞는다

핵심 로직이 agent_instructions 변수 하나에 담겨 있다. 이 FCoT 프롬프트는 강력하고 중앙화된 산출물이지만 여러 업무 관심사를 뒤섞는다. 서류 검증 규칙이 위험 평가 정책, 규제 준수 점검과 같은 지시 집합 안에 있다. 신용위험 부서가 점수 로직 갱신을 요청하면 개발자가 이 크고 복잡한 프롬프트를 조심스럽게 편집해야 하고, 준수나 검증 로직을 실수로 망가뜨릴 상당한 위험을 진다.

→ 나아갈 길: 관심사의 분리
에이전트 하나를 전문가 팀으로 분해한다. RiskAssessmentAgent는 위험 분석에만 집중하는 훨씬 짧고 단순한 프롬프트를 갖고, 신용위험 팀이 독립적으로 소유하고 갱신할 수 있다. ComplianceAgent법무·준수 부서가 정의하고 유지하는 지시로 통치된다. 이 모듈성이 시스템의 각 부분을 독립적이고 안전하게 진화하게 한다.

균열 3 · 인지 용량에는 한계가 있다

FCoT 프롬프트는 정의된 4단계 프로세스에서는 매우 잘 작동한다. 그러나 단일 LLM의 인지 용량은 유한하다. 사기 탐지, 담보 평가, 보험 검증 국면을 더해 워크플로를 10~15단계로 늘려야 한다면 프롬프트가 극도로 길고 복잡해진다. 긴 컨텍스트 창에 파묻힌 앞선 지시를 놓치는 “한가운데서 잃어버리는” 문제의 위험이 커진다.

→ 나아갈 길: 인지 노동의 분업
잘 운영되는 사람 팀처럼 나눈다. 감독자 패턴의 구현인 OrchestratorAgent는 사기 탐지의 세부를 알 필요가 없다. 언제 FraudDetectionAgent에 위임할지만 알면 된다. 각 전문 에이전트는 자기 도메인에 극도로 집중된 짧고 단순한 프롬프트로 일한다. 인지 부하가 줄고 정확도와 신뢰성이 오른다.

다만 이것을 실무에서 실현하려면 표준화된 메시지 스키마와 공유 상태 객체(데이터 계약)를 강제해야 한다. 그래야 새 에이전트가 맞춤 통합 로직 없이 기존 생태계와 매끄럽게 상호운용된다.

The reframe

균열은 결함이 아니라 다음 설계의 좌표다

이 세 가지는 현재 설계의 약점이 아니라 더 큰 회복력과 유지보수성과 복잡성 처리 능력을 여는 데 필요한 아키텍처 강화다. 단일체 에이전트를 성공시켜 봤기에 비로소 정확히 어디를 갈라야 하는지가 보인다.