myagent-ai 1.15.37 → 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/agents/main_agent.py +8 -1
- package/package.json +1 -1
- package/skills/search_skill.py +44 -35
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/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()
|