myagent-ai 1.15.67 → 1.15.73
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 +15 -7
- package/agents/__pycache__/main_agent.cpython-312.pyc +0 -0
- package/agents/main_agent.py +82 -9
- package/core/__pycache__/update_manager.cpython-312.pyc +0 -0
- package/core/context_builder.py +0 -0
- package/core/output_parser.py +0 -0
- package/core/utils.py +2 -2
- package/install/install.sh +9 -0
- package/package.json +1 -1
- package/skills/chromedev_mcp.py +0 -0
- package/start.js +44 -0
- package/web/__pycache__/api_server.cpython-312.pyc +0 -0
- package/web/api_server.py +45 -1
package/README.md
CHANGED
|
@@ -124,17 +124,13 @@ curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install
|
|
|
124
124
|
|
|
125
125
|
### 快速升级
|
|
126
126
|
|
|
127
|
-
> 已安装过 MyAgent
|
|
127
|
+
> 已安装过 MyAgent 的环境下,一行命令更新到最新版:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
-
|
|
131
|
-
myagent-ai reinstall
|
|
130
|
+
myagent-ai update
|
|
132
131
|
```
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
curl -fsSL https://raw.githubusercontent.com/ctz168/myagent/main/install/install.sh | bash
|
|
137
|
-
```
|
|
133
|
+
该命令会自动完成:npm 升级全局包 → 重建虚拟环境 → 重装所有依赖。
|
|
138
134
|
|
|
139
135
|
---
|
|
140
136
|
|
|
@@ -190,6 +186,9 @@ export MYAGENT_ANTHROPIC_API_KEY="sk-ant-..."
|
|
|
190
186
|
### 2. 运行
|
|
191
187
|
|
|
192
188
|
```bash
|
|
189
|
+
# 更新到最新版(推荐)
|
|
190
|
+
myagent-ai update
|
|
191
|
+
|
|
193
192
|
# Web 管理后台(推荐)
|
|
194
193
|
myagent-ai web
|
|
195
194
|
|
|
@@ -201,6 +200,15 @@ myagent-ai tray
|
|
|
201
200
|
|
|
202
201
|
# API 服务模式
|
|
203
202
|
myagent-ai server
|
|
203
|
+
|
|
204
|
+
# 安装/更新依赖
|
|
205
|
+
myagent-ai install
|
|
206
|
+
|
|
207
|
+
# 重装依赖(删除 venv 重建)
|
|
208
|
+
myagent-ai reinstall
|
|
209
|
+
|
|
210
|
+
# 卸载
|
|
211
|
+
myagent-ai uninstall
|
|
204
212
|
```
|
|
205
213
|
|
|
206
214
|
> 首次运行会自动安装所有依赖,后续启动秒开。
|
|
Binary file
|
package/agents/main_agent.py
CHANGED
|
@@ -202,6 +202,50 @@ class MainAgent(BaseAgent):
|
|
|
202
202
|
except Exception as e:
|
|
203
203
|
logger.debug(f"V2 SSE 事件发送失败 ({event_type}): {e}")
|
|
204
204
|
|
|
205
|
+
def _try_extract_partial_response(self, llm_raw: str) -> str:
|
|
206
|
+
"""[v1.15.73] 从不完整的 LLM 输出中提取部分回复内容。
|
|
207
|
+
|
|
208
|
+
当 <output> 块被截断(缺少 </output>)时,尝试:
|
|
209
|
+
1. 提取 <response>...</response> 中已闭合的内容
|
|
210
|
+
2. 提取 <output> 后到截断点之间的纯文本
|
|
211
|
+
3. 去除 XML 标签后的残余文本
|
|
212
|
+
"""
|
|
213
|
+
if not llm_raw:
|
|
214
|
+
return ""
|
|
215
|
+
|
|
216
|
+
import re
|
|
217
|
+
|
|
218
|
+
# 策略1: 尝试提取已闭合的 <response> 内容
|
|
219
|
+
response_match = re.search(
|
|
220
|
+
r"<response[^>]*>(.*?)</response>",
|
|
221
|
+
llm_raw,
|
|
222
|
+
re.DOTALL | re.IGNORECASE,
|
|
223
|
+
)
|
|
224
|
+
if response_match:
|
|
225
|
+
text = response_match.group(1).strip()
|
|
226
|
+
if text:
|
|
227
|
+
return text
|
|
228
|
+
|
|
229
|
+
# 策略2: 提取 <output> 标签后的内容(可能包含未闭合的 <response>)
|
|
230
|
+
output_match = re.search(r"<output[^>]*>", llm_raw, re.IGNORECASE)
|
|
231
|
+
if output_match:
|
|
232
|
+
after_output = llm_raw[output_match.end():].strip()
|
|
233
|
+
if after_output:
|
|
234
|
+
# 去除所有 XML 标签,保留纯文本
|
|
235
|
+
cleaned = re.sub(r"<[^>]+>", "", after_output).strip()
|
|
236
|
+
# 去除 reasoning/assistant 前缀
|
|
237
|
+
cleaned = re.sub(r"^(reasoning|assistant)\s*", "", cleaned, flags=re.IGNORECASE).strip()
|
|
238
|
+
if cleaned and len(cleaned) > 5:
|
|
239
|
+
return cleaned
|
|
240
|
+
|
|
241
|
+
# 策略3: 提取去除 XML 标签后的整体文本
|
|
242
|
+
cleaned = re.sub(r"<[^>]+>", "", llm_raw).strip()
|
|
243
|
+
cleaned = re.sub(r"^(reasoning|assistant)\s*", "", cleaned, flags=re.IGNORECASE).strip()
|
|
244
|
+
if cleaned and len(cleaned) > 10:
|
|
245
|
+
return cleaned
|
|
246
|
+
|
|
247
|
+
return ""
|
|
248
|
+
|
|
205
249
|
async def _merge_duplicate_memory(
|
|
206
250
|
self,
|
|
207
251
|
old_memory,
|
|
@@ -460,6 +504,8 @@ class MainAgent(BaseAgent):
|
|
|
460
504
|
get_knowledge_content = ""
|
|
461
505
|
# 追踪流式推送的 reasoning 文本(用于构建有意义的最终回复)
|
|
462
506
|
_v2_reasoning_collected: List[str] = []
|
|
507
|
+
# [v1.15.73] 追踪上一轮保存到 memory 的位置,避免重复保存
|
|
508
|
+
_last_saved_len: int = 0
|
|
463
509
|
# XML 解析失败时的 LLM 修正重试计数
|
|
464
510
|
_xml_correction_retries: int = 0
|
|
465
511
|
|
|
@@ -694,6 +740,14 @@ class MainAgent(BaseAgent):
|
|
|
694
740
|
)
|
|
695
741
|
if _xml_correction_retries < 1:
|
|
696
742
|
_xml_correction_retries += 1
|
|
743
|
+
# [v1.15.73] 在重试前,尝试从不完整的 llm_raw 中提取已有内容
|
|
744
|
+
# 防止 reasoning 模型因 max_tokens 截断导致内容完全丢失
|
|
745
|
+
_partial_text = self._try_extract_partial_response(llm_raw)
|
|
746
|
+
if _partial_text:
|
|
747
|
+
logger.info(f"[{task_id}] 从不完整XML中提取到部分回复: {_partial_text[:100]}")
|
|
748
|
+
_v2_reasoning_collected.append(_partial_text)
|
|
749
|
+
_emitted_reasoning_this_iter = True
|
|
750
|
+
await self._emit_v2_event("v2_reasoning", {"content": _partial_text}, stream_callback)
|
|
697
751
|
correction_prompt = (
|
|
698
752
|
"你的输出缺少 </output> 闭合标签,XML块不完整,"
|
|
699
753
|
"解析器不会处理不完整的块。\n"
|
|
@@ -716,12 +770,29 @@ class MainAgent(BaseAgent):
|
|
|
716
770
|
else:
|
|
717
771
|
# 已重试过,强制终止并提示用户
|
|
718
772
|
logger.warning(f"[{task_id}] XML块仍不完整且已重试,终止循环")
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
"
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
773
|
+
# [v1.15.73] 保存已积累的内容,避免空回复
|
|
774
|
+
if _v2_reasoning_collected:
|
|
775
|
+
_fallback_save = "\n".join(_v2_reasoning_collected)
|
|
776
|
+
if _fallback_save.strip():
|
|
777
|
+
context.working_memory["final_response"] = _fallback_save
|
|
778
|
+
if self.memory:
|
|
779
|
+
self.memory.add_session(
|
|
780
|
+
session_id=context.session_id,
|
|
781
|
+
role="assistant",
|
|
782
|
+
content=_fallback_save,
|
|
783
|
+
)
|
|
784
|
+
await self._emit_v2_event(
|
|
785
|
+
"v2_reasoning",
|
|
786
|
+
{"content": _fallback_save},
|
|
787
|
+
stream_callback,
|
|
788
|
+
)
|
|
789
|
+
else:
|
|
790
|
+
context.working_memory["final_response"] = "模型输出格式异常,请重新尝试。"
|
|
791
|
+
await self._emit_v2_event(
|
|
792
|
+
"v2_reasoning",
|
|
793
|
+
{"content": "模型输出格式异常,已自动终止。"},
|
|
794
|
+
stream_callback,
|
|
795
|
+
)
|
|
725
796
|
break
|
|
726
797
|
elif not parsed.output_block_complete and parsed.parse_success:
|
|
727
798
|
# 块不完整但已提取到有效内容,记录日志但正常继续
|
|
@@ -1285,15 +1356,17 @@ class MainAgent(BaseAgent):
|
|
|
1285
1356
|
# 回调前,保存当前轮次的 LLM 输出到会话记忆
|
|
1286
1357
|
# 这样每轮工具调用都有对应的 assistant 消息记录
|
|
1287
1358
|
if self.memory:
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1359
|
+
# [v1.15.73] 只保存自上次保存以来的新增内容,避免重复
|
|
1360
|
+
_new_items = _v2_reasoning_collected[_last_saved_len:]
|
|
1361
|
+
if _new_items:
|
|
1362
|
+
_round_output = "\n".join(_new_items)
|
|
1291
1363
|
if _round_output.strip():
|
|
1292
1364
|
self.memory.add_session(
|
|
1293
1365
|
session_id=context.session_id,
|
|
1294
1366
|
role="assistant",
|
|
1295
1367
|
content=_round_output,
|
|
1296
1368
|
)
|
|
1369
|
+
_last_saved_len = len(_v2_reasoning_collected)
|
|
1297
1370
|
|
|
1298
1371
|
# 循环正常结束(max_iter 耗尽)时兜底保存
|
|
1299
1372
|
else:
|
|
File without changes
|
package/core/context_builder.py
CHANGED
|
File without changes
|
package/core/output_parser.py
CHANGED
|
File without changes
|
package/core/utils.py
CHANGED
package/install/install.sh
CHANGED
|
@@ -216,6 +216,15 @@ fi
|
|
|
216
216
|
echo ""
|
|
217
217
|
echo -e " ${BOLD}${SUCCESS}安装完成!${NC}"
|
|
218
218
|
echo ""
|
|
219
|
+
echo -e " 常用命令:"
|
|
220
|
+
echo -e " ${ACCENT}myagent-ai web${NC} 启动 Web 界面 (默认)"
|
|
221
|
+
echo -e " ${ACCENT}myagent-ai cli${NC} 命令行模式"
|
|
222
|
+
echo -e " ${ACCENT}myagent-ai tray${NC} 系统托盘模式"
|
|
223
|
+
echo -e " ${ACCENT}myagent-ai server${NC} 后台服务模式"
|
|
224
|
+
echo -e " ${ACCENT}myagent-ai update${NC} 更新到最新版"
|
|
225
|
+
echo -e " ${ACCENT}myagent-ai reinstall${NC} 重装依赖"
|
|
226
|
+
echo -e " ${ACCENT}myagent-ai uninstall${NC} 卸载"
|
|
227
|
+
echo ""
|
|
219
228
|
|
|
220
229
|
# 启动 Web 模式
|
|
221
230
|
myagent_cmd="$(find_myagent_cmd)"
|
package/package.json
CHANGED
package/skills/chromedev_mcp.py
CHANGED
|
File without changes
|
package/start.js
CHANGED
|
@@ -384,6 +384,49 @@ function cmdReinstall(pkgDir) {
|
|
|
384
384
|
console.log("");
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
function cmdUpdate(pkgDir) {
|
|
388
|
+
console.log("");
|
|
389
|
+
console.log(" \x1b[36mMyAgent 更新到最新版\x1b[0m");
|
|
390
|
+
console.log("");
|
|
391
|
+
|
|
392
|
+
// 1. npm 升级全局包
|
|
393
|
+
console.log(" 正在通过 npm 更新 myagent-ai ...");
|
|
394
|
+
try {
|
|
395
|
+
execFileSync("npm", ["install", "-g", PKG_NAME, "--prefer-online"], {
|
|
396
|
+
encoding: "utf8", stdio: "inherit", timeout: 120000,
|
|
397
|
+
});
|
|
398
|
+
} catch (e) {
|
|
399
|
+
console.error("\x1b[31mnpm 更新失败,请尝试手动运行: npm install -g myagent-ai@latest\x1b[0m");
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
console.log(" \x1b[32m✓\x1b[0m npm 包已更新");
|
|
403
|
+
|
|
404
|
+
// 2. 重新定位 pkgDir(npm 更新后路径可能变化)
|
|
405
|
+
const newPkgDir = resolvePackageDir();
|
|
406
|
+
if (!fs.existsSync(path.join(newPkgDir, "main.py"))) {
|
|
407
|
+
console.error("\x1b[31m更新后找不到 main.py,请重新安装: npm install -g myagent-ai@latest\x1b[0m");
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 3. 删除旧 venv 并重建
|
|
412
|
+
const venvDir = getVenvDir();
|
|
413
|
+
if (fs.existsSync(venvDir)) {
|
|
414
|
+
console.log(" 删除旧虚拟环境...");
|
|
415
|
+
fs.rmSync(venvDir, { recursive: true, force: true });
|
|
416
|
+
}
|
|
417
|
+
const venvPython = ensureVenv();
|
|
418
|
+
installAllDeps(venvPython, newPkgDir);
|
|
419
|
+
|
|
420
|
+
let ver = "";
|
|
421
|
+
try { ver = JSON.parse(fs.readFileSync(path.join(newPkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
422
|
+
markDepsInstalled(ver);
|
|
423
|
+
|
|
424
|
+
console.log("");
|
|
425
|
+
console.log(` \x1b[32m✓ 更新完成${ver ? " → v" + ver : ""}\x1b[0m`);
|
|
426
|
+
console.log(" 运行 \x1b[36mmyagent-ai web\x1b[0m 启动");
|
|
427
|
+
console.log("");
|
|
428
|
+
}
|
|
429
|
+
|
|
387
430
|
function cmdUninstall() {
|
|
388
431
|
const venvDir = getVenvDir();
|
|
389
432
|
const dataDir = getDataDir();
|
|
@@ -520,6 +563,7 @@ function main() {
|
|
|
520
563
|
}
|
|
521
564
|
|
|
522
565
|
if (cmd === "uninstall") { cmdUninstall(); return; }
|
|
566
|
+
if (cmd === "update") { cmdUpdate(pkgDir); return; }
|
|
523
567
|
if (cmd === "reinstall") { cmdReinstall(pkgDir); return; }
|
|
524
568
|
if (cmd === "install") { cmdInstall(pkgDir); return; }
|
|
525
569
|
|
|
File without changes
|
package/web/api_server.py
CHANGED
|
@@ -2255,6 +2255,8 @@ class ApiServer:
|
|
|
2255
2255
|
conn.execute("DELETE FROM memories WHERE session_id = ?", (sid,))
|
|
2256
2256
|
conn.execute("DELETE FROM session_names WHERE session_id = ?", (sid,))
|
|
2257
2257
|
logger.info(f" 清理会话记忆: {sid}")
|
|
2258
|
+
# 清理该会话的后端运行状态
|
|
2259
|
+
self._cleanup_session_state(sid)
|
|
2258
2260
|
conn.commit()
|
|
2259
2261
|
logger.info(f" 已清理 Agent '{path}' 的 {len(rows)} 个会话记忆")
|
|
2260
2262
|
except Exception as e:
|
|
@@ -2697,10 +2699,51 @@ class ApiServer:
|
|
|
2697
2699
|
entries = [e for e in entries if (e.key or "") not in self._HIDDEN_KEYS]
|
|
2698
2700
|
return web.json_response([{"role": e.role, "content": e.content, "time": e.created_at, "key": e.key or ""} for e in entries])
|
|
2699
2701
|
|
|
2702
|
+
def _cleanup_session_state(self, sid: str):
|
|
2703
|
+
"""删除会话时清理所有关联的后端状态"""
|
|
2704
|
+
cleaned = []
|
|
2705
|
+
# 1. 运行中的会话
|
|
2706
|
+
if sid in self._running_sessions:
|
|
2707
|
+
self._running_sessions[sid]["running"] = False
|
|
2708
|
+
self._running_sessions[sid]["done"] = True
|
|
2709
|
+
del self._running_sessions[sid]
|
|
2710
|
+
cleaned.append("running_session")
|
|
2711
|
+
# 2. 消息队列
|
|
2712
|
+
if sid in self._msg_queues:
|
|
2713
|
+
del self._msg_queues[sid]
|
|
2714
|
+
cleaned.append("msg_queue")
|
|
2715
|
+
# 3. 任务列表
|
|
2716
|
+
if sid in self._task_list_store:
|
|
2717
|
+
del self._task_list_store[sid]
|
|
2718
|
+
cleaned.append("task_list")
|
|
2719
|
+
# 4. 执行进度
|
|
2720
|
+
if sid in self._exec_progress:
|
|
2721
|
+
del self._exec_progress[sid]
|
|
2722
|
+
cleaned.append("exec_progress")
|
|
2723
|
+
# 5. Agent 活跃上下文
|
|
2724
|
+
if self.core.main_agent and sid in self.core.main_agent.active_contexts:
|
|
2725
|
+
self.core.main_agent.active_contexts.pop(sid, None)
|
|
2726
|
+
cleaned.append("active_context")
|
|
2727
|
+
# 6. 自动知识库文件
|
|
2728
|
+
try:
|
|
2729
|
+
cfg = self.core.config_mgr.config
|
|
2730
|
+
kb_dir = getattr(cfg, 'knowledge_base_dir', '') or ''
|
|
2731
|
+
if kb_dir:
|
|
2732
|
+
safe_session = sid.replace("-", "").replace("/", "_")[:12]
|
|
2733
|
+
kb_file = Path(kb_dir) / "auto_knowledge" / f"{safe_session}.md"
|
|
2734
|
+
if kb_file.exists():
|
|
2735
|
+
kb_file.unlink()
|
|
2736
|
+
cleaned.append("knowledge")
|
|
2737
|
+
except Exception:
|
|
2738
|
+
pass
|
|
2739
|
+
if cleaned:
|
|
2740
|
+
logger.info(f"会话 {sid} 后端状态已清理: {', '.join(cleaned)}")
|
|
2741
|
+
|
|
2700
2742
|
async def handle_delete_session(self, request):
|
|
2701
|
-
"""DELETE /api/sessions/{sid} -
|
|
2743
|
+
"""DELETE /api/sessions/{sid} - 彻底删除会话(记忆 + 后端状态)"""
|
|
2702
2744
|
sid = request.match_info["sid"]
|
|
2703
2745
|
logger.info(f"删除会话: {sid}")
|
|
2746
|
+
self._cleanup_session_state(sid)
|
|
2704
2747
|
if self.core.memory:
|
|
2705
2748
|
count = self.core.memory.delete_session(sid)
|
|
2706
2749
|
logger.info(f"会话 {sid} 已删除,共清除 {count} 条记忆")
|
|
@@ -2712,6 +2755,7 @@ class ApiServer:
|
|
|
2712
2755
|
if not sid:
|
|
2713
2756
|
return web.json_response({"ok": False, "error": "missing sid"}, status=400)
|
|
2714
2757
|
logger.info(f"删除会话: {sid}")
|
|
2758
|
+
self._cleanup_session_state(sid)
|
|
2715
2759
|
if self.core.memory:
|
|
2716
2760
|
count = self.core.memory.delete_session(sid)
|
|
2717
2761
|
logger.info(f"会话 {sid} 已删除,共清除 {count} 条记忆")
|