myagent-ai 1.15.36 → 1.15.38
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 +10 -1
- package/agents/main_agent.py +8 -1
- package/main.py +14 -10
- package/package.json +1 -1
- package/skills/search_skill.py +44 -35
- package/start.js +13 -8
package/README.md
CHANGED
|
@@ -125,7 +125,16 @@ curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install
|
|
|
125
125
|
|
|
126
126
|
### 一键重装(跳过依赖检查)
|
|
127
127
|
|
|
128
|
-
> 已安装过 MyAgent
|
|
128
|
+
> 已安装过 MyAgent 的环境下,快速升级/重装,安装和启动时均不检查 pip 依赖。
|
|
129
|
+
|
|
130
|
+
**npm 方式(推荐,最简单):**
|
|
131
|
+
```bash
|
|
132
|
+
npx myagent-ai --skip-deps
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
> `npx` 会自动安装/升级最新版 `myagent-ai`,`--skip-deps` 跳过安装和启动时的所有依赖检查。
|
|
136
|
+
|
|
137
|
+
**脚本方式:**
|
|
129
138
|
|
|
130
139
|
**Windows(PowerShell):**
|
|
131
140
|
```powershell
|
package/agents/main_agent.py
CHANGED
|
@@ -1080,6 +1080,10 @@ class MainAgent(BaseAgent):
|
|
|
1080
1080
|
return out
|
|
1081
1081
|
return tr.get("error", "")
|
|
1082
1082
|
|
|
1083
|
+
# 防御 tool_result 为 None(极端情况)
|
|
1084
|
+
if tool_result is None:
|
|
1085
|
+
tool_result = {"success": False, "error": "工具返回了空结果", "timed_out": False}
|
|
1086
|
+
|
|
1083
1087
|
tool_output_text = _extract_tool_output(tool_result)
|
|
1084
1088
|
|
|
1085
1089
|
await self._emit_v2_event(
|
|
@@ -1328,7 +1332,10 @@ class MainAgent(BaseAgent):
|
|
|
1328
1332
|
|
|
1329
1333
|
elif self.skills:
|
|
1330
1334
|
exec_result = await self.skills.execute(tool_name, **params)
|
|
1331
|
-
|
|
1335
|
+
if exec_result is None:
|
|
1336
|
+
result["error"] = f"技能 {tool_name} 返回了空结果"
|
|
1337
|
+
else:
|
|
1338
|
+
result = exec_result.to_dict()
|
|
1332
1339
|
else:
|
|
1333
1340
|
result["error"] = f"未知工具: {tool_name}"
|
|
1334
1341
|
|
package/main.py
CHANGED
|
@@ -155,16 +155,20 @@ class MyAgentApp:
|
|
|
155
155
|
self.logger.info("=" * 60)
|
|
156
156
|
|
|
157
157
|
# 1.5 自动检测并安装缺失依赖(开箱即用)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
# 可通过 --skip-deps 参数或 MYAGENT_SKIP_DEPS=1 环境变量跳过
|
|
159
|
+
if os.environ.get("MYAGENT_SKIP_DEPS") == "1":
|
|
160
|
+
self.logger.info("跳过依赖检查(--skip-deps)")
|
|
161
|
+
else:
|
|
162
|
+
self.logger.info("检查依赖...")
|
|
163
|
+
deps_result = check_and_install_deps(auto_fix=True, silent=False)
|
|
164
|
+
if deps_result["installed"] > 0:
|
|
165
|
+
self.logger.info(
|
|
166
|
+
f"自动安装了 {deps_result['installed']} 个依赖"
|
|
167
|
+
)
|
|
168
|
+
if deps_result["failed"] > 0:
|
|
169
|
+
self.logger.warning(
|
|
170
|
+
f"{deps_result['failed']} 个依赖安装失败,相关功能可能不可用"
|
|
171
|
+
)
|
|
168
172
|
|
|
169
173
|
# 2. LLM 客户端
|
|
170
174
|
llm_cfg = self.config.llm
|
package/package.json
CHANGED
package/skills/search_skill.py
CHANGED
|
@@ -118,20 +118,22 @@ class WebSearchSkill(Skill):
|
|
|
118
118
|
import requests
|
|
119
119
|
from bs4 import BeautifulSoup
|
|
120
120
|
|
|
121
|
-
loop = asyncio.
|
|
121
|
+
loop = asyncio.get_running_loop()
|
|
122
122
|
|
|
123
123
|
def _fetch():
|
|
124
|
-
|
|
124
|
+
r = None
|
|
125
125
|
try:
|
|
126
|
-
r = requests.get(
|
|
126
|
+
r = requests.get("https://html.duckduckgo.com/html/", params={"q": query}, headers=_HEADERS, timeout=15)
|
|
127
127
|
r.raise_for_status()
|
|
128
128
|
except requests.exceptions.SSLError:
|
|
129
129
|
logger.warning("DuckDuckGo SSL 验证失败,跳过证书验证重试")
|
|
130
|
-
r = requests.get(
|
|
130
|
+
r = requests.get("https://html.duckduckgo.com/html/", params={"q": query}, headers=_HEADERS, timeout=15, verify=False)
|
|
131
131
|
r.raise_for_status()
|
|
132
|
-
|
|
132
|
+
if r is not None:
|
|
133
|
+
return r.text or ""
|
|
134
|
+
return ""
|
|
133
135
|
|
|
134
|
-
html = await loop.run_in_executor(None, _fetch)
|
|
136
|
+
html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
|
|
135
137
|
soup = BeautifulSoup(html, "html.parser")
|
|
136
138
|
|
|
137
139
|
results = []
|
|
@@ -170,31 +172,22 @@ class WebSearchSkill(Skill):
|
|
|
170
172
|
import requests
|
|
171
173
|
from bs4 import BeautifulSoup
|
|
172
174
|
|
|
173
|
-
loop = asyncio.
|
|
175
|
+
loop = asyncio.get_running_loop()
|
|
174
176
|
|
|
175
177
|
def _fetch():
|
|
176
|
-
|
|
178
|
+
r = None
|
|
177
179
|
try:
|
|
178
|
-
r = requests.get(
|
|
179
|
-
url,
|
|
180
|
-
params={"q": query, "count": num},
|
|
181
|
-
headers=_HEADERS,
|
|
182
|
-
timeout=15,
|
|
183
|
-
)
|
|
180
|
+
r = requests.get("https://www.bing.com/search", params={"q": query, "count": num}, headers=_HEADERS, timeout=15)
|
|
184
181
|
r.raise_for_status()
|
|
185
182
|
except requests.exceptions.SSLError:
|
|
186
183
|
logger.warning("Bing SSL 验证失败,跳过证书验证重试")
|
|
187
|
-
r = requests.get(
|
|
188
|
-
url,
|
|
189
|
-
params={"q": query, "count": num},
|
|
190
|
-
headers=_HEADERS,
|
|
191
|
-
timeout=15,
|
|
192
|
-
verify=False,
|
|
193
|
-
)
|
|
184
|
+
r = requests.get("https://www.bing.com/search", params={"q": query, "count": num}, headers=_HEADERS, timeout=15, verify=False)
|
|
194
185
|
r.raise_for_status()
|
|
195
|
-
|
|
186
|
+
if r is not None:
|
|
187
|
+
return r.text or ""
|
|
188
|
+
return ""
|
|
196
189
|
|
|
197
|
-
html = await loop.run_in_executor(None, _fetch)
|
|
190
|
+
html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
|
|
198
191
|
soup = BeautifulSoup(html, "html.parser")
|
|
199
192
|
|
|
200
193
|
results = []
|
|
@@ -250,22 +243,24 @@ class WebReadSkill(Skill):
|
|
|
250
243
|
]
|
|
251
244
|
|
|
252
245
|
async def execute(self, url: str = "", extract_text: bool = True, **kwargs) -> SkillResult:
|
|
246
|
+
if not url or not isinstance(url, str) or not url.startswith("http"):
|
|
247
|
+
return SkillResult(success=False, error=f"无效的URL: {url!r}")
|
|
248
|
+
|
|
253
249
|
# 确保搜索依赖已安装
|
|
254
250
|
try:
|
|
255
|
-
import requests
|
|
256
|
-
from bs4 import BeautifulSoup
|
|
251
|
+
import requests # noqa: F811
|
|
252
|
+
from bs4 import BeautifulSoup # noqa: F811
|
|
257
253
|
except ImportError as imp_err:
|
|
258
254
|
logger.warning(f"网页读取依赖缺失: {imp_err},尝试自动安装...")
|
|
259
255
|
from core.deps_checker import ensure_skill_deps
|
|
260
256
|
if not ensure_skill_deps("search"):
|
|
261
257
|
return SkillResult(success=False, error=f"依赖安装失败,请手动运行: pip install requests beautifulsoup4 lxml ({imp_err})")
|
|
262
|
-
try:
|
|
263
|
-
import requests # noqa: F811
|
|
264
|
-
from bs4 import BeautifulSoup # noqa: F811
|
|
265
258
|
|
|
266
|
-
|
|
259
|
+
try:
|
|
260
|
+
loop = asyncio.get_running_loop()
|
|
267
261
|
|
|
268
262
|
def _fetch():
|
|
263
|
+
r = None
|
|
269
264
|
try:
|
|
270
265
|
r = requests.get(url, headers=_HEADERS, timeout=30)
|
|
271
266
|
r.raise_for_status()
|
|
@@ -273,10 +268,17 @@ class WebReadSkill(Skill):
|
|
|
273
268
|
logger.warning(f"web_read SSL 验证失败 ({url}),跳过证书验证重试")
|
|
274
269
|
r = requests.get(url, headers=_HEADERS, timeout=30, verify=False)
|
|
275
270
|
r.raise_for_status()
|
|
276
|
-
r
|
|
277
|
-
|
|
271
|
+
if r is not None:
|
|
272
|
+
r.encoding = r.apparent_encoding
|
|
273
|
+
return r.text or ""
|
|
274
|
+
return ""
|
|
275
|
+
|
|
276
|
+
html = await asyncio.wait_for(
|
|
277
|
+
loop.run_in_executor(None, _fetch), timeout=60
|
|
278
|
+
)
|
|
278
279
|
|
|
279
|
-
html
|
|
280
|
+
if not html or not isinstance(html, str):
|
|
281
|
+
return SkillResult(success=False, error="网页内容为空")
|
|
280
282
|
|
|
281
283
|
soup = BeautifulSoup(html, "html.parser")
|
|
282
284
|
title = soup.title.get_text(strip=True) if soup.title else ""
|
|
@@ -318,6 +320,8 @@ class WebReadSkill(Skill):
|
|
|
318
320
|
},
|
|
319
321
|
message=f"已读取: {title} ({len(content)} 字符)",
|
|
320
322
|
)
|
|
323
|
+
except asyncio.TimeoutError:
|
|
324
|
+
return SkillResult(success=False, error=f"网页读取超时 (60s): {url}")
|
|
321
325
|
except Exception as e:
|
|
322
326
|
return SkillResult(success=False, error=f"网页读取失败: {e}")
|
|
323
327
|
|
|
@@ -337,6 +341,8 @@ class URLReadSkill(Skill):
|
|
|
337
341
|
|
|
338
342
|
async def execute(self, url: str = "", method: str = "GET",
|
|
339
343
|
headers: dict = None, body: str = "", **kwargs) -> SkillResult:
|
|
344
|
+
if not url:
|
|
345
|
+
return SkillResult(success=False, error="缺少 URL 参数")
|
|
340
346
|
# 确保依赖已安装
|
|
341
347
|
try:
|
|
342
348
|
import requests
|
|
@@ -349,9 +355,10 @@ class URLReadSkill(Skill):
|
|
|
349
355
|
import requests # noqa: F811
|
|
350
356
|
|
|
351
357
|
headers = headers or {}
|
|
352
|
-
loop = asyncio.
|
|
358
|
+
loop = asyncio.get_running_loop()
|
|
353
359
|
|
|
354
360
|
def _request():
|
|
361
|
+
r = None
|
|
355
362
|
try:
|
|
356
363
|
r = requests.request(
|
|
357
364
|
method=method,
|
|
@@ -370,9 +377,11 @@ class URLReadSkill(Skill):
|
|
|
370
377
|
timeout=30,
|
|
371
378
|
verify=False,
|
|
372
379
|
)
|
|
373
|
-
|
|
380
|
+
if r is not None:
|
|
381
|
+
return r
|
|
382
|
+
raise requests.exceptions.ConnectionError(f"请求失败: {url}")
|
|
374
383
|
|
|
375
|
-
response = await loop.run_in_executor(None, _request)
|
|
384
|
+
response = await asyncio.wait_for(loop.run_in_executor(None, _request), timeout=60)
|
|
376
385
|
|
|
377
386
|
try:
|
|
378
387
|
content = response.json()
|
package/start.js
CHANGED
|
@@ -584,17 +584,22 @@ function main() {
|
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
// 使用 venv 的 Python 启动
|
|
587
|
+
const spawnEnv = {
|
|
588
|
+
...process.env,
|
|
589
|
+
// 设置 VIRTUAL_ENV 环境变量,让 Python 程序知道自己运行在 venv 中
|
|
590
|
+
VIRTUAL_ENV: venvDir,
|
|
591
|
+
PATH: IS_WIN
|
|
592
|
+
? `${path.join(venvDir, "Scripts")};${process.env.PATH}`
|
|
593
|
+
: `${path.join(venvDir, "bin")}:${process.env.PATH}`,
|
|
594
|
+
};
|
|
595
|
+
// 将 --skip-deps 信号传递给 Python,让 main.py 也跳过依赖检查
|
|
596
|
+
if (skipDeps) {
|
|
597
|
+
spawnEnv.MYAGENT_SKIP_DEPS = "1";
|
|
598
|
+
}
|
|
587
599
|
const child = spawn(venvPython, pyArgs, {
|
|
588
600
|
cwd: pkgDir,
|
|
589
601
|
stdio: "inherit",
|
|
590
|
-
env:
|
|
591
|
-
...process.env,
|
|
592
|
-
// 设置 VIRTUAL_ENV 环境变量,让 Python 程序知道自己运行在 venv 中
|
|
593
|
-
VIRTUAL_ENV: venvDir,
|
|
594
|
-
PATH: IS_WIN
|
|
595
|
-
? `${path.join(venvDir, "Scripts")};${process.env.PATH}`
|
|
596
|
-
: `${path.join(venvDir, "bin")}:${process.env.PATH}`,
|
|
597
|
-
},
|
|
602
|
+
env: spawnEnv,
|
|
598
603
|
});
|
|
599
604
|
|
|
600
605
|
child.on("error", (err) => {
|