feat: Add agent workspace system and runtime management

- Add agent core modules (agent_core, factory, registry, skill_loader)
- Add runtime system for agent execution management
- Add REST API for agents, workspaces, and runtime control
- Add process supervisor for agent lifecycle management
- Add workspace template system with agent profiles
- Add frontend RuntimeView and runtime API integration
- Add per-agent skill workspaces for smoke_fullstack run
- Refactor skill system with active/installed separation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 16:43:29 +08:00
parent 2daf5717ba
commit 59b44545d0
121 changed files with 8384 additions and 358 deletions

View File

@@ -1,22 +1,30 @@
# -*- coding: utf-8 -*-
"""Toolkit factory following AgentScope's skill + tool group practices."""
"""Toolkit factory following AgentScope's skill + tool group practices.
from typing import Any, Dict, Iterable
支持从Agent工作空间动态创建工具集加载builtin/customized技能
以及合并Agent特定工具。
"""
from typing import Any, Dict, Iterable, List, Optional
from pathlib import Path
from .agent_workspace import load_agent_workspace_config
from backend.config.bootstrap_config import get_bootstrap_config_for_run
import yaml
from .skills_manager import SkillsManager
from backend.agents.agent_workspace import load_agent_workspace_config
from backend.agents.skills_manager import SkillsManager
from backend.agents.skill_loader import load_skill_from_dir, get_skill_tools
from backend.config.bootstrap_config import get_bootstrap_config_for_run
def load_agent_profiles() -> Dict[str, Dict[str, Any]]:
"""加载Agent配置文件"""
config_path = SkillsManager().project_root / "backend" / "config" / "agent_profiles.yaml"
with open(config_path, "r", encoding="utf-8") as file:
return yaml.safe_load(file) or {}
def _register_analysis_tool_groups(toolkit: Any) -> None:
"""注册分析工具组"""
from backend.tools.analysis_tools import TOOL_REGISTRY
tool_groups = {
@@ -95,6 +103,7 @@ def _register_analysis_tool_groups(toolkit: Any) -> None:
def _register_portfolio_tool_groups(toolkit: Any, pm_agent: Any) -> None:
"""注册投资组合工具组"""
toolkit.create_tool_group(
group_name="portfolio_ops",
description="Portfolio decision recording tools.",
@@ -111,6 +120,7 @@ def _register_portfolio_tool_groups(toolkit: Any, pm_agent: Any) -> None:
def _register_risk_tool_groups(toolkit: Any) -> None:
"""注册风险工具组"""
from backend.tools.risk_tools import (
assess_margin_and_liquidity,
assess_position_concentration,
@@ -146,7 +156,17 @@ def create_agent_toolkit(
owner: Any = None,
active_skill_dirs: Iterable[str] | None = None,
) -> Any:
"""Create a Toolkit with agent skills and grouped tools."""
"""Create a Toolkit with agent skills and grouped tools.
Args:
agent_id: Agent标识符
config_name: 运行配置名称
owner: Agent实例用于注册特定方法
active_skill_dirs: 显式指定的活动技能目录列表
Returns:
配置好的Toolkit实例
"""
from agentscope.tool import Toolkit
profiles = load_agent_profiles()
@@ -207,3 +227,173 @@ def create_agent_toolkit(
toolkit.update_tool_groups(group_names=active_groups, active=True)
return toolkit
def create_toolkit_from_workspace(
agent_id: str,
config_name: str,
owner: Any = None,
include_builtin: bool = True,
include_customized: bool = True,
include_local: bool = True,
active_groups: Optional[List[str]] = None,
) -> Any:
"""从Agent工作空间创建工具集
这是create_agent_toolkit的增强版本支持更灵活的技能加载策略。
Args:
agent_id: Agent标识符
config_name: 运行配置名称
owner: Agent实例
include_builtin: 是否包含builtin技能
include_customized: 是否包含customized技能
include_local: 是否包含agent-local技能
active_groups: 显式指定的活动工具组
Returns:
配置好的Toolkit实例
"""
from agentscope.tool import Toolkit
skills_manager = SkillsManager()
agent_config = load_agent_workspace_config(
skills_manager.get_agent_asset_dir(config_name, agent_id) / "agent.yaml",
)
toolkit = Toolkit(
agent_skill_instruction=(
"<system-info>You have access to project skills. Each skill lives in a "
"directory and is described by SKILL.md. Follow the skill instructions "
"when they are relevant to the current task.</system-info>"
),
agent_skill_template="- {name} (dir: {dir}): {description}",
)
# 注册Agent类型的默认工具组
if agent_id.endswith("_analyst"):
_register_analysis_tool_groups(toolkit)
elif agent_id == "portfolio_manager" and owner is not None:
_register_portfolio_tool_groups(toolkit, owner)
elif agent_id == "risk_manager":
_register_risk_tool_groups(toolkit)
# 收集所有要加载的技能目录
skill_dirs: List[Path] = []
# 1. 从active目录加载已同步的技能
active_root = skills_manager.get_agent_active_root(config_name, agent_id)
if active_root.exists():
for skill_dir in sorted(active_root.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
skill_dirs.append(skill_dir)
# 2. 从installed目录加载
installed_root = skills_manager.get_agent_installed_root(config_name, agent_id)
if installed_root.exists():
for skill_dir in sorted(installed_root.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
if skill_dir not in skill_dirs:
skill_dirs.append(skill_dir)
# 3. 从local目录加载agent-local技能
if include_local:
local_root = skills_manager.get_agent_local_root(config_name, agent_id)
if local_root.exists():
for skill_dir in sorted(local_root.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
if skill_dir not in skill_dirs:
skill_dirs.append(skill_dir)
# 注册技能到toolkit
for skill_dir in skill_dirs:
toolkit.register_agent_skill(str(skill_dir))
# 激活指定的工具组
if active_groups is None:
# 从配置中读取
profiles = load_agent_profiles()
profile = profiles.get(agent_id, {})
active_groups = agent_config.active_tool_groups or profile.get("active_tool_groups", [])
# 应用禁用列表
disabled_groups = set(agent_config.disabled_tool_groups)
if disabled_groups:
active_groups = [g for g in active_groups if g not in disabled_groups]
if active_groups:
toolkit.update_tool_groups(group_names=active_groups, active=True)
return toolkit
def get_toolkit_info(toolkit: Any) -> Dict[str, Any]:
"""获取工具集信息
Args:
toolkit: Toolkit实例
Returns:
工具集信息字典
"""
info = {
"tool_groups": {},
"skills": [],
"tools_count": 0,
}
# 获取工具组信息
groups = getattr(toolkit, "tool_groups", {})
for name, group in groups.items():
info["tool_groups"][name] = {
"description": getattr(group, "description", ""),
"active": getattr(group, "active", False),
"tools": [t.name for t in getattr(group, "tools", [])],
}
info["tools_count"] += len(getattr(group, "tools", []))
# 获取技能信息
skills = getattr(toolkit, "agent_skills", [])
for skill in skills:
info["skills"].append({
"name": getattr(skill, "name", "unknown"),
"path": getattr(skill, "path", ""),
"description": getattr(skill, "description", ""),
})
return info
def refresh_toolkit_skills(
toolkit: Any,
agent_id: str,
config_name: str,
) -> None:
"""刷新工具集中的技能
重新从工作空间加载技能,用于运行时技能变更。
Args:
toolkit: Toolkit实例
agent_id: Agent标识符
config_name: 运行配置名称
"""
skills_manager = SkillsManager()
# 清除现有技能
if hasattr(toolkit, "agent_skills"):
toolkit.agent_skills.clear()
# 重新加载active技能
active_root = skills_manager.get_agent_active_root(config_name, agent_id)
if active_root.exists():
for skill_dir in sorted(active_root.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
toolkit.register_agent_skill(str(skill_dir))
# 重新加载local技能
local_root = skills_manager.get_agent_local_root(config_name, agent_id)
if local_root.exists():
for skill_dir in sorted(local_root.iterdir()):
if skill_dir.is_dir() and (skill_dir / "SKILL.md").exists():
toolkit.register_agent_skill(str(skill_dir))