架构剖析:TinyRobot Bubble渲染器状态管理与工具调用机制

架构剖析:TinyRobot Bubble 渲染器、状态管理与工具调用机制

TinyRobot Bubble 的核心不是"一个组件",而是一套渲染引擎------它通过可插拔的渲染器体系、精细的状态管理和完整的工具调用支持,构建了高度灵活的消息渲染架构。

本文将从架构视角剖析这三个核心机制,帮助你理解 Bubble 的设计哲学,并在实际项目中自如地扩展它。


一、渲染器架构

1.1 双层渲染器设计

Bubble 的渲染器分为两层:

层级 职责 对应接口
Box 渲染器 渲染消息的外层容器------背景、圆角、阴影、布局 BubbleBoxRendererProps
Content 渲染器 渲染消息的具体内容------文本、图片、Markdown、代码 BubbleContentRendererProps

这种双层设计的好处是:样式和内容完全解耦

你可以单独替换 Box 渲染器改变气泡外观,而不影响内容渲染;也可以单独替换 Content 渲染器改变内容展示方式,而不影响气泡样式。

1.2 渲染器匹配机制

渲染器不是固定分配的,而是通过匹配规则动态选择:

markdown 复制代码
1. 按 priority 排序所有匹配规则
2. 依次执行 find 函数
3. 第一个返回 true 的规则 → 使用其对应的渲染器
4. 没有匹配到任何规则 → 使用 fallback 渲染器

优先级常量:

常量 适用场景
BubbleRendererMatchPriority.LOADING -1 加载状态渲染器
BubbleRendererMatchPriority.NORMAL 0 普通渲染器
BubbleRendererMatchPriority.CONTENT 10 基于 content 判断
BubbleRendererMatchPriority.ROLE 20 基于 role 判断

数值越小优先级越高。这意味着:加载状态检查总是最先执行,然后才是内容类型判断,最后是角色判断。

1.3 配置层级

渲染器配置支持三个层级,优先级从高到低:

层级 配置方式 作用范围
Prop 级别 BubblefallbackBoxRenderer / fallbackContentRenderer 当前组件
Provider 级别 BubbleProviderboxRendererMatches / contentRendererMatches 整个组件树
Default 级别 内置默认渲染器和匹配规则 全局兜底

最佳实践:

  • 全局统一配置 → Provider 级别
  • 个别消息特殊处理 → Prop 级别
  • 大多数场景 → Default 级别就够了

1.4 内置渲染器一览

typescript 复制代码
// 通过 BubbleRenderers 访问所有内置渲染器
import { BubbleRenderers } from '@opentiny/tiny-robot'

BubbleRenderers.Box       // 默认 Box 渲染器
BubbleRenderers.Text      // 文本内容渲染器(默认 Content 渲染器)
BubbleRenderers.Image     // 图片渲染器
BubbleRenderers.Markdown  // Markdown 渲染器
BubbleRenderers.Loading   // 加载状态渲染器
BubbleRenderers.Reasoning // 推理内容渲染器
BubbleRenderers.Tool      // 单个工具调用渲染器
BubbleRenderers.Tools     // 工具调用列表渲染器
BubbleRenderers.ToolRole  // 工具角色消息渲染器

1.5 实现自定义渲染器

Content 渲染器
vue 复制代码
<!-- CustomRenderer.vue -->
<template>
  <div class="custom-content">
    {{ message.content }}
  </div>
</template>

<script setup lang="ts">
import type { BubbleContentRendererProps } from '@opentiny/tiny-robot'

defineProps<BubbleContentRendererProps>()
</script>

使用 useMessageContent 获取正确内容:

typescript 复制代码
import { useMessageContent } from '@opentiny/tiny-robot'

const props = defineProps<BubbleContentRendererProps>()
const { content, contentText } = useMessageContent(props)

// content: 当前 contentIndex 对应的 ChatMessageContentItem
// contentText: content 的文本摘要
Box 渲染器
vue 复制代码
<script setup lang="ts">
import type { BubbleBoxRendererProps } from '@opentiny/tiny-robot'
defineProps<BubbleBoxRendererProps>()
</script>

<template>
  <div class="custom-box" :data-placement="placement" :data-shape="shape">
    <slot />
  </div>
</template>

1.6 通过 BubbleProvider 配置全局渲染器

vue 复制代码
<template>
  <tr-bubble-provider
    :box-renderer-matches="boxMatches"
    :content-renderer-matches="contentMatches"
  >
    <tr-bubble-list :messages="messages" :role-configs="roles" />
  </tr-bubble-provider>
</template>

<script setup lang="ts">
import { markRaw } from 'vue'
import { BubbleRendererMatchPriority, TrBubbleProvider } from '@opentiny/tiny-robot'

const boxMatches = [
  {
    find: (messages) => messages[0]?.content?.includes('VIP'),
    renderer: markRaw(TransparentBoxRenderer),
    priority: BubbleRendererMatchPriority.NORMAL,
  },
]

const contentMatches = [
  {
    find: (message) => message.content?.includes('VIP'),
    renderer: markRaw(CustomContentRenderer),
    priority: BubbleRendererMatchPriority.NORMAL,
  },
]
</script>

关键提醒 :使用 markRaw 包装渲染器组件,避免 Vue 的响应式处理。


二、状态管理机制

2.1 state 属性设计

Bubble 消息的 state 属性专门用于存储 UI 相关数据,不会影响消息内容:

typescript 复制代码
interface BubbleMessage<S extends Record<string, unknown> = Record<string, unknown>> {
  role?: string
  content?: ChatMessageContent
  state?: S  // UI 状态数据,不影响 content
}

设计原则 :为了不修改源数据的内部内容和结构,所有 UI 相关的状态都应该放在 state 中,而不是修改 content

2.2 state-change 事件

当状态变化时,Bubble 触发 state-change 事件:

typescript 复制代码
interface StateChangePayload {
  key: string           // 状态键名
  value: unknown        // 状态值
  messageIndex: number  // 消息索引
  contentIndex: number  // 内容索引
}

实战示例------管理工具调用的展开/收起状态:

vue 复制代码
<template>
  <tr-bubble
    :content="message.content"
    :tool_calls="toolCalls"
    :state="state"
    @state-change="handleStateChange"
  />
</template>

<script setup lang="ts">
const state = ref({
  toolCall: {
    call_0: { status: 'running', open: true },
    call_1: { status: 'success', open: false },
  },
})

const handleStateChange = (payload) => {
  if (payload.key === 'toolCall') {
    state.value.toolCall = payload.value
  }
}
</script>

2.3 BubbleProvider 的全局 store

BubbleProvider 还提供了 store 属性,用于在 BubbleList 和 Bubble 之间共享全局数据:

vue 复制代码
<tr-bubble-provider :store="{ theme: 'dark', userSettings: {} }">
  <tr-bubble-list :messages="messages" />
</tr-bubble-provider>

三、工具调用机制

3.1 消息层面的工具调用

Bubble 直接支持 OpenAI 风格的 tool_calls 字段:

typescript 复制代码
interface ToolCall {
  id: string
  type: 'function' | string
  function: {
    name: string
    arguments: string
  }
}

消息携带 tool_calls 时,Bubble 会自动匹配 Tool/Tools 渲染器:

vue 复制代码
<tr-bubble
  content="我来帮您查询天气。"
  :tool_calls="[
    { id: 'call_0', type: 'function', function: { name: 'get_weather', arguments: '{"city":"北京"}' } },
    { id: 'call_1', type: 'function', function: { name: 'get_weather', arguments: '{"city":"上海"}' } },
  ]"
  :state="{ toolCall: { call_0: { status: 'running', open: true } } }"
/>

3.2 工具调用状态

state.toolCall[id].status 支持四种值:

状态 说明 渲染效果
running 执行中 旋转加载图标
success 成功 绿色勾选图标
failed 失败 红色错误图标
cancelled 取消 灰色取消图标

state.toolCall[id].open 控制工具调用详情面板的展开/收起。

3.3 Kit 层的 toolPlugin

渲染器负责"展示",而 Kit 层的 toolPlugin 负责"执行":

typescript 复制代码
import { toolPlugin, useMessage } from '@opentiny/tiny-robot-kit'

useMessage({
  responseProvider,
  plugins: [
    toolPlugin({
      getTools: async () => tools,     // 向 API 注入工具列表
      callTool: async (toolCall) => {   // 执行工具调用
        // 执行逻辑...
        return result
      },
      onToolCallStart: (toolCall, ctx) => {
        // 工具开始执行时回调
      },
      onToolCallEnd: (toolCall, ctx) => {
        // ctx.status: 'success' | 'failed' | 'cancelled'
      },
    }),
  ],
})

3.4 工具调用流程

复制代码
用户提问 → API 返回 assistant 消息(含 tool_calls)
  → toolPlugin 解析 tool_calls
  → 为每个 tool_call 创建 tool 消息
  → callTool 执行 → 结果写入 tool 消息
  → 自动发起下一轮请求(带 tool 消息)
  → API 返回最终 assistant 回复

3.5 推理内容渲染

Bubble 还支持 reasoning_content 字段,配合 Thinking 渲染器展示 AI 的思考过程:

vue 复制代码
<tr-bubble
  content="最终回复内容"
  reasoning_content="这是我的思考过程..."
  :state="{ thinking: { open: true } }"
/>

在 Kit 层,thinkingPlugin 自动管理思考状态的展开/收起:

  • 思考进行中 → 自动展开
  • 思考结束 → 自动收起

四、架构总结

Bubble 的渲染架构遵循三大设计原则:

  1. 可插拔渲染器:Box 和 Content 双层分离,匹配规则动态选择,三层配置梯度
  2. 状态隔离 :UI 数据放在 state,不修改源数据内容
  3. 组件与 Kit 解耦:Bubble 只管渲染,useMessage + toolPlugin 只管逻辑

这种架构让 Bubble 既保持了组件的简洁性,又具备了强大的扩展能力。下一篇文章我们将深入底层,剖析 Bubble 消息气泡组件的核心技术原理。


TinyRobot 官网https://opentiny/tiny-robot GitHub 仓库github.com/opentiny/ti...

相关推荐
英勇无比的消炎药1 小时前
多模态消息渲染实战:TinyRobot Bubble内容解析与contentResolver用法
vue.js
gg159357284601 小时前
Uni-app跨平台开发全解课程:从零基础到企业级多端落地实战
vue.js·uni-app
阿猫的故乡2 小时前
Vue + Axios 从入门到封装:拦截器、错误处理、请求取消、接口管理全搞定
前端·javascript·vue.js
秃头网友小李3 小时前
前端难点:Vue3 响应式遇上 Three.js / ECharts —— 为什么要用 shallowRef?
前端·vue.js
长空任鸟飞_阿康3 小时前
RAG 文档摄入全链路,从原理到生产落地
vue.js·人工智能·python
星空3 小时前
Node.js (Express) + Vue2 Axios 前后端交互 CRUD
vue.js·node.js·express
Hooray4 小时前
前端暗黑模式的适配艺术
前端·vue.js·视觉设计
有梦想的程序星空5 小时前
【环境配置】使用 Vue CLI 构建 Vue 项目脚手架完整指南
前端·javascript·vue.js
岁月宁静14 小时前
RAG 文档摄入全链路,从原理到生产落地
vue.js·人工智能·python