멀티 에이전트

멀티 에이전트 (Multi-agent)

멀티 에이전트는 에이전트가 서브에이전트에게 작업을 위임하게 해 줘요. 각 서브에이전트는 자체 컨텍스트를 가지며 다른 서브에이전트와 병렬로 작업할 수 있어요. 메인 에이전트가 이들의 작업을 조율하고 결과를 합쳐요.

출처: 문서

본문

서브에이전트를 쓸 때 (When to use subagents)

서로 다른 문서를 검토하거나 실패의 서로 다른 원인을 조사하는 것 같은 독립적인 작업에 서브에이전트를 사용하세요. 각 작업에 명확한 질문과 기대 결과를 주세요.

짧은 작업과 의존적인 단계는 메인 에이전트에 두세요. 같은 파일을 편집하는 에이전트는 변경 사항을 서로 조율해야 해요.

멀티 에이전트 오케스트레이션 활성화하기 (Enable multi-agent orchestration)

세션을 만들 때 agent.multi_agent.enabled를 true로 설정하세요. 하네스가 서브에이전트를 만들고, 메시지하고, 기다리고, 중단하는 도구를 제공해요. 이 도구들을 직접 선언할 필요는 없어요.

이 예시는 두 서브에이전트에게 별도의 릴리스 노트를 검토하게 한 뒤 결과를 합쳐요. 환경이나 구성된 도구는 필요 없어요:

릴리스 노트 비교하기

import OpenAI from "openai";

const client = new OpenAI();

const events = await client.beta.agents.sessions.create({
  agent: {
    model: "gpt-6-astra",
    instructions:
      "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",
    multi_agent: { enabled: true, max_concurrent_subagents: 2 },
  },
  environment: { type: "none" },
  input:
    "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",
  stream: true,
});
for await (const event of events) {
  console.log(JSON.stringify(event));
}
from openai import OpenAI

client = OpenAI()

with client.beta.agents.sessions.create(
    agent={
        "model": "gpt-6-astra",
        "instructions": "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",
        "multi_agent": {"enabled": True, "max_concurrent_subagents": 2},
    },
    environment={"type": "none"},
    input="Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",
    stream=True,
) as events:
    for event in events:
        print(event.model_dump_json())
import (
	"context"
	"fmt"
	"github.com/openai/openai-go/v3"
)

ctx := context.Background()
client := openai.NewClient()
events := client.Beta.Agents.Sessions.NewStreaming(ctx, openai.BetaAgentSessionNewParams{Agent: openai.BetaAgentSessionNewParamsAgent{Model: openai.String("gpt-6-astra"),
	Instructions: openai.String("Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details."),
	MultiAgent: openai.MultiAgentConfigParam{Enabled: true,
		MaxConcurrentSubagents: openai.Int(2)}},
	Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},
	Input:       openai.BetaAgentSessionNewParamsInputUnion{OfString: openai.String("Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.")}})
defer events.Close()
for events.Next() {
	fmt.Println(events.Current().RawJSON())
}
if err := events.Err(); err != nil {
	panic(err)
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.beta.agents.MultiAgentConfigParam;
import com.openai.models.beta.agents.sessions.SessionCreateParams;

OpenAIClient client = OpenAIOkHttpClient.fromEnv();
try (var events =
    client
        .beta()
        .agents()
        .sessions()
        .createStreaming(
            SessionCreateParams.builder()
                .agent(
                    SessionCreateParams.Agent.builder()
                        .model("gpt-6-astra")
                        .instructions(
                            "Delegate each release to a separate subagent. Ask each to extract"
                                + " customer-visible changes and required migration steps using"
                                + " only its release notes. Wait for both results, then combine"
                                + " them into one release summary with release labels. Do not"
                                + " invent missing details.")
                        .multiAgent(
                            MultiAgentConfigParam.builder()
                                .enabled(true)
                                .maxConcurrentSubagents(2L)
                                .build())
                        .build())
                .environmentNone()
                .input(
                    "Release A: Search now supports filtering by date. Existing queries"
                        + " continue to work. Release B: The export endpoint now returns a"
                        + " download URL instead of file bytes. Update clients to fetch that"
                        + " URL.")
                .build())) {
  events.stream().forEach(System.out::println);
}
require "openai"
require "json"

client = OpenAI::Client.new

events = client.beta.agents.sessions.create_streaming(
  agent: {
    model: "gpt-6-astra",
    instructions: "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",
    multi_agent: {
      enabled: true,
      max_concurrent_subagents: 2
    }
  },
  environment: { type: "none" },
  input: "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL."
)
begin
  events.each { |event| puts JSON.generate(event.to_h) }
ensure
  events.close
end
curl --no-buffer --fail-with-body https://api.openai.com/v1/agents/sessions \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": {
      "model": "gpt-6-astra",
      "instructions": "Delegate each release to a separate subagent. Ask each to extract customer-visible changes and required migration steps using only its release notes. Wait for both results, then combine them into one release summary with release labels. Do not invent missing details.",
      "multi_agent": { "enabled": true, "max_concurrent_subagents": 2 }
    },
    "environment": { "type": "none" },
    "input": "Release A: Search now supports filtering by date. Existing queries continue to work. Release B: The export endpoint now returns a download URL instead of file bytes. Update clients to fetch that URL.",
    "stream": true
  }'

environment.type: "none"일 때는 create 요청에 초기 input을 포함하세요. stream: true로 설정하면 첫 턴도 스트리밍돼요. 스트림 처리와 복구는 세션 이벤트와 아이템을 참고하세요.

동시성 설정 (Concurrency settings)

max_concurrent_subagents는 한 번에 실행할 수 있는 서브에이전트 수를 제한해요. 기본값은 6이고 코디네이터는 제외돼요. 위임이 활성화되면 양의 정수로 설정하세요.

위임을 비활성화하려면 multi_agent를 생략하거나, enabled를 false로 설정하고 제한을 생략하세요. 이 설정은 세션 생성 시 적용돼요. 저장된 에이전트의 변경은 새 세션에 적용돼요.

환경 사용하기 (Use an environment)

에이전트가 파일이나 명령 실행을 필요로 하면 환경을 추가하세요. 코디네이터와 서브에이전트는 그 파일시스템을 공유해요. 서브에이전트를 만든다고 해서 또 다른 환경이 생기지는 않아요.

이 예시는 자체 환경에서 작업하는 세션을 만들어요:

자체 환경으로 위임 활성화하기

const result = await client.beta.agents.sessions.create({
  agent: {
    model: "gpt-6-astra",
    instructions:
      "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",
    multi_agent: {
      enabled: true,
      max_concurrent_subagents: 3,
    },
  },
  environment: {
    type: "self_hosted",
    workspace_directory: "/workspace",
  },
});
result = client.beta.agents.sessions.create(
    agent={
        "model": "gpt-6-astra",
        "instructions": "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",
        "multi_agent": {"enabled": True, "max_concurrent_subagents": 3},
    },
    environment={"type": "self_hosted", "workspace_directory": "/workspace"},
)
result, err := client.Beta.Agents.Sessions.New(ctx,
	openai.BetaAgentSessionNewParams{
		Agent: openai.BetaAgentSessionNewParamsAgent{
			Model:        openai.String("gpt-6-astra"),
			Instructions: openai.String("Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings."),
			MultiAgent: openai.MultiAgentConfigParam{
				Enabled:                true,
				MaxConcurrentSubagents: openai.Int(3),
			},
		},
		Environment: openai.EnvironmentParamUnion{
			OfParamSelfHosted: &openai.EnvironmentParamSelfHosted{WorkspaceDirectory: "/workspace"},
		},
	})
if err != nil {
	panic(err)
}
var result =
    client
        .beta()
        .agents()
        .sessions()
        .create(
            SessionCreateParams.builder()
                .agent(
                    SessionCreateParams.Agent.builder()
                        .model("gpt-6-astra")
                        .instructions(
                            "Prepare release notes from the repository. Have one subagent"
                                + " identify customer-visible changes and another check"
                                + " migration guides and examples, then combine their"
                                + " findings.")
                        .multiAgent(
                            MultiAgentConfigParam.builder()
                                .enabled(true)
                                .maxConcurrentSubagents(3L)
                                .build())
                        .build())
                .environment(
                    EnvironmentParam.SelfHosted.builder()
                        .workspaceDirectory("/workspace")
                        .build())
                .build());
result = client.beta.agents.sessions.create(
  agent: {
    model: "gpt-6-astra",
    instructions: "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",
    multi_agent: {
      enabled: true,
      max_concurrent_subagents: 3
    }
  },
  environment: {
    type: "self_hosted",
    workspace_directory: "/workspace"
  }
)
curl https://api.openai.com/v1/agents/sessions \
  -H "OpenAI-Beta: agents=v1" \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": {
      "model": "gpt-6-astra",
      "instructions": "Prepare release notes from the repository. Have one subagent identify customer-visible changes and another check migration guides and examples, then combine their findings.",
      "multi_agent": {
        "enabled": true,
        "max_concurrent_subagents": 3
      }
    },
    "environment": {
      "type": "self_hosted",
      "workspace_directory": "/workspace"
    }
  }'

반환된 세션과 환경 ID를 애플리케이션에 저장하세요. 환경을 연결한 뒤 입력을 보내 작업을 시작하세요.

서브에이전트가 사용할 수 있는 도구 (Tools available to subagents)

서브에이전트는 구성된 MCP 도구, 그 자격 증명과 허용된 도구, 웹 검색 설정을 상속받아요. 환경의 파일과 명령줄 도구도 사용할 수 있어요. 서브에이전트는 함수 도구를 지원하지 않아요.

위임 관찰하기 (Observe delegation)

세션 이벤트 스트림이 서브에이전트 활동을 보고해요:

  • agent.session.subagent.created는 새 서브에이전트의 ID를 제공해요.
  • agent.session.turn.item.added와 agent.session.turn.item.done은 조율 작업을 보고해요. 그 아이템 유형에는 create_subagent_call, send_subagent_input_call, wait_for_subagents_call, interrupt_subagent_call이 포함돼요.

하네스가 이 작업들을 실행해요. create 또는 wait 작업이 완료됐다고 서브에이전트가 과제를 끝냈다는 뜻은 아니에요. create 아이템에서 agent_id는 서브에이전트를 요청한 에이전트를 식별해요.

조율 아이템은 메시지 콘텐츠를 생략할 수 있어요. agent_message 아이템은 사용 가능할 때 에이전트 간 텍스트를 포함하지만, 스트림이 전체 대화 기록을 제공하지는 않아요.

결합된 결과를 위해 메인 에이전트의 응답을 읽으세요. 각 서브에이전트의 기록을 포함해 이전 작업을 검사하려면 저장된 아이템과 턴을 사용하세요.

명령 귀속시키기 (Attribute commands)

명령 아이템과 그 세션 ID가 주어지면, 명령의 턴을 검색해 실행한 에이전트를 식별하세요. 메인 에이전트의 턴 subagent_id는 null이에요.

명령을 실행한 에이전트 식별하기

// Use the saved session ID and command execution item from your application.
const turn = await client.beta.agents.sessions.turns.retrieve(
  command.turn_id,
  { session_id: sessionId }
);
console.log(turn.subagent_id);
# Use the saved session ID and command execution item from your application.
turn = client.beta.agents.sessions.turns.retrieve(
    command.turn_id, session_id=session_id
)
print(turn.subagent_id)
// Use the saved session ID and command execution item from your application.
turn, err := client.Beta.Agents.Sessions.Turns.Get(ctx, sessionID, item.TurnID)
if err != nil {
	panic(err)
}
fmt.Println(turn.SubagentID)
// Use the saved session ID and command execution item from your application.
var turn =
    client
        .beta()
        .agents()
        .sessions()
        .turns()
        .retrieve(
            TurnRetrieveParams.builder()
                .sessionId(sessionId)
                .turnId(command.turnId())
                .build());
System.out.println(turn.subagentId());
# Use the saved session ID and command execution item from your application.
turn = client.beta.agents.sessions.turns.retrieve(item.turn_id, session_id: session_id)
puts turn.subagent_id

더 알아보기 (Learn more)