myagent-ai 1.0.0 → 1.2.0
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.
- package/README.md +41 -22
- package/agents/__pycache__/main_agent.cpython-312.pyc +0 -0
- package/agents/base.py +57 -0
- package/agents/main_agent.py +153 -44
- package/agents/memory_agent.py +25 -5
- package/communication/manager.py +20 -0
- package/core/__init__.py +2 -8
- package/core/config_broadcast.py +48 -17
- package/core/config_validator.py +740 -0
- package/core/context_manager.py +506 -0
- package/core/llm.py +48 -4
- package/core/permissions.py +372 -0
- package/core/task_persistence.py +359 -0
- package/core/task_queue.py +78 -0
- package/core/update_manager.py +895 -0
- package/core/version.py +49 -0
- package/departments/__init__.py +4 -0
- package/departments/__pycache__/__init__.cpython-312.pyc +0 -0
- package/departments/__pycache__/manager.cpython-312.pyc +0 -0
- package/departments/manager.py +954 -0
- package/executor/engine.py +172 -26
- package/install/install.ps1 +221 -0
- package/install/install.sh +308 -0
- package/main.py +386 -82
- package/organization/__pycache__/manager.cpython-312.pyc +0 -0
- package/organization/manager.py +20 -4
- package/package.json +12 -4
- package/requirements.txt +21 -8
- package/setup.py +14 -3
- package/skills/base.py +2 -0
- package/skills/registry.py +80 -17
- package/skills/system_skill.py +10 -1
- package/start.sh +161 -25
- package/web/__pycache__/api_server.cpython-312.pyc +0 -0
- package/web/api_server.py +1439 -45
- package/web/tts_handler.py +196 -0
- package/web/ui/chat.html +2131 -34
- package/web/ui/index.html +537 -221
- package/Dockerfile +0 -30
- package/skills/LLM/LICENSE.txt +0 -21
- package/skills/LLM/SKILL.md +0 -856
- package/skills/LLM/scripts/chat.ts +0 -32
- package/skills/agent-browser/SKILL.md +0 -328
- package/skills/auto-target-tracker/SKILL.md +0 -317
- package/skills/content-strategy/SKILL.md +0 -181
- package/skills/content-strategy/_meta.json +0 -6
- package/skills/dream-interpreter/SKILL.md +0 -88
- package/skills/dream-interpreter/assets/example_asset.txt +0 -24
- package/skills/dream-interpreter/references/api_reference.md +0 -34
- package/skills/dream-interpreter/references/interpretation-guide.md +0 -83
- package/skills/dream-interpreter/references/output-schema.md +0 -65
- package/skills/dream-interpreter/references/questioning-strategy.md +0 -62
- package/skills/dream-interpreter/references/visual-mapping.md +0 -81
- package/skills/dream-interpreter/scripts/__pycache__/example.cpython-312.pyc +0 -0
- package/skills/dream-interpreter/scripts/example.py +0 -19
- package/skills/dream-interpreter/skill.json +0 -7
- package/skills/fullstack-dev/SKILL.md +0 -205
- package/skills/get-fortune-analysis/SKILL.md +0 -370
- package/skills/get-fortune-analysis/lunar_python.py +0 -91
- package/skills/gift-evaluator/SKILL.md +0 -83
- package/skills/gift-evaluator/__pycache__/html_tools.cpython-312.pyc +0 -0
- package/skills/gift-evaluator/html_tools.py +0 -268
- package/skills/mindfulness-meditation/SKILL.md +0 -65
- package/skills/mindfulness-meditation/_meta.json +0 -6
- package/skills/seo-content-writer/SKILL.md +0 -661
- package/skills/seo-content-writer/_meta.json +0 -6
- package/skills/seo-content-writer/references/content-structure-templates.md +0 -875
- package/skills/seo-content-writer/references/title-formulas.md +0 -339
- package/skills/skill-finder-cn/SKILL.md +0 -66
- package/skills/skill-finder-cn/_meta.json +0 -6
- package/skills/skill-finder-cn/package.json +0 -5
- package/skills/skill-finder-cn/scripts/search.sh +0 -15
- package/skills/web-reader/LICENSE.txt +0 -21
- package/skills/web-reader/SKILL.md +0 -1140
- package/skills/web-reader/scripts/web-reader.ts +0 -37
- package/skills/web-search/LICENSE.txt +0 -21
- package/skills/web-search/SKILL.md +0 -912
- package/skills/web-search/scripts/web_search.ts +0 -44
package/README.md
CHANGED
|
@@ -105,26 +105,45 @@ myagent/
|
|
|
105
105
|
|
|
106
106
|
---
|
|
107
107
|
|
|
108
|
-
## 🚀
|
|
108
|
+
## 🚀 一键安装
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
> 💡 **无需手动安装 Python、Node.js 等依赖**,脚本会自动检测并安装所有前置条件。仅复制一行命令即可完成安装。
|
|
111
|
+
|
|
112
|
+
### Windows(PowerShell)
|
|
113
|
+
|
|
114
|
+
```powershell
|
|
115
|
+
powershell -c "irm https://raw.githubusercontent.com/ctz168/myagent/main/install/install.ps1 | iex"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
> 首次运行时如果 PowerShell 提示执行策略限制,先执行:`Set-ExecutionPolicy RemoteSigned -Scope CurrentUser`
|
|
119
|
+
|
|
120
|
+
### macOS / Linux
|
|
111
121
|
|
|
112
122
|
```bash
|
|
113
|
-
|
|
114
|
-
git clone https://github.com/ctz168/myagent.git
|
|
115
|
-
cd myagent
|
|
116
|
-
|
|
117
|
-
# 安装核心依赖
|
|
118
|
-
pip install -r requirements.txt
|
|
119
|
-
|
|
120
|
-
# 按需安装聊天平台
|
|
121
|
-
pip install python-telegram-bot # Telegram
|
|
122
|
-
pip install discord.py # Discord
|
|
123
|
-
pip install playwright && playwright install chromium # 浏览器
|
|
124
|
-
pip install anthropic # Claude
|
|
123
|
+
curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash
|
|
125
124
|
```
|
|
126
125
|
|
|
127
|
-
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 📋 系统要求
|
|
129
|
+
|
|
130
|
+
| 项目 | 最低要求 | 推荐配置 |
|
|
131
|
+
|------|----------|----------|
|
|
132
|
+
| **操作系统** | Windows 10 / macOS 12 / Ubuntu 20.04 | Windows 11 / macOS 14 / Ubuntu 24.04 |
|
|
133
|
+
| **Python** | 3.10+ | 3.12+ |
|
|
134
|
+
| **Node.js** | 18+ (LTS) | 20+ (LTS) |
|
|
135
|
+
| **内存** | 512 MB | 2 GB+ |
|
|
136
|
+
| **磁盘空间** | 200 MB | 500 MB+ |
|
|
137
|
+
|
|
138
|
+
> 安装脚本会自动检测并安装 Python 3.12 和 Node.js 20 LTS,你不需要提前准备任何环境。
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## ⚙️ 安装后配置
|
|
143
|
+
|
|
144
|
+
安装完成后,运行 `myagent-ai` 即可启动。首次运行会自动创建配置文件 `~/.myagent/config.json`。
|
|
145
|
+
|
|
146
|
+
### 1. 配置 LLM
|
|
128
147
|
|
|
129
148
|
**方式一: 环境变量 (推荐)**
|
|
130
149
|
```bash
|
|
@@ -135,7 +154,7 @@ export MYAGENT_LLM_MODEL="gpt-4"
|
|
|
135
154
|
**方式二: 配置文件**
|
|
136
155
|
```bash
|
|
137
156
|
# 首次运行会自动创建 ~/.myagent/config.json
|
|
138
|
-
|
|
157
|
+
myagent-ai
|
|
139
158
|
```
|
|
140
159
|
|
|
141
160
|
**方式三: 使用 Ollama 本地模型 (免费)**
|
|
@@ -155,23 +174,23 @@ export MYAGENT_LLM_PROVIDER="anthropic"
|
|
|
155
174
|
export MYAGENT_ANTHROPIC_API_KEY="sk-ant-..."
|
|
156
175
|
```
|
|
157
176
|
|
|
158
|
-
###
|
|
177
|
+
### 2. 运行
|
|
159
178
|
|
|
160
179
|
```bash
|
|
161
180
|
# 交互式命令行
|
|
162
|
-
|
|
181
|
+
myagent-ai
|
|
163
182
|
|
|
164
183
|
# 系统托盘后台运行
|
|
165
|
-
|
|
184
|
+
myagent-ai --tray
|
|
166
185
|
|
|
167
186
|
# 调试模式
|
|
168
|
-
|
|
187
|
+
myagent-ai --debug
|
|
169
188
|
|
|
170
189
|
# 设置开机自启
|
|
171
|
-
|
|
190
|
+
myagent-ai --autostart
|
|
172
191
|
```
|
|
173
192
|
|
|
174
|
-
###
|
|
193
|
+
### 3. 配置聊天平台 (可选)
|
|
175
194
|
|
|
176
195
|
编辑 `~/.myagent/config.json`:
|
|
177
196
|
|
|
Binary file
|
package/agents/base.py
CHANGED
|
@@ -18,6 +18,21 @@ from core.utils import generate_id, timestamp
|
|
|
18
18
|
logger = get_logger("myagent.agent")
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
# 全局权限管理器实例(由 main.py 初始化)
|
|
22
|
+
_global_permission_manager = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def set_permission_manager(pm):
|
|
26
|
+
"""设置全局权限管理器(应用启动时调用一次)"""
|
|
27
|
+
global _global_permission_manager
|
|
28
|
+
_global_permission_manager = pm
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_permission_manager():
|
|
32
|
+
"""获取全局权限管理器"""
|
|
33
|
+
return _global_permission_manager
|
|
34
|
+
|
|
35
|
+
|
|
21
36
|
@dataclass
|
|
22
37
|
class AgentContext:
|
|
23
38
|
"""Agent 上下文,在 Agent 之间传递"""
|
|
@@ -59,6 +74,7 @@ class BaseAgent(ABC):
|
|
|
59
74
|
self.task_queue = task_queue
|
|
60
75
|
self.config = config or {}
|
|
61
76
|
self.config_broadcaster = config_broadcaster
|
|
77
|
+
self._permission_manager = None # 延迟绑定,在 process 时获取
|
|
62
78
|
self._stats = {"total_tasks": 0, "success": 0, "failed": 0}
|
|
63
79
|
|
|
64
80
|
@abstractmethod
|
|
@@ -113,3 +129,44 @@ class BaseAgent(ABC):
|
|
|
113
129
|
|
|
114
130
|
def get_stats(self) -> Dict:
|
|
115
131
|
return dict(self._stats)
|
|
132
|
+
|
|
133
|
+
# ── 权限检查 ────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def permission_manager(self):
|
|
137
|
+
"""获取权限管理器(延迟绑定)"""
|
|
138
|
+
if self._permission_manager is None:
|
|
139
|
+
from agents.base import get_permission_manager
|
|
140
|
+
self._permission_manager = get_permission_manager()
|
|
141
|
+
return self._permission_manager
|
|
142
|
+
|
|
143
|
+
def check_permission(self, permission: str) -> bool:
|
|
144
|
+
"""
|
|
145
|
+
检查当前 agent 是否拥有某项权限。
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
permission: 权限项名称 (execution/file_read/file_write/network/local_comm/remote_comm)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
True 如果权限开启,或权限管理器未初始化(向后兼容)
|
|
152
|
+
"""
|
|
153
|
+
pm = self.permission_manager
|
|
154
|
+
if pm is None:
|
|
155
|
+
# 权限管理器未初始化,允许所有操作(向后兼容)
|
|
156
|
+
return True
|
|
157
|
+
return pm.check_permission(self.name, permission)
|
|
158
|
+
|
|
159
|
+
def require_permission(self, permission: str) -> bool:
|
|
160
|
+
"""
|
|
161
|
+
要求指定权限,权限不足时记录警告并返回 False。
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
permission: 权限项名称
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
True 如果权限通过
|
|
168
|
+
"""
|
|
169
|
+
pm = self.permission_manager
|
|
170
|
+
if pm is None:
|
|
171
|
+
return True
|
|
172
|
+
return pm.require_permission(self.name, permission)
|
package/agents/main_agent.py
CHANGED
|
@@ -13,6 +13,7 @@ from core.logger import get_logger
|
|
|
13
13
|
from core.llm import LLMClient, LLMResponse, Message
|
|
14
14
|
from agents.base import BaseAgent, AgentContext
|
|
15
15
|
from core.utils import generate_id, timestamp, safe_json_parse, truncate_str
|
|
16
|
+
from core.context_manager import ContextManager, ContextConfig, estimate_tokens
|
|
16
17
|
|
|
17
18
|
logger = get_logger("myagent.agent.main")
|
|
18
19
|
|
|
@@ -111,7 +112,9 @@ class MainAgent(BaseAgent):
|
|
|
111
112
|
self._registered_task: bool = False
|
|
112
113
|
# 组织上下文缓存(避免重复加载)
|
|
113
114
|
self._org_context_cache: str = ""
|
|
114
|
-
self.
|
|
115
|
+
self._org_context_mtime: float = 0 # 文件修改时间,用于检测变更
|
|
116
|
+
# Token 上下文管理器 (滚动摘要 + 预算控制)
|
|
117
|
+
self.context_manager = ContextManager(ContextConfig())
|
|
115
118
|
|
|
116
119
|
async def process(self, context: AgentContext) -> AgentContext:
|
|
117
120
|
"""
|
|
@@ -180,12 +183,15 @@ class MainAgent(BaseAgent):
|
|
|
180
183
|
|
|
181
184
|
# 检查配置热加载广播(每次迭代前检查)
|
|
182
185
|
if self.config_broadcaster:
|
|
183
|
-
reloaded = await self.config_broadcaster.check_and_wait(task_id)
|
|
186
|
+
reloaded, reload_type = await self.config_broadcaster.check_and_wait(task_id)
|
|
184
187
|
if reloaded:
|
|
185
|
-
logger.info(f"[{task_id}] 🔄
|
|
188
|
+
logger.info(f"[{task_id}] 🔄 {reload_type}已热更新,继续执行 (iteration={self._iteration_count})")
|
|
186
189
|
# 更新迭代检查点
|
|
187
190
|
if self.config_broadcaster._active_tasks.get(task_id):
|
|
188
191
|
self.config_broadcaster._active_tasks[task_id].iteration = self._iteration_count
|
|
192
|
+
# 代码热更新后,LLM 客户端可能已被刷新,下次调用会自动重建
|
|
193
|
+
if reload_type == "code":
|
|
194
|
+
logger.info(f"[{task_id}] 代码模块已热更新,LLM 客户端将在下次调用时自动重建")
|
|
189
195
|
|
|
190
196
|
# 构建消息列表
|
|
191
197
|
messages = self._build_messages(context)
|
|
@@ -205,21 +211,22 @@ class MainAgent(BaseAgent):
|
|
|
205
211
|
|
|
206
212
|
# 检查是否有工具调用 (OpenAI function calling)
|
|
207
213
|
if response.tool_calls:
|
|
214
|
+
# 先将 assistant 的 tool_calls 消息加入历史(OpenAI 格式要求)
|
|
215
|
+
context.conversation_history.append(
|
|
216
|
+
Message(role="assistant",
|
|
217
|
+
content=response.content or "",
|
|
218
|
+
tool_calls=response.tool_calls)
|
|
219
|
+
)
|
|
220
|
+
# 执行工具调用
|
|
208
221
|
tool_results = await self._handle_tool_calls(
|
|
209
222
|
response.tool_calls, context, task_id
|
|
210
223
|
)
|
|
211
|
-
#
|
|
224
|
+
# 再将工具结果加入消息历史
|
|
212
225
|
for tc, result in tool_results:
|
|
213
226
|
context.conversation_history.append(
|
|
214
227
|
Message(role="tool", content=json.dumps(result, ensure_ascii=False),
|
|
215
228
|
tool_call_id=tc["id"], name=tc["name"])
|
|
216
229
|
)
|
|
217
|
-
context.conversation_history.append(
|
|
218
|
-
Message(role="assistant",
|
|
219
|
-
content=f"[已调用工具 {tc['name']},结果: "
|
|
220
|
-
f"{'成功' if result.get('success') else '失败'}]",
|
|
221
|
-
tool_calls=[tc])
|
|
222
|
-
)
|
|
223
230
|
# 继续循环,让 LLM 处理工具结果
|
|
224
231
|
continue
|
|
225
232
|
|
|
@@ -275,6 +282,39 @@ class MainAgent(BaseAgent):
|
|
|
275
282
|
final_response += "\n\n已完成: " + " → ".join(action_data["plan"])
|
|
276
283
|
break
|
|
277
284
|
|
|
285
|
+
# 如果有超时且诊断建议不重试,强制中断循环避免无效重试
|
|
286
|
+
if has_timeout:
|
|
287
|
+
should_abort = False
|
|
288
|
+
abort_reasons = []
|
|
289
|
+
for i, r in enumerate(results, 1):
|
|
290
|
+
if r.get("timed_out"):
|
|
291
|
+
diag = r.get("timeout_diagnosis", {})
|
|
292
|
+
if diag.get("should_retry") is False:
|
|
293
|
+
should_abort = True
|
|
294
|
+
abort_reasons.append(
|
|
295
|
+
f"命令{i}: {diag.get('diagnosis', '不可恢复的超时')}"
|
|
296
|
+
)
|
|
297
|
+
if should_abort:
|
|
298
|
+
logger.warning(
|
|
299
|
+
f"[{task_id}] ⛔ 超时诊断建议不重试,终止当前执行循环 "
|
|
300
|
+
f"(iteration={self._iteration_count})"
|
|
301
|
+
)
|
|
302
|
+
# 构建诊断反馈,强制LLM输出final_answer
|
|
303
|
+
abort_msg = (
|
|
304
|
+
"[系统通知] 以下命令因超时被终止,且超时诊断结果表明不应重试:\n"
|
|
305
|
+
)
|
|
306
|
+
for reason in abort_reasons:
|
|
307
|
+
abort_msg += f"- {reason}\n"
|
|
308
|
+
abort_msg += (
|
|
309
|
+
"\n请直接以纯文本或 {\"type\": \"final_answer\", \"content\": \"...\"} "
|
|
310
|
+
"格式回复,告知用户任务无法完成的原因和建议的替代方案。"
|
|
311
|
+
"不要再尝试执行相同的命令。"
|
|
312
|
+
)
|
|
313
|
+
context.conversation_history.append(
|
|
314
|
+
Message(role="user", content=abort_msg)
|
|
315
|
+
)
|
|
316
|
+
continue # 让LLM生成final_answer,而不是重试命令
|
|
317
|
+
|
|
278
318
|
continue
|
|
279
319
|
|
|
280
320
|
# 单个 action
|
|
@@ -300,44 +340,59 @@ class MainAgent(BaseAgent):
|
|
|
300
340
|
|
|
301
341
|
logger.info(f"[{task_id}] 处理完成 (迭代 {self._iteration_count} 次)")
|
|
302
342
|
|
|
303
|
-
#
|
|
343
|
+
# 检查是否需要增量总结对话 (使用滚动摘要,不再清空短期记忆)
|
|
304
344
|
if self.memory_agent and self._iteration_count > 5:
|
|
305
345
|
try:
|
|
306
|
-
|
|
346
|
+
summary_context = AgentContext(
|
|
307
347
|
session_id=context.session_id,
|
|
308
348
|
metadata={"memory_action": "summarize"},
|
|
309
|
-
)
|
|
349
|
+
)
|
|
350
|
+
await self.memory_agent.process(summary_context)
|
|
351
|
+
# 将生成的摘要更新到 ContextManager 的滚动摘要缓冲
|
|
352
|
+
summary_result = summary_context.working_memory.get("summary_result", "")
|
|
353
|
+
if summary_result:
|
|
354
|
+
self.context_manager.summary_buffer.update_summary(
|
|
355
|
+
session_id=context.session_id,
|
|
356
|
+
new_summary=summary_result,
|
|
357
|
+
summarized_count=summary_context.working_memory.get("summarized_count", 0),
|
|
358
|
+
)
|
|
359
|
+
logger.info(
|
|
360
|
+
f"[{task_id}] 滚动摘要已更新 (session={context.session_id})"
|
|
361
|
+
)
|
|
310
362
|
except Exception as e:
|
|
311
363
|
logger.warning(f"对话总结失败: {e}")
|
|
312
364
|
|
|
313
365
|
return context
|
|
314
366
|
|
|
315
367
|
def _build_messages(self, context: AgentContext) -> List[Message]:
|
|
316
|
-
"""构建完整的消息列表"""
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
# 添加组织上下文(如果已启用)
|
|
368
|
+
"""构建完整的消息列表 (使用 ContextManager 进行 Token 预算优化)"""
|
|
369
|
+
# 合并系统提示 + 组织上下文
|
|
370
|
+
system_prompt = self.SYSTEM_PROMPT
|
|
320
371
|
org_context = self._build_org_context(context)
|
|
321
372
|
if org_context:
|
|
322
|
-
|
|
323
|
-
role="system",
|
|
324
|
-
content=org_context,
|
|
325
|
-
))
|
|
373
|
+
system_prompt += "\n\n" + org_context
|
|
326
374
|
|
|
327
|
-
#
|
|
375
|
+
# 记忆上下文
|
|
328
376
|
memory_ctx = context.working_memory.get("memory_context_prompt", "")
|
|
329
|
-
if memory_ctx:
|
|
330
|
-
messages.append(Message(
|
|
331
|
-
role="system",
|
|
332
|
-
content=f"[记忆上下文]\n{memory_ctx}",
|
|
333
|
-
))
|
|
334
|
-
|
|
335
|
-
# 添加对话历史
|
|
336
|
-
for msg in context.conversation_history[-20:]:
|
|
337
|
-
messages.append(msg)
|
|
338
377
|
|
|
339
|
-
#
|
|
340
|
-
|
|
378
|
+
# 使用 ContextManager 构建优化后的消息列表
|
|
379
|
+
raw_messages = self.context_manager.build_context(
|
|
380
|
+
system_prompt=system_prompt,
|
|
381
|
+
memory_context=memory_ctx,
|
|
382
|
+
conversation_history=context.conversation_history,
|
|
383
|
+
user_message=context.user_message,
|
|
384
|
+
session_id=context.session_id,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# 转换回 Message 对象 (ContextManager 返回 dict)
|
|
388
|
+
messages = []
|
|
389
|
+
for m in raw_messages:
|
|
390
|
+
if isinstance(m, dict):
|
|
391
|
+
messages.append(Message(**m))
|
|
392
|
+
elif isinstance(m, Message):
|
|
393
|
+
messages.append(m)
|
|
394
|
+
else:
|
|
395
|
+
messages.append(m)
|
|
341
396
|
|
|
342
397
|
return messages
|
|
343
398
|
|
|
@@ -351,6 +406,31 @@ class MainAgent(BaseAgent):
|
|
|
351
406
|
logger.warning(f"获取工具列表失败: {e}")
|
|
352
407
|
return None
|
|
353
408
|
|
|
409
|
+
@staticmethod
|
|
410
|
+
def _get_skill_permission(skill_name: str) -> Optional[str]:
|
|
411
|
+
"""
|
|
412
|
+
根据技能名称映射到对应的权限项。
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
权限项名称,或 None(不需要特殊权限的技能)
|
|
416
|
+
"""
|
|
417
|
+
# 文件技能
|
|
418
|
+
if skill_name in ("file_write", "file_delete", "file_move"):
|
|
419
|
+
return "file_write"
|
|
420
|
+
if skill_name in ("file_read", "file_list", "file_search"):
|
|
421
|
+
return "file_read"
|
|
422
|
+
# 网络/搜索技能
|
|
423
|
+
if skill_name in ("web_search", "web_read", "url_read"):
|
|
424
|
+
return "network"
|
|
425
|
+
# 系统技能涉及执行
|
|
426
|
+
if skill_name in ("command_run",):
|
|
427
|
+
return "execution"
|
|
428
|
+
# 浏览器技能需要网络
|
|
429
|
+
if skill_name in ("browser_open", "browser_click", "browser_fill"):
|
|
430
|
+
return "network"
|
|
431
|
+
# 其他技能不需要特殊权限检查
|
|
432
|
+
return None
|
|
433
|
+
|
|
354
434
|
async def _handle_tool_calls(
|
|
355
435
|
self,
|
|
356
436
|
tool_calls: List[Dict],
|
|
@@ -397,6 +477,19 @@ class MainAgent(BaseAgent):
|
|
|
397
477
|
action_type = action.get("type", "")
|
|
398
478
|
|
|
399
479
|
if action_type == "skill" and self.skills:
|
|
480
|
+
# 权限检查: 根据技能类别检查权限
|
|
481
|
+
skill_name = action.get("name", "")
|
|
482
|
+
skill_perm = self._get_skill_permission(skill_name)
|
|
483
|
+
if skill_perm and not self.check_permission(skill_perm):
|
|
484
|
+
from core.permissions import PermissionManager
|
|
485
|
+
label = PermissionManager.PERMISSION_LABELS.get(skill_perm, skill_perm)
|
|
486
|
+
results.append({
|
|
487
|
+
"success": False,
|
|
488
|
+
"error": f"[权限] 当前 Agent 没有'{label}'权限,操作被拒绝",
|
|
489
|
+
"metadata": {"permission_denied": skill_perm},
|
|
490
|
+
})
|
|
491
|
+
continue
|
|
492
|
+
|
|
400
493
|
result = await self.skills.execute(
|
|
401
494
|
action.get("name", ""),
|
|
402
495
|
**action.get("params", {}),
|
|
@@ -404,6 +497,19 @@ class MainAgent(BaseAgent):
|
|
|
404
497
|
results.append(result.to_dict())
|
|
405
498
|
|
|
406
499
|
elif action_type == "code" and self.executor:
|
|
500
|
+
# 权限检查: execution
|
|
501
|
+
if not self.check_permission("execution"):
|
|
502
|
+
results.append({
|
|
503
|
+
"success": False,
|
|
504
|
+
"error": "[权限] 当前 Agent 没有代码执行权限,操作被拒绝",
|
|
505
|
+
"metadata": {"permission_denied": "execution"},
|
|
506
|
+
})
|
|
507
|
+
continue
|
|
508
|
+
|
|
509
|
+
# 注入权限检查器到 executor(用于更细粒度的检查)
|
|
510
|
+
self.executor.set_permission_checker(
|
|
511
|
+
self.check_permission, self.name
|
|
512
|
+
)
|
|
407
513
|
# 提取 LLM 预估的超时时间
|
|
408
514
|
timeout_seconds = action.get("timeout_seconds")
|
|
409
515
|
if timeout_seconds is None:
|
|
@@ -636,22 +742,25 @@ class MainAgent(BaseAgent):
|
|
|
636
742
|
|
|
637
743
|
parts = []
|
|
638
744
|
|
|
639
|
-
# 1. 读取 organization.md
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
745
|
+
# 1. 读取 organization.md(使用 mtime 缓存检测变更)
|
|
746
|
+
try:
|
|
747
|
+
from config import get_config
|
|
748
|
+
config_mgr = get_config()
|
|
749
|
+
org_dir = config_mgr.data_dir / "organization"
|
|
750
|
+
org_info_path = org_dir / "organization.md"
|
|
751
|
+
if org_info_path.exists():
|
|
752
|
+
current_mtime = org_info_path.stat().st_mtime
|
|
753
|
+
if current_mtime != self._org_context_mtime:
|
|
647
754
|
content = org_info_path.read_text(encoding="utf-8")
|
|
648
755
|
# 检查是否仍是模板(包含占位注释)
|
|
649
756
|
is_template = "<!-- 在此填写" in content
|
|
650
757
|
if not is_template:
|
|
651
758
|
self._org_context_cache = content
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
759
|
+
else:
|
|
760
|
+
self._org_context_cache = ""
|
|
761
|
+
self._org_context_mtime = current_mtime
|
|
762
|
+
except Exception as e:
|
|
763
|
+
logger.debug(f"读取组织信息失败: {e}")
|
|
655
764
|
|
|
656
765
|
if self._org_context_cache:
|
|
657
766
|
parts.append("## 组织信息\n")
|
package/agents/memory_agent.py
CHANGED
|
@@ -192,7 +192,13 @@ class MemoryAgent(BaseAgent):
|
|
|
192
192
|
]
|
|
193
193
|
|
|
194
194
|
async def _summarize(self, context: AgentContext, session_id: str):
|
|
195
|
-
"""
|
|
195
|
+
"""
|
|
196
|
+
增量总结当前对话 (滚动摘要模式)。
|
|
197
|
+
|
|
198
|
+
变更: 不再清空短期记忆,而是生成摘要并返回给 MainAgent,
|
|
199
|
+
由 ContextManager 的 RollingSummaryBuffer 管理滚动摘要。
|
|
200
|
+
同时仍将偏好和错误经验提取到长期记忆。
|
|
201
|
+
"""
|
|
196
202
|
if not self.memory or not self.llm:
|
|
197
203
|
return
|
|
198
204
|
|
|
@@ -213,9 +219,12 @@ class MemoryAgent(BaseAgent):
|
|
|
213
219
|
|
|
214
220
|
result = await self._call_llm_json(messages)
|
|
215
221
|
if "error" not in result:
|
|
222
|
+
summary_text = result.get("summary", "")
|
|
223
|
+
|
|
224
|
+
# 保存摘要到长期记忆 (保留原有功能)
|
|
216
225
|
self.memory.save_summary(
|
|
217
226
|
session_id=session_id,
|
|
218
|
-
summary=
|
|
227
|
+
summary=summary_text,
|
|
219
228
|
)
|
|
220
229
|
|
|
221
230
|
# 保存偏好
|
|
@@ -235,9 +244,20 @@ class MemoryAgent(BaseAgent):
|
|
|
235
244
|
fix=err_item.get("fix", ""),
|
|
236
245
|
)
|
|
237
246
|
|
|
238
|
-
|
|
239
|
-
#
|
|
240
|
-
|
|
247
|
+
# 返回摘要结果供 ContextManager 的滚动摘要缓冲使用
|
|
248
|
+
# (不再清空短期记忆,保持对话连续性)
|
|
249
|
+
key_points = result.get("key_points", [])
|
|
250
|
+
full_summary = summary_text
|
|
251
|
+
if key_points:
|
|
252
|
+
full_summary += "\n要点: " + "; ".join(key_points)
|
|
253
|
+
|
|
254
|
+
context.working_memory["summary_result"] = full_summary
|
|
255
|
+
context.working_memory["summarized_count"] = len(entries)
|
|
256
|
+
|
|
257
|
+
logger.info(
|
|
258
|
+
f"对话增量总结完成 (session={session_id}, "
|
|
259
|
+
f"entries={len(entries)}, 不再清空短期记忆)"
|
|
260
|
+
)
|
|
241
261
|
|
|
242
262
|
async def _record_error(self, context: AgentContext, session_id: str):
|
|
243
263
|
"""记录错误模式"""
|
package/communication/manager.py
CHANGED
|
@@ -61,6 +61,7 @@ class CommunicationManager:
|
|
|
61
61
|
"""
|
|
62
62
|
self.config = config
|
|
63
63
|
self._data_dir = Path(data_dir) if data_dir else Path.home() / ".myagent" / "data"
|
|
64
|
+
self._permission_checker = None # callable(perm_name) -> bool, set by app
|
|
64
65
|
|
|
65
66
|
# Identity keys (Ed25519)
|
|
66
67
|
self._ed_priv: bytes = b""
|
|
@@ -255,6 +256,9 @@ class CommunicationManager:
|
|
|
255
256
|
msg = None
|
|
256
257
|
# Check if the peer is on this machine (has a local queue)
|
|
257
258
|
if to_agent in LocalChannel._queues:
|
|
259
|
+
# 本机通信权限检查
|
|
260
|
+
if not self._check_permission("local_comm"):
|
|
261
|
+
raise RuntimeError("[权限] 当前 Agent 没有本机通信权限")
|
|
258
262
|
msg = await self._local_channel.send(
|
|
259
263
|
to_agent=to_agent,
|
|
260
264
|
content=content,
|
|
@@ -262,6 +266,9 @@ class CommunicationManager:
|
|
|
262
266
|
their_public_key=their_pub,
|
|
263
267
|
)
|
|
264
268
|
elif self._remote_channel:
|
|
269
|
+
# 跨电脑通信权限检查
|
|
270
|
+
if not self._check_permission("remote_comm"):
|
|
271
|
+
raise RuntimeError("[权限] 当前 Agent 没有跨电脑通信权限,请先在权限管理中开启")
|
|
265
272
|
their_x_pub_hex = peer.permissions.get("x25519_public_key", "")
|
|
266
273
|
msg = await self._remote_channel.send(
|
|
267
274
|
to_agent=to_agent,
|
|
@@ -371,6 +378,19 @@ class CommunicationManager:
|
|
|
371
378
|
"""Register a callback for incoming messages."""
|
|
372
379
|
self._message_callbacks.append(callback)
|
|
373
380
|
|
|
381
|
+
def set_permission_checker(self, checker):
|
|
382
|
+
"""设置权限检查回调(由应用启动时注入)"""
|
|
383
|
+
self._permission_checker = checker
|
|
384
|
+
|
|
385
|
+
def _check_permission(self, permission: str) -> bool:
|
|
386
|
+
"""检查权限,无权限检查器时默认通过"""
|
|
387
|
+
if self._permission_checker is None:
|
|
388
|
+
return True
|
|
389
|
+
try:
|
|
390
|
+
return bool(self._permission_checker(permission))
|
|
391
|
+
except Exception:
|
|
392
|
+
return True
|
|
393
|
+
|
|
374
394
|
# ==================================================================
|
|
375
395
|
# Status
|
|
376
396
|
# ==================================================================
|
package/core/__init__.py
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
"""
|
|
2
|
-
core/__init__.py -
|
|
2
|
+
core/__init__.py - 核心模块
|
|
3
3
|
"""
|
|
4
|
-
from core.
|
|
5
|
-
from core.utils import (
|
|
6
|
-
timestamp, generate_id, truncate_str,
|
|
7
|
-
safe_json_parse, validate_json_schema
|
|
8
|
-
)
|
|
9
|
-
from core.llm import LLMClient, get_llm_client
|
|
10
|
-
from core.task_queue import TaskQueue, TaskItem, TaskStatus
|
|
4
|
+
from core.version import __version__
|