myagent-ai 1.23.34 → 1.23.35
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
CHANGED
|
@@ -48,7 +48,7 @@ class MainAgent(BaseAgent):
|
|
|
48
48
|
<mainsubject>当前对话的6字以内标题(每轮都需输出,系统会每3轮自动更新会话名称)</mainsubject>
|
|
49
49
|
<usersays_correct>通过修正识别错误、调整标点,结合上下文,将用户语音转写文本"usersays"修正为更准确的文本,但尽量少改动。如"usersays"为空,则此处为空。</usersays_correct>
|
|
50
50
|
<response><reply>展示给用户的文本,格式上尽量使用md格式,直观形象展示,甚至可以包括超链接、表格等。内容上,针对用户问题,直接回应问题;针对任务,开始的时候,告诉用户,为完成任务,准备如何开展工作;执行过程中,根据工具调用结果,简单展示任务进展;任务完成后的详细最终总结。注意:这是给用户展示信息的最重要标签,尽量不要跟上次回复重复,执行过程展示内容尽量简洁,执行总结可以丰富一点。</reply><toolstocal>
|
|
51
|
-
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的JSON格式参数对象,如格式: {"query": "搜索关键词", "num": 5}</parms><timeout
|
|
51
|
+
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的JSON格式参数对象,如格式: {"query": "搜索关键词", "num": 5}</parms><timeout>最多给它执行多久(秒),工具调用超过这个时限会立即回调大语言模型,方便调整工具使用</timeout></tool>
|
|
52
52
|
</toolstocal>
|
|
53
53
|
</response>
|
|
54
54
|
<task_plan>若"context"包含非空"task_plan",则更新它:若任务条数已超8,则精简为3条,若主题发生明显变化,重新设计任务列表。若"context"包含空"task_plan",则先评估任务复杂度,针对单次查询、简单问答、格式转换、单文件修改、简单计算等简单任务,若预计操作步骤不超过2步,则此处输出为空,不创建任务列表;针对多文件修改、需要调研+实现+测试、涉及多个模块联动等复杂任务,如预计超过2步操作,则以Markdown列表格式制定新任务列表。格式:每项用 "- [ ] 任务描述" 或 "- [x] 已完成任务",含完成状态标记,排序按已完成在前。</task_plan>
|
|
@@ -63,7 +63,7 @@ class MainAgent(BaseAgent):
|
|
|
63
63
|
</output>
|
|
64
64
|
|
|
65
65
|
注意事项:
|
|
66
|
-
1. toolstocal标签: 尽量一次性列出所有需执行工具调用的,多个"tool"工具调用只要按顺序重复堆叠tool
|
|
66
|
+
1. toolstocal标签: 尽量一次性列出所有需执行工具调用的,多个"tool"工具调用只要按顺序重复堆叠tool标签即可,解析器会按顺序执行工具调用,最终全部执行完后,会连同所有结果,回调大语言模型。如果某个工具执行超过预估的时限,也会回调大模型,让大模型分析为什么超时,改用其他工具。要求每个工具调用尽快合并多个命令行。注意: docx/xlsx/ppt/pdf-create 命令执行后会自动发送文件给用户,不需要再拼接 send-file。仅在需要发送其他已存在的独立文件时才需要手动调用 send-file。多个独立查询或系统命令才需要用 && 拼接。
|
|
67
67
|
2. 上下文中的记忆系统说明
|
|
68
68
|
- <automemory>: 系统自动根据你通过 <remember> 保存的记忆和当前用户输入,搜索出的 top10 相关记忆。这些是你过去主动记住的内容(包含时间信息),可供参考。
|
|
69
69
|
- <recall_memory>: 你在上一轮通过 <recall> 指定的记忆搜索结果。系统根据你提供的关键字和时间点搜索了 top5 相关记忆。
|
|
@@ -184,7 +184,7 @@ class MainAgent(BaseAgent):
|
|
|
184
184
|
<mainsubject>当前对话的6字以内标题(每轮都需输出,系统会每3轮自动更新会话名称)</mainsubject>
|
|
185
185
|
<usersays_correct>通过修正识别错误、调整标点,结合上下文,将用户语音转写文本"usersays"修正为更准确的文本,但尽量少改动。如"usersays"为空,则此处为空。</usersays_correct>
|
|
186
186
|
<response><reply>展示给用户的文本,格式上尽量使用md格式,直观形象展示,甚至可以包括超链接、表格等。内容上,针对用户问题,直接回应问题;针对任务,开始的时候,告诉用户,为完成任务,准备如何开展工作;执行过程中,根据工具调用结果,简单展示任务进展;任务完成后的详细最终总结。注意:这是给用户展示信息的最重要标签,尽量不要跟上次回复重复,执行过程展示内容尽量简洁,执行总结可以丰富一点。</reply><toolstocal>
|
|
187
|
-
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的JSON格式参数对象,如格式: {"query": "搜索关键词", "num": 5}</parms><timeout
|
|
187
|
+
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的JSON格式参数对象,如格式: {"query": "搜索关键词", "num": 5}</parms><timeout>最多给它执行多久(秒),工具调用超过这个时限会立即回调大语言模型,方便调整工具使用。</timeout></tool>
|
|
188
188
|
</toolstocal>
|
|
189
189
|
</response>
|
|
190
190
|
<task_plan>若"context"包含非空"task_plan",则更新它:若任务条数已超8,则精简为3条,若主题发生明显变化,重新设计任务列表。若"context"包含空"task_plan",则先评估任务复杂度,针对单次查询、简单问答、格式转换、单文件修改、简单计算等简单任务,若预计操作步骤不超过2步,则此处输出为空,不创建任务列表;针对多文件修改、需要调研+实现+测试、涉及多个模块联动等复杂任务,如预计超过2步操作,则以Markdown列表格式制定新任务列表。格式:每项用 "- [ ] 任务描述" 或 "- [x] 已完成任务",含完成状态标记,排序按已完成在前。</task_plan>
|
package/core/output_parser.py
CHANGED
|
@@ -22,7 +22,6 @@ Expected XML schema produced by the LLM::
|
|
|
22
22
|
<toolname>工具名</toolname>
|
|
23
23
|
<parms>参数JSON或描述</parms>
|
|
24
24
|
<timeout>预估超时时限(秒)</timeout>
|
|
25
|
-
<callback>true/false</callback>
|
|
26
25
|
</tool>
|
|
27
26
|
</toolstocal>
|
|
28
27
|
</response>
|
|
@@ -62,7 +61,6 @@ logger = get_logger("myagent.output_parser")
|
|
|
62
61
|
# ---------------------------------------------------------------------------
|
|
63
62
|
|
|
64
63
|
_DEFAULT_TIMEOUT: int = 120
|
|
65
|
-
_DEFAULT_CALLBACK: bool = True
|
|
66
64
|
|
|
67
65
|
# All top-level tags we recognise inside <output>.
|
|
68
66
|
KNOWN_TOP_LEVEL_TAGS = [
|
|
@@ -89,7 +87,6 @@ TOOL_INNER_TAGS = [
|
|
|
89
87
|
"toolname",
|
|
90
88
|
"parms",
|
|
91
89
|
"timeout",
|
|
92
|
-
"callback",
|
|
93
90
|
]
|
|
94
91
|
|
|
95
92
|
# Inner tags inside <remember>.
|
|
@@ -590,10 +587,6 @@ def _parse_toolstocal(toolstocal_content: str, *, conservative: bool = False) ->
|
|
|
590
587
|
_extract_tag_content(block, "timeout", TOOL_INNER_TAGS, conservative=conservative),
|
|
591
588
|
_DEFAULT_TIMEOUT,
|
|
592
589
|
),
|
|
593
|
-
"callback": _parse_bool(
|
|
594
|
-
_extract_tag_content(block, "callback", TOOL_INNER_TAGS, conservative=conservative),
|
|
595
|
-
_DEFAULT_CALLBACK,
|
|
596
|
-
),
|
|
597
590
|
}
|
|
598
591
|
# Only add if toolname is present
|
|
599
592
|
if tool["toolname"]:
|
package/core/tool_dispatcher.py
CHANGED
|
@@ -300,16 +300,15 @@ class ToolDispatcher:
|
|
|
300
300
|
) -> Dict:
|
|
301
301
|
"""发送文件给用户 — 后端推送 v2_file SSE 事件 + 持久化到聊天记录"""
|
|
302
302
|
try:
|
|
303
|
-
from skills.file_send import
|
|
304
|
-
fskill = FileSendSkill()
|
|
303
|
+
from skills.file_send import UPLOADS_DIR
|
|
305
304
|
fpath = params.get("file_path", "")
|
|
306
305
|
fdesc = params.get("description", "")
|
|
307
306
|
if not fpath:
|
|
308
307
|
logger.warning(f"[{task_id}] file_send: 缺少 file_path 参数")
|
|
309
308
|
return {"success": False, "error": "缺少 file_path 参数,请提供要发送的文件路径"}
|
|
310
309
|
logger.info(f"[{task_id}] file_send: 发送文件 {fpath}")
|
|
311
|
-
|
|
312
|
-
# [v1.23.
|
|
310
|
+
|
|
311
|
+
# [v1.23.35] 先复制文件(不依赖 file_send.execute 的 SSE 发送)
|
|
313
312
|
from pathlib import Path as _P
|
|
314
313
|
import shutil, uuid as _uuid, time as _time
|
|
315
314
|
fpath_resolved = _P(fpath.strip().strip("'\"")).expanduser()
|
|
@@ -319,7 +318,8 @@ class ToolDispatcher:
|
|
|
319
318
|
return {"success": False, "error": f"不是文件: {fpath}"}
|
|
320
319
|
|
|
321
320
|
file_id = str(_uuid.uuid4())[:12]
|
|
322
|
-
|
|
321
|
+
# [v1.23.35] 直接使用模块级 UPLOADS_DIR,不依赖 FileSendSkill 实例属性
|
|
322
|
+
date_dir = UPLOADS_DIR / _time.strftime("%Y-%m")
|
|
323
323
|
date_dir.mkdir(parents=True, exist_ok=True)
|
|
324
324
|
stored_name = f"{file_id}_{fpath_resolved.name}"
|
|
325
325
|
stored_path = date_dir / stored_name
|
package/package.json
CHANGED
package/web/api_server.py
CHANGED
|
@@ -1810,12 +1810,9 @@ window.addEventListener('beforeunload', function() {{
|
|
|
1810
1810
|
agent_name = data.get("agent_name", "default") or "default"
|
|
1811
1811
|
# 支持 path 格式 (如 "coder/python-expert")
|
|
1812
1812
|
agent_path = data.get("agent_path", agent_name)
|
|
1813
|
-
|
|
1814
|
-
#
|
|
1815
|
-
|
|
1816
|
-
session_id = raw_session_id
|
|
1817
|
-
else:
|
|
1818
|
-
session_id = f"{agent_path}_{raw_session_id}"
|
|
1813
|
+
# [v1.23.35] 直接使用前端传来的 session_id,不再拼接 agent_path 前缀
|
|
1814
|
+
# 新格式(sess_uuid)和旧格式(agent_path_web_xxx)均兼容
|
|
1815
|
+
session_id = data.get("session_id", "") or "web_default"
|
|
1819
1816
|
chat_mode = data.get("mode", "") # "exec" = 执行模式
|
|
1820
1817
|
escalated = data.get("escalated", False) # 临时提权到 local
|
|
1821
1818
|
|
|
@@ -1970,12 +1967,8 @@ window.addEventListener('beforeunload', function() {{
|
|
|
1970
1967
|
return web.Response(text="data: " + json.dumps({"error": "message is required"}) + "\n\n", content_type="text/event-stream")
|
|
1971
1968
|
|
|
1972
1969
|
agent_path = data.get("agent_path", data.get("agent_name", "default")) or "default"
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
if raw_session_id.startswith(f"{agent_path}_"):
|
|
1976
|
-
session_id = raw_session_id
|
|
1977
|
-
else:
|
|
1978
|
-
session_id = f"{agent_path}_{raw_session_id}"
|
|
1970
|
+
# [v1.23.35] 直接使用前端传来的 session_id,不再拼接 agent_path 前缀
|
|
1971
|
+
session_id = data.get("session_id", "") or "web_default"
|
|
1979
1972
|
chat_mode = data.get("mode", "")
|
|
1980
1973
|
escalated = data.get("escalated", False)
|
|
1981
1974
|
voice_text = data.get("voice_text", "").strip() # 语音转文字原始文本(用于 usersays_correct)
|
|
@@ -2255,12 +2248,8 @@ window.addEventListener('beforeunload', function() {{
|
|
|
2255
2248
|
return web.json_response({"error": "message is required"}, status=400)
|
|
2256
2249
|
|
|
2257
2250
|
agent_path = data.get("agent_path", "default")
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
if raw_session_id.startswith(f"{agent_path}_"):
|
|
2261
|
-
session_id = raw_session_id
|
|
2262
|
-
else:
|
|
2263
|
-
session_id = f"{agent_path}_{raw_session_id}"
|
|
2251
|
+
# [v1.23.35] 直接使用前端传来的 session_id,不再拼接 agent_path 前缀
|
|
2252
|
+
session_id = data.get("session_id", "web_default")
|
|
2264
2253
|
choice = data.get("choice", "queue") # "continue" (插入后继续) 或 "queue" (排队)
|
|
2265
2254
|
|
|
2266
2255
|
# 检查会话是否正在运行
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -453,8 +453,12 @@ async function initChat() {
|
|
|
453
453
|
selectAgent(resolved);
|
|
454
454
|
}
|
|
455
455
|
} else if (urlSession) {
|
|
456
|
-
// 只有 session 没有 agent,尝试从 session ID 推断 agent
|
|
457
|
-
|
|
456
|
+
// 只有 session 没有 agent,尝试从 session ID 推断 agent(兼容旧格式 agent_web_xxx)
|
|
457
|
+
var targetAgent = 'default';
|
|
458
|
+
if (urlSession.indexOf('_web_') >= 0) {
|
|
459
|
+
targetAgent = urlSession.split('_web_')[0] || 'default';
|
|
460
|
+
}
|
|
461
|
+
// 新格式 sess_xxx 无法推断 agent,回退 default
|
|
458
462
|
var target = targetAgent;
|
|
459
463
|
if (!findAgentByPath(target)) {
|
|
460
464
|
console.warn('Session-inferred agent not found, fallback to default:', target);
|
|
@@ -2078,7 +2082,8 @@ function formatSessionName(id) {
|
|
|
2078
2082
|
if (!id) return '新会话';
|
|
2079
2083
|
if (id.startsWith('web_')) return id.replace('web_', '').replace(/_/g, ' ');
|
|
2080
2084
|
if (id.startsWith('cli_')) return 'CLI: ' + id.replace('cli_', '');
|
|
2081
|
-
//
|
|
2085
|
+
// [v1.23.35] 新格式 sess_xxx 直接显示"新对话",由后端自动命名
|
|
2086
|
+
if (id.startsWith('sess_')) return '新对话';
|
|
2082
2087
|
return id.length > 20 ? id.slice(0, 20) + '...' : id;
|
|
2083
2088
|
}
|
|
2084
2089
|
|
|
@@ -3069,9 +3074,9 @@ window.buildMessageHtml = function(msg, idx, agent) {
|
|
|
3069
3074
|
taskPlanHtml +
|
|
3070
3075
|
finishReasonHtml +
|
|
3071
3076
|
imageAttachmentHtml +
|
|
3072
|
-
mediaEmbedHtml +
|
|
3073
3077
|
wcHtml +
|
|
3074
3078
|
bubbleHtml +
|
|
3079
|
+
mediaEmbedHtml +
|
|
3075
3080
|
fileAttachmentHtml +
|
|
3076
3081
|
streamingIndicator +
|
|
3077
3082
|
(msg.time ? '<div class="message-time">' + formatTime(msg.time) + '</div>' : '') +
|
|
@@ -692,16 +692,16 @@ function updateStreamingMessage(msgIdx) {
|
|
|
692
692
|
}
|
|
693
693
|
|
|
694
694
|
// ── [v1.23.19] 增量渲染在线媒体嵌入播放器(v2_media 事件) ──
|
|
695
|
-
// [v1.23.
|
|
695
|
+
// [v1.23.35] 媒体容器放在 bubbleWrapper 之后(对话底部),不再放在前面
|
|
696
696
|
if (msg._media && msg._media.length > 0) {
|
|
697
697
|
var existingMedia = contentArea.querySelectorAll(':scope > .msg-attachments-media');
|
|
698
698
|
var mediaContainer = existingMedia.length > 0 ? existingMedia[0] : null;
|
|
699
699
|
if (!mediaContainer) {
|
|
700
700
|
mediaContainer = document.createElement('div');
|
|
701
701
|
mediaContainer.className = 'msg-attachments msg-attachments-media';
|
|
702
|
-
// 插入到 bubbleWrapper
|
|
702
|
+
// [v1.23.35] 插入到 bubbleWrapper 之后(对话末尾)
|
|
703
703
|
if (bubbleWrapper && bubbleWrapper.parentNode) {
|
|
704
|
-
bubbleWrapper.parentNode.insertBefore(mediaContainer, bubbleWrapper);
|
|
704
|
+
bubbleWrapper.parentNode.insertBefore(mediaContainer, bubbleWrapper.nextSibling);
|
|
705
705
|
} else {
|
|
706
706
|
contentArea.appendChild(mediaContainer);
|
|
707
707
|
}
|
|
@@ -1592,7 +1592,7 @@ async function sendMessage(opts) {
|
|
|
1592
1592
|
return;
|
|
1593
1593
|
}
|
|
1594
1594
|
|
|
1595
|
-
// Create session if needed
|
|
1595
|
+
// [v1.23.35] Create session if needed — 使用 UUID 防止 ID 重复/乱串
|
|
1596
1596
|
let sessionId = state.activeSessionId;
|
|
1597
1597
|
if (!sessionId || sessionId === '__new__') {
|
|
1598
1598
|
// 防御:如果当前已有消息(说明有活跃会话但 ID 丢失),尝试复用已有会话
|
|
@@ -1601,8 +1601,8 @@ async function sendMessage(opts) {
|
|
|
1601
1601
|
state.activeSessionId = sessionId;
|
|
1602
1602
|
console.warn('[sendMessage] activeSessionId was empty but messages exist, reusing session:', sessionId);
|
|
1603
1603
|
} else {
|
|
1604
|
-
|
|
1605
|
-
sessionId =
|
|
1604
|
+
// 使用 UUID v4 保证全局唯一性,不再暴露 agent 名明文
|
|
1605
|
+
sessionId = 'sess_' + crypto.randomUUID().replace(/-/g, '').slice(0, 20);
|
|
1606
1606
|
state.activeSessionId = sessionId;
|
|
1607
1607
|
document.getElementById('headerTitle').textContent = formatSessionName(sessionId);
|
|
1608
1608
|
// ── 立即在左侧边栏添加新会话条目(不等后端返回) ──
|
package/web/ui/chat/groupchat.js
CHANGED
|
@@ -842,6 +842,10 @@ function showAgentProfile(agentPath) {
|
|
|
842
842
|
var toolsStr = tools.length > 0 ? tools.join(', ') : '默认工具集';
|
|
843
843
|
|
|
844
844
|
var container = document.getElementById('groupModalContainer');
|
|
845
|
+
// [v1.23.35] 添加"对话"按钮 — 点击后退出群聊,切换到该 Agent 的个人对话
|
|
846
|
+
var chatBtnHtml = typeof selectAgent === 'function'
|
|
847
|
+
? '<button class="agent-modal-btn agent-modal-btn-primary" onclick="closeGroupModal();selectAgent(\'' + escapeHtml(agentPath) + '\')">💬 对话</button>'
|
|
848
|
+
: '';
|
|
845
849
|
container.innerHTML = '<div class="modal-overlay" onclick="if(event.target===this)closeGroupModal()"><div class="group-modal" style="max-width:380px">'
|
|
846
850
|
+ '<div style="display:flex;align-items:center;gap:14px;margin-bottom:16px">'
|
|
847
851
|
+ '<div class="group-msg-avatar" style="background:' + color + ';color:#fff;width:48px;height:48px;border-radius:12px;font-size:22px">' + emoji + '</div>'
|
|
@@ -850,6 +854,7 @@ function showAgentProfile(agentPath) {
|
|
|
850
854
|
+ '<div style="font-size:14px;color:var(--text2);line-height:1.6;margin-bottom:14px;padding:12px;background:var(--bg);border-radius:var(--radius-sm);border:1px solid var(--border-light)">' + escapeHtml(desc) + '</div>'
|
|
851
855
|
+ (tools.length > 0 ? '<div style="font-size:12px;color:var(--text3);margin-bottom:14px"><span style="font-weight:600">工具:</span> ' + escapeHtml(toolsStr) + '</div>' : '')
|
|
852
856
|
+ '<div class="agent-modal-actions">'
|
|
857
|
+
+ chatBtnHtml
|
|
853
858
|
+ '<button class="agent-modal-btn agent-modal-btn-cancel" onclick="closeGroupModal()">关闭</button>'
|
|
854
859
|
+ '</div></div></div>';
|
|
855
860
|
}
|