mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
feat: add agent roles to collab tools (#9275)
Add `agent_type` parameter to the collab tool `spawn_agent` that contains a preset to apply on the config when spawning this agent
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
pub(crate) mod control;
|
||||
pub(crate) mod role;
|
||||
pub(crate) mod status;
|
||||
|
||||
pub(crate) use codex_protocol::protocol::AgentStatus;
|
||||
pub(crate) use control::AgentControl;
|
||||
pub(crate) use role::AgentRole;
|
||||
pub(crate) use status::agent_status_from_event;
|
||||
|
||||
85
codex-rs/core/src/agent/role.rs
Normal file
85
codex-rs/core/src/agent/role.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::config::Config;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Base instructions for the orchestrator role.
|
||||
const ORCHESTRATOR_PROMPT: &str = include_str!("../../templates/agents/orchestrator.md");
|
||||
/// Base instructions for the worker role.
|
||||
const WORKER_PROMPT: &str = include_str!("../../gpt-5.2-codex_prompt.md");
|
||||
/// Default worker model override used by the worker role.
|
||||
const WORKER_MODEL: &str = "gpt-5.2-codex";
|
||||
|
||||
/// Enumerated list of all supported agent roles.
|
||||
const ALL_ROLES: [AgentRole; 3] = [
|
||||
AgentRole::Default,
|
||||
AgentRole::Orchestrator,
|
||||
AgentRole::Worker,
|
||||
];
|
||||
|
||||
/// Hard-coded agent role selection used when spawning sub-agents.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AgentRole {
|
||||
/// Inherit the parent agent's configuration unchanged.
|
||||
Default,
|
||||
/// Coordination-only agent that delegates to workers.
|
||||
Orchestrator,
|
||||
/// Task-executing agent with a fixed model override.
|
||||
Worker,
|
||||
}
|
||||
|
||||
/// Immutable profile data that drives per-agent configuration overrides.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct AgentProfile {
|
||||
/// Optional base instructions override.
|
||||
pub base_instructions: Option<&'static str>,
|
||||
/// Optional model override.
|
||||
pub model: Option<&'static str>,
|
||||
/// Whether to force a read-only sandbox policy.
|
||||
pub read_only: bool,
|
||||
}
|
||||
|
||||
impl AgentRole {
|
||||
/// Returns the string values used by JSON schema enums.
|
||||
pub fn enum_values() -> Vec<String> {
|
||||
ALL_ROLES
|
||||
.iter()
|
||||
.filter_map(|role| serde_json::to_string(role).ok())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the hard-coded profile for this role.
|
||||
pub fn profile(self) -> AgentProfile {
|
||||
match self {
|
||||
AgentRole::Default => AgentProfile::default(),
|
||||
AgentRole::Orchestrator => AgentProfile {
|
||||
base_instructions: Some(ORCHESTRATOR_PROMPT),
|
||||
..Default::default()
|
||||
},
|
||||
AgentRole::Worker => AgentProfile {
|
||||
base_instructions: Some(WORKER_PROMPT),
|
||||
model: Some(WORKER_MODEL),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies this role's profile onto the provided config.
|
||||
pub fn apply_to_config(self, config: &mut Config) -> Result<(), String> {
|
||||
let profile = self.profile();
|
||||
if let Some(base_instructions) = profile.base_instructions {
|
||||
config.base_instructions = Some(base_instructions.to_string());
|
||||
}
|
||||
if let Some(model) = profile.model {
|
||||
config.model = Some(model.to_string());
|
||||
}
|
||||
if profile.read_only {
|
||||
config
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_read_only_policy())
|
||||
.map_err(|err| format!("sandbox_policy is invalid: {err}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -541,24 +541,12 @@ impl Session {
|
||||
web_search_mode: per_turn_config.web_search_mode,
|
||||
});
|
||||
|
||||
let base_instructions = if per_turn_config.features.enabled(Feature::Collab) {
|
||||
const COLLAB_INSTRUCTIONS: &str =
|
||||
include_str!("../templates/collab/experimental_prompt.md");
|
||||
let base = session_configuration
|
||||
.base_instructions
|
||||
.as_deref()
|
||||
.unwrap_or(model_info.base_instructions.as_str());
|
||||
Some(format!("{base}\n\n{COLLAB_INSTRUCTIONS}"))
|
||||
} else {
|
||||
session_configuration.base_instructions.clone()
|
||||
};
|
||||
|
||||
TurnContext {
|
||||
sub_id,
|
||||
client,
|
||||
cwd: session_configuration.cwd.clone(),
|
||||
developer_instructions: session_configuration.developer_instructions.clone(),
|
||||
base_instructions,
|
||||
base_instructions: session_configuration.base_instructions.clone(),
|
||||
compact_prompt: session_configuration.compact_prompt.clone(),
|
||||
user_instructions: session_configuration.user_instructions.clone(),
|
||||
approval_policy: session_configuration.approval_policy.value(),
|
||||
|
||||
@@ -76,11 +76,13 @@ impl ToolHandler for CollabHandler {
|
||||
|
||||
mod spawn {
|
||||
use super::*;
|
||||
use crate::agent::AgentRole;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct SpawnAgentArgs {
|
||||
message: String,
|
||||
agent_type: Option<AgentRole>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -95,6 +97,7 @@ mod spawn {
|
||||
arguments: String,
|
||||
) -> Result<ToolOutput, FunctionCallError> {
|
||||
let args: SpawnAgentArgs = parse_arguments(&arguments)?;
|
||||
let agent_role = args.agent_type.unwrap_or(AgentRole::Default);
|
||||
let prompt = args.message;
|
||||
if prompt.trim().is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
@@ -112,7 +115,10 @@ mod spawn {
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
let config = build_agent_spawn_config(turn.as_ref())?;
|
||||
let mut config = build_agent_spawn_config(turn.as_ref())?;
|
||||
agent_role
|
||||
.apply_to_config(&mut config)
|
||||
.map_err(FunctionCallError::RespondToModel)?;
|
||||
let result = session
|
||||
.services
|
||||
.agent_control
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::agent::AgentRole;
|
||||
use crate::client_common::tools::ResponsesApiTool;
|
||||
use crate::client_common::tools::ToolSpec;
|
||||
use crate::features::Feature;
|
||||
@@ -441,6 +442,15 @@ fn create_spawn_agent_tool() -> ToolSpec {
|
||||
description: Some("Initial message to send to the new agent.".to_string()),
|
||||
},
|
||||
);
|
||||
properties.insert(
|
||||
"agent_type".to_string(),
|
||||
JsonSchema::String {
|
||||
description: Some(format!(
|
||||
"Optional agent type to spawn ({}).",
|
||||
AgentRole::enum_values().join(", ")
|
||||
)),
|
||||
},
|
||||
);
|
||||
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: "spawn_agent".to_string(),
|
||||
|
||||
37
codex-rs/core/templates/agents/orchestrator.md
Normal file
37
codex-rs/core/templates/agents/orchestrator.md
Normal file
@@ -0,0 +1,37 @@
|
||||
You are Codex Orchestrator, based on GPT-5. You are running as an orchestration agent in the Codex CLI on a user's computer.
|
||||
|
||||
## Role
|
||||
|
||||
- You do not solve the task yourself. Your job is to delegate, coordinate, and verify.
|
||||
- Monitor progress, resolve conflicts, and integrate results into a single, coherent outcome.
|
||||
- You should always spawn a worker to perform actual work but before this, you can discuss the problem, ask follow-up questions, discussion design etc. Workers are only here to perform the actual job.
|
||||
|
||||
## Multi-agent workflow
|
||||
|
||||
1. Understand the request and identify the minimum set of workers needed.
|
||||
2. Spawn worker(s) with precise goals, constraints, and expected deliverables.
|
||||
3. Monitor workers with `wait`, route questions via `send_input`, and keep scope boundaries clear.
|
||||
4. When all workers report done, spawn a verifier agent to review the work.
|
||||
5. If the verifier reports issues, assign fixes to the relevant worker(s) and repeat steps 3–5 until the verifier passes.
|
||||
6. Close all agents when you don't need them anymore (i.e. when the task if fully finished).
|
||||
|
||||
## Collaboration rules
|
||||
|
||||
- Tell every worker they are not alone in the environment and must not revert or overwrite others' work.
|
||||
- Default: workers must not spawn sub-agents unless you explicitly allow it.
|
||||
- For large logs or long-running tasks (tests, builds), delegate to a worker and instruct them not to spawn additional agents.
|
||||
- Use sensible `wait` timeouts and adjust for task size; do not exceed maximums.
|
||||
|
||||
## Collab tools
|
||||
|
||||
- `spawn_agent`: create a worker or verifier with an initial prompt (set `agent_type`).
|
||||
- `send_input`: send follow-ups, clarifications, or fix requests (`interrupt` can stop the current task first).
|
||||
- `wait`: poll an agent for completion or status.
|
||||
- `close_agent`: close the agent when done.
|
||||
|
||||
## Presenting your work and final message
|
||||
|
||||
- Keep responses concise, factual, and in plain text.
|
||||
- Summarize: what was delegated, key outcomes, tests/verification status, and any remaining risks.
|
||||
- If verification failed, state the issues clearly and what you asked workers to change.
|
||||
- Do not dump large files; reference paths with backticks.
|
||||
Reference in New Issue
Block a user