|
@@ -1,697 +1,233 @@
|
|
|
-# Sub-Agent 机制设计
|
|
|
|
|
|
|
+# Sub-Agent 实现指南
|
|
|
|
|
|
|
|
-> **可执行规格书**:本文档定义 Sub-Agent 架构。代码修改必须同步更新此文档。
|
|
|
|
|
->
|
|
|
|
|
-> 📖 **快速开始**:查看 [快速参考指南](./sub-agents-quickref.md) 了解常用操作
|
|
|
|
|
|
|
+> **可执行规格书**:Sub-Agent 实现细节。代码修改必须同步更新此文档。
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 概述
|
|
|
|
|
|
|
+## 数据模型
|
|
|
|
|
|
|
|
-**Sub-Agent** 是在独立 Trace 中运行的专门化 Agent,用于处理复杂的子任务。
|
|
|
|
|
-
|
|
|
|
|
-### 核心特性
|
|
|
|
|
-
|
|
|
|
|
-- **任务隔离**:每个 Sub-Agent 在独立的 Trace 中运行,有完整的执行记录
|
|
|
|
|
-- **权限控制**:Sub-Agent 有独立的权限配置,限制其行为范围
|
|
|
|
|
-- **层级关系**:通过 `parent_trace_id` 建立父子关系,支持多层嵌套
|
|
|
|
|
-- **专门化**:不同类型的 Sub-Agent 专注于特定领域(如代码探索、研究分析)
|
|
|
|
|
-- **防止递归**:默认禁止 Sub-Agent 再启动其他 Sub-Agent,避免无限嵌套
|
|
|
|
|
-
|
|
|
|
|
-### 与主 Agent 的区别
|
|
|
|
|
-
|
|
|
|
|
-| 特性 | 主 Agent (Primary) | Sub-Agent |
|
|
|
|
|
-|------|-------------------|-----------|
|
|
|
|
|
-| 触发方式 | 用户直接调用 | 主 Agent 通过 Task 工具调用 |
|
|
|
|
|
-| 运行环境 | 独立 Trace | 独立 Trace(有父 Trace) |
|
|
|
|
|
-| 权限范围 | 完整权限 | 受限权限(可配置) |
|
|
|
|
|
-| 工具访问 | 所有工具 | 受限工具集 |
|
|
|
|
|
-| 可见性 | 用户可见 | 主 Agent 可见,用户可选可见 |
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## 架构设计
|
|
|
|
|
-
|
|
|
|
|
-### 1. Agent 类型定义
|
|
|
|
|
|
|
+### AgentDefinition
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
# agent/models/agent.py
|
|
# agent/models/agent.py
|
|
|
|
|
|
|
|
@dataclass
|
|
@dataclass
|
|
|
class AgentDefinition:
|
|
class AgentDefinition:
|
|
|
- """Agent 定义"""
|
|
|
|
|
name: str
|
|
name: str
|
|
|
description: Optional[str] = None
|
|
description: Optional[str] = None
|
|
|
mode: Literal["primary", "subagent", "all"] = "all"
|
|
mode: Literal["primary", "subagent", "all"] = "all"
|
|
|
|
|
|
|
|
# 权限配置
|
|
# 权限配置
|
|
|
|
|
+ allowed_tools: Optional[List[str]] = None # 白名单
|
|
|
|
|
+ denied_tools: Optional[List[str]] = None # 黑名单
|
|
|
permissions: Dict[str, Any] = field(default_factory=dict)
|
|
permissions: Dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
|
|
- # 工具限制
|
|
|
|
|
- allowed_tools: Optional[List[str]] = None
|
|
|
|
|
- denied_tools: Optional[List[str]] = None
|
|
|
|
|
-
|
|
|
|
|
# 模型配置
|
|
# 模型配置
|
|
|
model: Optional[str] = None
|
|
model: Optional[str] = None
|
|
|
temperature: Optional[float] = None
|
|
temperature: Optional[float] = None
|
|
|
max_iterations: Optional[int] = None
|
|
max_iterations: Optional[int] = None
|
|
|
-
|
|
|
|
|
- # 自定义 System Prompt
|
|
|
|
|
system_prompt: Optional[str] = None
|
|
system_prompt: Optional[str] = None
|
|
|
|
|
|
|
|
# 是否可以调用其他 Sub-Agent
|
|
# 是否可以调用其他 Sub-Agent
|
|
|
can_spawn_subagent: bool = False
|
|
can_spawn_subagent: bool = False
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现位置**:`agent/models/agent.py:AgentDefinition`
|
|
|
|
|
|
|
+**实现位置**:`agent/models/agent.py:AgentDefinition`(待实现)
|
|
|
|
|
|
|
|
-### 2. Trace 层级关系
|
|
|
|
|
|
|
+### Trace 扩展
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
# agent/models/trace.py (扩展现有模型)
|
|
# agent/models/trace.py (扩展现有模型)
|
|
|
|
|
|
|
|
@dataclass
|
|
@dataclass
|
|
|
class Trace:
|
|
class Trace:
|
|
|
- trace_id: str
|
|
|
|
|
- mode: Literal["call", "agent"]
|
|
|
|
|
-
|
|
|
|
|
- # 新增:Sub-Agent 支持
|
|
|
|
|
- parent_trace_id: Optional[str] = None # 父 Trace ID
|
|
|
|
|
- agent_definition: Optional[str] = None # Agent 类型名称
|
|
|
|
|
- spawned_by_tool: Optional[str] = None # 启动此 Sub-Agent 的工具调用 ID
|
|
|
|
|
-
|
|
|
|
|
- # 原有字段...
|
|
|
|
|
- task: Optional[str] = None
|
|
|
|
|
- agent_type: Optional[str] = None
|
|
|
|
|
- status: Literal["running", "completed", "failed"] = "running"
|
|
|
|
|
- # ...
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-**实现位置**:`agent/models/trace.py:Trace`(扩展现有类)
|
|
|
|
|
-
|
|
|
|
|
-### 3. Task Tool 实现
|
|
|
|
|
-
|
|
|
|
|
-Task Tool 是启动 Sub-Agent 的核心工具:
|
|
|
|
|
|
|
+ # ... 原有字段
|
|
|
|
|
|
|
|
-```python
|
|
|
|
|
-# agent/tools/builtin/task.py
|
|
|
|
|
-
|
|
|
|
|
-@tool(
|
|
|
|
|
- name="task",
|
|
|
|
|
- description="启动sub-agent处理复杂的子任务",
|
|
|
|
|
- requires_confirmation=False
|
|
|
|
|
-)
|
|
|
|
|
-async def task_tool(
|
|
|
|
|
- subagent_type: str, # Sub-Agent 类型
|
|
|
|
|
- description: str, # 任务简短描述(3-5词)
|
|
|
|
|
- prompt: str, # 详细任务描述
|
|
|
|
|
- ctx: ToolContext
|
|
|
|
|
-) -> ToolResult:
|
|
|
|
|
- """
|
|
|
|
|
- 启动一个 Sub-Agent 执行子任务
|
|
|
|
|
-
|
|
|
|
|
- Args:
|
|
|
|
|
- subagent_type: Sub-Agent 类型(如 "explore", "general")
|
|
|
|
|
- description: 任务简短描述
|
|
|
|
|
- prompt: 完整的任务描述
|
|
|
|
|
- ctx: 工具上下文
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Sub-Agent 的执行结果
|
|
|
|
|
- """
|
|
|
|
|
- # 1. 验证 Sub-Agent 类型
|
|
|
|
|
- agent_def = await get_agent_definition(subagent_type)
|
|
|
|
|
- if not agent_def or agent_def.mode == "primary":
|
|
|
|
|
- raise ValueError(f"Invalid subagent type: {subagent_type}")
|
|
|
|
|
-
|
|
|
|
|
- # 2. 检查权限
|
|
|
|
|
- if not ctx.current_agent.can_spawn_subagent:
|
|
|
|
|
- raise PermissionError("Current agent cannot spawn sub-agents")
|
|
|
|
|
-
|
|
|
|
|
- # 3. 创建子 Trace
|
|
|
|
|
- sub_trace = Trace.create(
|
|
|
|
|
- mode="agent",
|
|
|
|
|
- parent_trace_id=ctx.trace_id,
|
|
|
|
|
- agent_definition=subagent_type,
|
|
|
|
|
- spawned_by_tool=ctx.step_id,
|
|
|
|
|
- task=prompt,
|
|
|
|
|
- agent_type=subagent_type,
|
|
|
|
|
- context={
|
|
|
|
|
- "parent_task": ctx.trace.task,
|
|
|
|
|
- "description": description,
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- sub_trace_id = await ctx.trace_store.create_trace(sub_trace)
|
|
|
|
|
-
|
|
|
|
|
- # 4. 配置 Sub-Agent Runner
|
|
|
|
|
- sub_config = AgentConfig(
|
|
|
|
|
- agent_type=subagent_type,
|
|
|
|
|
- max_iterations=agent_def.max_iterations or 10,
|
|
|
|
|
- # 继承父 Agent 的某些配置
|
|
|
|
|
- skills_dir=ctx.runner.config.skills_dir,
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- sub_runner = AgentRunner(
|
|
|
|
|
- trace_store=ctx.trace_store,
|
|
|
|
|
- memory_store=ctx.memory_store,
|
|
|
|
|
- state_store=ctx.state_store,
|
|
|
|
|
- tool_registry=_build_restricted_registry(agent_def),
|
|
|
|
|
- llm_call=ctx.runner.llm_call,
|
|
|
|
|
- config=sub_config,
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- # 5. 运行 Sub-Agent(收集所有事件)
|
|
|
|
|
- events = []
|
|
|
|
|
- async for event in sub_runner.run(
|
|
|
|
|
- task=prompt,
|
|
|
|
|
- model=agent_def.model or ctx.model,
|
|
|
|
|
- trace_id=sub_trace_id,
|
|
|
|
|
- ):
|
|
|
|
|
- events.append(event)
|
|
|
|
|
- # 可选:将事件转发给父 Agent
|
|
|
|
|
-
|
|
|
|
|
- # 6. 提取结果
|
|
|
|
|
- conclusion_events = [e for e in events if e.type == "conclusion"]
|
|
|
|
|
- final_result = conclusion_events[-1].data["content"] if conclusion_events else ""
|
|
|
|
|
-
|
|
|
|
|
- # 7. 生成摘要
|
|
|
|
|
- tool_summary = _summarize_tool_calls(events)
|
|
|
|
|
-
|
|
|
|
|
- output = f"{final_result}\n\n<task_metadata>\n"
|
|
|
|
|
- output += f"sub_trace_id: {sub_trace_id}\n"
|
|
|
|
|
- output += f"total_steps: {len(events)}\n"
|
|
|
|
|
- output += f"tool_calls: {tool_summary}\n"
|
|
|
|
|
- output += "</task_metadata>"
|
|
|
|
|
-
|
|
|
|
|
- return ToolResult(
|
|
|
|
|
- title=description,
|
|
|
|
|
- output=output,
|
|
|
|
|
- metadata={
|
|
|
|
|
- "sub_trace_id": sub_trace_id,
|
|
|
|
|
- "subagent_type": subagent_type,
|
|
|
|
|
- "tool_summary": tool_summary,
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def _build_restricted_registry(agent_def: AgentDefinition) -> ToolRegistry:
|
|
|
|
|
- """根据 Agent 定义构建受限的工具注册表"""
|
|
|
|
|
- registry = ToolRegistry()
|
|
|
|
|
- global_registry = get_tool_registry()
|
|
|
|
|
-
|
|
|
|
|
- for tool_name, tool_def in global_registry.tools.items():
|
|
|
|
|
- # 检查是否在允许列表中
|
|
|
|
|
- if agent_def.allowed_tools and tool_name not in agent_def.allowed_tools:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # 检查是否在拒绝列表中
|
|
|
|
|
- if agent_def.denied_tools and tool_name in agent_def.denied_tools:
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- registry.register_tool(tool_def)
|
|
|
|
|
-
|
|
|
|
|
- # 默认禁止 task 工具(防止递归)
|
|
|
|
|
- if not agent_def.can_spawn_subagent:
|
|
|
|
|
- registry.tools.pop("task", None)
|
|
|
|
|
-
|
|
|
|
|
- return registry
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-def _summarize_tool_calls(events: List[AgentEvent]) -> str:
|
|
|
|
|
- """总结工具调用情况"""
|
|
|
|
|
- tool_calls = [e for e in events if e.type == "tool_call"]
|
|
|
|
|
- summary = {}
|
|
|
|
|
- for event in tool_calls:
|
|
|
|
|
- tool = event.data.get("tool", "unknown")
|
|
|
|
|
- summary[tool] = summary.get(tool, 0) + 1
|
|
|
|
|
-
|
|
|
|
|
- return ", ".join(f"{tool}×{count}" for tool, count in summary.items())
|
|
|
|
|
|
|
+ # Sub-Agent 支持(新增)
|
|
|
|
|
+ parent_trace_id: Optional[str] = None # 父 Trace ID
|
|
|
|
|
+ agent_definition: Optional[str] = None # Agent 类型名称
|
|
|
|
|
+ spawned_by_tool: Optional[str] = None # 启动此 Sub-Agent 的 Step ID
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现位置**:`agent/tools/builtin/task.py:task_tool`
|
|
|
|
|
|
|
+**实现位置**:`agent/models/trace.py:Trace`
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 内置 Sub-Agent 类型
|
|
|
|
|
|
|
+## 内置 Sub-Agent
|
|
|
|
|
|
|
|
### 1. general - 通用型
|
|
### 1. general - 通用型
|
|
|
|
|
|
|
|
-**用途**:执行复杂的多步骤任务和研究分析
|
|
|
|
|
-
|
|
|
|
|
```python
|
|
```python
|
|
|
GENERAL_AGENT = AgentDefinition(
|
|
GENERAL_AGENT = AgentDefinition(
|
|
|
name="general",
|
|
name="general",
|
|
|
- description="通用型 Sub-Agent,用于执行复杂的多步骤任务和研究分析",
|
|
|
|
|
|
|
+ description="通用型 Sub-Agent,执行复杂的多步骤任务",
|
|
|
mode="subagent",
|
|
mode="subagent",
|
|
|
- allowed_tools=None, # 允许所有工具
|
|
|
|
|
- denied_tools=["task"], # 禁止启动其他 Sub-Agent
|
|
|
|
|
|
|
+ denied_tools=["task"],
|
|
|
max_iterations=20,
|
|
max_iterations=20,
|
|
|
)
|
|
)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**典型使用场景**:
|
|
|
|
|
-- 多步骤的数据收集和分析
|
|
|
|
|
-- 复杂的文件处理任务
|
|
|
|
|
-- 需要多个工具协同的任务
|
|
|
|
|
|
|
+**用途**:多步骤任务、数据收集、复杂分析
|
|
|
|
|
|
|
|
### 2. explore - 探索型
|
|
### 2. explore - 探索型
|
|
|
|
|
|
|
|
-**用途**:快速探索代码库,查找文件和代码
|
|
|
|
|
-
|
|
|
|
|
```python
|
|
```python
|
|
|
EXPLORE_AGENT = AgentDefinition(
|
|
EXPLORE_AGENT = AgentDefinition(
|
|
|
name="explore",
|
|
name="explore",
|
|
|
- description="探索型 Sub-Agent,专门用于快速探索代码库、查找文件和搜索代码",
|
|
|
|
|
|
|
+ description="探索型 Sub-Agent,快速查找文件和代码",
|
|
|
mode="subagent",
|
|
mode="subagent",
|
|
|
- allowed_tools=[
|
|
|
|
|
- "read_file",
|
|
|
|
|
- "list_files",
|
|
|
|
|
- "search_code",
|
|
|
|
|
- "search_files",
|
|
|
|
|
- ],
|
|
|
|
|
- denied_tools=[
|
|
|
|
|
- "write_file",
|
|
|
|
|
- "edit_file",
|
|
|
|
|
- "execute_bash",
|
|
|
|
|
- "task",
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ allowed_tools=["read_file", "list_files", "search_code", "search_files"],
|
|
|
|
|
+ denied_tools=["write_file", "edit_file", "execute_bash", "task"],
|
|
|
max_iterations=15,
|
|
max_iterations=15,
|
|
|
- system_prompt="""你是一个代码探索专家。专注于:
|
|
|
|
|
-1. 快速定位相关文件和代码
|
|
|
|
|
-2. 理解代码结构和依赖关系
|
|
|
|
|
-3. 总结关键信息
|
|
|
|
|
-
|
|
|
|
|
-注意:
|
|
|
|
|
-- 你只能读取和搜索,不能编辑或执行代码
|
|
|
|
|
-- 优先使用搜索工具,而不是逐个读取文件
|
|
|
|
|
-- 提供清晰的文件路径和行号引用
|
|
|
|
|
-""",
|
|
|
|
|
)
|
|
)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**典型使用场景**:
|
|
|
|
|
-- "找出所有使用了 Redis 的代码"
|
|
|
|
|
-- "分析 API 路由的实现方式"
|
|
|
|
|
-- "查找配置文件的加载逻辑"
|
|
|
|
|
|
|
+**用途**:代码库探索、文件查找、结构分析
|
|
|
|
|
|
|
|
### 3. analyst - 分析型
|
|
### 3. analyst - 分析型
|
|
|
|
|
|
|
|
-**用途**:深度分析和报告生成
|
|
|
|
|
-
|
|
|
|
|
```python
|
|
```python
|
|
|
ANALYST_AGENT = AgentDefinition(
|
|
ANALYST_AGENT = AgentDefinition(
|
|
|
name="analyst",
|
|
name="analyst",
|
|
|
- description="分析型 Sub-Agent,专注于深度分析和报告生成",
|
|
|
|
|
|
|
+ description="分析型 Sub-Agent,深度分析和报告生成",
|
|
|
mode="subagent",
|
|
mode="subagent",
|
|
|
- allowed_tools=[
|
|
|
|
|
- "read_file",
|
|
|
|
|
- "list_files",
|
|
|
|
|
- "search_code",
|
|
|
|
|
- "web_search",
|
|
|
|
|
- "fetch_url",
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ allowed_tools=["read_file", "list_files", "search_code", "web_search", "fetch_url"],
|
|
|
denied_tools=["task", "write_file", "edit_file"],
|
|
denied_tools=["task", "write_file", "edit_file"],
|
|
|
max_iterations=25,
|
|
max_iterations=25,
|
|
|
- temperature=0.3, # 更精确的分析
|
|
|
|
|
|
|
+ temperature=0.3,
|
|
|
)
|
|
)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**典型使用场景**:
|
|
|
|
|
-- 技术栈分析
|
|
|
|
|
-- 性能瓶颈分析
|
|
|
|
|
-- 安全审计报告
|
|
|
|
|
|
|
+**用途**:技术栈分析、性能分析、安全审计
|
|
|
|
|
+
|
|
|
|
|
+**实现位置**:`agent/builtin_agents.py`(待实现)
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## Agent 配置系统
|
|
|
|
|
|
|
+## 核心 API
|
|
|
|
|
|
|
|
-### 配置文件格式
|
|
|
|
|
|
|
+### Task Tool
|
|
|
|
|
|
|
|
-```json
|
|
|
|
|
-{
|
|
|
|
|
- "agents": {
|
|
|
|
|
- "custom-reviewer": {
|
|
|
|
|
- "description": "代码审查专家",
|
|
|
|
|
- "mode": "subagent",
|
|
|
|
|
- "allowed_tools": ["read_file", "search_code", "list_files"],
|
|
|
|
|
- "denied_tools": ["write_file", "edit_file", "execute_bash"],
|
|
|
|
|
- "max_iterations": 10,
|
|
|
|
|
- "temperature": 0.2,
|
|
|
|
|
- "system_prompt": "你是一个代码审查专家...",
|
|
|
|
|
- "can_spawn_subagent": false
|
|
|
|
|
- },
|
|
|
|
|
- "my-primary": {
|
|
|
|
|
- "description": "自定义主 Agent",
|
|
|
|
|
- "mode": "primary",
|
|
|
|
|
- "can_spawn_subagent": true
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- "default_agent": "my-primary"
|
|
|
|
|
-}
|
|
|
|
|
|
|
+```python
|
|
|
|
|
+# agent/tools/builtin/task.py
|
|
|
|
|
+
|
|
|
|
|
+@tool(name="task")
|
|
|
|
|
+async def task_tool(
|
|
|
|
|
+ subagent_type: str, # Sub-Agent 类型
|
|
|
|
|
+ description: str, # 任务简短描述(3-5词)
|
|
|
|
|
+ prompt: str, # 详细任务描述
|
|
|
|
|
+ ctx: ToolContext
|
|
|
|
|
+) -> ToolResult:
|
|
|
|
|
+ """启动 Sub-Agent 处理子任务"""
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### 加载和管理
|
|
|
|
|
|
|
+**实现位置**:`agent/tools/builtin/task.py:task_tool`(待实现)
|
|
|
|
|
+
|
|
|
|
|
+### Agent Registry
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
# agent/agent_registry.py
|
|
# agent/agent_registry.py
|
|
|
|
|
|
|
|
class AgentRegistry:
|
|
class AgentRegistry:
|
|
|
- """Agent 注册表"""
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self):
|
|
|
|
|
- self.agents: Dict[str, AgentDefinition] = {}
|
|
|
|
|
- self._load_builtin_agents()
|
|
|
|
|
-
|
|
|
|
|
- def _load_builtin_agents(self):
|
|
|
|
|
- """加载内置 Agent"""
|
|
|
|
|
- self.register(GENERAL_AGENT)
|
|
|
|
|
- self.register(EXPLORE_AGENT)
|
|
|
|
|
- self.register(ANALYST_AGENT)
|
|
|
|
|
-
|
|
|
|
|
- def load_from_config(self, config_path: str):
|
|
|
|
|
- """从配置文件加载自定义 Agent"""
|
|
|
|
|
- import json
|
|
|
|
|
- with open(config_path) as f:
|
|
|
|
|
- config = json.load(f)
|
|
|
|
|
-
|
|
|
|
|
- for name, cfg in config.get("agents", {}).items():
|
|
|
|
|
- agent_def = AgentDefinition(
|
|
|
|
|
- name=name,
|
|
|
|
|
- **cfg
|
|
|
|
|
- )
|
|
|
|
|
- self.register(agent_def)
|
|
|
|
|
-
|
|
|
|
|
- def register(self, agent: AgentDefinition):
|
|
|
|
|
|
|
+ def register(self, agent: AgentDefinition) -> None:
|
|
|
"""注册 Agent"""
|
|
"""注册 Agent"""
|
|
|
- self.agents[agent.name] = agent
|
|
|
|
|
|
|
|
|
|
def get(self, name: str) -> Optional[AgentDefinition]:
|
|
def get(self, name: str) -> Optional[AgentDefinition]:
|
|
|
"""获取 Agent 定义"""
|
|
"""获取 Agent 定义"""
|
|
|
- return self.agents.get(name)
|
|
|
|
|
|
|
|
|
|
def list_subagents(self) -> List[AgentDefinition]:
|
|
def list_subagents(self) -> List[AgentDefinition]:
|
|
|
"""列出所有可用的 Sub-Agent"""
|
|
"""列出所有可用的 Sub-Agent"""
|
|
|
- return [
|
|
|
|
|
- agent for agent in self.agents.values()
|
|
|
|
|
- if agent.mode in ("subagent", "all")
|
|
|
|
|
- ]
|
|
|
|
|
|
|
|
|
|
|
|
+ def load_from_config(self, config_path: str) -> None:
|
|
|
|
|
+ """从配置文件加载"""
|
|
|
|
|
|
|
|
# 全局注册表
|
|
# 全局注册表
|
|
|
-_agent_registry = AgentRegistry()
|
|
|
|
|
-
|
|
|
|
|
def get_agent_registry() -> AgentRegistry:
|
|
def get_agent_registry() -> AgentRegistry:
|
|
|
"""获取全局 Agent 注册表"""
|
|
"""获取全局 Agent 注册表"""
|
|
|
- return _agent_registry
|
|
|
|
|
-
|
|
|
|
|
-async def get_agent_definition(name: str) -> Optional[AgentDefinition]:
|
|
|
|
|
- """获取 Agent 定义"""
|
|
|
|
|
- return _agent_registry.get(name)
|
|
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现位置**:`agent/agent_registry.py:AgentRegistry`
|
|
|
|
|
|
|
+**实现位置**:`agent/agent_registry.py:AgentRegistry`(待实现)
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## 权限控制
|
|
|
|
|
|
|
+## 配置文件
|
|
|
|
|
|
|
|
-### 权限模型
|
|
|
|
|
|
|
+### 格式
|
|
|
|
|
|
|
|
-每个 Agent 可以定义细粒度的权限:
|
|
|
|
|
-
|
|
|
|
|
-```python
|
|
|
|
|
-PERMISSIONS_EXAMPLE = {
|
|
|
|
|
- # 工具级别权限
|
|
|
|
|
- "tools": {
|
|
|
|
|
- "write_file": "deny",
|
|
|
|
|
- "read_file": "allow",
|
|
|
|
|
- "execute_bash": "deny",
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- # 路径级别权限
|
|
|
|
|
- "paths": {
|
|
|
|
|
- "/etc": "deny",
|
|
|
|
|
- "/tmp": "allow",
|
|
|
|
|
- "~/.ssh": "deny",
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- # 网络级别权限
|
|
|
|
|
- "network": {
|
|
|
|
|
- "allowed_domains": ["*.example.com", "api.github.com"],
|
|
|
|
|
- "blocked_domains": ["*.evil.com"],
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- # 资源限制
|
|
|
|
|
- "limits": {
|
|
|
|
|
- "max_file_size": 10_000_000, # 10MB
|
|
|
|
|
- "max_iterations": 15,
|
|
|
|
|
- "timeout_seconds": 300,
|
|
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "agents": {
|
|
|
|
|
+ "code-reviewer": {
|
|
|
|
|
+ "description": "代码审查专家",
|
|
|
|
|
+ "mode": "subagent",
|
|
|
|
|
+ "allowed_tools": ["read_file", "search_code", "list_files"],
|
|
|
|
|
+ "max_iterations": 15,
|
|
|
|
|
+ "temperature": 0.2,
|
|
|
|
|
+ "system_prompt": "你是代码审查专家...",
|
|
|
|
|
+ "can_spawn_subagent": false
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### 权限检查流程
|
|
|
|
|
-
|
|
|
|
|
-```python
|
|
|
|
|
-# agent/permission.py
|
|
|
|
|
-
|
|
|
|
|
-class PermissionChecker:
|
|
|
|
|
- """权限检查器"""
|
|
|
|
|
-
|
|
|
|
|
- def __init__(self, agent_def: AgentDefinition):
|
|
|
|
|
- self.agent_def = agent_def
|
|
|
|
|
- self.permissions = agent_def.permissions
|
|
|
|
|
-
|
|
|
|
|
- def check_tool_access(self, tool_name: str) -> bool:
|
|
|
|
|
- """检查工具访问权限"""
|
|
|
|
|
- # 检查拒绝列表
|
|
|
|
|
- if self.agent_def.denied_tools and tool_name in self.agent_def.denied_tools:
|
|
|
|
|
- return False
|
|
|
|
|
-
|
|
|
|
|
- # 检查允许列表
|
|
|
|
|
- if self.agent_def.allowed_tools and tool_name not in self.agent_def.allowed_tools:
|
|
|
|
|
- return False
|
|
|
|
|
-
|
|
|
|
|
- # 检查工具权限配置
|
|
|
|
|
- tool_perms = self.permissions.get("tools", {})
|
|
|
|
|
- if tool_perms.get(tool_name) == "deny":
|
|
|
|
|
- return False
|
|
|
|
|
-
|
|
|
|
|
- return True
|
|
|
|
|
|
|
+**配置示例**:`sub_agents.json.example`
|
|
|
|
|
|
|
|
- def check_path_access(self, path: str, mode: str = "read") -> bool:
|
|
|
|
|
- """检查路径访问权限"""
|
|
|
|
|
- path_perms = self.permissions.get("paths", {})
|
|
|
|
|
|
|
+### 加载
|
|
|
|
|
|
|
|
- for pattern, action in path_perms.items():
|
|
|
|
|
- if self._match_path(path, pattern):
|
|
|
|
|
- return action == "allow"
|
|
|
|
|
-
|
|
|
|
|
- # 默认允许读取,拒绝写入
|
|
|
|
|
- return mode == "read"
|
|
|
|
|
|
|
+```python
|
|
|
|
|
+from agent.agent_registry import get_agent_registry
|
|
|
|
|
|
|
|
- def _match_path(self, path: str, pattern: str) -> bool:
|
|
|
|
|
- """路径匹配(支持通配符)"""
|
|
|
|
|
- import fnmatch
|
|
|
|
|
- return fnmatch.fnmatch(path, pattern)
|
|
|
|
|
|
|
+# 加载配置
|
|
|
|
|
+get_agent_registry().load_from_config("sub_agents.json")
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-**实现位置**:`agent/permission.py:PermissionChecker`
|
|
|
|
|
-
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
## 使用示例
|
|
## 使用示例
|
|
|
|
|
|
|
|
-### 1. 主 Agent 调用 Sub-Agent
|
|
|
|
|
|
|
+### 基本使用
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
-# 主 Agent 的 System Prompt 中会包含 Task 工具说明
|
|
|
|
|
-system_prompt = """
|
|
|
|
|
-你是一个智能助手。当遇到复杂任务时,可以使用 task 工具启动专门的 Sub-Agent。
|
|
|
|
|
-
|
|
|
|
|
-可用的 Sub-Agent:
|
|
|
|
|
-- explore: 探索代码库,查找文件和代码
|
|
|
|
|
-- general: 执行复杂的多步骤任务
|
|
|
|
|
-- analyst: 深度分析和报告生成
|
|
|
|
|
-
|
|
|
|
|
-示例:
|
|
|
|
|
-用户:"这个项目用了哪些数据库?"
|
|
|
|
|
-你:使用 task(subagent_type="explore", description="查找数据库使用", prompt="...")
|
|
|
|
|
-"""
|
|
|
|
|
-
|
|
|
|
|
-# 主 Agent 执行时
|
|
|
|
|
-async for event in runner.run(task="分析这个项目的架构"):
|
|
|
|
|
- if event.type == "tool_call" and event.data["tool"] == "task":
|
|
|
|
|
- # Sub-Agent 被启动
|
|
|
|
|
- print(f"启动 Sub-Agent: {event.data['args']['subagent_type']}")
|
|
|
|
|
|
|
+from agent import AgentRunner, AgentConfig
|
|
|
|
|
+
|
|
|
|
|
+runner = AgentRunner(
|
|
|
|
|
+ llm_call=your_llm,
|
|
|
|
|
+ config=AgentConfig(max_iterations=20),
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+# 主 Agent 会自动使用 task 工具启动 Sub-Agent
|
|
|
|
|
+async for event in runner.run(
|
|
|
|
|
+ task="使用 explore sub-agent 查找所有配置文件"
|
|
|
|
|
+):
|
|
|
|
|
+ if event.type == "conclusion":
|
|
|
|
|
+ print(event.data["content"])
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### 2. 自定义 Sub-Agent
|
|
|
|
|
|
|
+### 自定义 Sub-Agent
|
|
|
|
|
|
|
|
```python
|
|
```python
|
|
|
-from agent import AgentRunner, AgentConfig
|
|
|
|
|
-from agent.agent_registry import get_agent_registry
|
|
|
|
|
from agent.models.agent import AgentDefinition
|
|
from agent.models.agent import AgentDefinition
|
|
|
|
|
+from agent.agent_registry import get_agent_registry
|
|
|
|
|
|
|
|
-# 1. 定义自定义 Sub-Agent
|
|
|
|
|
-custom_agent = AgentDefinition(
|
|
|
|
|
|
|
+# 定义
|
|
|
|
|
+custom = AgentDefinition(
|
|
|
name="security-scanner",
|
|
name="security-scanner",
|
|
|
description="安全扫描专家",
|
|
description="安全扫描专家",
|
|
|
mode="subagent",
|
|
mode="subagent",
|
|
|
- allowed_tools=["read_file", "search_code", "list_files"],
|
|
|
|
|
- system_prompt="""你是一个安全扫描专家。专注于:
|
|
|
|
|
-1. 查找常见安全漏洞(SQL注入、XSS、CSRF等)
|
|
|
|
|
-2. 检查敏感信息泄露(密钥、密码等)
|
|
|
|
|
-3. 分析依赖项的安全问题
|
|
|
|
|
-
|
|
|
|
|
-输出格式:
|
|
|
|
|
-- 漏洞类型
|
|
|
|
|
-- 影响范围
|
|
|
|
|
-- 修复建议
|
|
|
|
|
-""",
|
|
|
|
|
- max_iterations=20,
|
|
|
|
|
|
|
+ allowed_tools=["read_file", "search_code"],
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-# 2. 注册到全局注册表
|
|
|
|
|
-get_agent_registry().register(custom_agent)
|
|
|
|
|
-
|
|
|
|
|
-# 3. 主 Agent 可以调用它
|
|
|
|
|
-# 在 System Prompt 中会自动列出这个新的 Sub-Agent
|
|
|
|
|
|
|
+# 注册
|
|
|
|
|
+get_agent_registry().register(custom)
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-### 3. 监控 Sub-Agent 执行
|
|
|
|
|
-
|
|
|
|
|
-```python
|
|
|
|
|
-from agent.storage import TraceStore
|
|
|
|
|
-
|
|
|
|
|
-async def analyze_subagent_performance(trace_id: str, trace_store: TraceStore):
|
|
|
|
|
- """分析 Sub-Agent 性能"""
|
|
|
|
|
- trace = await trace_store.get_trace(trace_id)
|
|
|
|
|
- steps = await trace_store.get_steps(trace_id)
|
|
|
|
|
-
|
|
|
|
|
- # 找出所有 Sub-Agent 调用
|
|
|
|
|
- subagent_calls = []
|
|
|
|
|
- for step in steps:
|
|
|
|
|
- if step.step_type == "tool_call" and step.data.get("tool") == "task":
|
|
|
|
|
- sub_trace_id = step.data.get("result", {}).get("metadata", {}).get("sub_trace_id")
|
|
|
|
|
- if sub_trace_id:
|
|
|
|
|
- sub_trace = await trace_store.get_trace(sub_trace_id)
|
|
|
|
|
- subagent_calls.append({
|
|
|
|
|
- "type": sub_trace.agent_type,
|
|
|
|
|
- "task": sub_trace.task,
|
|
|
|
|
- "steps": sub_trace.total_steps,
|
|
|
|
|
- "tokens": sub_trace.total_tokens,
|
|
|
|
|
- "cost": sub_trace.total_cost,
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- # 生成报告
|
|
|
|
|
- print(f"主任务: {trace.task}")
|
|
|
|
|
- print(f"Sub-Agent 调用次数: {len(subagent_calls)}")
|
|
|
|
|
- for i, call in enumerate(subagent_calls, 1):
|
|
|
|
|
- print(f"\n{i}. {call['type']}")
|
|
|
|
|
- print(f" 任务: {call['task'][:50]}...")
|
|
|
|
|
- print(f" 步骤: {call['steps']}, Token: {call['tokens']}, 成本: ${call['cost']:.4f}")
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## 实现计划
|
|
|
|
|
-
|
|
|
|
|
-### Phase 1: 基础架构(MVP)
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **AgentDefinition 模型** (`agent/models/agent.py`)
|
|
|
|
|
- - 定义 Agent 类型、权限、配置
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **Trace 扩展** (`agent/models/trace.py`)
|
|
|
|
|
- - 添加 `parent_trace_id`、`agent_definition` 等字段
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **AgentRegistry** (`agent/agent_registry.py`)
|
|
|
|
|
- - Agent 注册和管理
|
|
|
|
|
- - 加载内置和自定义 Agent
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **Task Tool** (`agent/tools/builtin/task.py`)
|
|
|
|
|
- - 实现 Sub-Agent 启动逻辑
|
|
|
|
|
- - Trace 层级管理
|
|
|
|
|
- - 结果聚合
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **内置 Sub-Agent** (`agent/builtin_agents.py`)
|
|
|
|
|
- - `general`: 通用型
|
|
|
|
|
- - `explore`: 探索型
|
|
|
|
|
-
|
|
|
|
|
-### Phase 2: 权限控制
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **PermissionChecker** (`agent/permission.py`)
|
|
|
|
|
- - 工具级别权限检查
|
|
|
|
|
- - 路径级别权限检查
|
|
|
|
|
- - 资源限制检查
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **受限 ToolRegistry** (`agent/tools/builtin/task.py`)
|
|
|
|
|
- - 根据 Agent 定义过滤工具
|
|
|
|
|
-
|
|
|
|
|
-### Phase 3: 高级特性
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **配置系统**
|
|
|
|
|
- - JSON 配置文件支持
|
|
|
|
|
- - 动态加载自定义 Agent
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **监控和分析**
|
|
|
|
|
- - Sub-Agent 性能统计
|
|
|
|
|
- - 调用链可视化
|
|
|
|
|
-
|
|
|
|
|
-- [ ] **递归控制**
|
|
|
|
|
- - 嵌套深度限制
|
|
|
|
|
- - 循环检测
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## 集成点
|
|
|
|
|
-
|
|
|
|
|
-### 与现有系统的集成
|
|
|
|
|
-
|
|
|
|
|
-1. **AgentRunner** (`agent/runner.py`)
|
|
|
|
|
- - 扩展 `run()` 方法支持 `agent_definition` 参数
|
|
|
|
|
- - 根据 Agent 定义配置工具和权限
|
|
|
|
|
-
|
|
|
|
|
-2. **ToolRegistry** (`agent/tools/registry.py`)
|
|
|
|
|
- - 添加 Task 工具到全局注册表
|
|
|
|
|
- - 支持工具过滤和权限检查
|
|
|
|
|
-
|
|
|
|
|
-3. **TraceStore** (`agent/storage/protocols.py`)
|
|
|
|
|
- - 支持根据 `parent_trace_id` 查询子 Trace
|
|
|
|
|
- - 添加层级查询方法
|
|
|
|
|
-
|
|
|
|
|
-4. **Events** (`agent/events.py`)
|
|
|
|
|
- - 添加 Sub-Agent 相关事件类型
|
|
|
|
|
- - `subagent_started`、`subagent_completed`
|
|
|
|
|
|
|
+**完整示例**:`examples/subagent_example.py`
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
## 注意事项
|
|
## 注意事项
|
|
|
|
|
|
|
|
-1. **防止无限递归**
|
|
|
|
|
- - 默认禁止 Sub-Agent 调用 Task 工具
|
|
|
|
|
- - 设置最大嵌套深度(建议 3 层)
|
|
|
|
|
-
|
|
|
|
|
-2. **性能考虑**
|
|
|
|
|
- - Sub-Agent 增加了额外的 LLM 调用开销
|
|
|
|
|
- - 合理设置 `max_iterations` 限制
|
|
|
|
|
-
|
|
|
|
|
-3. **成本控制**
|
|
|
|
|
- - 跟踪每个 Sub-Agent 的 token 消耗
|
|
|
|
|
- - 在父 Trace 中聚合成本统计
|
|
|
|
|
-
|
|
|
|
|
-4. **错误处理**
|
|
|
|
|
- - Sub-Agent 失败不应导致主任务完全失败
|
|
|
|
|
- - 提供降级策略(如超时、重试)
|
|
|
|
|
-
|
|
|
|
|
-5. **可观测性**
|
|
|
|
|
- - 完整记录 Sub-Agent 的执行过程
|
|
|
|
|
- - 支持调用链追踪和分析
|
|
|
|
|
-
|
|
|
|
|
----
|
|
|
|
|
-
|
|
|
|
|
-## 参考资料
|
|
|
|
|
-
|
|
|
|
|
-- OpenCode 实现:`/Users/sunlit/Code/opencode/packages/opencode/src/tool/task.ts`
|
|
|
|
|
-- OpenCode Agent 定义:`/Users/sunlit/Code/opencode/packages/opencode/src/agent/agent.ts`
|
|
|
|
|
-- 当前项目架构:`docs/README.md`
|
|
|
|
|
|
|
+1. **防止递归**:默认禁止 Sub-Agent 调用 task 工具
|
|
|
|
|
+2. **权限隔离**:Sub-Agent 只能访问允许的工具
|
|
|
|
|
+3. **Trace 完整性**:每个 Sub-Agent 有独立的 Trace
|
|
|
|
|
+4. **成本控制**:跟踪每个 Sub-Agent 的 token 消耗
|