From ed9bbeecb7d9e69c74bcde91096b10eb0b88fadf Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Thu, 25 Jun 2026 12:38:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E5=AD=90=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E4=BD=93=E7=BB=93=E6=9E=9C=E6=B7=BB=E5=8A=A0=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=B9=B6=E8=A7=84=E8=8C=83=20meta/content=20?= =?UTF-8?q?=E5=88=86=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - create_sub_agent 增强显示增加工作时间、调用次数、工具次数 - 将统计信息与最终回复放入 tool-result-content(结果区) - 工具参数保留在 tool-result-meta(参数区) - 在 AGENTS.md 新增 §5.6 工具结果显示规范 --- AGENTS.md | 10 +++- .../components/chat/actions/ToolAction.vue | 55 +++++++++++++++++-- .../components/chat/actions/toolRenderers.ts | 55 +++++++++++++++++-- 3 files changed, 109 insertions(+), 11 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5ec1ef0..47f7817 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Repository Guidelines (Code-Verified) -> Last verified against current codebase: 2026-06-21 +> Last verified against current codebase: 2026-06-25 > Scope: `/Users/jojo/Desktop/agents/正在修复中/agents` 这份文档基于当前仓库实际代码重写。若与未来代码冲突,以代码为准并及时更新本文档。 @@ -183,6 +183,14 @@ 8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。 9. **实体面板禁止半透明**:实体 UI 容器(对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏)背景必须不透明,且不加 `backdrop-filter` 磨砂。唯一例外:遮罩层 scrim(`--overlay-scrim`,`position:fixed;inset:0`)和刻意玻璃质感装饰保留半透明。 +## 5.6) 工具结果显示规范(强制) + +> 适用范围:所有使用 `tool-result-meta` / `tool-result-content` 结构渲染的工具结果(如 `static/src/components/chat/actions/ToolAction.vue`、`toolRenderers.ts` 等)。 +> 约束级别:新增或修改工具结果渲染时**必须遵守**。 + +1. **meta 部分只放参数**:`tool-result-meta` 仅用于展示工具调用的参数、配置、状态标识等元信息。例如:状态、ID、路径、任务描述、超时时间等。禁止在 meta 区域放置执行结果、统计摘要、输出内容等。 +2. **content 部分只放结果**:`tool-result-content` 仅用于展示工具执行后的结果、输出、统计、总结等。例如:文件内容、命令输出、搜索命中、执行统计(工作时间 / 调用次数 / 工具次数)、最终回复摘要等。禁止在 content 区域重复展示工具参数。 + ## 6) Git 工作流(开发 + Review) ### 6.1 核心原则 diff --git a/static/src/components/chat/actions/ToolAction.vue b/static/src/components/chat/actions/ToolAction.vue index 532c658..c364abc 100644 --- a/static/src/components/chat/actions/ToolAction.vue +++ b/static/src/components/chat/actions/ToolAction.vue @@ -89,6 +89,22 @@ function formatBytes(bytes: number): string { return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; } +function formatDuration(seconds: number): string { + if (seconds <= 0) return '0 秒'; + if (seconds < 60) return `${seconds} 秒`; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + if (minutes < 60) { + return remainingSeconds > 0 ? `${minutes} 分 ${remainingSeconds} 秒` : `${minutes} 分`; + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + if (remainingMinutes === 0 && remainingSeconds === 0) { + return `${hours} 小时`; + } + return `${hours} 小时 ${remainingMinutes} 分 ${remainingSeconds} 秒`; +} + function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string { const state = String(result?.status || props.action?.tool?.status || '').toLowerCase(); if (state === 'awaiting_user_answer') { @@ -1099,6 +1115,18 @@ function renderCreateSubAgent(result: any, args: any): string { const taskDescription = args.task ?? ''; const message = result.message ?? result.summary ?? ''; + const stats = result.stats || {}; + const runtimeSeconds = result.runtime_seconds ?? result.elapsed_seconds ?? stats.runtime_seconds ?? 0; + const apiCalls = stats.api_calls ?? stats.turn_count ?? 0; + const toolCount = + (stats.files_read || 0) + + (stats.write_files || 0) + + (stats.edit_files || 0) + + (stats.searches || 0) + + (stats.web_pages || 0) + + (stats.commands || 0); + const hasStats = runtimeSeconds > 0 || apiCalls > 0 || toolCount > 0; + let html = '
'; html += `
状态:${status}
`; if (agentId !== '') { @@ -1117,9 +1145,26 @@ function renderCreateSubAgent(result: any, args: any): string { } html += '
'; - if (message) { - html += '
'; - html += `
${escapeHtml(String(message))}
`; + if (message || hasStats) { + html += '
'; + if (hasStats) { + html += ''; + if (runtimeSeconds > 0) { + html += `
工作时间:${formatDuration(runtimeSeconds)}
`; + } + if (apiCalls > 0) { + html += `
调用次数:${apiCalls} 次
`; + } + if (toolCount > 0) { + html += `
工具次数:${toolCount} 次
`; + } + } + if (message) { + if (hasStats) { + html += ''; + } + html += `
${escapeHtml(String(message))}
`; + } html += '
'; } @@ -1143,7 +1188,7 @@ function renderTerminateSubAgent(result: any, args: any): string { html += '
'; if (message) { - html += '
'; + html += '
'; html += `
${escapeHtml(String(message))}
`; html += '
'; } @@ -1196,7 +1241,7 @@ function renderGetSubAgentStatus(result: any, args: any): string { html += '
'; if (summary) { - html += '
'; + html += '
'; html += `
${escapeHtml(String(summary))}
`; html += '
'; } diff --git a/static/src/components/chat/actions/toolRenderers.ts b/static/src/components/chat/actions/toolRenderers.ts index 22b6b06..d20f3e6 100644 --- a/static/src/components/chat/actions/toolRenderers.ts +++ b/static/src/components/chat/actions/toolRenderers.ts @@ -14,6 +14,22 @@ export function formatBytes(bytes: number): string { return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]; } +function formatDuration(seconds: number): string { + if (seconds <= 0) return '0 秒'; + if (seconds < 60) return `${seconds} 秒`; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + if (minutes < 60) { + return remainingSeconds > 0 ? `${minutes} 分 ${remainingSeconds} 秒` : `${minutes} 分`; + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + if (remainingMinutes === 0 && remainingSeconds === 0) { + return `${hours} 小时`; + } + return `${hours} 小时 ${remainingMinutes} 分 ${remainingSeconds} 秒`; +} + function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string { const state = String(result?.status || '').toLowerCase(); if (state === 'awaiting_user_answer') { @@ -1067,6 +1083,18 @@ function renderCreateSubAgent(result: any, args: any): string { const taskDescription = args.task ?? ''; const message = result.message ?? result.summary ?? ''; + const stats = result.stats || {}; + const runtimeSeconds = result.runtime_seconds ?? result.elapsed_seconds ?? stats.runtime_seconds ?? 0; + const apiCalls = stats.api_calls ?? stats.turn_count ?? 0; + const toolCount = + (stats.files_read || 0) + + (stats.write_files || 0) + + (stats.edit_files || 0) + + (stats.searches || 0) + + (stats.web_pages || 0) + + (stats.commands || 0); + const hasStats = runtimeSeconds > 0 || apiCalls > 0 || toolCount > 0; + let html = '
'; html += `
状态:${status}
`; if (agentId !== '') { @@ -1085,9 +1113,26 @@ function renderCreateSubAgent(result: any, args: any): string { } html += '
'; - if (message) { - html += '
'; - html += `
${escapeHtml(String(message))}
`; + if (message || hasStats) { + html += '
'; + if (hasStats) { + html += ''; + if (runtimeSeconds > 0) { + html += `
工作时间:${formatDuration(runtimeSeconds)}
`; + } + if (apiCalls > 0) { + html += `
调用次数:${apiCalls} 次
`; + } + if (toolCount > 0) { + html += `
工具次数:${toolCount} 次
`; + } + } + if (message) { + if (hasStats) { + html += ''; + } + html += `
${escapeHtml(String(message))}
`; + } html += '
'; } @@ -1111,7 +1156,7 @@ function renderTerminateSubAgent(result: any, args: any): string { html += '
'; if (message) { - html += '
'; + html += '
'; html += `
${escapeHtml(String(message))}
`; html += '
'; } @@ -1164,7 +1209,7 @@ function renderGetSubAgentStatus(result: any, args: any): string { html += '
'; if (summary) { - html += '
'; + html += '
'; html += `
${escapeHtml(String(summary))}
`; html += '
'; }