PicoBot/src/tools/traits.rs
oudecheng c27efedb6c feat(capability): 扩展 CapabilityPolicy 支持子代理黑白名单
- 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 覆盖。
2026-07-30 17:37:29 +08:00

67 lines
2.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = 主 agent1 = 子 agent2 = 孙 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
}
}