opencode-collaboration 2.1.0__py3-none-any.whl → 2.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opencode_collaboration-2.1.0.dist-info → opencode_collaboration-2.2.0.dist-info}/METADATA +1 -1
- {opencode_collaboration-2.1.0.dist-info → opencode_collaboration-2.2.0.dist-info}/RECORD +10 -5
- src/core/agent_manager.py +553 -0
- src/core/meeting_manager.py +502 -0
- src/core/project_manager.py +549 -0
- src/core/resource_lock.py +468 -0
- src/core/story_manager.py +712 -0
- {opencode_collaboration-2.1.0.dist-info → opencode_collaboration-2.2.0.dist-info}/WHEEL +0 -0
- {opencode_collaboration-2.1.0.dist-info → opencode_collaboration-2.2.0.dist-info}/entry_points.txt +0 -0
- {opencode_collaboration-2.1.0.dist-info → opencode_collaboration-2.2.0.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,7 @@ src/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
4
4
|
src/cli/agent.py,sha256=o8-3UpfcnUMePltpBdVBuBFMECMiKnOA7airyvi1m54,9874
|
|
5
5
|
src/cli/main.py,sha256=E91PR24_r1gfKPlmtgQin2OZ-YHCck4dUe01X81FpIo,44189
|
|
6
6
|
src/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
src/core/agent_manager.py,sha256=Mxe4G_IfEaQ1lERdGNfMvK0tIS6DuE3WJUNOxgtAD8o,16535
|
|
7
8
|
src/core/auto_doc_git.py,sha256=_yphH8tLAMq3R-yMHpc2bcmmap2p7b2So4qVmXUtCh8,1396
|
|
8
9
|
src/core/auto_docs.py,sha256=rQfJrrKGe8fiQK4eSq4gAp1rfFAu_o-sDV_ZloaFQVQ,11142
|
|
9
10
|
src/core/auto_engine.py,sha256=bV9VXa0naPpxKuE7p7xHAZKZJe9DIGfzrFwHhiaDHKA,16031
|
|
@@ -21,13 +22,17 @@ src/core/git.py,sha256=guy7aE9FX4w62HQ-Wf_N3jzu_rn8JfVFLf8-Ulz-5k8,8928
|
|
|
21
22
|
src/core/git_monitor.py,sha256=PFZxq_KodaI6zcO67-40h2vid6zdSpr3bGVZiF2fEsA,15009
|
|
22
23
|
src/core/git_workflow_enforcer.py,sha256=0B-xMX4T9bvyuulgqrgOlRYYwi7Vr32apjGz-9NVK00,11583
|
|
23
24
|
src/core/iteration_status_manager.py,sha256=ckPsJK_brsP_NSXtBLLlNrn5DqvmxvEGsE_VqjHOPJ8,9916
|
|
25
|
+
src/core/meeting_manager.py,sha256=lcwZ1fgZHl8Mq8s3Oh5PjFxREgiXoDt852J3B7yJjvY,15375
|
|
24
26
|
src/core/monitor.py,sha256=3s0xuc6l7pF4MtnRjYVxbfn8MLfKk7p7mljqtsZDQZ0,8949
|
|
25
27
|
src/core/phase_advance.py,sha256=76UN8TI7RpJgRfjgV4bvs7uWlvNt-h_dz6gTDgK2zrY,11285
|
|
28
|
+
src/core/project_manager.py,sha256=qayc8NHy3go26LWZgw17nL57p8LXKjpprAS1_iKZHDg,16637
|
|
29
|
+
src/core/resource_lock.py,sha256=1G2yOdbqZnWqVdcIt6IKbhRLhSg23kLgJhdwiFt7jdY,13889
|
|
26
30
|
src/core/signoff.py,sha256=fG6KFNz3AYh6hKYMPfognU_SBs5amCW2q4bne4huFf0,6643
|
|
27
31
|
src/core/state_machine.py,sha256=L1gfdsYC6mlWcHP5RwQQen8vCTwV2spIP-Ktwz4gux8,14919
|
|
28
32
|
src/core/state_manager.py,sha256=jkgci5q71DRjmKe8igeFhOTxHJ2xqNjAHJo6THFtw_M,16672
|
|
29
33
|
src/core/state_migrator.py,sha256=OYXtwxA_ePFAS_XqOMhKR2fyU1werLePbqCyNZg8eXQ,13746
|
|
30
34
|
src/core/state_validator.py,sha256=Q86jbEO0fNdzDi3zIPB_G_ibQ0QWyXFYV_rPj6Eakjw,18827
|
|
35
|
+
src/core/story_manager.py,sha256=8b0Kelw6zdvZYr9sb0Rnlf7_XeXXOSdVar4q2vQjdbI,21867
|
|
31
36
|
src/core/supervisor.py,sha256=pT_5CkimpFgB_gyqzsUL-25l3MsRGP1TWFEnHdGJtwo,7290
|
|
32
37
|
src/core/task_executor.py,sha256=xcM9sNu8MyAVqlNvtc2GL4eiYeTMmeduTrrA3j292aM,23720
|
|
33
38
|
src/core/workflow.py,sha256=LpH9g6xbtCmYOhhCSxpcstRR7TptN8e6b0mEag3UcW4,5634
|
|
@@ -36,8 +41,8 @@ src/utils/date.py,sha256=iWS0hTaoDE2iC0jJb3lTIB5yK5xxRbrC1C98Fgb8LFc,577
|
|
|
36
41
|
src/utils/file.py,sha256=5IFKkT2m1emJUHDzIiLsa4YG9GCqOhhmiLvc6aVY9-Y,1301
|
|
37
42
|
src/utils/lock.py,sha256=soxYFsBKJHUzN-_QXkorVfgnmt0D5p1SZtqwPNqcWPI,2880
|
|
38
43
|
src/utils/yaml.py,sha256=zcbh0OP7NOqxTexEAR3akQkllUh8xeKt42O2CHIImyg,777
|
|
39
|
-
opencode_collaboration-2.
|
|
40
|
-
opencode_collaboration-2.
|
|
41
|
-
opencode_collaboration-2.
|
|
42
|
-
opencode_collaboration-2.
|
|
43
|
-
opencode_collaboration-2.
|
|
44
|
+
opencode_collaboration-2.2.0.dist-info/METADATA,sha256=JUqcf6qs5uBE2tjP4CNyLXxGR8ev6msaZIFNiFnNJjg,3145
|
|
45
|
+
opencode_collaboration-2.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
46
|
+
opencode_collaboration-2.2.0.dist-info/entry_points.txt,sha256=fYyHWa_NefMp527B7fHl-29SwZQCElRdtxm_7LoUK-Y,48
|
|
47
|
+
opencode_collaboration-2.2.0.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
|
|
48
|
+
opencode_collaboration-2.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
"""Agent 管理模块 - v2.2.0 M1 多 Agent 动态管理
|
|
2
|
+
|
|
3
|
+
提供 Agent 角色体系、动态添加、职责约束等功能。
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import uuid
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional, Set
|
|
11
|
+
import yaml
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentType(Enum):
|
|
20
|
+
"""Agent 类型枚举。"""
|
|
21
|
+
PRODUCT_MANAGER = "product_manager"
|
|
22
|
+
DEVELOPMENT_LEAD = "development_lead"
|
|
23
|
+
FRONTEND_DEV = "frontend_dev"
|
|
24
|
+
BACKEND_DEV = "backend_dev"
|
|
25
|
+
DESIGNER = "designer"
|
|
26
|
+
TESTER = "tester"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ActionType(Enum):
|
|
30
|
+
"""操作类型枚举。"""
|
|
31
|
+
CREATE_REQUIREMENTS = "CREATE_REQUIREMENTS"
|
|
32
|
+
REVIEW_DESIGN = "REVIEW_DESIGN"
|
|
33
|
+
SIGN_OFF = "SIGN_OFF"
|
|
34
|
+
MANAGE_PROJECT = "MANAGE_PROJECT"
|
|
35
|
+
WRITE_CODE = "WRITE_CODE"
|
|
36
|
+
CREATE_DESIGN = "CREATE_DESIGN"
|
|
37
|
+
CODE_REVIEW = "CODE_REVIEW"
|
|
38
|
+
WRITE_CODE_FRONTEND = "WRITE_CODE_FRONTEND"
|
|
39
|
+
WRITE_CODE_BACKEND = "WRITE_CODE_BACKEND"
|
|
40
|
+
REVIEW_DESIGN_FRONTEND = "REVIEW_DESIGN_FRONTEND"
|
|
41
|
+
API_DESIGN = "API_DESIGN"
|
|
42
|
+
UPLOAD_DESIGN = "UPLOAD_DESIGN"
|
|
43
|
+
EXECUTE_TEST = "EXECUTE_TEST"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class AgentConfig:
|
|
48
|
+
"""Agent 配置。"""
|
|
49
|
+
agent_id: str
|
|
50
|
+
agent_type: AgentType
|
|
51
|
+
name: str
|
|
52
|
+
role: str
|
|
53
|
+
responsibilities: List[str]
|
|
54
|
+
forbidden: List[str]
|
|
55
|
+
tech_stack: Optional[str] = None
|
|
56
|
+
created_at: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
57
|
+
last_active: Optional[str] = None
|
|
58
|
+
status: str = "active"
|
|
59
|
+
|
|
60
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
61
|
+
"""转换为字典。"""
|
|
62
|
+
return {
|
|
63
|
+
"agent_id": self.agent_id,
|
|
64
|
+
"agent_type": self.agent_type.value,
|
|
65
|
+
"name": self.name,
|
|
66
|
+
"role": self.role,
|
|
67
|
+
"responsibilities": self.responsibilities,
|
|
68
|
+
"forbidden": self.forbidden,
|
|
69
|
+
"tech_stack": self.tech_stack,
|
|
70
|
+
"created_at": self.created_at,
|
|
71
|
+
"last_active": self.last_active,
|
|
72
|
+
"status": self.status
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_dict(cls, data: Dict[str, Any]) -> "AgentConfig":
|
|
77
|
+
"""从字典创建。"""
|
|
78
|
+
return cls(
|
|
79
|
+
agent_id=data["agent_id"],
|
|
80
|
+
agent_type=AgentType(data["agent_type"]),
|
|
81
|
+
name=data["name"],
|
|
82
|
+
role=data["role"],
|
|
83
|
+
responsibilities=data["responsibilities"],
|
|
84
|
+
forbidden=data["forbidden"],
|
|
85
|
+
tech_stack=data.get("tech_stack"),
|
|
86
|
+
created_at=data.get("created_at", str(uuid.uuid4())),
|
|
87
|
+
last_active=data.get("last_active"),
|
|
88
|
+
status=data.get("status", "active")
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
AGENT_ROLE_CONFIG: Dict[AgentType, Dict[str, Any]] = {
|
|
93
|
+
AgentType.PRODUCT_MANAGER: {
|
|
94
|
+
"role": "产品经理/项目经理",
|
|
95
|
+
"allowed": [
|
|
96
|
+
ActionType.CREATE_REQUIREMENTS,
|
|
97
|
+
ActionType.REVIEW_DESIGN,
|
|
98
|
+
ActionType.SIGN_OFF,
|
|
99
|
+
ActionType.MANAGE_PROJECT
|
|
100
|
+
],
|
|
101
|
+
"forbidden": [
|
|
102
|
+
ActionType.WRITE_CODE,
|
|
103
|
+
ActionType.CREATE_DESIGN
|
|
104
|
+
],
|
|
105
|
+
"required": True,
|
|
106
|
+
"initial_count": 1
|
|
107
|
+
},
|
|
108
|
+
AgentType.DEVELOPMENT_LEAD: {
|
|
109
|
+
"role": "开发负责人",
|
|
110
|
+
"allowed": [
|
|
111
|
+
ActionType.CREATE_DESIGN,
|
|
112
|
+
ActionType.WRITE_CODE,
|
|
113
|
+
ActionType.CODE_REVIEW
|
|
114
|
+
],
|
|
115
|
+
"forbidden": [
|
|
116
|
+
],
|
|
117
|
+
"required": True,
|
|
118
|
+
"initial_count": 1
|
|
119
|
+
},
|
|
120
|
+
AgentType.FRONTEND_DEV: {
|
|
121
|
+
"role": "前端开发",
|
|
122
|
+
"allowed": [
|
|
123
|
+
ActionType.WRITE_CODE_FRONTEND,
|
|
124
|
+
ActionType.REVIEW_DESIGN_FRONTEND
|
|
125
|
+
],
|
|
126
|
+
"forbidden": [
|
|
127
|
+
ActionType.WRITE_CODE_BACKEND,
|
|
128
|
+
ActionType.CREATE_REQUIREMENTS
|
|
129
|
+
],
|
|
130
|
+
"required": False,
|
|
131
|
+
"initial_count": 0
|
|
132
|
+
},
|
|
133
|
+
AgentType.BACKEND_DEV: {
|
|
134
|
+
"role": "后端开发",
|
|
135
|
+
"allowed": [
|
|
136
|
+
ActionType.WRITE_CODE_BACKEND,
|
|
137
|
+
ActionType.API_DESIGN
|
|
138
|
+
],
|
|
139
|
+
"forbidden": [
|
|
140
|
+
ActionType.WRITE_CODE_FRONTEND,
|
|
141
|
+
ActionType.CREATE_REQUIREMENTS
|
|
142
|
+
],
|
|
143
|
+
"required": False,
|
|
144
|
+
"initial_count": 0
|
|
145
|
+
},
|
|
146
|
+
AgentType.DESIGNER: {
|
|
147
|
+
"role": "UI/UE 设计",
|
|
148
|
+
"allowed": [
|
|
149
|
+
ActionType.CREATE_DESIGN,
|
|
150
|
+
ActionType.UPLOAD_DESIGN,
|
|
151
|
+
ActionType.REVIEW_DESIGN
|
|
152
|
+
],
|
|
153
|
+
"forbidden": [
|
|
154
|
+
ActionType.WRITE_CODE,
|
|
155
|
+
ActionType.CREATE_REQUIREMENTS
|
|
156
|
+
],
|
|
157
|
+
"required": False,
|
|
158
|
+
"initial_count": 0
|
|
159
|
+
},
|
|
160
|
+
AgentType.TESTER: {
|
|
161
|
+
"role": "测试",
|
|
162
|
+
"allowed": [
|
|
163
|
+
ActionType.EXECUTE_TEST,
|
|
164
|
+
ActionType.REVIEW_DESIGN
|
|
165
|
+
],
|
|
166
|
+
"forbidden": [
|
|
167
|
+
ActionType.WRITE_CODE,
|
|
168
|
+
ActionType.CREATE_REQUIREMENTS,
|
|
169
|
+
ActionType.CREATE_DESIGN
|
|
170
|
+
],
|
|
171
|
+
"required": False,
|
|
172
|
+
"initial_count": 0
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class AgentManagerError(Exception):
|
|
178
|
+
"""Agent 管理异常基类。"""
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class AgentNotFoundError(AgentManagerError):
|
|
183
|
+
"""Agent 未找到异常。"""
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class AgentTypeNotSupportedError(AgentManagerError):
|
|
188
|
+
"""Agent 类型不支持异常。"""
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class AgentConstraintViolationError(AgentManagerError):
|
|
193
|
+
"""Agent 约束违反异常。"""
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class AgentManager:
|
|
198
|
+
"""Agent 管理器。"""
|
|
199
|
+
|
|
200
|
+
DEFAULT_AGENTS_DIR = "agents"
|
|
201
|
+
AGENT_CONFIG_FILE = "agent_config.yaml"
|
|
202
|
+
|
|
203
|
+
def __init__(self, project_path: str, agents_dir: Optional[str] = None):
|
|
204
|
+
"""初始化 Agent 管理器。
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
project_path: 项目路径
|
|
208
|
+
agents_dir: Agent 配置目录,默认为 project_path/agents
|
|
209
|
+
"""
|
|
210
|
+
self.project_path = Path(project_path)
|
|
211
|
+
self.agents_dir = self.project_path / (agents_dir or self.DEFAULT_AGENTS_DIR)
|
|
212
|
+
self.agents: Dict[str, AgentConfig] = {}
|
|
213
|
+
self._ensure_agents_directory()
|
|
214
|
+
|
|
215
|
+
def _ensure_agents_directory(self) -> None:
|
|
216
|
+
"""确保 Agent 目录存在。"""
|
|
217
|
+
self.agents_dir.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
|
|
219
|
+
def _generate_agent_id(self, agent_type: AgentType, tech_stack: Optional[str] = None) -> str:
|
|
220
|
+
"""生成 Agent ID。
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
agent_type: Agent 类型
|
|
224
|
+
tech_stack: 技术栈(可选)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Agent ID
|
|
228
|
+
"""
|
|
229
|
+
base_name = agent_type.value
|
|
230
|
+
if tech_stack:
|
|
231
|
+
base_name = f"{base_name}_{tech_stack}"
|
|
232
|
+
existing_count = sum(
|
|
233
|
+
1 for a in self.agents.values()
|
|
234
|
+
if a.agent_type == agent_type and (a.tech_stack or "") == (tech_stack or "")
|
|
235
|
+
)
|
|
236
|
+
return f"agent_{base_name}_{existing_count + 1}"
|
|
237
|
+
|
|
238
|
+
def _create_default_agents(self) -> List[AgentConfig]:
|
|
239
|
+
"""创建默认的 Agent 列表。
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
默认 Agent 列表
|
|
243
|
+
"""
|
|
244
|
+
agents = []
|
|
245
|
+
|
|
246
|
+
for agent_type, config in AGENT_ROLE_CONFIG.items():
|
|
247
|
+
for i in range(config["initial_count"]):
|
|
248
|
+
agent_id = self._generate_agent_id(agent_type)
|
|
249
|
+
agent = AgentConfig(
|
|
250
|
+
agent_id=agent_id,
|
|
251
|
+
agent_type=agent_type,
|
|
252
|
+
name=f"{config['role']} {i + 1}",
|
|
253
|
+
role=config["role"],
|
|
254
|
+
responsibilities=config["role"].split("/"),
|
|
255
|
+
forbidden=[a.value for a in config["forbidden"]],
|
|
256
|
+
tech_stack=None
|
|
257
|
+
)
|
|
258
|
+
agents.append(agent)
|
|
259
|
+
|
|
260
|
+
return agents
|
|
261
|
+
|
|
262
|
+
def initialize_agents(self, custom_agents: Optional[List[AgentConfig]] = None) -> List[AgentConfig]:
|
|
263
|
+
"""初始化 Agent。
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
custom_agents: 自定义 Agent 列表
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
初始化后的 Agent 列表
|
|
270
|
+
"""
|
|
271
|
+
if custom_agents:
|
|
272
|
+
agents = custom_agents
|
|
273
|
+
else:
|
|
274
|
+
agents = self._create_default_agents()
|
|
275
|
+
|
|
276
|
+
for agent in agents:
|
|
277
|
+
self.agents[agent.agent_id] = agent
|
|
278
|
+
self._save_agent_config(agent)
|
|
279
|
+
|
|
280
|
+
logger.info(f"初始化了 {len(agents)} 个 Agent")
|
|
281
|
+
return agents
|
|
282
|
+
|
|
283
|
+
def _save_agent_config(self, agent: AgentConfig) -> None:
|
|
284
|
+
"""保存 Agent 配置。
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
agent: Agent 配置
|
|
288
|
+
"""
|
|
289
|
+
config_file = self.agents_dir / f"{agent.agent_id}.yaml"
|
|
290
|
+
with open(config_file, 'w', encoding='utf-8') as f:
|
|
291
|
+
yaml.dump(agent.to_dict(), f, allow_unicode=True)
|
|
292
|
+
|
|
293
|
+
def load_agents(self) -> List[AgentConfig]:
|
|
294
|
+
"""加载所有 Agent 配置。
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Agent 配置列表
|
|
298
|
+
"""
|
|
299
|
+
self.agents.clear()
|
|
300
|
+
|
|
301
|
+
for config_file in self.agents_dir.glob("*.yaml"):
|
|
302
|
+
try:
|
|
303
|
+
with open(config_file, 'r', encoding='utf-8') as f:
|
|
304
|
+
data = yaml.safe_load(f)
|
|
305
|
+
if data:
|
|
306
|
+
agent = AgentConfig.from_dict(data)
|
|
307
|
+
self.agents[agent.agent_id] = agent
|
|
308
|
+
except Exception as e:
|
|
309
|
+
logger.warning(f"加载 Agent 配置失败: {config_file}, 错误: {e}")
|
|
310
|
+
|
|
311
|
+
return list(self.agents.values())
|
|
312
|
+
|
|
313
|
+
def add_agent(
|
|
314
|
+
self,
|
|
315
|
+
agent_type: AgentType,
|
|
316
|
+
tech_stack: Optional[str] = None,
|
|
317
|
+
name: Optional[str] = None
|
|
318
|
+
) -> AgentConfig:
|
|
319
|
+
"""添加新 Agent。
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
agent_type: Agent 类型
|
|
323
|
+
tech_stack: 技术栈(可选)
|
|
324
|
+
name: Agent 名称(可选)
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
新创建的 Agent 配置
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
AgentTypeNotSupportedError: Agent 类型不支持
|
|
331
|
+
"""
|
|
332
|
+
if agent_type not in AGENT_ROLE_CONFIG:
|
|
333
|
+
raise AgentTypeNotSupportedError(f"不支持的 Agent 类型: {agent_type}")
|
|
334
|
+
|
|
335
|
+
role_config = AGENT_ROLE_CONFIG[agent_type]
|
|
336
|
+
agent_id = self._generate_agent_id(agent_type, tech_stack)
|
|
337
|
+
|
|
338
|
+
agent = AgentConfig(
|
|
339
|
+
agent_id=agent_id,
|
|
340
|
+
agent_type=agent_type,
|
|
341
|
+
name=name or f"{role_config['role']} {len([a for a in self.agents.values() if a.agent_type == agent_type]) + 1}",
|
|
342
|
+
role=role_config["role"],
|
|
343
|
+
responsibilities=role_config["role"].split("/"),
|
|
344
|
+
forbidden=[a.value for a in role_config["forbidden"]],
|
|
345
|
+
tech_stack=tech_stack
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
self.agents[agent.agent_id] = agent
|
|
349
|
+
self._save_agent_config(agent)
|
|
350
|
+
|
|
351
|
+
logger.info(f"添加了新 Agent: {agent_id} ({agent_type.value})")
|
|
352
|
+
return agent
|
|
353
|
+
|
|
354
|
+
def remove_agent(self, agent_id: str) -> bool:
|
|
355
|
+
"""移除 Agent。
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
agent_id: Agent ID
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
是否成功移除
|
|
362
|
+
|
|
363
|
+
Raises:
|
|
364
|
+
AgentNotFoundError: Agent 未找到
|
|
365
|
+
"""
|
|
366
|
+
if agent_id not in self.agents:
|
|
367
|
+
raise AgentNotFoundError(f"Agent 未找到: {agent_id}")
|
|
368
|
+
|
|
369
|
+
agent = self.agents.pop(agent_id)
|
|
370
|
+
config_file = self.agents_dir / f"{agent_id}.yaml"
|
|
371
|
+
|
|
372
|
+
if config_file.exists():
|
|
373
|
+
config_file.unlink()
|
|
374
|
+
|
|
375
|
+
logger.info(f"移除了 Agent: {agent_id}")
|
|
376
|
+
return True
|
|
377
|
+
|
|
378
|
+
def get_agent(self, agent_id: str) -> AgentConfig:
|
|
379
|
+
"""获取 Agent 配置。
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
agent_id: Agent ID
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Agent 配置
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
AgentNotFoundError: Agent 未找到
|
|
389
|
+
"""
|
|
390
|
+
if agent_id not in self.agents:
|
|
391
|
+
raise AgentNotFoundError(f"Agent 未找到: {agent_id}")
|
|
392
|
+
return self.agents[agent_id]
|
|
393
|
+
|
|
394
|
+
def list_agents(self, agent_type: Optional[AgentType] = None, status: Optional[str] = None) -> List[AgentConfig]:
|
|
395
|
+
"""列出 Agent。
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
agent_type: Agent 类型过滤
|
|
399
|
+
status: 状态过滤
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
Agent 配置列表
|
|
403
|
+
"""
|
|
404
|
+
agents = list(self.agents.values())
|
|
405
|
+
|
|
406
|
+
if agent_type:
|
|
407
|
+
agents = [a for a in agents if a.agent_type == agent_type]
|
|
408
|
+
|
|
409
|
+
if status:
|
|
410
|
+
agents = [a for a in agents if a.status == status]
|
|
411
|
+
|
|
412
|
+
return agents
|
|
413
|
+
|
|
414
|
+
def check_action_allowed(self, agent_id: str, action: ActionType) -> bool:
|
|
415
|
+
"""检查 Agent 是否允许执行某个操作。
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
agent_id: Agent ID
|
|
419
|
+
action: 操作类型
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
是否允许
|
|
423
|
+
|
|
424
|
+
Raises:
|
|
425
|
+
AgentNotFoundError: Agent 未找到
|
|
426
|
+
"""
|
|
427
|
+
agent = self.get_agent(agent_id)
|
|
428
|
+
|
|
429
|
+
if action.value in agent.forbidden:
|
|
430
|
+
return False
|
|
431
|
+
|
|
432
|
+
if agent.agent_type in AGENT_ROLE_CONFIG:
|
|
433
|
+
role_config = AGENT_ROLE_CONFIG[agent.agent_type]
|
|
434
|
+
return action in role_config["allowed"]
|
|
435
|
+
|
|
436
|
+
return True
|
|
437
|
+
|
|
438
|
+
def get_allowed_actions(self, agent_id: str) -> Set[ActionType]:
|
|
439
|
+
"""获取 Agent 允许的操作列表。
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
agent_id: Agent ID
|
|
443
|
+
|
|
444
|
+
Returns:
|
|
445
|
+
允许的操作集合
|
|
446
|
+
|
|
447
|
+
Raises:
|
|
448
|
+
AgentNotFoundError: Agent 未找到
|
|
449
|
+
"""
|
|
450
|
+
agent = self.get_agent(agent_id)
|
|
451
|
+
|
|
452
|
+
if agent.agent_type in AGENT_ROLE_CONFIG:
|
|
453
|
+
return set(AGENT_ROLE_CONFIG[agent.agent_type]["allowed"])
|
|
454
|
+
|
|
455
|
+
return set()
|
|
456
|
+
|
|
457
|
+
def get_forbidden_actions(self, agent_id: str) -> Set[ActionType]:
|
|
458
|
+
"""获取 Agent 被禁止的操作列表。
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
agent_id: Agent ID
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
被禁止的操作集合
|
|
465
|
+
|
|
466
|
+
Raises:
|
|
467
|
+
AgentNotFoundError: Agent 未找到
|
|
468
|
+
"""
|
|
469
|
+
agent = self.get_agent(agent_id)
|
|
470
|
+
return set(ActionType(a) for a in agent.forbidden)
|
|
471
|
+
|
|
472
|
+
def update_agent_status(self, agent_id: str, status: str) -> AgentConfig:
|
|
473
|
+
"""更新 Agent 状态。
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
agent_id: Agent ID
|
|
477
|
+
status: 新状态
|
|
478
|
+
|
|
479
|
+
Returns:
|
|
480
|
+
更新后的 Agent 配置
|
|
481
|
+
|
|
482
|
+
Raises:
|
|
483
|
+
AgentNotFoundError: Agent 未找到
|
|
484
|
+
"""
|
|
485
|
+
agent = self.get_agent(agent_id)
|
|
486
|
+
agent.status = status
|
|
487
|
+
self._save_agent_config(agent)
|
|
488
|
+
|
|
489
|
+
return agent
|
|
490
|
+
|
|
491
|
+
def get_agent_summary(self) -> Dict[str, Any]:
|
|
492
|
+
"""获取 Agent 管理摘要。
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
摘要信息
|
|
496
|
+
"""
|
|
497
|
+
agent_counts = {}
|
|
498
|
+
for agent_type in AgentType:
|
|
499
|
+
count = len(self.list_agents(agent_type=agent_type))
|
|
500
|
+
agent_counts[agent_type.value] = count
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
"total_agents": len(self.agents),
|
|
504
|
+
"by_type": agent_counts,
|
|
505
|
+
"agents_dir": str(self.agents_dir)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
def export_config(self, output_path: Optional[str] = None) -> Dict[str, Any]:
|
|
509
|
+
"""导出 Agent 管理配置。
|
|
510
|
+
|
|
511
|
+
Args:
|
|
512
|
+
output_path: 输出路径(可选)
|
|
513
|
+
|
|
514
|
+
Returns:
|
|
515
|
+
配置字典
|
|
516
|
+
"""
|
|
517
|
+
config = {
|
|
518
|
+
"project_path": str(self.project_path),
|
|
519
|
+
"agents_dir": str(self.agents_dir),
|
|
520
|
+
"agents": [agent.to_dict() for agent in self.agents.values()],
|
|
521
|
+
"agent_types": {
|
|
522
|
+
at.value: {
|
|
523
|
+
"role": AGENT_ROLE_CONFIG[at]["role"],
|
|
524
|
+
"required": AGENT_ROLE_CONFIG[at]["required"]
|
|
525
|
+
}
|
|
526
|
+
for at in AgentType
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if output_path:
|
|
531
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
532
|
+
yaml.dump(config, f, allow_unicode=True)
|
|
533
|
+
|
|
534
|
+
return config
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
if __name__ == "__main__":
|
|
538
|
+
import tempfile
|
|
539
|
+
|
|
540
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
541
|
+
manager = AgentManager(tmpdir)
|
|
542
|
+
|
|
543
|
+
agents = manager.initialize_agents()
|
|
544
|
+
print(f"初始化了 {len(agents)} 个 Agent")
|
|
545
|
+
|
|
546
|
+
for agent in agents:
|
|
547
|
+
print(f" - {agent.agent_id}: {agent.role}")
|
|
548
|
+
|
|
549
|
+
new_agent = manager.add_agent(AgentType.FRONTEND_DEV, "react")
|
|
550
|
+
print(f"\n添加了新 Agent: {new_agent.agent_id}")
|
|
551
|
+
|
|
552
|
+
summary = manager.get_agent_summary()
|
|
553
|
+
print(f"\nAgent 摘要: {summary}")
|