- domain: CapabilityPolicy 新增 allowed_subagents/denied_subagents 字段及 check_subagent_allowed 方法 - experts: 专家 frontmatter 解析/渲染支持子代理策略字段 - task/runtime: spawn/resume 双路径校验父代理子代理策略;新增 update_subagent 写回 SUBAGENT.md(与 update_expert 对称);新增 SubagentPromptProvider 按专家策略过滤子代理索引 - task/runtime: 子代理自身 capability 作为孙代理的 parent_capability 透传(ToolContext),保持解耦 - traits: ToolContext 新增 parent_capability 字段 - agent_factory: 主 agent 注入 expert_capability 到 ToolContext 安全:策略不通过即拒绝(与 def 不可用即拒绝范式一致),防止 LLM 通过选择被禁子代理绕过限制;max_nesting_depth 兜底防递归不可被 def 覆盖。
67 lines
2.3 KiB
Rust
67 lines
2.3 KiB
Rust
use async_trait::async_trait;
|
||
|
||
use crate::domain::CapabilityPolicy;
|
||
|
||
#[derive(Debug, Clone)]
|
||
pub struct ToolResult {
|
||
pub success: bool,
|
||
pub output: String,
|
||
pub error: Option<String>,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Default)]
|
||
pub struct ToolContext {
|
||
pub channel_name: Option<String>,
|
||
pub sender_id: Option<String>,
|
||
pub chat_id: Option<String>,
|
||
pub session_id: Option<String>,
|
||
pub topic_id: Option<String>,
|
||
pub message_id: Option<String>,
|
||
pub message_seq: Option<i64>,
|
||
/// 子代理标识,用于标注消息来源
|
||
pub subagent_description: Option<String>,
|
||
/// 当前嵌套深度(0 = 主 agent,1 = 子 agent,2 = 孙 agent...)
|
||
pub nesting_depth: u32,
|
||
/// 当前智能体自身的任务 ID(主 agent 为 None,子/孙 agent 为 Some(uuid))
|
||
pub task_id: Option<String>,
|
||
/// 父任务 ID(仅子/孙智能体有值,用于构建任务层级)
|
||
pub parent_task_id: Option<String>,
|
||
/// 当前工具调用的 ID(由 agent_loop 在执行前注入,用于精确关联 TaskStarted 事件)
|
||
pub tool_call_id: Option<String>,
|
||
/// 父智能体(主 agent 所选专家或上级子代理)的 capability 策略快照。
|
||
/// TaskTool 据此强制校验子代理加载(白/黑名单),与 spawn/resume 安全范式一致。
|
||
/// 以数据形式传递,避免 task 模块反向依赖 experts 模块。
|
||
pub parent_capability: Option<CapabilityPolicy>,
|
||
}
|
||
|
||
#[async_trait]
|
||
pub trait Tool: Send + Sync + 'static {
|
||
fn name(&self) -> &str;
|
||
fn description(&self) -> &str;
|
||
fn parameters_schema(&self) -> serde_json::Value;
|
||
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult>;
|
||
|
||
async fn execute_with_context(
|
||
&self,
|
||
_context: &ToolContext,
|
||
args: serde_json::Value,
|
||
) -> anyhow::Result<ToolResult> {
|
||
self.execute(args).await
|
||
}
|
||
|
||
/// Whether this tool is side-effect free and safe to parallelize.
|
||
fn read_only(&self) -> bool {
|
||
false
|
||
}
|
||
|
||
/// Whether this tool can run alongside other concurrency-safe tools.
|
||
fn concurrency_safe(&self) -> bool {
|
||
self.read_only() && !self.exclusive()
|
||
}
|
||
|
||
/// Whether this tool should run alone even if concurrency is enabled.
|
||
fn exclusive(&self) -> bool {
|
||
false
|
||
}
|
||
}
|