tiny_agent/tools/default_tools/
spawn_agent.rs1use crate::{
2 core::{Agent, AgentRunTime, AgentRuntimeConfig},
3 sandbox::Sandbox,
4 tools::{ToolCtx, ToolOutcome, ToolRegistry},
5};
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8use serde_json::{Value, json};
9use std::sync::Arc;
10use tokio_util::sync::CancellationToken;
11
12#[derive(Debug, Deserialize, JsonSchema)]
13pub struct SpawnAgentArgs {
14 task: String,
16}
17
18#[derive(Debug, Serialize)]
19struct SpawnAgentOutput {
20 status: String,
21 child_session_id: String,
22 final_resp: Option<String>,
23 detail: Value,
24}
25
26pub fn register_spawn_agent(registry: &mut ToolRegistry, child_config: AgentRuntimeConfig) {
27 registry.register_with_ctx(
28 "spawn_agent",
29 "启动一个子 agent runtime 执行独立子任务。子 agent 使用独立会话历史,但共享父 agent 当前 sandbox;\
30 子会话会登记为父会话的 subsession,返回子会话 id 和最终结果",
31 false,
32 move |sandbox, args: SpawnAgentArgs, ctx: ToolCtx| {
33 let child_config = child_config.clone();
34 let parent_session_id = ctx.session_id().to_string();
35 let cancellation = ctx.cancellation_token().child_token();
37 async move {
38 spawn_agent(child_config, sandbox, args, parent_session_id, cancellation).await
39 }
40 },
41 );
42}
43
44async fn spawn_agent(
45 mut child_config: AgentRuntimeConfig,
46 parent_sandbox: Arc<dyn Sandbox>,
47 args: SpawnAgentArgs,
48 parent_session_id: String,
49 cancellation: CancellationToken,
50) -> Result<ToolOutcome, String> {
51 child_config.sandbox = parent_sandbox;
53 let runtime = AgentRunTime::from_config(child_config).map_err(|e| e.to_string())?;
54 let child_session_id = runtime
56 .create_subsession(&parent_session_id)
57 .await
58 .map_err(|e| e.to_string())?;
59 let ready = runtime
60 .submit(&child_session_id, args.task)
61 .await
62 .map_err(|e| e.to_string())?;
63 let outcome = runtime
64 .run(ready, cancellation)
65 .await
66 .map_err(|e| e.to_string())?;
67
68 let output = match outcome {
69 Agent::Success(ctx) => SpawnAgentOutput {
70 status: "success".to_string(),
71 child_session_id,
72 final_resp: ctx.final_resp,
73 detail: json!({}),
74 },
75 Agent::Fail(_, failure) => SpawnAgentOutput {
76 status: "failed".to_string(),
77 child_session_id,
78 final_resp: None,
79 detail: json!({
80 "kind": failure.kind.as_str(),
81 "message": failure.message,
82 }),
83 },
84 Agent::WaitingForUser(_, interaction) => SpawnAgentOutput {
85 status: "waiting_for_user".to_string(),
86 child_session_id,
87 final_resp: None,
88 detail: json!({
89 "interaction": interaction,
90 }),
91 },
92 Agent::Interrupted(_) => SpawnAgentOutput {
93 status: "interrupted".to_string(),
94 child_session_id,
95 final_resp: None,
96 detail: json!({}),
97 },
98 other => SpawnAgentOutput {
99 status: "stopped".to_string(),
100 child_session_id,
101 final_resp: None,
102 detail: json!({
103 "state": format!("{other:?}"),
104 }),
105 },
106 };
107 let value = serde_json::to_value(output).map_err(|e| e.to_string())?;
108 Ok(value.into())
109}