mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Set multi-agent v2 dogfood defaults (#25266)
## Summary - default multi-agent v2 to direct-model-only tools so code mode does not wrap subagent tools - add default root/subagent team prompts aligned with dogfood training assumptions - tighten spawn-agent model override wording to prefer the inherited model by default ## Tests - just fmt - just test -p codex-core spawn_agent_description_lists_visible_models_and_reasoning_efforts - just test -p codex-core multi_agent_v2_default_session_thread_cap_counts_root - just test -p codex-rollout-trace - just fix -p codex-core - just fix -p codex-rollout-trace Note: a broad just test -p codex-core run was attempted locally, but this sandbox produced unrelated environment failures around sandbox-exec, missing test_stdio_server, and realtime timeouts.
This commit is contained in:
committed by
GitHub
Unverified
parent
cf0911076f
commit
8d49394feb
@@ -475,6 +475,7 @@ impl AgentControl {
|
||||
}
|
||||
if preserve_reference_context_item
|
||||
&& config.features.enabled(Feature::MultiAgentV2)
|
||||
&& config.multi_agent_v2.usage_hint_enabled
|
||||
&& let Some(subagent_usage_hint_text) =
|
||||
config.multi_agent_v2.subagent_usage_hint_text.clone()
|
||||
&& let Some(subagent_usage_hint_message) =
|
||||
|
||||
@@ -717,6 +717,53 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
"full-history forked child should preserve the parent diff baseline"
|
||||
);
|
||||
|
||||
let mut disabled_hint_child_config = harness.config.clone();
|
||||
let _ = disabled_hint_child_config
|
||||
.features
|
||||
.enable(Feature::MultiAgentV2);
|
||||
disabled_hint_child_config.multi_agent_v2.usage_hint_enabled = false;
|
||||
disabled_hint_child_config
|
||||
.multi_agent_v2
|
||||
.subagent_usage_hint_text = Some("Disabled child subagent guidance.".to_string());
|
||||
let disabled_hint_child_thread_id = harness
|
||||
.control
|
||||
.spawn_agent_with_metadata(
|
||||
disabled_hint_child_config,
|
||||
text_input("child task without hints"),
|
||||
Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id,
|
||||
depth: 1,
|
||||
agent_path: None,
|
||||
agent_nickname: None,
|
||||
agent_role: None,
|
||||
})),
|
||||
SpawnAgentOptions {
|
||||
fork_parent_spawn_call_id: Some(parent_spawn_call_id.clone()),
|
||||
fork_mode: Some(SpawnAgentForkMode::FullHistory),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("forked spawn should honor disabled usage hints")
|
||||
.thread_id;
|
||||
let disabled_hint_child_thread = harness
|
||||
.manager
|
||||
.get_thread(disabled_hint_child_thread_id)
|
||||
.await
|
||||
.expect("disabled-hint child thread should be registered");
|
||||
let disabled_hint_history = disabled_hint_child_thread
|
||||
.codex
|
||||
.session
|
||||
.clone_history()
|
||||
.await;
|
||||
assert!(
|
||||
!history_contains_text(
|
||||
disabled_hint_history.raw_items(),
|
||||
"Disabled child subagent guidance.",
|
||||
),
|
||||
"full-history forked child should not add subagent guidance when usage hints are disabled"
|
||||
);
|
||||
|
||||
let expected = (
|
||||
child_thread_id,
|
||||
Op::UserInput {
|
||||
@@ -743,6 +790,11 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
||||
.shutdown_live_agent(child_thread_id)
|
||||
.await
|
||||
.expect("child shutdown should submit");
|
||||
let _ = harness
|
||||
.control
|
||||
.shutdown_live_agent(disabled_hint_child_thread_id)
|
||||
.await
|
||||
.expect("disabled-hint child shutdown should submit");
|
||||
let _ = parent_thread
|
||||
.submit(Op::Shutdown {})
|
||||
.await
|
||||
|
||||
@@ -9846,7 +9846,55 @@ enabled = true
|
||||
assert_eq!(config.multi_agent_v2.max_wait_timeout_ms, 3_600_000);
|
||||
assert_eq!(config.multi_agent_v2.default_wait_timeout_ms, 30_000);
|
||||
assert_eq!(config.agent_max_threads, Some(3));
|
||||
assert!(!config.multi_agent_v2.non_code_mode_only);
|
||||
assert_eq!(
|
||||
config.multi_agent_v2.root_agent_usage_hint_text.as_deref(),
|
||||
Some(DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT)
|
||||
);
|
||||
assert!(
|
||||
!config
|
||||
.multi_agent_v2
|
||||
.root_agent_usage_hint_text
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
.contains("maximum concurrency"),
|
||||
);
|
||||
assert_eq!(
|
||||
config.multi_agent_v2.subagent_usage_hint_text.as_deref(),
|
||||
Some(DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT)
|
||||
);
|
||||
assert!(
|
||||
!config
|
||||
.multi_agent_v2
|
||||
.subagent_usage_hint_text
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
.contains("maximum concurrency"),
|
||||
);
|
||||
assert!(config.multi_agent_v2.non_code_mode_only);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_empty_usage_hint_overrides_clear_default_hints() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
std::fs::write(
|
||||
codex_home.path().join(CONFIG_TOML_FILE),
|
||||
r#"[features.multi_agent_v2]
|
||||
enabled = true
|
||||
root_agent_usage_hint_text = ""
|
||||
subagent_usage_hint_text = ""
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let config = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.fallback_cwd(Some(codex_home.path().to_path_buf()))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.multi_agent_v2.root_agent_usage_hint_text, None);
|
||||
assert_eq!(config.multi_agent_v2.subagent_usage_hint_text, None);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -189,6 +189,44 @@ pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_CONCURRENT_THREADS_PER_SESSION: usiz
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS: i64 = 3600 * 1000;
|
||||
pub(crate) const DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS: i64 = 30_000;
|
||||
const DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT: &str = r#"You are `/root`, the primary agent in a team of agents collaborating to fulfill the user's goals.
|
||||
|
||||
At the start of your turn, you are the active agent.
|
||||
You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents.
|
||||
All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
|
||||
|
||||
You can use `spawn_agent` to create a new agent, `assign_task` to give an existing agent a new task and trigger a turn, and `send_message` to pass a message to a running agent without triggering a turn.
|
||||
Child agents can also spawn their own sub-agents.
|
||||
You can decide how much context you want to propagate to your sub-agents with the `fork_turns` parameter.
|
||||
|
||||
You will receive messages in the analysis channel in the form:
|
||||
```
|
||||
Message Type: MESSAGE | FINAL_ANSWER
|
||||
Sender: <author>
|
||||
Payload:
|
||||
<payload text>
|
||||
```
|
||||
They may be addressed as to=/root
|
||||
"#;
|
||||
const DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT: &str = r#"You are an agent in a team of agents collaborating to complete a task.
|
||||
|
||||
You can spawn sub-agents to handle subtasks, and those sub-agents can spawn their own sub-agents. All agents in the team, including the agents that you can assign tasks to, are equally intelligent and capable, and have access to the same set of tools.
|
||||
|
||||
You can use `spawn_agent` to create a new agent, `assign_task` to give an existing agent a new task and trigger a turn, and `send_message` to pass a message to a running agent.
|
||||
Child agents can also spawn their own sub-agents.
|
||||
|
||||
When you provide a response in the final channel, that content is immediately delivered back to your parent agent.
|
||||
|
||||
You will receive messages in the analysis channel in the form:
|
||||
```
|
||||
Message Type: NEW_TASK | MESSAGE | FINAL_ANSWER
|
||||
Task name: <recipient> # only for NEW_TASK -- this determines your identity
|
||||
Sender: <author>
|
||||
Payload:
|
||||
<payload text>
|
||||
```
|
||||
You may also see them addressed as to=/root/..., which indicates your identity is /root/...
|
||||
"#;
|
||||
pub(crate) const HARD_MIN_MULTI_AGENT_V2_TIMEOUT_MS: i64 = 0;
|
||||
pub(crate) const HARD_MAX_MULTI_AGENT_V2_TIMEOUT_MS: i64 =
|
||||
DEFAULT_MULTI_AGENT_V2_MAX_WAIT_TIMEOUT_MS;
|
||||
@@ -1019,11 +1057,15 @@ impl Default for MultiAgentV2Config {
|
||||
default_wait_timeout_ms: DEFAULT_MULTI_AGENT_V2_DEFAULT_WAIT_TIMEOUT_MS,
|
||||
usage_hint_enabled: true,
|
||||
usage_hint_text: None,
|
||||
root_agent_usage_hint_text: None,
|
||||
subagent_usage_hint_text: None,
|
||||
root_agent_usage_hint_text: Some(
|
||||
DEFAULT_MULTI_AGENT_V2_ROOT_AGENT_USAGE_HINT_TEXT.to_string(),
|
||||
),
|
||||
subagent_usage_hint_text: Some(
|
||||
DEFAULT_MULTI_AGENT_V2_SUBAGENT_USAGE_HINT_TEXT.to_string(),
|
||||
),
|
||||
tool_namespace: None,
|
||||
hide_spawn_agent_metadata: false,
|
||||
non_code_mode_only: false,
|
||||
non_code_mode_only: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2239,14 +2281,14 @@ fn resolve_multi_agent_v2_config(config_toml: &ConfigToml) -> MultiAgentV2Config
|
||||
.and_then(|config| config.usage_hint_text.as_ref())
|
||||
.cloned()
|
||||
.or(default.usage_hint_text);
|
||||
let root_agent_usage_hint_text = base
|
||||
.and_then(|config| config.root_agent_usage_hint_text.as_ref())
|
||||
.cloned()
|
||||
.or(default.root_agent_usage_hint_text);
|
||||
let subagent_usage_hint_text = base
|
||||
.and_then(|config| config.subagent_usage_hint_text.as_ref())
|
||||
.cloned()
|
||||
.or(default.subagent_usage_hint_text);
|
||||
let root_agent_usage_hint_text = resolve_optional_prompt_text(
|
||||
base.map(|config| &config.root_agent_usage_hint_text),
|
||||
default.root_agent_usage_hint_text,
|
||||
);
|
||||
let subagent_usage_hint_text = resolve_optional_prompt_text(
|
||||
base.map(|config| &config.subagent_usage_hint_text),
|
||||
default.subagent_usage_hint_text,
|
||||
);
|
||||
let tool_namespace = base
|
||||
.and_then(|config| config.tool_namespace.as_ref())
|
||||
.cloned()
|
||||
@@ -2287,6 +2329,17 @@ fn resolve_terminal_resize_reflow_config(config_toml: &ConfigToml) -> TerminalRe
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_optional_prompt_text(
|
||||
configured: Option<&Option<String>>,
|
||||
default: Option<String>,
|
||||
) -> Option<String> {
|
||||
match configured {
|
||||
Some(Some(value)) if value.is_empty() => None,
|
||||
Some(Some(value)) => Some(value.clone()),
|
||||
Some(None) | None => default,
|
||||
}
|
||||
}
|
||||
|
||||
fn multi_agent_v2_toml_config(features: Option<&FeaturesToml>) -> Option<&MultiAgentV2ConfigToml> {
|
||||
match features?.multi_agent_v2.as_ref()? {
|
||||
FeatureToml::Enabled(_) => None,
|
||||
|
||||
@@ -12,6 +12,10 @@ pub(super) fn usage_hint_text<'a>(
|
||||
}
|
||||
|
||||
let multi_agent_v2 = &turn_context.config.multi_agent_v2;
|
||||
if !multi_agent_v2.usage_hint_enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
match session_source {
|
||||
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { .. }) => {
|
||||
multi_agent_v2.subagent_usage_hint_text.as_deref()
|
||||
|
||||
@@ -7185,6 +7185,34 @@ async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_feature_dis
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_initial_context_omits_multi_agent_v2_usage_hints_when_hint_disabled() {
|
||||
let (session, turn_context, _rx_event) = make_session_and_context_with_auth_and_config_and_rx(
|
||||
CodexAuth::from_api_key("Test API Key"),
|
||||
Vec::new(),
|
||||
|config| {
|
||||
let _ = config.features.enable(Feature::MultiAgentV2);
|
||||
config.multi_agent_v2.usage_hint_enabled = false;
|
||||
config.multi_agent_v2.root_agent_usage_hint_text = Some("Root guidance.".to_string());
|
||||
config.multi_agent_v2.subagent_usage_hint_text = Some("Subagent guidance.".to_string());
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let initial_context = session.build_initial_context(turn_context.as_ref()).await;
|
||||
|
||||
let developer_messages = developer_message_texts(&initial_context);
|
||||
assert!(
|
||||
!developer_messages.iter().any(|message| {
|
||||
matches!(
|
||||
message.as_slice(),
|
||||
["Root guidance."] | ["Subagent guidance."]
|
||||
)
|
||||
}),
|
||||
"did not expect multi-agent v2 usage hint developer messages, got {developer_messages:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn build_initial_context_omits_default_image_save_location_with_image_history() {
|
||||
let (session, turn_context) = make_session_and_context().await;
|
||||
|
||||
@@ -11,9 +11,9 @@ use std::collections::BTreeMap;
|
||||
pub const MULTI_AGENT_V1_NAMESPACE: &str = "multi_agent_v1";
|
||||
const MULTI_AGENT_V1_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents.";
|
||||
|
||||
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent.";
|
||||
const SPAWN_AGENT_INHERITED_MODEL_GUIDANCE: &str = "Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed.";
|
||||
const SPAWN_AGENT_MODEL_OVERRIDE_DESCRIPTION: &str =
|
||||
"Model override for the new agent. Omit to inherit the parent model.";
|
||||
"Model override for the new agent. Omit unless an explicit override is needed.";
|
||||
const SPAWN_AGENT_SERVICE_TIER_OVERRIDE_DESCRIPTION: &str =
|
||||
"Service tier override for the new agent. Omit unless explicitly requested.";
|
||||
const MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT_DESCRIPTION: usize = 5;
|
||||
|
||||
@@ -184,7 +184,7 @@ async fn spawn_agent_description_lists_visible_models_and_reasoning_efforts() ->
|
||||
);
|
||||
assert!(
|
||||
description.contains(
|
||||
"Spawned agents inherit your current model by default. If provided, `model` specifies the model to use for the spawned agent."
|
||||
"Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed."
|
||||
),
|
||||
"expected inherited-model guidance in spawn_agent description: {description:?}"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user