ElGraph.Orchestration (ElGraph v0.3.0)

Copy Markdown View Source

멀티 에이전트 오케스트레이션 패턴 템플릿 (SPEC §6, R5; 트렌드 보고서 Tier 2.5).

빌딩 블록은 기존 프리미티브뿐이다 — 조건부 엣지 + 노드(워커) + :messages 채널. 신규 추상화 없이, 자주 쓰는 멀티 에이전트 형태를 미리 조립한 컴파일된 그래프로 제공한다.

supervisor (오케스트레이터-워커)

오케스트레이터 노드가 LLM으로 다음 워커를 고르고(또는 종료), 조건부 엣지가 해당 워커로 라우팅한다. 워커는 결과를 :messages에 append하고 오케스트레이터로 복귀한다. max_steps로 라운드 폭주를 막는다.

workers = [
  %{name: :researcher, description: "gathers facts", run: &MyApp.research/2},
  %{name: :writer, description: "writes the answer", run: &MyApp.write/2}
]
graph = ElGraph.Orchestration.supervisor(llm, workers, system: "...")
ElGraph.invoke(graph, %{messages: [ElGraph.LLM.user("Write a report")]})

magentic (task-ledger)

magentic/3 is supervisor/3 plus a task ledger and a stall guard (magentic-one pattern). The ledger (:ledger channel) records every worker the orchestrator chooses; when the same worker is picked more than :max_stalls times in a row the run is forced to terminate, defusing the classic "agent loops forever" failure.

Cross-agent handoff

In both templates worker results flow back to the orchestrator purely via the shared :messages channel — a worker appends its output and the orchestrator reads the full transcript on its next turn. Direct, out-of-band handoff between agents is also expressible without new orchestration code: have a worker emit on the signal bus (ElGraph.Skills.SignalReAct :emit) and have the target worker subscribe — the bus delivers the payload independently of the :messages transcript. The orchestration templates here deliberately stick to :messages; the signal bus is the escape hatch when you need agent-to-agent side-channels.

Summary

Types

magentic 작업 원장 — 작업(task) + 누적 사실(facts) + 진행(chosen/stalls). (magentic-one 패턴)

워커 스펙: 노드 이름(atom) + 설명 + 실행 함수((state, ctx) -> 상태 업데이트).

Functions

group-chat 그래프를 빌드한다 — 스피커 선택 정책으로 매 턴 발화자를 고른다.

magentic(task-ledger) 그래프를 빌드한다 — supervisor/3 + 작업 원장 + 정체(stall) 가드.

오케스트레이터-워커 그래프를 빌드한다.

Types

ledger()

@type ledger() :: %{
  task: String.t() | nil,
  chosen: [atom()],
  facts: [String.t()],
  stalls: non_neg_integer()
}

magentic 작업 원장 — 작업(task) + 누적 사실(facts) + 진행(chosen/stalls). (magentic-one 패턴)

worker()

@type worker() :: %{
  name: atom(),
  description: String.t(),
  run: (map(), ElGraph.Ctx.t() -> map())
}

워커 스펙: 노드 이름(atom) + 설명 + 실행 함수((state, ctx) -> 상태 업데이트).

Functions

group_chat(agents, opts \\ [])

@spec group_chat(
  [worker()],
  keyword()
) :: ElGraph.Graph.t()

group-chat 그래프를 빌드한다 — 스피커 선택 정책으로 매 턴 발화자를 고른다.

opts: :select((state -> agent_name | :end) 순수 정책 — 미지정 시 :rounds회 라운드로빈), :rounds(기본 6), :max_steps.

magentic(llm, workers, opts \\ [])

@spec magentic({module(), term()}, [worker()], keyword()) :: ElGraph.Graph.t()

magentic(task-ledger) 그래프를 빌드한다 — supervisor/3 + 작업 원장 + 정체(stall) 가드.

오케스트레이터가 매 턴 다음 워커를 고르고, 선택을 원장(:ledger.chosen)에 기록한다. 동일 워커를 :max_stalls회를 초과해 연속 선택하면(기본 2) 강제 종료한다 — "에이전트 무한 루프" 실패를 막는 결정적·테스트 가능한 가드다.

opts: :system(시스템 프롬프트 보강), :max_stalls(기본 2), :max_steps(기본 25).

supervisor(llm, workers, opts \\ [])

@spec supervisor({module(), term()}, [worker()], keyword()) :: ElGraph.Graph.t()

오케스트레이터-워커 그래프를 빌드한다.

opts: :system(오케스트레이터 시스템 프롬프트 보강), :max_steps(기본 25).