From 4d346103b4a56207cfd0ba406631017a2c08d85e Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Mon, 13 Apr 2026 18:06:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=8A=98=E5=8F=A0=E5=B1=95=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=B9=E8=AF=9D=E4=B8=AD=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=9A=84=E6=98=BE=E7=A4=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yuxi/agents/buildin/chatbot/prompt.py | 4 + docs/develop-guides/roadmap.md | 1 + web/src/components/AgentChatComponent.vue | 144 +++++++++- web/src/components/AgentMessageComponent.vue | 34 +-- .../ToolCallingResult/BaseToolCall.vue | 173 +++++++----- .../ToolCallingResult/ToolCallRenderer.vue | 25 +- .../ToolCallingResult/toolRegistry.js | 5 + .../ToolCallingResult/tools/EditFileTool.vue | 17 +- .../ToolCallingResult/tools/ReadFileTool.vue | 47 +++- .../ToolCallingResult/tools/TaskTool.vue | 9 +- .../ToolCallingResult/tools/TodoListTool.vue | 5 +- .../components/ToolCallsGroupComponent.vue | 246 ++++++++++++++++++ 12 files changed, 577 insertions(+), 133 deletions(-) create mode 100644 web/src/components/ToolCallsGroupComponent.vue diff --git a/backend/package/yuxi/agents/buildin/chatbot/prompt.py b/backend/package/yuxi/agents/buildin/chatbot/prompt.py index c572ca70..df1d3c9b 100644 --- a/backend/package/yuxi/agents/buildin/chatbot/prompt.py +++ b/backend/package/yuxi/agents/buildin/chatbot/prompt.py @@ -25,6 +25,10 @@ PROMPT = f""" 当 query_kb 中没有找到相关的内容,或者需要进一步基于检索到的内容获取更加详细的上下文的时候,还可以直接访问知识库文件系统 (路径为 {VIRTUAL_KBS_PATH})来获取信息。 源文件可能无法直接读取,可以在 {VIRTUAL_KBS_PATH}//parsed/ 中找到解析后的 markdown 文件。 +""" + +# 效果不好,暂时不启用 +SOURCE_CITE_PROMPT = """ <| 引用来源 |> 当你提供的信息来自于用户上传的文件或者知识库中的内容时,请务必在回答中注明信息来源,以增加答案的可信度和透明度。 diff --git a/docs/develop-guides/roadmap.md b/docs/develop-guides/roadmap.md index a5d41adc..858a8057 100644 --- a/docs/develop-guides/roadmap.md +++ b/docs/develop-guides/roadmap.md @@ -51,6 +51,7 @@ - 修复前端依赖安全告警:通过 `pnpm.overrides` 将传递依赖 `flatted` 锁定到 `3.4.2`、`lodash-es` 锁定到 `4.18.1`,并同步更新 `pnpm-lock.yaml` 以消除 DriftGuard 报告的高危 CVE - 重写界面设计规范:参考 `DESIGN.md` 写法补充视觉气质、颜色 token、组件状态、布局层级、响应式与 Agent Prompt Guide,并基于该规范收敛首页视觉表现,移除装饰性渐变、重阴影、hover 位移和入场动画。 - 修复对话摘要中间件的工具结果卸载链路:摘要触发时改为将大体积 `ToolMessage` 写入当前 agent 可见的 sandbox outputs 路径,修正 `summary_offload` 路径拼接错误、`messages` 触发条件下不会真正裁剪历史的问题,并避免将 system message 重复纳入摘要与最终消息列表;补充对应单元测试覆盖。 +- 调整智能体对话中的工具调用展示:连续工具调用默认折叠为“调用了 N 个工具”的轻量摘要,展开后改为弱化时间线样式,减少工具结果卡片对正文阅读节奏的干扰。 --- diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index ca796568..44ec53fd 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -86,19 +86,22 @@
- - + { } // ==================== HELPER FUNCTIONS ==================== +const extractAssistantMessageBody = (message) => { + let content = typeof message?.content === 'string' ? message.content.trim() : '' + let reasoningContent = message?.additional_kwargs?.reasoning_content || '' + + if (!reasoningContent && content) { + const thinkRegex = /(.*?)<\/think>|(.*?)$/s + const thinkMatch = content.match(thinkRegex) + + if (thinkMatch) { + reasoningContent = (thinkMatch[1] || thinkMatch[2] || '').trim() + content = content.replace(thinkMatch[0], '').trim() + } + } + + return { content, reasoningContent } +} + +const hasVisibleAssistantBody = (message) => { + if (!message || message.type !== 'ai') return true + + const { content, reasoningContent } = extractAssistantMessageBody(message) + return Boolean( + content || + reasoningContent || + message.error_type || + message.extra_metadata?.error_type || + message.isStoppedByUser + ) +} + +const getMessageToolCalls = (message) => { + if (!Array.isArray(message?.tool_calls)) return [] + + return message.tool_calls.filter((toolCall) => { + return ( + toolCall && + (toolCall.id || toolCall.name || toolCall.function?.name) && + (toolCall.args !== undefined || + toolCall.function?.arguments !== undefined || + toolCall.tool_call_result !== undefined) + ) + }) +} + +// 将 AI 消息拆成“正文块”和“工具块”,再跨消息合并相邻工具块。 +const getConversationDisplayItems = (conv) => { + if (!Array.isArray(conv?.messages) || conv.messages.length === 0) return [] + + const items = [] + let pendingToolGroup = null + + const flushToolGroup = () => { + if (pendingToolGroup && pendingToolGroup.toolCalls.length > 0) { + items.push(pendingToolGroup) + } + pendingToolGroup = null + } + + conv.messages.forEach((message, index) => { + if (message.type !== 'ai') { + flushToolGroup() + items.push({ + type: 'message', + key: message.id || `message-${index}`, + message, + sourceIndex: index + }) + return + } + + if (hasVisibleAssistantBody(message)) { + flushToolGroup() + items.push({ + type: 'message', + key: message.id || `message-${index}`, + message, + sourceIndex: index + }) + } + + const toolCalls = getMessageToolCalls(message) + if (toolCalls.length === 0) return + + if (!pendingToolGroup) { + pendingToolGroup = { + type: 'tool-group', + key: `tool-group-${message.id || index}`, + toolCalls: [] + } + } + pendingToolGroup.toolCalls.push(...toolCalls) + }) + + flushToolGroup() + return items +} + +const isDisplayMessageProcessing = (conv, displayItem) => { + return ( + displayItem?.type === 'message' && + isProcessing.value && + conv?.status === 'streaming' && + displayItem.sourceIndex === conv.messages.length - 1 + ) +} + +const isToolGroupActive = (conv, itemIndex, displayItems) => { + return ( + isProcessing.value && + conv?.status === 'streaming' && + itemIndex === displayItems.length - 1 + ) +} + const getLastMessage = (conv) => { if (!conv?.messages?.length) return null for (let i = conv.messages.length - 1; i >= 0; i--) { diff --git a/web/src/components/AgentMessageComponent.vue b/web/src/components/AgentMessageComponent.vue index 34fdd4fb..f25d3abc 100644 --- a/web/src/components/AgentMessageComponent.vue +++ b/web/src/components/AgentMessageComponent.vue @@ -63,15 +63,10 @@ {{ message.error_type || '未知错误' }}
-
-
- -
-
+
你停止生成了本次回答 @@ -111,7 +106,7 @@ import { computed, ref } from 'vue' import { CaretRightOutlined } from '@ant-design/icons-vue' import RefsComponent from '@/components/RefsComponent.vue' import { Copy, Check } from 'lucide-vue-next' -import { ToolCallRenderer } from '@/components/ToolCallingResult' +import ToolCallsGroupComponent from '@/components/ToolCallsGroupComponent.vue' import { useAgentStore } from '@/stores/agent' import { useInfoStore } from '@/stores/info' import { useThemeStore } from '@/stores/theme' @@ -147,6 +142,10 @@ const props = defineProps({ type: Boolean, default: false }, + hideToolCalls: { + type: Boolean, + default: false + }, // 是否显示调试信息 (已废弃,使用 infoStore.debugMode) debugMode: { type: Boolean, @@ -250,7 +249,7 @@ const validToolCalls = computed(() => { // 过滤掉无效的工具调用 return ( toolCall && - (toolCall.id || toolCall.name) && + (toolCall.id || toolCall.name || toolCall.function?.name) && (toolCall.args !== undefined || toolCall.function?.arguments !== undefined || toolCall.tool_call_result !== undefined) @@ -474,19 +473,6 @@ const parsedData = computed(() => { max-height: 200px; overflow-y: auto; } - - :deep(.tool-calls-container) { - width: 100%; - margin-top: 10px; - - .tool-call-container { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - } } .retry-hint { diff --git a/web/src/components/ToolCallingResult/BaseToolCall.vue b/web/src/components/ToolCallingResult/BaseToolCall.vue index 29a3b892..ab74606d 100644 --- a/web/src/components/ToolCallingResult/BaseToolCall.vue +++ b/web/src/components/ToolCallingResult/BaseToolCall.vue @@ -1,5 +1,8 @@