ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RAG 에이전트 완전 조립 — create_agent부터 동작 추적까지
    IT 2026. 6. 29. 21:00
    RAG 에이전트 완전 조립 — create_agent부터 동작 추적까지

    RAG 파이프라인을 구성하는 세 가지 재료가 있다. 문서를 저장한 벡터 DB에서 검색해 오는 리트리버, 그 리트리버를 LLM이 호출할 수 있는 형태로 포장한 도구(@tool), 그리고 질문의 의도에 따라 어떤 도구를 몇 번 쓸지 스스로 결정하는 에이전트. 이 세 가지를 하나로 묶는 접착제가 create_agent다.

    이 글에서는 create_agent로 에이전트를 조립하는 방법부터 시작해, 단일 카테고리 질문과 복합 카테고리 질문이 내부에서 어떻게 다르게 처리되는지, 그리고 messages 배열을 읽어 에이전트 동작을 추적하는 방법까지 순서대로 살펴본다.

    1. create_agent로 RAG 에이전트 조립

    에이전트를 만드는 데 필요한 최소 재료는 세 가지다. LLM(model), 도구 리스트(tools), 그리고 에이전트의 역할을 정의하는 system_prompt.

    # 에이전트 역할·말투·행동 기준을 한 문단으로 정의
    system_prompt = (
        "당신은 영화 전문가입니다. "
        "사용자 질문에 검색된 문서를 기반으로 이해하기 쉽게 답변하세요."
    )
    
    # 세 재료를 create_agent에 넘기면 ReAct 에이전트가 완성된다
    agent = create_agent(
        model=model,           # 추론을 담당하는 LLM
        tools=tools,           # @tool로 감싼 검색 도구 리스트
        system_prompt=system_prompt
    )
    

    create_agent는 내부적으로 LangGraph의 ReAct 그래프를 구성한다. 에이전트는 질문을 받으면 "어떤 도구를 써야 하는가"를 먼저 추론하고, 도구를 호출한 뒤 결과를 보고 최종 답변을 생성한다. 개발자가 이 분기 로직을 직접 짤 필요가 없다는 점이 핵심이다. system_prompt를 생략하면 에이전트가 답변의 톤·범위를 스스로 결정하므로, 영화 장르 특화 응용에서는 반드시 명시하는 것이 좋다.

    diagram

    세 입력이 합쳐져 ReAct 에이전트 하나가 만들어지는 구조다. system_prompt는 에이전트가 "어떤 맥락에서 답변해야 하는지"를 모든 추론 단계에 걸쳐 유지시켜 주는 역할을 한다. 빠뜨리면 에이전트가 범용 챗봇처럼 동작하므로 도메인 특화 응용에서는 필수다.

    2. system_prompt가 에이전트 동작에 미치는 영향

    system_prompt가 있을 때와 없을 때 에이전트의 동작 방식이 어떻게 달라지는지 비교해 보면, 단순한 문구 차이 이상의 영향이 있음을 알 수 있다.

    diagram

    두 경우를 나란히 보면 차이가 명확하다. system_prompt가 없는 에이전트는 도구를 쓸지 말지, 어떤 형식으로 답할지를 매 질문마다 즉흥적으로 결정한다. 반면 system_prompt가 있는 에이전트는 "검색된 문서를 기반으로 답변한다"는 기준이 고정되어 있어 응답 일관성이 높아진다. RAG 에이전트의 신뢰성은 검색 정확도만큼이나 system_prompt의 명확성에 달려 있다.

    3. 단일 카테고리 질문 처리 흐름

    "액션 영화 추천작을 알려줘"처럼 하나의 카테고리에만 관련된 질문은 에이전트가 도구를 한 번만 호출한다. 내부적으로는 네 개의 메시지가 순서대로 생성된다.

    diagram

    질문이 들어오면 에이전트는 우선 어떤 도구를 쓸지 결정하고 그 내용을 AIMessagetool_calls 필드에 기록한다. 그다음 실제 도구가 실행되어 검색 결과가 ToolMessage로 돌아오고, 에이전트가 그 내용을 읽어 최종 답변을 생성한다. "액션 영화 추천작을 알려줘"처럼 단일 카테고리 질문에서는 이 사이클이 한 번만 돈다. 놓치기 쉬운 함정은, 첫 번째 AIMessage가 최종 답변이 아니라는 점이다 — tool_calls가 있으면 아직 추론이 끝나지 않은 중간 단계다.

    4. 복합 카테고리 질문 처리 흐름 — 핵심 차별점

    "드라마와 SF 영화를 비교해줘"처럼 두 카테고리를 동시에 묻는 질문에서 에이전트의 진가가 드러난다. 개발자가 분기 로직을 작성하지 않아도 에이전트가 스스로 두 도구를 각각 호출한다.

    diagram

    단일 질문 흐름과 비교하면 ToolMessage가 두 개 생기는 것이 핵심 차이다. 에이전트는 하나의 AIMessage에서 두 도구 호출을 동시에 결정하고, 두 검색 결과가 모두 도착한 뒤에 비교 답변을 생성한다. 개발자가 "드라마 질문인지 SF 영화 질문인지"를 if-else로 분기할 필요가 없다. 단, 도구가 너무 많으면 에이전트가 불필요한 도구까지 호출하는 과잉 추론이 발생할 수 있으므로, 각 도구의 description을 명확하게 작성해 호출 범위를 좁혀 주어야 한다.

    5. 동작 추적 — messages 배열 읽기

    에이전트를 invoke하면 반환값의 messages 배열에 모든 실행 단계가 기록된다. 이 배열을 순서대로 읽으면 에이전트가 어떤 도구를 왜 선택했는지, 검색 결과가 무엇이었는지 전부 추적할 수 있다.

    for msg in response["messages"]:
        if hasattr(msg, 'tool_calls') and msg.tool_calls:
            print(f"도구 선택: {msg.tool_calls[0]['name']}")  # AIMessage with tool_calls = 추론 중간
        elif getattr(msg, 'type', '') == 'tool':
            print(f"검색 결과: {msg.content[:100]}...")
    print(f"최종 답변: {response['messages'][-1].content}")   # 마지막 AIMessage = 완료
    

    messages 배열에서 주의해야 할 메시지 타입은 세 가지다. tool_calls가 있는 AIMessage는 에이전트가 도구를 호출하기로 결정한 순간을 기록한다. ToolMessage는 실제 도구가 실행되어 검색 결과를 돌려준 순간이다. 그리고 배열의 마지막 AIMessage가 모든 검색 결과를 종합한 최종 답변이다. 디버깅 시 흔한 실수는 첫 번째 AIMessage를 최종 답변으로 착각하는 것 — tool_calls 유무를 먼저 확인해야 한다.

    diagram

    배열의 각 메시지 타입별 역할을 한눈에 보면, messages가 단순한 로그가 아니라 에이전트의 추론 과정 전체를 순서대로 담은 실행 기록임을 알 수 있다. tool_calls 유무로 "아직 실행 중"과 "완료"를 구분하고, 타입이 tool인 메시지들을 모으면 에이전트가 실제로 읽은 문서를 역추적할 수 있다.

    6. 전체 아키텍처 — 3개 레이어

    지금까지 살펴본 구성 요소들이 실제로 어떻게 연결되는지 전체 그림으로 정리해 보자. 문서에서 에이전트 답변까지 세 개의 레이어를 거친다.

    diagram

    세 레이어는 역할이 명확하게 분리되어 있다. 레이어 1은 "어디서 검색할 것인가"를 정의한다 — 문서를 잘게 쪼개고 벡터로 저장하는 일회성 사전 작업이다. 레이어 2는 "어떻게 검색 결과를 에이전트에 전달할 것인가"를 정의한다 — 리트리버에 @tool을 씌워 에이전트가 호출할 수 있는 단위로 만든다. 레이어 3은 "누가 판단하는가"를 담당한다 — create_agent가 LLM에 도구 목록과 역할을 부여해 자율 추론 주체를 만든다. 레이어를 분리해 두면 문서를 추가하거나 도구 설명을 바꾸는 변경이 에이전트 코드에 영향을 주지 않는다는 장점이 있다. 세 레이어 중 어느 하나를 바꾸더라도 나머지는 그대로 유지된다.

    여기까지가 RAG 에이전트의 완전한 조립 과정이다. 핵심을 한 줄로 요약하면 — 리트리버를 도구로 포장하고, 도구를 create_agent에 넘기면, 에이전트가 질문의 복잡도에 맞게 도구 호출 횟수와 순서를 스스로 결정한다. messages 배열은 그 모든 과정의 감사 로그다.


    이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.

Designed by Tistory.