myagent-ai 1.26.7 → 1.26.8
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 +33 -25
- package/package.json +1 -1
- package/skills/registry.py +2 -1
- package/web/ui/admin/admin-sessions.js +11 -1
- package/web/ui/chat/chat_main.js +22 -6
- package/web/ui/chat/flow_engine.js +30 -8
package/agents/main_agent.py
CHANGED
|
@@ -52,7 +52,7 @@ class MainAgent(BaseAgent):
|
|
|
52
52
|
<mainsubject>当前对话的6字以内标题(每轮都需输出,系统会每3轮自动更新会话名称)</mainsubject>
|
|
53
53
|
<usersays_correct>通过修正识别错误、调整标点,结合上下文,将用户语音转写文本"usersays"修正为更准确的文本,但尽量少改动。如"usersays"为空,则此处为空。</usersays_correct>
|
|
54
54
|
<response><reply>展示给用户的文本,格式上尽量使用md格式,直观形象展示,甚至可以包括超链接、表格等。内容上,针对用户问题,直接回应问题;针对任务,开始的时候,告诉用户,为完成任务,准备如何开展工作;执行过程中,根据工具调用结果,简单展示任务进展;任务完成后的详细最终总结。注意:这是给用户展示信息的最重要标签,尽量不要跟上次回复重复,执行过程展示内容尽量简洁,执行总结可以丰富一点。</reply><toolstocal>
|
|
55
|
-
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的
|
|
55
|
+
<tool><beforecalltext>展示给用户的简单工具调用信息。格式:先使用"接下来、下一步、接着、现在、然后、最后、等下"等连接词➕调用"工具名"。</beforecalltext><toolname>工具名,用于后台解析器解析调用工具</toolname><parms>调用工具的XML格式参数,每个参数用独立子标签表示,如: <command>要执行的命令</command> 或 <query>搜索关键词</query><num>5</num></parms><timeout>最多给它执行多久(秒),工具调用超过这个时限会立即回调大语言模型,方便调整工具使用</timeout></tool>
|
|
56
56
|
</toolstocal>
|
|
57
57
|
</response>
|
|
58
58
|
<task_plan>若"context"包含非空"task_plan",则更新它:若任务条数已超8,则精简为3条,若主题发生明显变化,重新设计任务列表。若"context"包含空"task_plan",则先评估任务复杂度,针对单次查询、简单问答、格式转换、单文件修改、简单计算等简单任务,若预计操作步骤不超过2步,则此处输出为空,不创建任务列表;针对多文件修改、需要调研+实现+测试、涉及多个模块联动等复杂任务,如预计超过2步操作,则以Markdown列表格式制定新任务列表。格式:每项用 "- [ ] 任务描述" 或 "- [x] 已完成任务",含完成状态标记,排序按已完成在前。</task_plan>
|
|
@@ -78,7 +78,7 @@ class MainAgent(BaseAgent):
|
|
|
78
78
|
4. 文件自动发送与下载链接: docx-create、xlsx-create、ppt-create、pdf-create 等命令执行成功后会**自动**将文件发送给用户(无需手动调用 send-file)。系统会返回包含下载链接的文件卡片,LLM 不需要自行拼接文件链接。仅在需要发送其他独立文件(如已存在的文件)时,才使用 myagent-ai send-file <文件路径> [描述]。向用户展示下载链接时,务必使用 Markdown 超链接格式: [文件名](完整URL)。
|
|
79
79
|
|
|
80
80
|
**command**(执行命令行,所有操作都通过它完成):
|
|
81
|
-
<tool><toolname>command</toolname><parms
|
|
81
|
+
<tool><toolname>command</toolname><parms><command>要执行的命令</command></parms><timeout>超时秒数</timeout></tool>
|
|
82
82
|
|
|
83
83
|
【重要】命令执行规则:
|
|
84
84
|
- Shell 原生命令(ls/cat/grep/ps/df/uname/python3/pip/npm/git/curl/wget 等)直接执行,不要加任何前缀
|
|
@@ -123,30 +123,30 @@ GUI桌面 (仅Windows/macOS):
|
|
|
123
123
|
- 播放音频/视频: myagent-ai playaudio/playvideo --url URL [--title 标题]
|
|
124
124
|
|
|
125
125
|
调用示例:
|
|
126
|
-
<tool><toolname>command</toolname><parms>
|
|
127
|
-
<tool><toolname>command</toolname><parms>
|
|
128
|
-
<tool><toolname>command</toolname><parms>
|
|
129
|
-
<tool><toolname>command</toolname><parms>
|
|
126
|
+
<tool><toolname>command</toolname><parms><command>ls -la /tmp && df -h && python3 --version</command></parms><timeout>10</timeout></tool>
|
|
127
|
+
<tool><toolname>command</toolname><parms><command>myagent-ai search 人工智能最新进展</command></parms><timeout>15</timeout></tool>
|
|
128
|
+
<tool><toolname>command</toolname><parms><command>myagent-ai docx-create -c '{"title": "报告", "sections": [{"heading": "摘要", "body": "内容"}]}' -t 周报</command></parms><timeout>30</timeout></tool>
|
|
129
|
+
<tool><toolname>command</toolname><parms><command>cat /etc/os-release && uname -a && free -h</command></parms><timeout>10</timeout></tool>
|
|
130
130
|
|
|
131
131
|
**file_send**(向用户发送文件,文件会以卡片形式显示在聊天中):
|
|
132
|
-
<tool><toolname>file_send</toolname><parms
|
|
132
|
+
<tool><toolname>file_send</toolname><parms><file_path>文件的绝对路径</file_path><description>文件描述(可选)</description></parms><timeout>30</timeout></tool>
|
|
133
133
|
- 当你需要把生成的文件(PDF、Excel、图片、脚本等)发送给用户时,直接使用此工具
|
|
134
134
|
- 当你需要发送一个已存在的文件时,直接使用此工具
|
|
135
135
|
- 不要把文件路径当成文本展示给用户,而是用 file_send 工具发送文件卡片
|
|
136
136
|
|
|
137
137
|
**web_control**(网页控制器,在聊天中打开可操作的浏览器面板):
|
|
138
|
-
<tool><toolname>web_control</toolname><parms>
|
|
139
|
-
- 打开:
|
|
140
|
-
- 导航:
|
|
141
|
-
- 获取内容:
|
|
142
|
-
- 点击:
|
|
143
|
-
- 填写:
|
|
144
|
-
- 滚动:
|
|
145
|
-
- 执行JS:
|
|
146
|
-
- 截图:
|
|
147
|
-
- 等待:
|
|
148
|
-
- Cookie: {"
|
|
149
|
-
- 关闭:
|
|
138
|
+
<tool><toolname>web_control</toolname><parms><action>open</action><url>https://example.com</url></parms><timeout>30</timeout></tool>
|
|
139
|
+
- 打开: <parms><action>open</action><url>URL</url></parms>
|
|
140
|
+
- 导航: <parms><action>navigate</action><url>URL</url><session_id>xxx</session_id></parms>
|
|
141
|
+
- 获取内容: <parms><action>get_content</action><what>text|html|url|title|links|images|forms|inputs</what><session_id>xxx</session_id></parms>
|
|
142
|
+
- 点击: <parms><action>click</action><selector>CSS选择器</selector><session_id>xxx</session_id></parms>
|
|
143
|
+
- 填写: <parms><action>fill</action><selector>CSS选择器</selector><value>内容</value><session_id>xxx</session_id></parms>
|
|
144
|
+
- 滚动: <parms><action>scroll</action><direction>up|down|top|bottom</direction><distance>300</distance><session_id>xxx</session_id></parms>
|
|
145
|
+
- 执行JS: <parms><action>evaluate</action><script>JS代码</script><session_id>xxx</session_id></parms>
|
|
146
|
+
- 截图: <parms><action>screenshot</action><session_id>xxx</session_id></parms>
|
|
147
|
+
- 等待: <parms><action>wait</action><time>1000</time></parms> 或 <parms><action>wait</action><selector>.result</selector><timeout>10</timeout></parms>
|
|
148
|
+
- Cookie: <parms><action>set_cookies</action><cookies>[{"name":"key","value":"val"}]</cookies><session_id>xxx</session_id></parms> 或 <parms><action>get_cookies</action><session_id>xxx</session_id></parms>
|
|
149
|
+
- 关闭: <parms><action>close</action><session_id>xxx</session_id></parms>
|
|
150
150
|
|
|
151
151
|
专业技能指令: 系统内置了丰富的专业技能指南(PDF/DOCX/XLSX/PPT 生成、图表绘制、前端开发等),通过 <get_knowledge> 请求相关技能指令。
|
|
152
152
|
"""
|
|
@@ -1651,16 +1651,24 @@ GUI桌面 (仅Windows/macOS):
|
|
|
1651
1651
|
stream_callback: Optional[Callable] = None,
|
|
1652
1652
|
sent_files: Optional[List[Dict[str, Any]]] = None,
|
|
1653
1653
|
) -> Dict[str, Any]:
|
|
1654
|
-
"""[v1.22.0] V2 工具执行 — 统一分发到 ToolDispatcher
|
|
1654
|
+
"""[v1.22.0] V2 工具执行 — 统一分发到 ToolDispatcher
|
|
1655
|
+
[v1.27.0] parms 使用 XML 子标签格式:
|
|
1656
|
+
<command>ls -la</command> → {"command": "ls -la"}
|
|
1657
|
+
<file_path>/tmp/a.txt</file_path><description>描述</description> → {"file_path": "/tmp/a.txt", "description": "描述"}
|
|
1658
|
+
"""
|
|
1655
1659
|
try:
|
|
1656
|
-
import
|
|
1660
|
+
import re as _re
|
|
1657
1661
|
import html as _html
|
|
1658
1662
|
try:
|
|
1659
|
-
# [v1.23.38] 防御性修复: 解码 HTML 实体 (& → & 等)
|
|
1660
|
-
# LLM 可能在 XML 输出中使用 & 代替 &,导致 bash 命令失败
|
|
1661
1663
|
_clean_parms = _html.unescape(parms_str) if parms_str else ""
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
+
# XML 子标签解析: <key>value</key> → {"key": "value"}
|
|
1665
|
+
params = {}
|
|
1666
|
+
for _tag_name, _tag_value in _re.findall(
|
|
1667
|
+
r"<([a-zA-Z_][a-zA-Z0-9_]*)\s*>([\s\S]*?)</\1\s*>",
|
|
1668
|
+
_clean_parms,
|
|
1669
|
+
):
|
|
1670
|
+
params[_tag_name] = _tag_value.strip()
|
|
1671
|
+
except Exception:
|
|
1664
1672
|
params = {"raw_input": parms_str}
|
|
1665
1673
|
|
|
1666
1674
|
if self.dispatcher:
|
package/package.json
CHANGED
package/skills/registry.py
CHANGED
|
@@ -116,7 +116,8 @@ INTERNAL_SERVICES: List[Dict[str, Any]] = [
|
|
|
116
116
|
# CLI 子命令元数据 — 通过 command 工具间接调用
|
|
117
117
|
# ==============================================================================
|
|
118
118
|
# [v1.23.0] 这些命令由 scripts/cli.py 实现,LLM 通过 command 工具调用:
|
|
119
|
-
# <toolname>command</toolname><parms>
|
|
119
|
+
# <toolname>command</toolname><parms><command>myagent-ai <cmd> [args...]</command></parms>
|
|
120
|
+
# [v1.27.0] parms 从 JSON 改为 XML 子标签格式
|
|
120
121
|
|
|
121
122
|
CLI_COMMANDS: List[Dict[str, Any]] = [
|
|
122
123
|
# ── 感知 ──
|
|
@@ -54,7 +54,17 @@ async function _loadSessionMessages(){
|
|
|
54
54
|
const label=isResult?'工具执行结果':(isCall?'工具调用':'工具过程');
|
|
55
55
|
const isOk=isResult&&!((m.content||'').includes('失败'));
|
|
56
56
|
const badge=isResult?`<span class="badge ${isOk?'badge-green':'badge-red'}" style="margin-left:6px">${isOk?'成功':'失败'}</span>`:'';
|
|
57
|
-
|
|
57
|
+
// [v1.27.0] 工具调用: 格式化参数行显示(提取"参数:"行并用统一格式化)
|
|
58
|
+
let tc=(m.content||'');
|
|
59
|
+
if(isCall && tc && typeof window.formatToolParamsPreview==='function'){
|
|
60
|
+
const lines=tc.split('\n');
|
|
61
|
+
const formatted=lines.map(l=>{
|
|
62
|
+
const pm=l.match(/^参数:\s*([\s\S]*)/);
|
|
63
|
+
if(pm){return '参数: '+window.formatToolParamsPreview(pm[1].trim(),500);}
|
|
64
|
+
return l;
|
|
65
|
+
}).join('\n');
|
|
66
|
+
tc=formatted;
|
|
67
|
+
}
|
|
58
68
|
const tcTrunc=tc.length>800;
|
|
59
69
|
html+=`<details style="margin:4px 0;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden" ${tcTrunc?'':'open'}><summary style="padding:8px 12px;cursor:pointer;font-size:13px;color:var(--text2);background:var(--surface2)">${icon} ${label}${badge}</summary><div style="padding:8px 12px;background:var(--surface);font-size:12px;white-space:pre-wrap;word-break:break-all;color:var(--text2)">${escHtml(tcTrunc?tc.slice(0,800)+'... (共'+tc.length+'字符)':'')}</div></details>`;
|
|
60
70
|
} else {
|
package/web/ui/chat/chat_main.js
CHANGED
|
@@ -3051,6 +3051,7 @@ async function clearCurrentChat() {
|
|
|
3051
3051
|
// 推理思考 → role="assistant", key="reasoning"
|
|
3052
3052
|
|
|
3053
3053
|
// 解析 tool_call 消息内容,兼容新旧格式
|
|
3054
|
+
// [v1.27.0] params 现在支持 XML 子标签和 JSON 两种格式
|
|
3054
3055
|
function parseToolCallContent(content) {
|
|
3055
3056
|
if (!content) return { title: '调用工具', toolName: '', params: '' };
|
|
3056
3057
|
var lines = content.split('\n');
|
|
@@ -3066,7 +3067,12 @@ function parseToolCallContent(content) {
|
|
|
3066
3067
|
var tcIdx = lines.findIndex(function(l) { return l.trim().match(/^调用工具:/); });
|
|
3067
3068
|
var title = tcIdx > 0 ? lines.slice(0, tcIdx).join('\n').trim() : '';
|
|
3068
3069
|
if (!title) title = toolName ? ('调用工具: ' + toolName) : '调用工具';
|
|
3069
|
-
|
|
3070
|
+
// [v1.27.0] 使用统一格式化函数生成显示用 params
|
|
3071
|
+
var displayParams = params;
|
|
3072
|
+
if (typeof window.formatToolParamsPreview === 'function' && params) {
|
|
3073
|
+
displayParams = window.formatToolParamsPreview(params, 300);
|
|
3074
|
+
}
|
|
3075
|
+
return { title: title, toolName: toolName, params: params, displayParams: displayParams };
|
|
3070
3076
|
}
|
|
3071
3077
|
|
|
3072
3078
|
// 解析 tool_result 消息: "[tool_name] 成功/失败\n{output}"
|
|
@@ -3094,7 +3100,7 @@ function groupHistoryMessages(messages) {
|
|
|
3094
3100
|
if (key === 'tool_call') {
|
|
3095
3101
|
var tc = m._parsedToolCall || parseToolCallContent(m.content);
|
|
3096
3102
|
m._parsedToolCall = tc;
|
|
3097
|
-
return { type: 'exec', data: { id: evtId, type: 'tool_call', title: tc.title, tool_name: tc.toolName, params: tc.params || undefined, status: 'done' } };
|
|
3103
|
+
return { type: 'exec', data: { id: evtId, type: 'tool_call', title: tc.title, tool_name: tc.toolName, params: tc.displayParams || tc.params || undefined, status: 'done' } };
|
|
3098
3104
|
}
|
|
3099
3105
|
if (m.role === 'tool') {
|
|
3100
3106
|
var tr = parseToolResultContent(m.content);
|
|
@@ -3236,11 +3242,15 @@ function groupHistoryMessages(messages) {
|
|
|
3236
3242
|
var ep = parts[ei];
|
|
3237
3243
|
if (ep.type === 'exec' && ep.data.tool_name && (ep.data.tool_name === 'playaudio' || ep.data.tool_name === 'playvideo')) {
|
|
3238
3244
|
try {
|
|
3239
|
-
|
|
3240
|
-
var
|
|
3245
|
+
// [v1.27.0] 从 params 中提取 XML 子标签 <url> 和 <title>
|
|
3246
|
+
var _p = ep.data.params || '';
|
|
3247
|
+
var _urlMatch = typeof _p === 'string' ? _p.match(/<url\s*>([\s\S]*?)<\/url\s*>/) : null;
|
|
3248
|
+
var murl = _urlMatch ? _urlMatch[1].trim() : '';
|
|
3249
|
+
var _titleMatch = typeof _p === 'string' ? _p.match(/<title\s*>([\s\S]*?)<\/title\s*>/) : null;
|
|
3250
|
+
var mtitle = _titleMatch ? _titleMatch[1].trim() : '';
|
|
3241
3251
|
if (murl) {
|
|
3242
3252
|
var mtype = ep.data.tool_name === 'playaudio' ? 'audio' : 'video';
|
|
3243
|
-
mediaEmbeds.push({ media_type: mtype, embed_url: murl, title:
|
|
3253
|
+
mediaEmbeds.push({ media_type: mtype, embed_url: murl, title: mtitle, original_url: murl });
|
|
3244
3254
|
}
|
|
3245
3255
|
} catch(mpe) { /* ignore parse errors */ }
|
|
3246
3256
|
}
|
|
@@ -3253,7 +3263,13 @@ function groupHistoryMessages(messages) {
|
|
|
3253
3263
|
var wp = parts[wi];
|
|
3254
3264
|
if (wp.type === 'exec' && wp.data.tool_name === 'web_control') {
|
|
3255
3265
|
try {
|
|
3256
|
-
|
|
3266
|
+
// [v1.27.0] 从 params 中提取 XML 子标签
|
|
3267
|
+
var _p = wp.data.params || '';
|
|
3268
|
+
var wcparams = {};
|
|
3269
|
+
if (typeof _p === 'string') {
|
|
3270
|
+
var _re = /<([a-zA-Z_][a-zA-Z0-9_]*)\s*>([\s\S]*?)<\/\1\s*>/g;
|
|
3271
|
+
var _m; while ((_m = _re.exec(_p)) !== null) { wcparams[_m[1]] = _m[2].trim(); }
|
|
3272
|
+
}
|
|
3257
3273
|
wcEvents.push(wcparams);
|
|
3258
3274
|
} catch(wce) { /* ignore */ }
|
|
3259
3275
|
}
|
|
@@ -1108,6 +1108,31 @@ function _updateToolCardInDOM(msgIdx, partIdx) {
|
|
|
1108
1108
|
targetCard.outerHTML = updatedHtml;
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
|
+
// ══════════════════════════════════════════════════════
|
|
1112
|
+
// ── [v1.27.0] 统一工具参数格式化 — XML 格式显示 ──
|
|
1113
|
+
// 所有页面共用此函数,将 raw parms 字符串格式化为人类可读的简短预览
|
|
1114
|
+
// ══════════════════════════════════════════════════════
|
|
1115
|
+
window.formatToolParamsPreview = function(rawParams, maxLen) {
|
|
1116
|
+
maxLen = maxLen || 200;
|
|
1117
|
+
if (!rawParams) return '';
|
|
1118
|
+
var str = (typeof rawParams === 'string') ? rawParams : JSON.stringify(rawParams);
|
|
1119
|
+
// XML 格式: 提取所有 <key>value</key> 对,格式化为 "key: value"
|
|
1120
|
+
var pairs = [];
|
|
1121
|
+
var re = /<([a-zA-Z_][a-zA-Z0-9_]*)\s*>([\s\S]*?)<\/\1\s*>/g;
|
|
1122
|
+
var m;
|
|
1123
|
+
while ((m = re.exec(str)) !== null) {
|
|
1124
|
+
var val = m[2].trim();
|
|
1125
|
+
if (val.length > 100) val = val.substring(0, 100) + '...';
|
|
1126
|
+
pairs.push(m[1] + ': ' + val);
|
|
1127
|
+
}
|
|
1128
|
+
if (pairs.length > 0) {
|
|
1129
|
+
var result = pairs.join(' | ');
|
|
1130
|
+
return result.length > maxLen ? result.substring(0, maxLen) + '...' : result;
|
|
1131
|
+
}
|
|
1132
|
+
// 非 XML: 直接截断返回
|
|
1133
|
+
return str.length > maxLen ? str.substring(0, maxLen) + '...' : str;
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1111
1136
|
function renderInlineExecEvent(data, msgIdx) {
|
|
1112
1137
|
// V2 Tool Event handling (called with full part: {type:'v2_tool', data:{...}})
|
|
1113
1138
|
if (data.type === 'v2_tool') {
|
|
@@ -1142,10 +1167,9 @@ function renderInlineExecEvent(data, msgIdx) {
|
|
|
1142
1167
|
if (isResult && inner.result) {
|
|
1143
1168
|
bodyHtml += '<button class="inline-exec-result-btn" onclick="showToolResultModal(' + msgIdx + ', \'' + inner.id + '\')">查看详情</button>';
|
|
1144
1169
|
}
|
|
1145
|
-
// Show params for tool_start
|
|
1170
|
+
// Show params for tool_start — [v1.27.0] 使用统一格式化函数
|
|
1146
1171
|
if (isStart && inner.params) {
|
|
1147
|
-
let paramPreview =
|
|
1148
|
-
if (paramPreview.length > 200) paramPreview = paramPreview.substring(0, 200) + '...';
|
|
1172
|
+
let paramPreview = window.formatToolParamsPreview(inner.params, 200);
|
|
1149
1173
|
bodyHtml += '<div class="inline-exec-code">' + escapeHtml(paramPreview) + '</div>';
|
|
1150
1174
|
}
|
|
1151
1175
|
|
|
@@ -1184,10 +1208,9 @@ function renderInlineExecEvent(data, msgIdx) {
|
|
|
1184
1208
|
|
|
1185
1209
|
// Build body content
|
|
1186
1210
|
let bodyHtml = '';
|
|
1187
|
-
// Params for tool_call/skill_call (合并卡片中 params 在折叠区域显示)
|
|
1211
|
+
// Params for tool_call/skill_call (合并卡片中 params 在折叠区域显示) — [v1.27.0] 统一格式化
|
|
1188
1212
|
if (data.params && (data.type === 'tool_call' || data.type === 'skill_call') && !data.has_result) {
|
|
1189
|
-
let paramPreview =
|
|
1190
|
-
if (paramPreview.length > 300) paramPreview = paramPreview.substring(0, 300) + '...';
|
|
1213
|
+
let paramPreview = window.formatToolParamsPreview(data.params, 300);
|
|
1191
1214
|
bodyHtml += '<div class="inline-exec-code">' + escapeHtml(paramPreview) + '</div>';
|
|
1192
1215
|
}
|
|
1193
1216
|
// Code preview for code_exec/code_result
|
|
@@ -1198,8 +1221,7 @@ function renderInlineExecEvent(data, msgIdx) {
|
|
|
1198
1221
|
if (data.has_result) {
|
|
1199
1222
|
// 折叠的参数区域
|
|
1200
1223
|
if (data.params) {
|
|
1201
|
-
let paramPreview =
|
|
1202
|
-
if (paramPreview.length > 300) paramPreview = paramPreview.substring(0, 300) + '...';
|
|
1224
|
+
let paramPreview = window.formatToolParamsPreview(data.params, 300);
|
|
1203
1225
|
bodyHtml += '<div class="tool-result-collapsible"><div class="tool-collapsible-toggle" onclick="this.parentElement.classList.toggle(\'collapsed\')">📋 参数</div><div class="tool-collapsible-body"><div class="inline-exec-code">' + escapeHtml(paramPreview) + '</div></div></div>';
|
|
1204
1226
|
}
|
|
1205
1227
|
// 结果摘要
|