Coverage for tools / agent_tools.py: 25%
75 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-29 02:55 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-29 02:55 +0800
1"""
2Agent 管理工具
3"""
4from pathlib import Path
5import shutil
6import yaml
7from pydantic import BaseModel, Field
8from qrclaw.tools.registry import register
9from qrclaw.workspace import AGENTS_ROOT
10from qrclaw.logger import get_logger
12logger = get_logger("qrclaw.tools.agent_tools")
14# 权限配置文件路径
15PERMISSIONS_FILE = Path.home() / ".qrclaw" / "permissions.yaml"
18class CreateAgentArgs(BaseModel):
19 name: str = Field(description="Agent 名称,只能包含字母、数字、中划线和下划线")
22class DeleteAgentArgs(BaseModel):
23 name: str = Field(description="要删除的 Agent 名称")
26def _is_valid_name(name: str) -> bool:
27 """检查 agent 名称是否合法"""
28 import re
29 return bool(re.match(r'^[a-zA-Z0-9_-]+$', name))
32def _remove_agent_permissions(name: str):
33 """从 permissions.yaml 中移除 agent 的权限配置"""
34 if not PERMISSIONS_FILE.exists():
35 return
37 try:
38 with open(PERMISSIONS_FILE, "r", encoding="utf-8") as f:
39 config = yaml.safe_load(f) or {}
41 if "agents" not in config or name not in config["agents"]:
42 return
44 # 移除该 agent 的配置
45 del config["agents"][name]
47 with open(PERMISSIONS_FILE, "w", encoding="utf-8") as f:
48 yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
50 logger.info(f"已从 permissions.yaml 中移除 agent '{name}' 的权限配置")
52 except Exception as e:
53 logger.warning(f"清理权限配置失败: {e}")
56@register(description="创建一个新的 Agent,会自动创建工作目录结构(sessions/logs/skills)和初始化文件", args_model=CreateAgentArgs)
57def create_agent(name: str) -> str:
58 """
59 创建一个新的 Agent 工作空间。
61 目录结构:
62 ~/.qrclaw/agents/<name>/
63 ├── sessions/ # 会话历史
64 ├── logs/ # 日志文件
65 ├── skills/ # 技能脚本
66 └── MEMORY.md # 中期记忆
68 注意:新创建的 Agent 默认为 scoped 权限(只能访问自己的工作目录)。
69 如需访问外部目录,需手动编辑 ~/.qrclaw/permissions.yaml 配置白名单。
70 """
71 logger.info(f"创建 Agent: {name}")
73 # 验证名称
74 if not _is_valid_name(name):
75 error_msg = f"错误:Agent 名称不合法,只能包含字母、数字、中划线和下划线: {name}"
76 logger.warning(error_msg)
77 return error_msg
79 # 检查是否已存在
80 agent_dir = AGENTS_ROOT / name
81 if agent_dir.exists():
82 error_msg = f"错误:Agent '{name}' 已存在: {agent_dir}"
83 logger.warning(error_msg)
84 return error_msg
86 try:
87 # 创建目录结构
88 (agent_dir / "sessions").mkdir(parents=True, exist_ok=True)
89 (agent_dir / "logs").mkdir(parents=True, exist_ok=True)
90 (agent_dir / "skills").mkdir(parents=True, exist_ok=True)
92 # 创建 MEMORY.md
93 memory_file = agent_dir / "MEMORY.md"
94 memory_file.write_text(f"# {name} 中期记忆\n\n", encoding="utf-8")
96 logger.info(f"Agent '{name}' 创建成功: {agent_dir}")
97 return f"✅ Agent '{name}' 创建成功\n\n工作目录: {agent_dir}\n\n目录结构:\n- sessions/ (会话历史)\n- logs/ (日志文件)\n- skills/ (技能脚本)\n- MEMORY.md (中期记忆)\n\n默认权限: scoped (只能访问自己的工作目录)\n如需访问外部目录,请在 ~/.qrclaw/permissions.yaml 中配置白名单。"
99 except Exception as e:
100 error_msg = f"错误:创建 Agent 失败: {e}"
101 logger.error(f"创建 Agent 失败: {name}, 错误: {e}", exc_info=True)
102 return error_msg
105@register(description="删除一个 Agent 及其工作目录,同时清理权限配置,此操作不可恢复", args_model=DeleteAgentArgs, confirm=True)
106def delete_agent(name: str) -> str:
107 """
108 删除一个 Agent 及其所有数据。
110 警告:此操作会删除会话历史、日志、技能、记忆等所有数据,并清理权限配置,不可恢复!
111 """
112 logger.info(f"删除 Agent: {name}")
114 # 不允许删除 default agent
115 if name == "default":
116 error_msg = "错误:不能删除 default agent"
117 logger.warning(error_msg)
118 return error_msg
120 # 检查是否存在
121 agent_dir = AGENTS_ROOT / name
122 if not agent_dir.exists():
123 error_msg = f"错误:Agent '{name}' 不存在"
124 logger.warning(error_msg)
125 return error_msg
127 try:
128 # 删除整个目录
129 shutil.rmtree(agent_dir)
131 # 清理权限配置
132 _remove_agent_permissions(name)
134 logger.info(f"Agent '{name}' 已删除: {agent_dir}")
135 return f"✅ Agent '{name}' 已删除\n\n已移除:\n- 工作目录: {agent_dir}\n- 权限配置: ~/.qrclaw/permissions.yaml\n\n⚠️ 所有数据(会话、日志、技能、记忆)已永久删除。"
137 except Exception as e:
138 error_msg = f"错误:删除 Agent 失败: {e}"
139 logger.error(f"删除 Agent 失败: {name}, 错误: {e}", exc_info=True)
140 return error_msg