myagent-ai 1.15.84 → 1.15.85
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/memory/manager.py +32 -17
- package/package.json +1 -1
- package/skills/search_skill.py +66 -21
package/memory/manager.py
CHANGED
|
@@ -231,7 +231,15 @@ class MemoryManager:
|
|
|
231
231
|
params.append(timestamp())
|
|
232
232
|
|
|
233
233
|
where = " AND ".join(conditions) if conditions else "1=1"
|
|
234
|
-
|
|
234
|
+
# 安全白名单:仅允许预定义的排序字段
|
|
235
|
+
_allowed_orders = {
|
|
236
|
+
"created_at ASC", "created_at DESC",
|
|
237
|
+
"importance ASC", "importance DESC",
|
|
238
|
+
"access_count ASC", "access_count DESC",
|
|
239
|
+
"updated_at ASC", "updated_at DESC",
|
|
240
|
+
}
|
|
241
|
+
_safe_order = order_by if order_by in _allowed_orders else "created_at ASC"
|
|
242
|
+
sql = f"SELECT * FROM memories WHERE {where} ORDER BY {_safe_order} LIMIT ?"
|
|
235
243
|
params.append(limit)
|
|
236
244
|
|
|
237
245
|
rows = conn.execute(sql, params).fetchall()
|
|
@@ -512,8 +520,8 @@ class MemoryManager:
|
|
|
512
520
|
return None # 空内容不查重
|
|
513
521
|
|
|
514
522
|
conn = self._get_conn()
|
|
515
|
-
conditions = [
|
|
516
|
-
params: list = []
|
|
523
|
+
conditions = ["category = ?"]
|
|
524
|
+
params: list = [category]
|
|
517
525
|
|
|
518
526
|
if session_id:
|
|
519
527
|
conditions.append("session_id = ?")
|
|
@@ -868,10 +876,13 @@ class MemoryManager:
|
|
|
868
876
|
time_decay=time_decay, half_life_days=half_life_days)
|
|
869
877
|
else:
|
|
870
878
|
# 混合模式:取两种搜索结果的加权和 + 时间衰减
|
|
879
|
+
# 注意:传入 update_access=False 避免子搜索重复更新 access_count
|
|
871
880
|
keyword_results = self._search_keyword(conn, query, where, params, limit * 2,
|
|
872
|
-
time_decay=False, half_life_days=half_life_days
|
|
881
|
+
time_decay=False, half_life_days=half_life_days,
|
|
882
|
+
update_access=False)
|
|
873
883
|
semantic_results = self._search_semantic(conn, query, where, params, limit * 2,
|
|
874
|
-
time_decay=False, half_life_days=half_life_days
|
|
884
|
+
time_decay=False, half_life_days=half_life_days,
|
|
885
|
+
update_access=False)
|
|
875
886
|
|
|
876
887
|
# 合并评分(相关性 + 时间衰减)
|
|
877
888
|
combined: Dict[str, Tuple[MemoryEntry, float]] = {}
|
|
@@ -920,6 +931,7 @@ class MemoryManager:
|
|
|
920
931
|
limit: int,
|
|
921
932
|
time_decay: bool = False,
|
|
922
933
|
half_life_days: float = 30.0,
|
|
934
|
+
update_access: bool = True,
|
|
923
935
|
) -> List[MemoryEntry]:
|
|
924
936
|
"""关键词 LIKE 搜索(支持时间衰减重排序)"""
|
|
925
937
|
like_pattern = f"%{query}%"
|
|
@@ -949,12 +961,13 @@ class MemoryManager:
|
|
|
949
961
|
entries = [e for e, _ in scored[:limit]]
|
|
950
962
|
|
|
951
963
|
# 更新访问计数
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
964
|
+
if update_access:
|
|
965
|
+
for entry in entries:
|
|
966
|
+
conn.execute(
|
|
967
|
+
"UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
|
|
968
|
+
(entry.id,),
|
|
969
|
+
)
|
|
970
|
+
conn.commit()
|
|
958
971
|
|
|
959
972
|
return entries
|
|
960
973
|
|
|
@@ -967,6 +980,7 @@ class MemoryManager:
|
|
|
967
980
|
limit: int,
|
|
968
981
|
time_decay: bool = False,
|
|
969
982
|
half_life_days: float = 30.0,
|
|
983
|
+
update_access: bool = True,
|
|
970
984
|
) -> List[MemoryEntry]:
|
|
971
985
|
"""TF-IDF 语义搜索(支持时间衰减重排序)"""
|
|
972
986
|
# 先取一批候选文档
|
|
@@ -1001,12 +1015,13 @@ class MemoryManager:
|
|
|
1001
1015
|
result = [MemoryEntry.from_row(row_map[doc_id]) for doc_id in top_ids if doc_id in row_map]
|
|
1002
1016
|
|
|
1003
1017
|
# 更新访问计数
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1018
|
+
if update_access:
|
|
1019
|
+
for entry in result:
|
|
1020
|
+
conn.execute(
|
|
1021
|
+
"UPDATE memories SET access_count = access_count + 1 WHERE id = ?",
|
|
1022
|
+
(entry.id,),
|
|
1023
|
+
)
|
|
1024
|
+
conn.commit()
|
|
1010
1025
|
|
|
1011
1026
|
return result
|
|
1012
1027
|
|
package/package.json
CHANGED
package/skills/search_skill.py
CHANGED
|
@@ -127,10 +127,21 @@ class WebSearchSkill(Skill):
|
|
|
127
127
|
r.raise_for_status()
|
|
128
128
|
except requests.exceptions.SSLError:
|
|
129
129
|
logger.warning("DuckDuckGo SSL 验证失败,跳过证书验证重试")
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
try:
|
|
131
|
+
r = requests.get("https://html.duckduckgo.com/html/", params={"q": query}, headers=_HEADERS, timeout=15, verify=False)
|
|
132
|
+
r.raise_for_status()
|
|
133
|
+
except Exception as ssl_retry_err:
|
|
134
|
+
logger.error(f"DuckDuckGo SSL 重试也失败: {ssl_retry_err}")
|
|
135
|
+
return ""
|
|
136
|
+
except Exception as req_err:
|
|
137
|
+
logger.error(f"DuckDuckGo 请求失败: {req_err}")
|
|
138
|
+
return ""
|
|
132
139
|
if r is not None:
|
|
133
|
-
|
|
140
|
+
try:
|
|
141
|
+
return r.text or ""
|
|
142
|
+
except Exception as read_err:
|
|
143
|
+
logger.error(f"DuckDuckGo 读取响应失败: {read_err}")
|
|
144
|
+
return ""
|
|
134
145
|
return ""
|
|
135
146
|
|
|
136
147
|
html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
|
|
@@ -181,10 +192,21 @@ class WebSearchSkill(Skill):
|
|
|
181
192
|
r.raise_for_status()
|
|
182
193
|
except requests.exceptions.SSLError:
|
|
183
194
|
logger.warning("Bing SSL 验证失败,跳过证书验证重试")
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
try:
|
|
196
|
+
r = requests.get("https://www.bing.com/search", params={"q": query, "count": num}, headers=_HEADERS, timeout=15, verify=False)
|
|
197
|
+
r.raise_for_status()
|
|
198
|
+
except Exception as ssl_retry_err:
|
|
199
|
+
logger.error(f"Bing SSL 重试也失败: {ssl_retry_err}")
|
|
200
|
+
return ""
|
|
201
|
+
except Exception as req_err:
|
|
202
|
+
logger.error(f"Bing 请求失败: {req_err}")
|
|
203
|
+
return ""
|
|
186
204
|
if r is not None:
|
|
187
|
-
|
|
205
|
+
try:
|
|
206
|
+
return r.text or ""
|
|
207
|
+
except Exception as read_err:
|
|
208
|
+
logger.error(f"Bing 读取响应失败: {read_err}")
|
|
209
|
+
return ""
|
|
188
210
|
return ""
|
|
189
211
|
|
|
190
212
|
html = await asyncio.wait_for(loop.run_in_executor(None, _fetch), timeout=30)
|
|
@@ -266,12 +288,23 @@ class WebReadSkill(Skill):
|
|
|
266
288
|
r.raise_for_status()
|
|
267
289
|
except requests.exceptions.SSLError:
|
|
268
290
|
logger.warning(f"web_read SSL 验证失败 ({url}),跳过证书验证重试")
|
|
269
|
-
|
|
270
|
-
|
|
291
|
+
try:
|
|
292
|
+
r = requests.get(url, headers=_HEADERS, timeout=30, verify=False)
|
|
293
|
+
r.raise_for_status()
|
|
294
|
+
except Exception as ssl_retry_err:
|
|
295
|
+
logger.error(f"web_read SSL 重试也失败 ({url}): {ssl_retry_err}")
|
|
296
|
+
return None
|
|
297
|
+
except Exception as req_err:
|
|
298
|
+
logger.error(f"web_read 请求失败 ({url}): {req_err}")
|
|
299
|
+
return None
|
|
271
300
|
if r is not None:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
301
|
+
try:
|
|
302
|
+
r.encoding = r.apparent_encoding
|
|
303
|
+
return r.text or ""
|
|
304
|
+
except Exception as read_err:
|
|
305
|
+
logger.error(f"web_read 读取响应内容失败 ({url}): {read_err}")
|
|
306
|
+
return None
|
|
307
|
+
return None
|
|
275
308
|
|
|
276
309
|
html = await asyncio.wait_for(
|
|
277
310
|
loop.run_in_executor(None, _fetch), timeout=60
|
|
@@ -323,6 +356,7 @@ class WebReadSkill(Skill):
|
|
|
323
356
|
except asyncio.TimeoutError:
|
|
324
357
|
return SkillResult(success=False, error=f"网页读取超时 (60s): {url}")
|
|
325
358
|
except Exception as e:
|
|
359
|
+
logger.error(f"web_read 异常 ({url}): {e}", exc_info=True)
|
|
326
360
|
return SkillResult(success=False, error=f"网页读取失败: {e}")
|
|
327
361
|
|
|
328
362
|
|
|
@@ -369,23 +403,33 @@ class URLReadSkill(Skill):
|
|
|
369
403
|
)
|
|
370
404
|
except requests.exceptions.SSLError:
|
|
371
405
|
logger.warning(f"url_read SSL 验证失败 ({url}),跳过证书验证重试")
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
406
|
+
try:
|
|
407
|
+
r = requests.request(
|
|
408
|
+
method=method,
|
|
409
|
+
url=url,
|
|
410
|
+
headers=headers,
|
|
411
|
+
data=body,
|
|
412
|
+
timeout=30,
|
|
413
|
+
verify=False,
|
|
414
|
+
)
|
|
415
|
+
except Exception as ssl_retry_err:
|
|
416
|
+
logger.error(f"url_read SSL 重试也失败 ({url}): {ssl_retry_err}")
|
|
417
|
+
return None
|
|
418
|
+
except Exception as req_err:
|
|
419
|
+
logger.error(f"url_read 请求失败 ({url}): {req_err}")
|
|
420
|
+
return None
|
|
380
421
|
if r is not None:
|
|
381
422
|
return r
|
|
382
|
-
|
|
423
|
+
return None
|
|
383
424
|
|
|
384
425
|
response = await asyncio.wait_for(loop.run_in_executor(None, _request), timeout=60)
|
|
385
426
|
|
|
427
|
+
if response is None:
|
|
428
|
+
return SkillResult(success=False, error=f"请求失败: 无法连接到 {url}")
|
|
429
|
+
|
|
386
430
|
try:
|
|
387
431
|
content = response.json()
|
|
388
|
-
except ValueError:
|
|
432
|
+
except (ValueError, AttributeError):
|
|
389
433
|
content = response.text[:20000]
|
|
390
434
|
|
|
391
435
|
return SkillResult(
|
|
@@ -398,4 +442,5 @@ class URLReadSkill(Skill):
|
|
|
398
442
|
message=f"HTTP {response.status_code}",
|
|
399
443
|
)
|
|
400
444
|
except Exception as e:
|
|
445
|
+
logger.error(f"url_read 异常 ({url}): {e}", exc_info=True)
|
|
401
446
|
return SkillResult(success=False, error=f"请求失败: {e}")
|