feat(frontend): 子智能体结果添加统计信息并规范 meta/content 分区
- create_sub_agent 增强显示增加工作时间、调用次数、工具次数 - 将统计信息与最终回复放入 tool-result-content(结果区) - 工具参数保留在 tool-result-meta(参数区) - 在 AGENTS.md 新增 §5.6 工具结果显示规范
This commit is contained in:
parent
38ff51416f
commit
ed9bbeecb7
10
AGENTS.md
10
AGENTS.md
@ -1,6 +1,6 @@
|
|||||||
# Repository Guidelines (Code-Verified)
|
# 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`
|
> Scope: `/Users/jojo/Desktop/agents/正在修复中/agents`
|
||||||
|
|
||||||
这份文档基于当前仓库实际代码重写。若与未来代码冲突,以代码为准并及时更新本文档。
|
这份文档基于当前仓库实际代码重写。若与未来代码冲突,以代码为准并及时更新本文档。
|
||||||
@ -183,6 +183,14 @@
|
|||||||
8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。
|
8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。
|
||||||
9. **实体面板禁止半透明**:实体 UI 容器(对话区 / 侧栏 / 下拉/二级菜单 / 抽屉 / 对话框本体 / 状态条 / tooltip / 输入栏)背景必须不透明,且不加 `backdrop-filter` 磨砂。唯一例外:遮罩层 scrim(`--overlay-scrim`,`position:fixed;inset:0`)和刻意玻璃质感装饰保留半透明。
|
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) Git 工作流(开发 + Review)
|
||||||
|
|
||||||
### 6.1 核心原则
|
### 6.1 核心原则
|
||||||
|
|||||||
@ -89,6 +89,22 @@ function formatBytes(bytes: number): string {
|
|||||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
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 {
|
function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string {
|
||||||
const state = String(result?.status || props.action?.tool?.status || '').toLowerCase();
|
const state = String(result?.status || props.action?.tool?.status || '').toLowerCase();
|
||||||
if (state === 'awaiting_user_answer') {
|
if (state === 'awaiting_user_answer') {
|
||||||
@ -1099,6 +1115,18 @@ function renderCreateSubAgent(result: any, args: any): string {
|
|||||||
const taskDescription = args.task ?? '';
|
const taskDescription = args.task ?? '';
|
||||||
const message = result.message ?? result.summary ?? '';
|
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 = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
if (agentId !== '') {
|
if (agentId !== '') {
|
||||||
@ -1117,9 +1145,26 @@ function renderCreateSubAgent(result: any, args: any): string {
|
|||||||
}
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (message) {
|
if (message || hasStats) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(message))}</div>`;
|
if (hasStats) {
|
||||||
|
html += '<div class="content-label">执行统计</div>';
|
||||||
|
if (runtimeSeconds > 0) {
|
||||||
|
html += `<div><strong>工作时间:</strong>${formatDuration(runtimeSeconds)}</div>`;
|
||||||
|
}
|
||||||
|
if (apiCalls > 0) {
|
||||||
|
html += `<div><strong>调用次数:</strong>${apiCalls} 次</div>`;
|
||||||
|
}
|
||||||
|
if (toolCount > 0) {
|
||||||
|
html += `<div><strong>工具次数:</strong>${toolCount} 次</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message) {
|
||||||
|
if (hasStats) {
|
||||||
|
html += '<div class="content-label">最终回复</div>';
|
||||||
|
}
|
||||||
|
html += `<div>${escapeHtml(String(message))}</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1143,7 +1188,7 @@ function renderTerminateSubAgent(result: any, args: any): string {
|
|||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(message))}</div>`;
|
html += `<div>${escapeHtml(String(message))}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
@ -1196,7 +1241,7 @@ function renderGetSubAgentStatus(result: any, args: any): string {
|
|||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (summary) {
|
if (summary) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(summary))}</div>`;
|
html += `<div>${escapeHtml(String(summary))}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,22 @@ export function formatBytes(bytes: number): string {
|
|||||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
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 {
|
function formatToolStatusLabel(result: any, successLabel: string, failedLabel = '✗ 失败'): string {
|
||||||
const state = String(result?.status || '').toLowerCase();
|
const state = String(result?.status || '').toLowerCase();
|
||||||
if (state === 'awaiting_user_answer') {
|
if (state === 'awaiting_user_answer') {
|
||||||
@ -1067,6 +1083,18 @@ function renderCreateSubAgent(result: any, args: any): string {
|
|||||||
const taskDescription = args.task ?? '';
|
const taskDescription = args.task ?? '';
|
||||||
const message = result.message ?? result.summary ?? '';
|
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 = '<div class="tool-result-meta">';
|
let html = '<div class="tool-result-meta">';
|
||||||
html += `<div><strong>状态:</strong>${status}</div>`;
|
html += `<div><strong>状态:</strong>${status}</div>`;
|
||||||
if (agentId !== '') {
|
if (agentId !== '') {
|
||||||
@ -1085,9 +1113,26 @@ function renderCreateSubAgent(result: any, args: any): string {
|
|||||||
}
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (message) {
|
if (message || hasStats) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(message))}</div>`;
|
if (hasStats) {
|
||||||
|
html += '<div class="content-label">执行统计</div>';
|
||||||
|
if (runtimeSeconds > 0) {
|
||||||
|
html += `<div><strong>工作时间:</strong>${formatDuration(runtimeSeconds)}</div>`;
|
||||||
|
}
|
||||||
|
if (apiCalls > 0) {
|
||||||
|
html += `<div><strong>调用次数:</strong>${apiCalls} 次</div>`;
|
||||||
|
}
|
||||||
|
if (toolCount > 0) {
|
||||||
|
html += `<div><strong>工具次数:</strong>${toolCount} 次</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message) {
|
||||||
|
if (hasStats) {
|
||||||
|
html += '<div class="content-label">最终回复</div>';
|
||||||
|
}
|
||||||
|
html += `<div>${escapeHtml(String(message))}</div>`;
|
||||||
|
}
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1111,7 +1156,7 @@ function renderTerminateSubAgent(result: any, args: any): string {
|
|||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(message))}</div>`;
|
html += `<div>${escapeHtml(String(message))}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
@ -1164,7 +1209,7 @@ function renderGetSubAgentStatus(result: any, args: any): string {
|
|||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
if (summary) {
|
if (summary) {
|
||||||
html += '<div class="tool-result-content">';
|
html += '<div class="tool-result-content scrollable">';
|
||||||
html += `<div>${escapeHtml(String(summary))}</div>`;
|
html += `<div>${escapeHtml(String(summary))}</div>`;
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user