에이전트가 비동기로 넘어가고 있다

TMT

https://www.langchain.com/blog/agents-are-going-async

큰 프로젝트를 맡길 때마다 자취를 감췄다가 일이 다 끝나야 돌아오는 비서실장을 떠올려 보세요. 아마 당장 해고하실 겁니다. 유능한 비서실장은 언제든 연락이 닿고 중간중간 상황을 알려 주면서도, 뒤에서는 프로젝트를 계속 진행시킵니다. 에이전트에게 더 큰 목표를 맡길수록 에이전트에게도 같은 두 가지 특성이 필요합니다. 에이전트는 새 지시를 받으면 그전까지의 맥락을 모두 반영해 곧바로 움직이는 반응성과, 여러 큰 작업을 동시에 진척시키는 생산성을 함께 갖춰야 합니다.

그동안 에이전트를 만들 때 써 온 아키텍처로는 두 요구를 모두 충족할 수 없습니다. 표준 ReAct 루프에서는 오래 걸리는 도구 호출이 일어날 때마다 루프가 막히고, 다른 작업은 모두 그 뒤에 줄을 섭니다. 그래서 지금 생태계는 사용자 곁에 머무는 에이전트 하나와 오래 걸리는 일을 맡는 백그라운드 워커들로 역할을 나누는 쪽으로 모이고 있습니다. Claude Code는 기본적으로 서브에이전트를 백그라운드에서 실행하고, OpenAI의 dots는 여러 일을 동시에 진척시키려고 작업을 백그라운드 워커에 넘깁니다. DeepSeek Harness는 사람과 대화하는 라우터와 작업을 병렬로 실행하는 백그라운드 워커를 중심으로 슈퍼 에이전트를 만들었습니다.

저희는 이 패턴을 비동기 서브에이전트라는 이름으로 Deep Agents에 도입합니다. 오케스트레이터는 사용자가 언제든 말을 걸 수 있는 상태로 남아 대화의 맥락을 정리하고, 비동기 서브에이전트는 백그라운드 워커로 실행되다가 일을 마치면 결과를 보고합니다. 그러면 에이전트가 맡을 수 있는 일의 양은 루프 하나의 길이가 아니라 백그라운드 워커의 수에 따라 늘어납니다.

비동기 서브에이전트의 작동 방식

에이전트는 루프 안에서 실행됩니다. 모델이 도구를 호출하고, 결과를 기다린 뒤, 다음에 할 일을 정합니다. 지금까지는 도구와 서브에이전트가 결과를 돌려줄 때까지 이 루프를 막았습니다. 느린 호출을 하나씩 차례로 await하는 코드와 같은 방식입니다. 비동기 서브에이전트는 asyncio.create_task로 시작한 태스크나 아직 await하지 않은 Promise처럼 동작합니다. 호출은 곧바로 반환되고, 작업은 백그라운드에서 독립적으로 실행되며, 결과는 나중에 돌아옵니다.

Image

오케스트레이터는 작업을 비동기 서브에이전트에 넘기고, 자신은 계속 사용자와 대화할 수 있는 상태로 남습니다.

비동기 작업이 가능하려면 시스템이 두 가지 특성을 갖춰야 합니다.

  • 사용자가 오케스트레이터에게 말을 걸 수 있습니다. 오케스트레이터는 사용자가 직접 상호작용하는 에이전트입니다. 이제 서브에이전트가 오케스트레이터의 루프를 붙잡고 있지 않으므로, 사용자는 모든 서브에이전트가 끝나기를 기다릴 필요 없이 오케스트레이터 자신의 루프가 바쁘지 않을 때면 언제든 말을 걸 수 있습니다.
  • 작업이 서로를 막지 않고 병렬로 실행됩니다. 오케스트레이터는 한 묶음이 전부 돌아오기를 기다리지 않고, 일이 들어오는 대로 서브에이전트를 시작합니다.

오케스트레이터는 다섯 가지 도구로 비동기 서브에이전트를 관리합니다.

  • start_async_task는 서브에이전트를 별도 스레드에서 시작하고 곧바로 반환합니다.
  • check_async_task는 작업의 상태를 가져오고, 작업이 끝났으면 결과도 가져옵니다.
  • update_async_task는 실행 중인 작업에 새 지시를 보냅니다.
  • cancel_async_task는 실행 중인 작업을 중단합니다.
  • list_async_tasks는 모든 작업과 각 작업의 상태를 나열합니다.

실행 중인 서브에이전트의 방향을 바꾸는 일은 오케스트레이터에게 말을 거는 일과 별개입니다. 오케스트레이터가 update_async_task로 후속 지시를 보내면, 그 지시를 어떻게 처리할지는 해당 서브에이전트 정의의 update_strategy 필드가 정합니다.

  • "interrupt"(기본값)는 현재 실행을 멈추고 새 지시로 서브에이전트를 다시 시작합니다. "대신 Postgres를 써"처럼 방향을 바로잡을 때 쓰세요.
  • **"enqueue"**는 현재 실행이 끝날 때까지 후속 지시를 보류합니다. "다 끝나면 테스트를 돌려"처럼 할 일을 덧붙일 때 쓰세요.

알림의 작동 방식

끝나지 않은 작업은 모두 오케스트레이터의 시스템 프롬프트에 나열되므로, 오케스트레이터는 지금 무엇이 진행 중인지 항상 알고 있습니다. 오케스트레이터가 check_async_task로 직접 확인할 수도 있고, 작업이 스스로 결과를 보고하게 할 수도 있습니다.

  • 오케스트레이터가 바쁜 상태라면, 모델을 호출하기 전마다 끝나지 않은 작업을 확인해 실행 도중에 결과를 받아 옵니다.
  • 유휴 상태라면, 서브에이전트가 새 실행을 시작해 오케스트레이터를 깨웁니다.
  • 승인을 기다리며 일시 정지한 상태라면, 승인 요청은 그대로 두고 사용자가 답한 뒤에 결과가 도착합니다.
Image

비동기 서브에이전트는 오케스트레이터가 바쁘든, 유휴 상태든, 승인을 기다리며 멈춰 있든 결과를 보고합니다.

승인이 필요한 서브에이전트는 대기 중으로 표시되고, 사용자는 오케스트레이터를 방해하지 않고 그 서브에이전트의 스레드에서 직접 답합니다. Slack에서 쓰는 Managed Deep Agents에서는 승인 요청이 원래 스레드에 다시 올라옵니다.

저희는 이 패턴이 서브에이전트를 넘어 도구 전반으로 퍼질 것으로 봅니다. OpenAI는 최근 비동기 도구 호출 지원을 추가했습니다. 모델은 도구를 호출한 뒤에도 계속 작업하고, 애플리케이션은 나중 요청에서 결과를 돌려보내는 방식입니다.

비동기 서브에이전트 사용하기

비동기 서브에이전트는 Python과 JavaScript용 Deep Agents, 그리고 Managed Deep Agents에서 쓸 수 있습니다. Managed Deep Agents에서는 필드 하나만 지정하면 서브에이전트가 비동기로 동작합니다.

from managed_deepagents import define_deep_agent

agent = define_deep_agent(
    name="research-assistant",
    model="openai:gpt-5.5",
    subagents=[
        {
            "name": "researcher",
            "description": "Researches a topic in the background.",
            "system_prompt": "Research the topic and report your findings.",
            "async": True,
        },
    ],
)

Managed Deep Agents에서는 서빙되는 모든 에이전트가 범용 서브에이전트의 비동기 버전도 함께 받습니다. 그래서 위임한 작업을 포그라운드에서 실행할지 백그라운드에서 실행할지 런타임에 정할 수 있습니다.

작업 알림은 베타 기능이라 API가 아직 바뀔 수 있습니다. async-deep-agents 저장소에는 researcher와 coder 서브에이전트를 갖춘 슈퍼바이저가 Python과 TypeScript로 구현되어 있어 바로 실행해 볼 수 있습니다.

비동기 서브에이전트를 한번 써 보시고, GitHub 이슈나 포럼에서 의견을 들려주세요.

Edit this page

Search the archive

Find a page by title, or search the text inside one.