diff --git a/src/components/MessageBubble.tsx b/src/components/MessageBubble.tsx
index 150a11f..e542c18 100644
--- a/src/components/MessageBubble.tsx
+++ b/src/components/MessageBubble.tsx
@@ -1,11 +1,12 @@
import type { Message } from '../types/protocol'
import SegmentRenderer from './SegmentRenderer'
-import { User, Bot, Settings } from 'lucide-react'
+import { User, Bot, Settings, Wrench } from 'lucide-react'
const roleConfig = {
- system: { icon: Settings, label: 'SYSTEM', align: 'left', bg: 'bg-gray-100', text: 'text-gray-500' },
- user: { icon: User, label: 'YOU', align: 'right', bg: 'bg-blue-50 border-blue-100', text: 'text-blue-600' },
- assistant: { icon: Bot, label: 'ASSISTANT', align: 'left', bg: 'bg-white border-gray-100', text: 'text-green-600' },
+ system: { icon: Settings, label: 'system', align: 'left', bg: 'bg-gray-100', text: 'text-gray-500' },
+ user: { icon: User, label: 'user', align: 'right', bg: 'bg-blue-50 border-blue-100', text: 'text-blue-600' },
+ assistant: { icon: Bot, label: 'assistant', align: 'left', bg: 'bg-white border-gray-100', text: 'text-green-600' },
+ tool: { icon: Wrench, label: 'tool', align: 'left', bg: 'bg-amber-50 border-amber-200', text: 'text-amber-700' },
}
interface MessageBubbleProps {
@@ -27,7 +28,7 @@ export default function MessageBubble({ message, varMap = {} }: MessageBubblePro
{/* Role header */}
-
+
{cfg.label}
diff --git a/src/components/MessageList.tsx b/src/components/MessageList.tsx
index a425105..60371a8 100644
--- a/src/components/MessageList.tsx
+++ b/src/components/MessageList.tsx
@@ -1,5 +1,5 @@
import { useMemo } from 'react'
-import type { Message, StaticVarSegment } from '../types/protocol'
+import type { Message, Segment, StaticVarSegment } from '../types/protocol'
import SessionBar from './SessionBar'
import MessageBubble from './MessageBubble'
@@ -41,13 +41,82 @@ function extractSessionVars(messages: Message[]): {
return { variables, varMap, cleanedMessages }
}
+/**
+ * 将消息中的 tool_call_request / tool_call_result segment
+ * 拆分为独立的 role: "tool" 消息,与非 tool segment 交替排列。
+ *
+ * 例如一条 assistant 消息包含 [text, tool_call, tool_result, text]
+ * 会拆分为:
+ * assistant [text] → tool [tool_call] → tool [tool_result] → assistant [text]
+ */
+function extractToolMessages(messages: Message[]): Message[] {
+ const result: Message[] = []
+
+ for (const msg of messages) {
+ // 将 segments 按 "是否为 tool segment" 分组为连续的 run
+ const runs: Segment[][] = []
+ let currentRun: Segment[] = []
+ let currentIsTool: boolean | null = null
+
+ for (const seg of msg.segments) {
+ const isTool =
+ seg.kind === 'tool_call_request' || seg.kind === 'tool_call_result'
+ if (currentIsTool === null) {
+ currentIsTool = isTool
+ currentRun.push(seg)
+ } else if (isTool === currentIsTool) {
+ currentRun.push(seg)
+ } else {
+ runs.push(currentRun)
+ currentRun = [seg]
+ currentIsTool = isTool
+ }
+ }
+ if (currentRun.length > 0) {
+ runs.push(currentRun)
+ }
+
+ // 每个 run 生成一条独立消息
+ let counter = 0
+ for (const run of runs) {
+ if (run.length === 0) continue
+ const isTool =
+ run[0].kind === 'tool_call_request' ||
+ run[0].kind === 'tool_call_result'
+
+ if (isTool) {
+ counter++
+ result.push({
+ id: `${msg.id}-tool-${counter}`,
+ role: 'tool',
+ segments: run,
+ timestamp: msg.timestamp,
+ })
+ } else {
+ result.push({
+ ...msg,
+ id: counter > 0 ? `${msg.id}-text-${counter}` : msg.id,
+ segments: run,
+ })
+ }
+ }
+ }
+
+ return result
+}
+
export default function MessageList({ messages }: MessageListProps) {
const { variables, varMap, cleanedMessages } = useMemo(
() => extractSessionVars(messages),
[messages]
)
- if (cleanedMessages.length === 0) {
+ const splitMessages = useMemo(
+ () => extractToolMessages(cleanedMessages),
+ [cleanedMessages]
+ )
+
+ if (splitMessages.length === 0) {
return (
选择一个 Demo 场景开始
@@ -62,7 +131,7 @@ export default function MessageList({ messages }: MessageListProps) {
{/* 消息列表 */}
- {cleanedMessages.map((msg) => (
+ {splitMessages.map((msg) => (
))}
diff --git a/src/data/demos.ts b/src/data/demos.ts
index 43525c5..758e2d0 100644
--- a/src/data/demos.ts
+++ b/src/data/demos.ts
@@ -883,15 +883,16 @@ const demoE: PromptEnvelope = {
],
timestamp: now - 280000,
},
- // --- Skill 触发:指令以 system 消息形式追加到对话 ---
- // (而非合并进原有的 System Prompt segment)
+ // --- Skill 触发:指令以 tool_call_result 形式追加到对话 ---
{
id: 'e-4',
- role: 'system',
+ role: 'assistant',
segments: [
{
- kind: 'system_prompt',
- content: `[/deep-research 已触发]
+ kind: 'tool_call_result',
+ toolName: 'run_skill',
+ success: true,
+ result: `[/deep-research 已触发]
深度研究工作流程:
1. 分析用户问题,拆解为 3-5 个子问题
diff --git a/src/types/protocol.ts b/src/types/protocol.ts
index 2b46c42..4d871cb 100644
--- a/src/types/protocol.ts
+++ b/src/types/protocol.ts
@@ -12,7 +12,7 @@ export interface PromptEnvelope {
// --- 单条消息 ---
export interface Message {
id: string
- role: 'system' | 'user' | 'assistant'
+ role: 'system' | 'user' | 'assistant' | 'tool'
segments: Segment[]
timestamp: number
}