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

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 

11 

12logger = get_logger("qrclaw.tools.agent_tools") 

13 

14# 权限配置文件路径 

15PERMISSIONS_FILE = Path.home() / ".qrclaw" / "permissions.yaml" 

16 

17 

18class CreateAgentArgs(BaseModel): 

19 name: str = Field(description="Agent 名称,只能包含字母、数字、中划线和下划线") 

20 

21 

22class DeleteAgentArgs(BaseModel): 

23 name: str = Field(description="要删除的 Agent 名称") 

24 

25 

26def _is_valid_name(name: str) -> bool: 

27 """检查 agent 名称是否合法""" 

28 import re 

29 return bool(re.match(r'^[a-zA-Z0-9_-]+$', name)) 

30 

31 

32def _remove_agent_permissions(name: str): 

33 """从 permissions.yaml 中移除 agent 的权限配置""" 

34 if not PERMISSIONS_FILE.exists(): 

35 return 

36 

37 try: 

38 with open(PERMISSIONS_FILE, "r", encoding="utf-8") as f: 

39 config = yaml.safe_load(f) or {} 

40 

41 if "agents" not in config or name not in config["agents"]: 

42 return 

43 

44 # 移除该 agent 的配置 

45 del config["agents"][name] 

46 

47 with open(PERMISSIONS_FILE, "w", encoding="utf-8") as f: 

48 yaml.dump(config, f, default_flow_style=False, allow_unicode=True) 

49 

50 logger.info(f"已从 permissions.yaml 中移除 agent '{name}' 的权限配置") 

51 

52 except Exception as e: 

53 logger.warning(f"清理权限配置失败: {e}") 

54 

55 

56@register(description="创建一个新的 Agent,会自动创建工作目录结构(sessions/logs/skills)和初始化文件", args_model=CreateAgentArgs) 

57def create_agent(name: str) -> str: 

58 """ 

59 创建一个新的 Agent 工作空间。 

60 

61 目录结构: 

62 ~/.qrclaw/agents/<name>/ 

63 ├── sessions/ # 会话历史 

64 ├── logs/ # 日志文件 

65 ├── skills/ # 技能脚本 

66 └── MEMORY.md # 中期记忆 

67  

68 注意:新创建的 Agent 默认为 scoped 权限(只能访问自己的工作目录)。 

69 如需访问外部目录,需手动编辑 ~/.qrclaw/permissions.yaml 配置白名单。 

70 """ 

71 logger.info(f"创建 Agent: {name}") 

72 

73 # 验证名称 

74 if not _is_valid_name(name): 

75 error_msg = f"错误:Agent 名称不合法,只能包含字母、数字、中划线和下划线: {name}" 

76 logger.warning(error_msg) 

77 return error_msg 

78 

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 

85 

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) 

91 

92 # 创建 MEMORY.md 

93 memory_file = agent_dir / "MEMORY.md" 

94 memory_file.write_text(f"# {name} 中期记忆\n\n", encoding="utf-8") 

95 

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 中配置白名单。" 

98 

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 

103 

104 

105@register(description="删除一个 Agent 及其工作目录,同时清理权限配置,此操作不可恢复", args_model=DeleteAgentArgs, confirm=True) 

106def delete_agent(name: str) -> str: 

107 """ 

108 删除一个 Agent 及其所有数据。 

109 

110 警告:此操作会删除会话历史、日志、技能、记忆等所有数据,并清理权限配置,不可恢复! 

111 """ 

112 logger.info(f"删除 Agent: {name}") 

113 

114 # 不允许删除 default agent 

115 if name == "default": 

116 error_msg = "错误:不能删除 default agent" 

117 logger.warning(error_msg) 

118 return error_msg 

119 

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 

126 

127 try: 

128 # 删除整个目录 

129 shutil.rmtree(agent_dir) 

130 

131 # 清理权限配置 

132 _remove_agent_permissions(name) 

133 

134 logger.info(f"Agent '{name}' 已删除: {agent_dir}") 

135 return f"✅ Agent '{name}' 已删除\n\n已移除:\n- 工作目录: {agent_dir}\n- 权限配置: ~/.qrclaw/permissions.yaml\n\n⚠️ 所有数据(会话、日志、技能、记忆)已永久删除。" 

136 

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