Pular para o conteúdo principal
Use este padrão quando você quiser que uma resposta divida o trabalho em tarefas menores. Esse padrão implementa um loop síncrono de delegação de respostas filhas. Uma resposta pai chama uma ferramenta de função que seu aplicativo resolve executando outra requisição de Responses. Um ou vários resultados filhos são retornados para o pai como function_call_output, e o pai continua a geração. Uma resposta filha às vezes é chamada de subagente. Neste guia, é apenas outra requisição de Responses que executa uma tarefa focada. Resposta pai -> function_call spawn_subagent -> seu app executa uma ou mais respostas filhas -> resultados filhos retornados como function_call_output -> resposta pai retoma a geração

Como o loop funciona

Neste padrão, a resposta pai pausa sempre que chama spawn_subagent. Seu app executa as tarefas delegadas e então retoma o pai com os resultados.
  1. Crie uma resposta pai com uma ferramenta de função spawn_subagent.
  2. Quando o modelo chamar a ferramenta, analise os argumentos de cada chamada de ferramenta.
  3. Execute uma ou mais requisições Responses filhas para realizar as tarefas delegadas.
  4. Aguarde todas as respostas filhas terminarem.
  5. Retorne cada resultado filho como function_call_output usando o call_id correspondente.
  6. Retome a resposta pai com previous_response_id.
  7. Repita até que o pai produza uma message normal do assistente.

Defina uma ferramenta para delegação

Mantenha a ferramenta focada. Passe apenas os campos que a resposta filha precisa.
{
  "type": "function",
  "name": "spawn_subagent",
  "description": "Delegue uma tarefa focada para uma resposta filha e retorne o resultado.",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "task": {
        "type": "string",
        "description": "A tarefa para a resposta filha."
      },
      "instructions": {
        "type": "string",
        "description": "Instruções opcionais para a resposta filha."
      },
      "model": {
        "type": "string",
        "description": "Modelo opcional para sobrescrever na resposta filha."
      }
    },
    "required": ["task"],
    "additionalProperties": false
  }
}
Use tool_choice: "auto" quando o modelo deve decidir quando delegar. Use tool_choice: "required" quando cada turno deve passar por uma ferramenta.

Receita com SDK TypeScript

Este exemplo usa sdk.llm.responses.create tanto para as respostas pai quanto para as filhas. Isso permite que o pai emita múltiplas chamadas spawn_subagent em um único turno. Seu app executa essas respostas filhas em paralelo, aguarda todas terminarem e então retoma o pai de uma vez com todos os resultados das ferramentas. Mantenha cada resultado filho pequeno para que o pai possa usá-lo no próximo turno sem consumir muito contexto.
import { SDK } from "@meetkai/mka1";

const sdk = new SDK({
  bearerAuth: process.env.MKA1_API_KEY!,
});

const spawnSubagentTool = {
  type: "function" as const,
  name: "spawn_subagent",
  description: "Delegate a focused task to a child response and return the result.",
  strict: true,
  parameters: {
    type: "object",
    properties: {
      task: { type: "string" },
      instructions: { type: "string" },
      model: { type: "string" },
    },
    required: ["task"],
    additionalProperties: false,
  },
};

type SpawnSubagentArgs = {
  task: string;
  instructions?: string;
  model?: string;
};

async function runChildResponse(args: SpawnSubagentArgs) {
  const child = await sdk.llm.responses.create({
    model: args.model ?? "auto",
    instructions:
      args.instructions ??
      "You are a specialist assistant. Complete the task and return the result only.",
    input: args.task,
    store: true,
  });

  return {
    response_id: child.id,
    output_text: child.outputText,
  };
}

export async function runDelegatingAgent(input: string) {
  let response = await sdk.llm.responses.create({
    model: "auto",
    instructions:
      "You are an orchestrator. Use spawn_subagent for focused side tasks. You may call it multiple times in one turn when tasks can be parallelized. After all tool results return, answer the user directly.",
    input,
    tools: [spawnSubagentTool],
    tool_choice: "auto",
    parallel_tool_calls: true,
    max_tool_calls: 8,
    store: true,
  });

  while (true) {
    const toolCalls = response.output.filter(
      (item): item is {
        type: "function_call";
        name: string;
        call_id: string;
        arguments: string;
      } => item.type === "function_call" && item.name === "spawn_subagent",
    );

    if (toolCalls.length === 0) {
      return response.outputText;
    }

    const toolOutputs = await Promise.all(
      toolCalls.map(async (toolCall) => {
        const args = JSON.parse(toolCall.arguments) as SpawnSubagentArgs;
        const childResult = await runChildResponse(args);

        return {
          type: "function_call_output" as const,
          call_id: toolCall.call_id,
          output: JSON.stringify(childResult),
        };
      }),
    );

    response = await sdk.llm.responses.create({
      model: response.model,
      previous_response_id: response.id,
      input: toolOutputs,
      tools: [spawnSubagentTool],
      parallel_tool_calls: true,
      max_tool_calls: 8,
      store: true,
    });
  }
}

Distribua e aguarde todas as respostas filhas

Quando parallel_tool_calls está como true, o pai pode emitir vários itens function_call em um turno. Trate esse conjunto de chamadas de ferramenta como um lote. Inicie cada resposta filha, aguarde todas terminarem e só então retome o pai. Isso cria uma barreira:
  1. A resposta pai emite vários itens function_call.
  2. Seu app inicia várias respostas filhas.
  3. Seu app aguarda todas as respostas filhas completarem.
  4. Seu app envia todos os itens function_call_output de volta em uma única requisição de acompanhamento.
  5. A resposta pai continua com o conjunto completo de resultados delegados.
Se você retomar o pai cedo com apenas parte do lote, o modelo continua sem os resultados que faltam. Isso normalmente torna a orquestração menos previsível. O passo chave de junção (fan-in) é assim:
const toolOutputs = await Promise.all(
  toolCalls.map(async (toolCall) => {
    const args = JSON.parse(toolCall.arguments) as SpawnSubagentArgs;
    const childResult = await runChildResponse(args);

    return {
        type: "function_call_output" as const,
        call_id: toolCall.call_id,
        output: JSON.stringify(childResult),
    };
  }),
);
Se seu app age em nome de um usuário final, envie o mesmo valor de X-On-Behalf-Of nas requisições pai e filhas. Se uma resposta filha pode demorar mais, você pode definir background: true na requisição filha e fazer polling com sdk.llm.responses.get até que ela termine. Quando você distribui para múltiplas respostas filhas, aguarde até que todos os resultados estejam disponíveis antes de retomar a resposta pai.

Requisição bruta de Responses para o passo de retomada

A transferência crítica é a requisição de acompanhamento. Você passa todos os resultados das ferramentas em input e aponta para o turno anterior do pai com previous_response_id.
bash
curl https://apigw.mka1.com/api/v1/llm/responses \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <mka1-api-key>' \
  --header 'X-On-Behalf-Of: <end-user-id>' \
  --data '{
    "model": "auto",
    "previous_response_id": "resp_parent123",
    "input": [
      {
        "type": "function_call_output",
        "call_id": "call_abc123",
        "output": "{\"response_id\":\"resp_child456\",\"output_text\":\"Pesquisa concluída. Recomendo uma implantação em etapas.\"}"
      },
      {
        "type": "function_call_output",
        "call_id": "call_def456",
        "output": "{\"response_id\":\"resp_child789\",\"output_text\":\"Rascunho concluído. Manchete: Implantação mais rápida com menor risco.\"}"
      }
    ]
  }'
Se você não estiver agindo em nome de um usuário final, omita o X-On-Behalf-Of.

Tratamento básico de erros e recuperação

Em sistemas reais, esse loop irá falhar às vezes. Falhas comuns incluem argumentos de ferramenta malformados, timeouts em requisições filhas, erros 5xx de upstream, limites de taxa e saídas filhas que são fracas demais para serem úteis. O padrão mais seguro é:
  • Analise os argumentos das ferramentas de forma defensiva.
  • Retente falhas transitórias de requisições filhas algumas vezes com backoff.
  • Retorne um payload de falha estruturado para o pai ao invés de travar todo o lote quando uma filha falhar.
  • Mantenha os resultados das filhas pequenos e explícitos para que o pai possa decidir se continua, retenta ou responde com resultados parciais.
Uma abordagem simples é envolver a execução da filha e sempre retornar um objeto de sucesso ou de erro:
type ChildSuccess = {
  ok: true;
  response_id: string;
  output_text: string;
};

type ChildFailure = {
  ok: false;
  error_code: "invalid_arguments" | "child_request_failed";
  message: string;
};

type ChildResult = ChildSuccess | ChildFailure;

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function runChildResponseWithRecovery(rawArgs: string): Promise<ChildResult> {
  let args: SpawnSubagentArgs;

  try {
    args = JSON.parse(rawArgs) as SpawnSubagentArgs;
    if (!args.task || typeof args.task !== "string") {
      return {
        ok: false,
        error_code: "invalid_arguments",
        message: "spawn_subagent requires a string task",
      };
    }
  } catch {
    return {
      ok: false,
      error_code: "invalid_arguments",
      message: "Could not parse spawn_subagent arguments as JSON",
    };
  }

  for (let attempt = 0; attempt < 3; attempt += 1) {
    try {
      const child = await sdk.llm.responses.create({
        model: args.model ?? "auto",
        instructions:
          args.instructions ??
          "You are a specialist assistant. Complete the task and return the result only.",
        input: args.task,
        store: true,
      });

      return {
        ok: true,
        response_id: child.id,
        output_text: child.outputText,
      };
    } catch (error) {
      const isLastAttempt = attempt === 2;
      if (isLastAttempt) {
        return {
          ok: false,
          error_code: "child_request_failed",
          message:
            error instanceof Error ? error.message : "Child response failed",
        };
      }

      await sleep(500 * (attempt + 1));
    }
  }

  return {
    ok: false,
    error_code: "child_request_failed",
    message: "Child response failed",
  };
}
Depois, envie os resultados de volta ao pai mesmo que algumas filhas falhem:
const toolOutputs = await Promise.all(
  toolCalls.map(async (toolCall) => {
    const childResult = await runChildResponseWithRecovery(toolCall.arguments);

    return {
      type: "function_call_output" as const,
      call_id: toolCall.call_id,
      output: JSON.stringify(childResult),
    };
  }),
);
Isso permite que o pai veja falhas parciais e continue o raciocínio. Por exemplo, o pai pode decidir retentar com uma tarefa mais restrita, lançar um subagente substituto ou responder com os resultados filhos bem-sucedidos disponíveis enquanto observa a lacuna. Para filhas de longa duração, você também pode combinar isso com background: true mais polling. A lógica de recuperação permanece a mesma: aguarde a filha atingir um estado terminal e então retorne um payload de sucesso ou de falha estruturada para o pai.

Limites práticos

  • Mantenha parallel_tool_calls como true quando o pai deve poder delegar várias respostas filhas em um turno.
  • Defina parallel_tool_calls como false apenas quando as respostas filhas compartilham estado ou precisam ser executadas em ordem.
  • Defina max_tool_calls para que uma resposta pai não possa entrar em loop infinito.
  • Mantenha as saídas das filhas compactas para que o pai possa incorporar o resultado sem consumir muito contexto.
  • Mantenha store: true enquanto constrói o fluxo de trabalho para poder inspecionar as respostas pai e filhas depois.
  • Use a página de itens de input de Responses na Referência da API quando precisar depurar os itens exatos enviados de volta ao modelo.

Veja também

Revise pesquisa profunda com subagentes para uma implementação concreta desse padrão voltada para pesquisa. Revise gerar uma resposta para o padrão básico de requisição de Responses. Use gerenciar conversas quando quiser que o fluxo de trabalho pai ou filho mantenha estado durável fora de uma única cadeia de respostas.