핵심 개념: 에이전트란 무엇인가 (What are agents?)

핵심 개념: 에이전트란 무엇인가

에이전트 시스템이란

AI를 효율적으로 쓰는 어떤 시스템이든 LLM에게 현실 세계에 접근할 방법을 줘야 해요. 예를 들어 외부 정보를 얻으려고 검색 도구를 호출하거나, 작업을 풀기 위해 특정 프로그램에 작용하는 식이죠. 다시 말해 LLM은 **agency(행동 주체성)**를 가져야 해요. 에이전트 프로그램이 바로 LLM을 외부 세계로 이어주는 관문이에요.

[!TIP] AI 에이전트는 LLM 출력이 워크플로우를 제어하는 프로그램이에요.

LLM을 쓰는 어떤 시스템이든 LLM 출력을 코드에 통합하게 돼요. LLM의 입력이 코드 워크플로우에 미치는 영향력이 곧 그 시스템 안에서 LLM의 agency 수준이에요.

여기서 주의할 점이 있어요. 이 정의에서 '에이전트'는 0 또는 1로 딱 갈리는 이산적인 개념이 아니에요. 대신 agency는 연속적인 스펙트럼 위에서 움직여요. 워크플로우에 LLM에게 더 많이 힘을 줄수록 agency가 높아지죠.

아래 표를 보면 시스템마다 agency가 얼마나 다르게 변하는지 알 수 있어요.

Agency 수준 설명 이름 예시 코드
☆☆☆ LLM 출력이 프로그램 흐름에 영향 없음 단순 처리기 process_llm_output(llm_response)
★☆☆ LLM 출력이 if/else 분기를 제어 라우터 if llm_decision(): path_a() else: path_b()
★★☆ LLM 출력이 함수 실행을 제어 툴 콜 run_function(llm_chosen_tool, llm_chosen_args)
★★☆ LLM 출력이 반복과 프로그램 이어가기를 제어 멀티스텝 에이전트 while llm_should_continue(): execute_next_step()
★★★ 하나의 에이전트 워크플로우가 다른 에이전트 워크플로우를 시작 멀티에이전트 if llm_trigger(): execute_agent()
★★★ LLM이 코드로 작용, 자신의 도구를 정의하거나 다른 에이전트 시작 코드 에이전트 def custom_tool(args): ...

멀티스텝 에이전트는 이런 코드 구조를 가져요.

memory = [user_defined_task]
while llm_should_continue(memory): # 이 루프가 멀티스텝 부분
    action = llm_get_next_action(memory) # 이 부분이 툴 콜
    observations = execute_action(action)
    memory += [action, observations]

이 에이전트 시스템은 루프로 돌면서 각 스텝마다 새 액션을 실행해요. 액션에는 사전 정의된 도구(그냥 함수) 호출이 포함될 수 있고요. 관찰 결과가 주어진 작업을 풀 수 있는 만족스러운 상태에 도달했다는 게 분명해질 때까지 반복하죠.

에이전트를 써야 할 때 / 피해야 할 때

에이전트는 앱의 워크플로우를 LLM이 정해야 할 때 유용해요. 하지만 자주 과합니다. 핵심 질문은 이거예요. "내 손에 있는 작업을 효율적으로 풀려면 정말 워크플로우의 유연성이 필요한가?"

사전 정의된 워크플로우가 너무 자주 부족해진다면 더 많은 유연성이 필요하다는 뜻이에요. 서핑 여행 웹사이트의 고객 요청 처리 앱을 만드는 경우를 예로 들어볼게요.

요청이 (사용자 선택 기준으로) 두 종류로 나뉜다는 걸 미리 알 수 있다고 해볼게요. 두 경우 각각에 사전 정의된 워크플로우가 있다고요.

  1. 여행에 대한 지식을 원한다면 → 검색창으로 지식 베이스를 검색하게 하기
  2. 영업 담당자와 이야기하고 싶다면 → 문의 폼에 입력하게 하기

이 결정적(deterministic) 워크플로우가 모든 질의를 처리한다면 그냥 전부 코드로 짜는 게 좋아요. 그러면 예측 불가능한 LLM이 워크플로우에 개입하면서 생길 수 있는 오류 위험 없이 100% 신뢰할 수 있는 시스템이 되거든요. 단순함과 견고함을 위해 에이전트 동작을 되도록 쓰지 않는 쪽으로 정규화하는 게 권장돼요.

그런데 워크플로우를 그렇게 미리 정할 수 없다면 어떨까요? 예를 들어 사용자가 이렇게 묻는 경우를 생각해봐요. "월요일에 갈 수 있는데 여권을 두고 와서 수요일로 늦어질지도 몰라요. 화요일 아침에 저와 물건을 서핑하러 데려가주면서 취소 보험도 들 수 있을까요?" 이 질문은 많은 요인에 걸쳐 있어서, 위의 사전 정의된 기준 중 어느 것도 이 요청에 부합하지 않을 가능성이 높아요. 사전 정의된 워크플로우가 자주 부족해진다면 유연성이 더 필요하다는 뜻이고, 이때 에이전트 구조가 도움이 돼요.

위 예시라면 날씨 API(예보), Google Maps API(이동 거리 계산), 직원 가용성 대시보드, 지식 베이스의 RAG 시스템에 접근할 수 있는 멀티스텝 에이전트를 만들면 돼요.

최근까지 컴퓨터 프로그램은 사전 정의된 워크플로우에 제한돼 있었고, 복잡성을 다루려고 if/else 스위치를 쌓아 올리는 방식이었죠. 그래서 "이 숫자들의 합을 계산해"나 "이 그래프에서 최단 경로를 찾아"처럼 극도로 좁은 작업에만 집중했어요. 하지만 실제로 대부분의 실생활 작업은 위 여행 예시처럼 사전 정의된 워크플로우에 들어맞지 않아요. 에이전트 시스템이 프로그램에게 현실 세계 작업의 넓은 세계를 열어준 거죠.

smolagents일까

체인(chain)이나 라우터 같은 저수준 에이전트 용례는 코드를 전부 직접 짤 수 있어요. 그러면 시스템을 더 잘 제어하고 이해할 수 있어서 훨씬 낫겠죠.

하지만 LLM이 함수를 호출하게 하는 것("툴 콜")이나 LLM이 while 루프를 돌리게 하는 것("멀티스텝 에이전트") 같은 더 복잡한 동작으로 들어가면, 몇몇 추상화가 필요해져요.

  • 툴 콜에는 에이전트 출력을 파싱해야 하므로, 출력에 Thought: I should call tool 'get_weather'. Action: get_weather(Paris). 같은 사전 정의된 형식이 필요하고, 이를 사전 정의된 함수로 파싱하며, 시스템 프롬프트가 LLM에게 이 형식을 알려줘야 해요.
  • LLM 출력이 루프를 결정하는 멀티스텝 에이전트에서는 마지막 루프 반복에서 무슨 일이 있었느냐에 따라 LLM에게 다른 프롬프트를 줘야 하니, 일종의 메모리가 필요해요.

이 두 예시만으로도 필요한 요소가 몇 가지 드러나요.

  • 당연히 시스템을 구동하는 엔진 역할의 LLM
  • 에이전트가 접근할 수 있는 도구 목록
  • LLM을 에이전트 로직으로 이끄는 시스템 프롬프트 — ReAct 루프(Reflection → Action → Observation), 사용 가능한 도구, 툴 콜 형식 등
  • 시스템 프롬프트가 지시한 형식대로 LLM 출력에서 툴 콜을 추출하는 파서
  • 메모리

게다가 결정에 LLM에게 공간을 줬으니 분명 실수도 하겠죠. 그래서 오류 로깅과 재시도 메커니즘도 필요해요. 이 모든 요소는 잘 작동하는 시스템이 되려면 서로 긴밀하게 결합되어야 해요. 그래서 이 모든 걸 함께 동작시키는 기본 빌딩 블록이 필요하다고 판단한 거예요.

코드 에이전트(Code agents)

멀티스텝 에이전트에서 각 스텝마다 LLM은 외부 도구 호출 형태의 액션을 쓸 수 있어요. 이 액션을 쓰는 일반적인 형식(Anthropic, OpenAI 등이 사용)은 "도구 이름과 사용할 인자의 JSON으로 액션을 작성하고, 파싱해서 어떤 도구를 어떤 인자로 실행할지 알아내는 것"의 다양한 변형이에요.

하지만 여러 연구 논문이 LLM의 액션을 코드 스니펫으로 쓰는 게 더 자연스럽고 유연하다는 걸 보여줬어요. 그 이유는 단순해요. 우리는 코드 언어를 컴퓨터가 수행하는 액션을 표현하도록 특별히 설계했기 때문이에요. 다시 말해 우리 에이전트는 사용자 문제를 풀기 위해 프로그램을 쓰게 되는데, 파이썬 블록으로 쓰는 게 더 쉬울까요, JSON으로 쓰는 게 더 쉬울까요?

Executable Code Actions Elicit Better LLM Agents에서 가져온 아래 그림이 코드로 액션을 쓰는 것의 장점 몇 가지를 보여줘요.

JSON 같은 스니펫보다 코드로 액션을 쓰면 이런 점이 더 좋아져요.

  • 합성성(Composability) — 파이썬 함수를 정의하듯 JSON 액션을 서로 중첩하거나 나중에 재사용할 JSON 액션 세트를 정의할 수 있을까요?
  • 객체 관리(Object management)generate_image 같은 액션의 출력을 JSON으로 어떻게 저장하나요?
  • 일반성(Generality) — 코드는 컴퓨터가 할 수 있는 무엇이든 표현하도록 만들어진 언어예요.
  • LLM 훈련 데이터에서의 표현 — 이미 많은 고품질 코드 액션이 LLM 훈련 데이터에 포함되어 있어서, LLM이 이미 이에 대해 훈련돼 있어요!

출처 인용

더 알아보기