LLM WikiAccess-protected knowledge portal

WIKI

LlamaIndex Workflows 1.0: 이벤트 주도 에이전트 워크플로우의 독립화와 llama-deploy 프로덕션 배포 아키텍처

왜 지금 봐야 하나 LlamaIndex Workflows 1.0은 2026년 6월 22일에 공개됐다. 표면적으로는 기존 LlamaIndex 코어 안에 있던 워크플로우 기능을 별도 패키지로 분리한 것처럼 보이지만, 세 가지 구조적 전환이 들어 있다. 워크플로우 런타임이 llama index 메인 패키지 의존성에서 독립해 llama index workflows 라는 독자 패키지와 저장소를 가지게 됐다. Typed State와 Re

경로human/study/content/ai-frontier/36-llamaindex-workflows-1-0-event-driven-agentic-deploy.md
카테고리Study
태그#agentic #ai-review #deploy #driven #event #study #workflows

왜 지금 봐야 하나

LlamaIndex Workflows 1.0은 2026년 6월 22일에 공개됐다. 표면적으로는 기존 LlamaIndex 코어 안에 있던 워크플로우 기능을 별도 패키지로 분리한 것처럼 보이지만, 세 가지 구조적 전환이 들어 있다.

이 세 변화는 하나의 방향을 가리킨다. RAG·멀티에이전트 파이프라인을 전체 LlamaIndex 의존성 없이 경량으로 구성하고, 프로덕션에서는 분산 런타임으로 올리는 경로가 처음으로 공식화됐다. RAG 파이프라인을 직접 운영하거나 멀티스텝 에이전트 워크플로우를 설계하는 팀이라면, 이 분리가 "문서 업데이트"가 아닌 배포 구조 결정이다.


핵심 변화 한눈에 보기

1.0 이전1.0 이후운영 영향
패키지 경계llama-index 코어 내장llama-index-workflows 독립 패키지의존성 최소화; import 경로 마이그레이션 필요
상태 타입 안전비구조화 dict 기반TypedDict / Pydantic typed state정적 분석으로 키 오타·타입 불일치 조기 탐지
리소스 주입생성자 의존 또는 글로벌@inject dynamic injectionDB 클라이언트·설정을 Step에 느슨하게 전달
병렬 실행수동 구현이벤트 팬아웃으로 자연스럽게 구성동일 이벤트 타입을 수신하는 Step이 병렬로 실행
프로덕션 서빙외부 방식 자체 구현llama-deploy: Control Plane + MQRedis/Kafka/RabbitMQ 위에 분산 서비스로 배포

Workflows 1.0 아키텍처: 이벤트, 스텝, 컨텍스트

LlamaIndex Workflows 1.0: 이벤트 주도 실행과 llama-deploy 분산 배포 단일 프로세스 (llama-index-workflows) StartEvent 워크플로우 트리거 Step: retrieve @step async def query → retrieved_nodes RetrieveEvent 커스텀 이벤트 라우팅 Step: generate @step async def nodes → LLM 합성 StopEvent 최종 결과 반환 Context ctx.set / ctx.get Step 간 공유 상태 Typed State TypedDict / Pydantic 정적 타입 안전성 Resource Injection @inject decorator DB client, Config 주입 llama-deploy (프로덕션 런타임) Control Plane REST API 진입점 Orchestrator StateStorage ServiceMetadata Message Queue Redis / Kafka RabbitMQ / AWS SQS 워크플로우별 독립 큐 Workflow Services Workflow A (RAG) Workflow B (Agent) Workflow C (Pipeline) 각각 독립 마이크로서비스 배포 Workflows 1.0 설계 원칙 단일 프로세스: Step은 이벤트를 받아 이벤트를 발행한다. 같은 이벤트 타입을 수신하는 여러 Step이 병렬로 실행된다. 프로덕션: llama-deploy가 워크플로우를 독립 서비스로 격리하고, Control Plane이 라우팅·상태·큐를 중재한다.
LlamaIndex Workflows 1.0: 단일 프로세스 실행 구조와 llama-deploy 분산 배포 경계

1. 패키지 분리: 무엇이 달라지나

Workflows 1.0의 첫 번째 변화는 설치 방식이다.

# 이전: llama-index 전체를 설치해야 워크플로우 사용 가능
pip install llama-index

# 1.0 이후: 워크플로우만 독립 설치 가능
pip install llama-index-workflows

TypeScript도 마찬가지다.

npm install @llamaindex/workflow-core

이 분리가 중요한 이유는 단순한 패키지 이름 변경이 아니기 때문이다. llama-index 메인 패키지는 벡터 스토어 커넥터, 임베딩 모델, 데이터 로더 등 수십 개의 의존성을 끌고 온다. 워크플로우 오케스트레이션 로직만 필요한 경우에는 이 의존성이 불필요한 설치 비용과 충돌 가능성으로 이어진다.

독립 패키지는 다음을 의미한다.

기존 코드 마이그레이션

1.0 이전 코드를 사용 중이라면 import 경로가 달라진다.

# 이전
from llama_index.core.workflow import Workflow, StartEvent, StopEvent, step

# 1.0 이후
from llama_index.workflows import Workflow, StartEvent, StopEvent, step

공식 패키지 이름이 바뀌었으므로, CI 파이프라인에서 requirements.txtpyproject.toml의 의존성도 함께 갱신해야 한다.


2. 이벤트·스텝·컨텍스트: 핵심 실행 모델

Workflows 1.0의 실행 모델은 세 개의 원시 요소로 이루어진다.

이벤트(Event)

이벤트는 워크플로우 안에서 데이터를 운반하는 메시지다. Pydantic 모델로 정의한다.

from llama_index.workflows import Event

class RetrieveEvent(Event):
    nodes: list[str]

class GenerateEvent(Event):
    context: str
    query: str

StartEventStopEvent는 프레임워크가 제공하는 특수 이벤트다. StartEvent는 워크플로우 실행을 시작하고, StopEvent는 최종 결과를 반환한다. 그 사이의 모든 이벤트는 커스텀으로 정의한다.

스텝(Step)

스텝은 이벤트를 받아 처리하고 새 이벤트를 발행하는 비동기 함수다.

from llama_index.workflows import Workflow, step, StartEvent, StopEvent, Context

class RAGWorkflow(Workflow):
    @step
    async def retrieve(self, ctx: Context, ev: StartEvent) -> RetrieveEvent:
        # 벡터 검색 수행
        query = ev.query
        nodes = await self.retriever.aretrieve(query)
        return RetrieveEvent(nodes=[n.text for n in nodes])

    @step
    async def generate(self, ctx: Context, ev: RetrieveEvent) -> StopEvent:
        context_str = "\n".join(ev.nodes)
        response = await self.llm.acomplete(f"{context_str}\n\nQuery: {ev.query}")
        return StopEvent(result=str(response))

같은 이벤트 타입을 수신하는 Step이 여럿이면 병렬로 실행된다. 이 특성이 Workflows의 핵심이다. 예를 들어 RetrieveEvent를 수신하는 Step이 두 개 있으면, 두 Step이 동시에 실행되고 각자 다른 이벤트를 발행할 수 있다.

컨텍스트(Context)

Context는 워크플로우 실행 전체에 걸쳐 공유되는 상태 저장소다.

@step
async def retrieve(self, ctx: Context, ev: StartEvent) -> RetrieveEvent:
    # 원본 쿼리를 컨텍스트에 저장
    await ctx.set("original_query", ev.query)
    ...

@step
async def generate(self, ctx: Context, ev: RetrieveEvent) -> StopEvent:
    # 이전 스텝에서 저장한 값 꺼내기
    query = await ctx.get("original_query")
    ...

이 구조가 중요한 이유는 Step 간에 직접 함수 호출이 없기 때문이다. Step은 서로 이벤트를 통해 소통하고, 공유가 필요한 상태는 Context를 경유한다. 이 덕분에 Step의 실행 순서를 이벤트 타입만으로 선언할 수 있고, 동시 실행과 브랜치 분기도 자연스럽게 표현된다.


3. Typed State와 Resource Injection

1.0에서 안정화된 두 기능이다.

Typed State

Typed State는 Context에 저장하는 값의 구조를 미리 선언한다.

from typing import TypedDict

class WorkflowState(TypedDict):
    query: str
    retrieved_nodes: list[str]
    response: str

class RAGWorkflow(Workflow):
    @step
    async def retrieve(
        self, ctx: Context[WorkflowState], ev: StartEvent
    ) -> RetrieveEvent:
        # state는 이제 타입이 지정되어 있다
        ctx.data["query"] = ev.query
        ...

Pydantic v2 기반 Workflow도 지원된다. 중요한 효과는 두 가지다.

  1. 잘못된 키 이름이나 타입 불일치를 정적 분석(mypy, Pyright)으로 실행 전에 잡는다.
  2. 팀원이 워크플로우 상태 구조를 코드에서 바로 읽을 수 있다.

Resource Injection

Resource Injection은 DB 클라이언트·설정 객체 같은 인프라 의존성을 Step에 주입한다.

from llama_index.workflows import inject

class RAGWorkflow(Workflow):
    @step
    async def retrieve(
        self,
        ctx: Context,
        ev: StartEvent,
        db: VectorStoreClient = inject(),    # 주입된 DB 클라이언트
        config: AppConfig = inject(),         # 주입된 설정 객체
    ) -> RetrieveEvent:
        nodes = await db.search(ev.query, top_k=config.top_k)
        return RetrieveEvent(nodes=[n.text for n in nodes])

실행 시에는 inject_resource()로 주입할 객체를 등록한다.

from llama_index.workflows import inject_resource

workflow = RAGWorkflow()
inject_resource(workflow, db=VectorStoreClient(url=settings.DB_URL))
inject_resource(workflow, config=AppConfig(top_k=5))
result = await workflow.run(query="...")

이 패턴은 테스트 시에 Mock 객체를 주입하기 쉽게 만든다. 기존 방식처럼 생성자에 의존성을 하드코딩하거나 글로벌 변수에 두지 않아도 된다.


4. llama-deploy: 분산 실행 아키텍처

단일 프로세스 실행으로 충분한 경우도 있지만, 다음 상황에서는 분산 배포가 필요하다.

이때 쓰는 것이 llama-deploy다.

핵심 구성 요소

llama-deploy는 세 요소로 이루어진다.

Control Plane: 워크플로우 실행 요청을 받아 서비스에 라우팅하는 REST API 서버다. Orchestrator, StateStorage, ServiceMetadata를 내장한다. 여러 워크플로우 서비스를 같은 Control Plane에 등록할 수 있다.

Message Queue: Control Plane과 워크플로우 서비스 간 비동기 통신을 담당한다. Redis, Kafka, RabbitMQ, AWS SQS를 플러그인 방식으로 교체할 수 있다. 워크플로우별로 독립된 큐를 사용하므로 한 워크플로우의 장애가 다른 워크플로우로 전파되지 않는다.

Workflow Services: 실제 Workflow 인스턴스를 실행하는 프로세스다. Control Plane에 등록하면 Message Queue를 통해 실행 요청을 받는다. 서비스를 여러 개 띄워 수평 확장하거나, 특정 서비스만 재시작할 수 있다.

배포 예시

# deploy.py
from llama_deploy import deploy_workflow, WorkflowServiceConfig, ControlPlaneConfig

await deploy_workflow(
    workflow=RAGWorkflow(),
    workflow_config=WorkflowServiceConfig(
        host="0.0.0.0",
        port=8002,
        service_name="rag-workflow",
    ),
    control_plane_config=ControlPlaneConfig(
        host="0.0.0.0",
        port=8000,
    ),
)

실행 요청은 Control Plane의 REST API로 전달한다.

# 워크플로우 실행 요청
curl -X POST http://localhost:8000/workflows/run \
  -H "Content-Type: application/json" \
  -d '{"query": "LlamaIndex Workflows란 무엇인가?"}'

5. 병렬 실행과 브랜치 분기 패턴

Workflows 1.0의 이벤트 라우팅 모델은 몇 가지 패턴을 자연스럽게 구현한다.

팬아웃(Fan-out): 여러 소스를 동시에 검색

class MultiSourceRAG(Workflow):
    @step
    async def start_parallel_retrieve(
        self, ctx: Context, ev: StartEvent
    ) -> RetrieveFromDBEvent | RetrieveFromAPIEvent:
        # 두 이벤트를 동시에 발행해 병렬 실행을 유도
        await ctx.send_event(RetrieveFromDBEvent(query=ev.query))
        await ctx.send_event(RetrieveFromAPIEvent(query=ev.query))
        return None  # 이벤트를 모아서 다음 스텝에서 처리

    @step
    async def retrieve_from_db(self, ctx: Context, ev: RetrieveFromDBEvent) -> RetrievedEvent:
        nodes = await self.vector_db.search(ev.query)
        return RetrievedEvent(source="db", nodes=nodes)

    @step
    async def retrieve_from_api(self, ctx: Context, ev: RetrieveFromAPIEvent) -> RetrievedEvent:
        results = await self.api_client.search(ev.query)
        return RetrievedEvent(source="api", nodes=results)

    @step(num_workers=2)  # 두 RetrievedEvent를 모두 받을 때까지 대기
    async def merge_and_generate(
        self, ctx: Context, ev: RetrievedEvent
    ) -> StopEvent:
        all_nodes = ev.nodes  # 각 이벤트마다 축적됨
        ...

루프(Loop): 결과가 기준을 충족할 때까지 반복

class IterativeRAG(Workflow):
    @step
    async def check_quality(
        self, ctx: Context, ev: GenerateEvent
    ) -> StopEvent | RetryEvent:
        if ev.score >= 0.8:
            return StopEvent(result=ev.response)
        else:
            # 기준 미달이면 다시 retrieve 단계로
            return RetryEvent(query=ev.query, attempt=ev.attempt + 1)

6. 운영 체크리스트

Workflows 1.0을 도입하거나 기존 코드를 마이그레이션할 때 확인할 항목이다.

패키지 마이그레이션

Typed State 적용

Resource Injection

llama-deploy (분산 배포)

스트리밍과 Human-in-the-Loop


결론

LlamaIndex Workflows 1.0이 중요한 이유는 기능 수가 아니라 경계 재배치 때문이다.

이 세 변화는 데모에서는 잘 보이지 않지만, 운영에서는 바로 드러난다. 의존성 충돌, 타입 오류, 프로덕션 확장 방식이 모두 이 결정에서 나오기 때문이다.

References