myagent-ai 1.15.45 → 1.15.47
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.
|
Binary file
|
package/agents/main_agent.py
CHANGED
|
@@ -47,7 +47,7 @@ class MainAgent(BaseAgent):
|
|
|
47
47
|
<task_plan>任务计划(仅复杂任务使用):如"context"包含非空"task_plan",则更新它。否则,先评估任务复杂度——如果预计操作步骤不超过3步(如:单次查询、简单问答、格式转换、单文件修改、简单计算等简单任务),则<task_plan>输出为空,不要创建任务列表;只有当任务较复杂(预计超过3步操作,如:多文件修改、需要调研+实现+测试、涉及多个模块联动等),才以Markdown列表格式制定新任务列表。格式:每项用 "- [ ] 任务描述" 或 "- [x] 已完成任务",含完成状态标记。</task_plan>
|
|
48
48
|
|
|
49
49
|
<toolstocal>
|
|
50
|
-
<tool><beforecalltext>连接词,介绍调用什么工具,达到什么目的。</beforecalltext><toolname>工具名</toolname><parms>JSON格式的参数对象,例如: {"query": "搜索关键词", "num": 5}</parms><timeout>预估超时时限(秒)</timeout
|
|
50
|
+
<tool><beforecalltext>连接词,介绍调用什么工具,达到什么目的。</beforecalltext><toolname>工具名</toolname><parms>JSON格式的参数对象,例如: {"query": "搜索关键词", "num": 5}</parms><timeout>预估超时时限(秒)</timeout></tool>
|
|
51
51
|
</toolstocal>
|
|
52
52
|
<remember><type>global或session</type><content>仅从最新用户输入(userprint 或 usersays_correct)中提炼值得记忆的信息(如用户偏好、重要结论、错误经验等)。type=global表示跨会话全局记忆,type=session表示仅当前会话可用的记忆。如果本轮没有新信息需要记忆,则<content>为空、<type>不填。</content></remember>
|
|
53
53
|
<recall>下一轮需要主动召回的记忆描述。填写需要从记忆库中检索的关键字或描述。如果不填写则为空。如果需要更多记忆支持当前任务,填写相关关键词(可包含时间参考,如"2025年1月的项目"),系统将在下一轮搜索top5相关记忆并通过<recall_memory>注入上下文。你也可以直接调用recall_memory工具即时搜索。</recall>
|
|
@@ -69,17 +69,16 @@ class MainAgent(BaseAgent):
|
|
|
69
69
|
5. <toolstocal>: 列出所有需要执行的工具调用,每个工具包含完整的参数说明
|
|
70
70
|
6. <parms>: **必须使用严格合法的JSON格式**,例如 {"query": "关键词", "num": 10},不要使用其他格式
|
|
71
71
|
7. <timeout>: 预估超时秒数(简单操作10-30s,文件操作30-60s,网络请求60-120s,数据处理120-300s)
|
|
72
|
-
8. <
|
|
73
|
-
9. <
|
|
74
|
-
10. <
|
|
75
|
-
11. <
|
|
76
|
-
12. <
|
|
77
|
-
13. <
|
|
78
|
-
14. <
|
|
79
|
-
15. <
|
|
80
|
-
16. <
|
|
81
|
-
17.
|
|
82
|
-
18. 使用中文输出所有内容
|
|
72
|
+
8. <remember>: 包含 <type> 和 <content> 子标签。type 填 "global"(跨会话全局记忆)或 "session"(仅当前会话)。content 填从最新用户输入中提炼的值得记忆的关键信息。如果本轮无需记忆,content 为空且不填 type。注意:用户个人偏好、重要结论、通用经验用 global;当前任务的临时上下文、过程信息用 session
|
|
73
|
+
9. <recall>: 填写下一轮需要从记忆库中主动召回的内容描述和关键字(可包含时间参考)。系统将根据这些信息搜索top5相关记忆,在下一轮通过 <recall_memory> 标签注入上下文。如果当前 <automemory> 中的记忆已足够完成任务,<recall> 为空;如果需要更多历史记忆支撑,则填写。你也可以直接使用 recall_memory 工具在当前轮即时搜索
|
|
74
|
+
10. <knowledge>: 从本轮对话或工具执行结果中提炼值得长期保存的专业知识、事实、经验法则、技术要点等。这些知识会被持久化到知识库文件,未来可通过 get_knowledge 检索复用。如果没有需要保存的知识,则为空。格式:简洁明确,每条知识一行
|
|
75
|
+
11. <get_knowledge>: 如果当前 <knowledge> 内容不足以完成任务,填写需要从知识库搜索的关键词;否则为空
|
|
76
|
+
12. <askuser>: 当信息不足需要用户补充时,在此填写要问的问题
|
|
77
|
+
13. <finish>: 当任务已完成或需要等待用户回应时为 true;否则为 false 继续执行
|
|
78
|
+
14. <finish_reason>: **finish=true 时必须填写**,详细说明结束原因(任务完成/等待用户/信息不足/无法处理等)
|
|
79
|
+
15. <next_step>: **finish=false 时必须填写**,描述下一步计划做什么,要求简洁明确(1-2句话)
|
|
80
|
+
16. <mainsubject>: 为当前对话生成6字以内的简短标题,概括对话主题。仅在对话刚开始的前几轮需要输出,已有标题后留空
|
|
81
|
+
17. 使用中文输出所有内容
|
|
83
82
|
|
|
84
83
|
## 上下文中的记忆系统说明
|
|
85
84
|
- <automemory>: 系统自动根据你通过 <remember> 保存的记忆和当前用户输入,搜索出的 top10 相关记忆。这些是你过去主动记住的内容(包含时间信息),可供参考。
|
|
@@ -462,8 +461,6 @@ class MainAgent(BaseAgent):
|
|
|
462
461
|
_v2_reasoning_collected: List[str] = []
|
|
463
462
|
# XML 解析失败时的 LLM 修正重试计数
|
|
464
463
|
_xml_correction_retries: int = 0
|
|
465
|
-
# 连续无工具调用计数(防止模型一直不调用工具导致死循环)
|
|
466
|
-
_consecutive_no_tool_count: int = 0
|
|
467
464
|
|
|
468
465
|
conversation_history = list(context.conversation_history or [])
|
|
469
466
|
|
|
@@ -907,9 +904,8 @@ class MainAgent(BaseAgent):
|
|
|
907
904
|
)
|
|
908
905
|
break
|
|
909
906
|
|
|
910
|
-
# Step 10:
|
|
907
|
+
# Step 10: 无工具调用
|
|
911
908
|
if not parsed.tools_to_call:
|
|
912
|
-
_consecutive_no_tool_count += 1
|
|
913
909
|
# 如果 finish=true 且无工具,任务完成
|
|
914
910
|
if parsed.finish:
|
|
915
911
|
logger.info(f"[{task_id}] finish=true 且无工具调用,任务完成")
|
|
@@ -928,35 +924,10 @@ class MainAgent(BaseAgent):
|
|
|
928
924
|
content=final_text,
|
|
929
925
|
)
|
|
930
926
|
break
|
|
931
|
-
elif _consecutive_no_tool_count >= 2:
|
|
932
|
-
# 连续 2 轮无工具调用,视为模型无意继续执行任务
|
|
933
|
-
logger.warning(
|
|
934
|
-
f"[{task_id}] 连续 {_consecutive_no_tool_count} 轮无工具调用且未 finish,"
|
|
935
|
-
f"视为任务结束"
|
|
936
|
-
)
|
|
937
|
-
if _v2_reasoning_collected:
|
|
938
|
-
final_text = "\n".join(_v2_reasoning_collected)
|
|
939
|
-
else:
|
|
940
|
-
before, after = extract_surrounding_text(llm_raw)
|
|
941
|
-
final_text = (before + "\n" + after).strip() if (before.strip() or after.strip()) else "任务已完成。"
|
|
942
|
-
context.working_memory["final_response"] = final_text
|
|
943
|
-
if not _emitted_reasoning_this_iter:
|
|
944
|
-
await self._emit_v2_event("v2_reasoning", {"content": truncate_str(final_text, 3000)}, stream_callback)
|
|
945
|
-
if self.memory:
|
|
946
|
-
self.memory.add_session(
|
|
947
|
-
session_id=context.session_id,
|
|
948
|
-
role="assistant",
|
|
949
|
-
content=final_text,
|
|
950
|
-
)
|
|
951
|
-
break
|
|
952
927
|
else:
|
|
953
|
-
#
|
|
954
|
-
#
|
|
955
|
-
logger.info(
|
|
956
|
-
f"[{task_id}] 无工具调用且未 finish(连续第 {_consecutive_no_tool_count} 轮),"
|
|
957
|
-
f"继续回调 LLM"
|
|
958
|
-
)
|
|
959
|
-
# 将当前输出作为对话历史,回调 LLM 继续执行
|
|
928
|
+
# finish=false,不限次数继续回调 LLM
|
|
929
|
+
# 模型可能在中间步骤输出说明文字,下一轮才调用工具
|
|
930
|
+
logger.info(f"[{task_id}] 无工具调用且 finish=false,继续回调 LLM")
|
|
960
931
|
if llm_raw.strip():
|
|
961
932
|
conversation_history.append(Message(role="assistant", content=llm_raw))
|
|
962
933
|
conversation_history.append(Message(
|
|
@@ -967,23 +938,22 @@ class MainAgent(BaseAgent):
|
|
|
967
938
|
))
|
|
968
939
|
continue
|
|
969
940
|
|
|
970
|
-
# Step 11: 有工具调用 —
|
|
971
|
-
|
|
972
|
-
need_callback = False
|
|
941
|
+
# Step 11: 有工具调用 — 顺序执行所有工具
|
|
942
|
+
# 统一回调策略: 所有工具执行完毕,或任一工具超时 → 立即用已收集的结果回调 LLM
|
|
973
943
|
tool_outputs_parts = []
|
|
974
|
-
_reasoning_len_before_round = len(_v2_reasoning_collected)
|
|
944
|
+
_reasoning_len_before_round = len(_v2_reasoning_collected)
|
|
945
|
+
_has_timeout = False
|
|
975
946
|
|
|
976
947
|
for tool_info in parsed.tools_to_call:
|
|
977
948
|
tool_name = tool_info.get("toolname", "").strip()
|
|
978
949
|
before_call = tool_info.get("beforecalltext", "")
|
|
979
950
|
parms = tool_info.get("parms", "")
|
|
980
951
|
timeout = tool_info.get("timeout", 120)
|
|
981
|
-
should_callback = tool_info.get("callback", True)
|
|
982
952
|
|
|
983
953
|
if not tool_name:
|
|
984
954
|
continue
|
|
985
955
|
|
|
986
|
-
logger.info(f"[{task_id}] 执行工具: {tool_name} (timeout={timeout}s
|
|
956
|
+
logger.info(f"[{task_id}] 执行工具: {tool_name} (timeout={timeout}s)")
|
|
987
957
|
|
|
988
958
|
# 发送 beforecalltext 作为显示文本
|
|
989
959
|
if before_call:
|
|
@@ -1003,7 +973,6 @@ class MainAgent(BaseAgent):
|
|
|
1003
973
|
"beforecalltext": before_call,
|
|
1004
974
|
"parms": truncate_str(parms, 500),
|
|
1005
975
|
"timeout": timeout,
|
|
1006
|
-
"callback": should_callback,
|
|
1007
976
|
}},
|
|
1008
977
|
stream_callback,
|
|
1009
978
|
)
|
|
@@ -1167,10 +1136,8 @@ class MainAgent(BaseAgent):
|
|
|
1167
1136
|
|
|
1168
1137
|
is_timeout = tool_result.get("timed_out", False)
|
|
1169
1138
|
if is_timeout:
|
|
1170
|
-
|
|
1171
|
-
logger.warning(f"[{task_id}] 工具 {tool_name} 超时 ({timeout}s)")
|
|
1172
|
-
elif should_callback:
|
|
1173
|
-
need_callback = True
|
|
1139
|
+
_has_timeout = True
|
|
1140
|
+
logger.warning(f"[{task_id}] 工具 {tool_name} 超时 ({timeout}s),停止执行剩余工具")
|
|
1174
1141
|
|
|
1175
1142
|
output_str = tool_output_text
|
|
1176
1143
|
# 数据密集型工具允许更长的输出
|
|
@@ -1180,10 +1147,12 @@ class MainAgent(BaseAgent):
|
|
|
1180
1147
|
_max_output = 6000
|
|
1181
1148
|
else:
|
|
1182
1149
|
_max_output = 3000
|
|
1150
|
+
# 超时工具明确标注状态,让 LLM 清楚知道哪个工具超时了
|
|
1151
|
+
_status = "超时" if is_timeout else ('成功' if tool_result.get('success') else '失败')
|
|
1183
1152
|
tool_outputs_parts.append(
|
|
1184
1153
|
f"### {before_call}\n"
|
|
1185
1154
|
f"**工具**: {tool_name}\n"
|
|
1186
|
-
f"**结果**: {
|
|
1155
|
+
f"**结果**: {_status}\n"
|
|
1187
1156
|
f"{truncate_str(output_str, _max_output)}\n"
|
|
1188
1157
|
)
|
|
1189
1158
|
|
|
@@ -1193,7 +1162,7 @@ class MainAgent(BaseAgent):
|
|
|
1193
1162
|
))
|
|
1194
1163
|
conversation_history.append(Message(
|
|
1195
1164
|
role="user",
|
|
1196
|
-
content=f"[工具 {tool_name}
|
|
1165
|
+
content=f"[工具 {tool_name} {'超时' if is_timeout else '执行完成'}] {_status}",
|
|
1197
1166
|
))
|
|
1198
1167
|
|
|
1199
1168
|
# 保存工具调用到会话记忆
|
|
@@ -1210,15 +1179,32 @@ class MainAgent(BaseAgent):
|
|
|
1210
1179
|
self.memory.add_session(
|
|
1211
1180
|
session_id=context.session_id,
|
|
1212
1181
|
role="tool",
|
|
1213
|
-
content=f"[{tool_name}] {
|
|
1182
|
+
content=f"[{tool_name}] {_status}\n{truncate_str(output_str, 5000)}",
|
|
1214
1183
|
key="tool_result",
|
|
1215
1184
|
importance=0.4,
|
|
1216
1185
|
)
|
|
1217
1186
|
|
|
1187
|
+
# 任一工具超时 → 立即停止执行剩余工具
|
|
1188
|
+
if _has_timeout:
|
|
1189
|
+
# 收集当前工具之后未执行的工具,一并写入回调数据
|
|
1190
|
+
_current_idx = parsed.tools_to_call.index(tool_info)
|
|
1191
|
+
_skipped_tools = []
|
|
1192
|
+
for _remaining in parsed.tools_to_call[_current_idx + 1:]:
|
|
1193
|
+
_rname = _remaining.get("toolname", "").strip()
|
|
1194
|
+
if _rname:
|
|
1195
|
+
_skipped_tools.append(_rname)
|
|
1196
|
+
if _skipped_tools:
|
|
1197
|
+
tool_outputs_parts.append(
|
|
1198
|
+
f"**注意**: 工具 {tool_name} 执行超时 ({timeout}s),"
|
|
1199
|
+
f"以下工具未执行: {', '.join(_skipped_tools)}"
|
|
1200
|
+
)
|
|
1201
|
+
break
|
|
1202
|
+
|
|
1218
1203
|
all_tool_outputs = "\n".join(tool_outputs_parts)
|
|
1219
1204
|
|
|
1220
|
-
# Step 12:
|
|
1221
|
-
#
|
|
1205
|
+
# Step 12: 统一回调决策
|
|
1206
|
+
# 所有工具结果已收集到 all_tool_outputs(或任一超时提前收集)
|
|
1207
|
+
# finish=true → 任务完成,不回调;finish=false → 将结果喂回 LLM 继续执行
|
|
1222
1208
|
if parsed.finish:
|
|
1223
1209
|
logger.info(f"[{task_id}] finish=true,任务已完成,不回调 LLM")
|
|
1224
1210
|
# 构建有意义的最终回复:使用当前轮次的 reasoning text + 任务计划摘要
|
|
@@ -1249,10 +1235,9 @@ class MainAgent(BaseAgent):
|
|
|
1249
1235
|
)
|
|
1250
1236
|
break
|
|
1251
1237
|
|
|
1252
|
-
# finish=false:
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
logger.info(f"[{task_id}] finish=false,继续回调 LLM...")
|
|
1238
|
+
# finish=false: 统一回调 LLM(所有工具结果已收集到 all_tool_outputs)
|
|
1239
|
+
_timeout_info = "(有工具超时,已停止剩余工具)" if _has_timeout else f"({len(tool_outputs_parts)} 个工具结果已收集)"
|
|
1240
|
+
logger.info(f"[{task_id}] finish=false,继续回调 LLM {_timeout_info}")
|
|
1256
1241
|
|
|
1257
1242
|
# 回调前,保存当前轮次的 LLM 输出到会话记忆
|
|
1258
1243
|
# 这样每轮工具调用都有对应的 assistant 消息记录
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myagent-ai",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.47",
|
|
4
4
|
"description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
|
|
5
5
|
"main": "main.py",
|
|
6
6
|
"bin": {
|
|
@@ -65,4 +65,4 @@
|
|
|
65
65
|
"departments/",
|
|
66
66
|
"web/"
|
|
67
67
|
]
|
|
68
|
-
}
|
|
68
|
+
}
|