bricks/aidocs/llm.md
2025-10-05 06:39:58 +08:00

414 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

以下是针对您提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,结构清晰、注释完整,适用于前端开发团队或项目维护者使用。
---
# 📘 `bricks.LlmIO` 模块技术文档
> 基于 `bricks.js` 框架实现的多模型大语言模型LLM交互系统
> 支持流式响应、文本转语音TTS、用户反馈评分等功能
---
## 🔧 模块结构概览
```js
bricks = window.bricks || {}
bricks.LlmMsgAudio // 音频播放与流式消息处理
bricks.ModelOutput // 模型输出 UI 组件
bricks.LlmModel // 单个 LLM 模型控制器
bricks.LlmIO // 主控容器:管理输入、多个模型和输出展示
```
所有组件均继承自 `bricks.js` 提供的基础 UI 类(如 `VBox`, `HBox`, `JsWidget` 等),并支持动态构建、事件绑定和异步数据流处理。
---
## 1. `bricks.LlmMsgAudio` —— 流式音频消息处理器
### 功能说明
用于处理来自后端的流式文本响应,并根据标点符号分段发送至音频播放器。自动检测语言以适配中英文标点。
### 继承关系
```js
class LlmMsgAudio extends bricks.UpStreaming
```
### 构造函数:`constructor(opts)`
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 传递给父类的配置项 |
#### 初始化字段:
- `this.olddata`: 上一次接收到的数据(用于增量比较)
- `this.data`: 当前累积的完整文本
- `this.cn_p`: 中文标点符号数组 `[“。”,”,”,”!”,”?”,”\n”]`
- `this.other_p`: 英文标点符号数组 `[“.”,”,”,”!”, “?”, “\n”]`
- `this.audio`: 实例化的音频播放器(通过 `AudioPlayer({})` 创建)
### 方法列表
#### ✅ `detectLanguage(text)` → `String`
尝试使用浏览器内置的 `Intl.LocaleDetector` 推测文本语言。
- 返回示例:`'zh'`, `'en'`
- 若失败则返回 `'未知'`
> ⚠️ 注意:`Intl.LocaleDetector` 并非所有浏览器都支持,需注意兼容性降级处理。
#### ✅ `send(data)` → `void`
接收增量数据并进行分句处理,将已完成句子通过 `super.send()` 发送。
##### 工作流程:
1. 计算新增部分:`newdata = data.slice(this.olddata.length)`
2. 更新历史数据缓存
3. 调用 `detectLanguage(this.data)` 判断语言
4. 使用对应标点分割成句子数组(过滤空字符串)
5. 将除最后一句外的所有完整句子发送出去
6. 保留最后未完成句在 `this.data` 中等待下一批数据
> 💡 示例:
> 输入 `"你好!今天天气"` → 输出 `"你好!"`,缓存 `"今天天气"`
#### ✅ `async go()` → `Promise<Response>`
调用父类 `go()` 获取最终响应,并设置音频源为该响应内容。
```js
await super.go();
this.audio.set_source_from_response(resp);
```
通常用于非流式场景下的 TTS 音频播放。
---
## 2. `bricks.ModelOutput` —— 模型输出 UI 控件
### 功能说明
显示单个模型的输出结果,包含图标、加载动画、内容区域及满意度评价功能。
### 继承关系
```js
class ModelOutput extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `modelname` | String | 是 | - | 显示的模型名称 |
| `icon` | URL/String | 否 | `llm.svg` | 自定义图标路径 |
| `response_mode` | String | 否 | - | 可选 `'stream'`, `'sync'`, `'async'` |
| `estimate_url` | URL | 否 | - | 用户反馈提交地址 |
#### 内部组件初始化:
- `img`: 模型图标 SVG
- `content`: 主体 HBox 容器
- `run`: 加载动画组件(`BaseRunning`
- `filler`: 实际内容显示区(`LlmOut` 组件)
- `estimate_w`: 满意度打分组件(点赞/踩)
### 核心方法
#### ✅ `build_estimate_widgets()` → `void`
创建“结果满意吗?”评分控件(仅当 `estimate_url` 存在时)。
包含:
- 文本提示:“结果满意吗?”
- 👍 点赞图标(绑定 `estimate_llm(1)`
- 👎 点踩图标(绑定 `estimate_llm(-1)`
初始状态为隐藏(`.hide()`),可在完成后显示。
#### ✅ `async estimate_llm(val, event)` → `Promise<void>`
用户点击点赞/点踩时触发,向服务端上报评分。
**请求参数:**
```json
{
"widgettype": "urlwidget",
"options": {
"url": this.estimate_url,
"params": {
"logid": this.logid,
"value": val // 1 或 -1
}
}
}
```
> 执行后禁用评分按钮防止重复提交。
#### ✅ `async update_data(data)` → `Promise<void>`
更新模型输出内容。
- 移除加载动画 `run`
- 调用 `filler.update(data)` 渲染内容
- 支持流式逐步更新
#### ✅ `finish()` → `void`
生命周期钩子,当前无实际逻辑,可扩展。
---
## 3. `bricks.LlmModel` —— 单个 LLM 模型控制器
### 功能说明
封装一个 LLM 模型的行为逻辑,包括请求发送、格式化、响应处理等。
### 继承关系
```js
class LlmModel extends bricks.JsWidget
```
### 构造函数:`constructor(llmio, opts)`
| 参数 | 类型 | 说明 |
|------|------|------|
| `llmio` | LlmIO 实例 | 上层控制容器引用 |
| `opts` | Object | 模型配置对象 |
#### 配置项 (`opts`) 支持字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `model` | String | 模型标识符(如 `gpt-3.5-turbo` |
| `modelname` | String | 显示名 |
| `url` | URL | 请求接口地址 |
| `params` | Object | 固定请求参数 |
| `input_from` | String | 允许的数据来源标签 |
| `textvoice` | Boolean | 是否启用 TTS 语音播报 |
| `tts_url` | URL | TTS 接口地址 |
| `response_mode` | String | `'stream' \| 'sync' \| 'async'` |
| `icon` | URL | 图标路径 |
### 核心方法
#### ✅ `render_title()` → `HBox`
生成顶部标题栏(含图标),点击可打开设置面板(预留接口)。
#### ✅ `show_setup_panel(event)` → `void`
待实现:点击标题弹出模型配置面板。
#### ✅ `inputdata2uploaddata(data)` → `FormData \| Object`
将输入数据转换为适合上传的格式,并注入模型相关信息。
- 若是 `FormData`,使用 `.append()` 添加字段
- 否则直接赋值对象属性
- 注入 `model``llmid`
> 特殊逻辑:若 `llmio.model_inputed` 为真,则不添加 `model` 字段(避免重复)
#### ✅ `async model_inputed(data)` → `Promise<void>`
主入口方法:接收用户输入并发起模型请求。
**行为分支:**
| 模式 | 行为 |
|------|------|
| `'stream'` / `'async'` | 使用 `HttpResponseStream` 处理流式响应 |
| `'sync'` | 使用 `HttpJson` 发起同步 POST 请求 |
流程:
1. 创建 `ModelOutput` 显示组件并加入页面
2. 准备请求数据
3. 根据模式发起请求
4. 流式情况下逐块调用 `chunk_response(mout, line)`
#### ✅ `is_accept_source(source)` → `Boolean`
判断是否接受来自指定源的数据。
#### ✅ `llm_msg_format()` → `Object`
返回默认的消息结构模板:
```js
{ role: 'assistant', content: "${content}" }
```
可用于格式化聊天记录。
#### ✅ `chunk_response(mout, l)` → `void`
处理每一个流式响应片段(每行 JSON 字符串)。
- 去除空白字符
- 尝试解析 JSON
- 异步模式下只处理 `status === 'SUCCEEDED'` 的消息
- 调用 `mout.update_data(d)` 更新 UI
#### ✅ `chunk_ended()` → `void`
流结束回调(目前仅打印日志)
---
## 4. `bricks.LlmIO` —— 主控容器LLM 输入输出管理器)
### 功能说明
集成式组件,提供以下能力:
- 多模型管理
- 输入表单弹窗
- 模型选择弹窗
- 自动渲染模型输出
- 支持 TTS 和用户反馈
### 继承关系
```js
class LlmIO extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 说明 |
|------|------|------|
| `user_icon` | URL | 用户头像图标 |
| `list_models_url` | URL | 获取可用模型列表的接口 |
| `input_fields` | Array | 表单字段定义(参考 `bricks.Form` |
| `models` | Array | 初始已添加的模型列表 |
| `tts_url` | URL | TTS 接口地址 |
| `estimate_url` | URL | 用户反馈提交地址 |
#### 初始化行为:
- 创建标题栏 `title_w`
- 创建输出区域 `o_w`(滚动容器)
- 创建底部操作按钮:
- `i_w`: 输入按钮(打开输入表单)
- `nm_w`: 添加模型按钮(打开模型搜索窗口)
- 绑定点击事件
- 若模型少于 2 个且存在 `tts_url`,启用 `textvoice` 模式
- 遍历 `models` 初始化每个 `LlmModel`
### 核心方法
#### ✅ `async show_input(params)` → `Promise<void>`
显示用户输入内容在对话区域上方。
- 构建 `UserInputView` 显示输入数据
- 添加用户头像图标
- 插入到 `o_w` 容器中
#### ✅ `show_added_model(m)` → `void`
注册一个新的模型实例并将其标题添加到顶部工具栏。
- 若启用了 `textvoice`,自动注入 `tts_url`
- 实例化 `LlmModel`
- 调用 `.render_title()` 并加入 `title_w`
#### ✅ `async open_search_models(event)` → `Promise<void>`
打开“选择模型”弹窗,从远程获取模型列表。
使用 `PopupWindow + Cols` 组件展示表格形式的模型列表。
**表格列定义:**
- 图标 + 名称HBox
- 描述、启用时间VBox 内嵌 Text
点击某条记录会触发:
- `add_new_model(event)`
- 并关闭弹窗
#### ✅ `async add_new_model(event)` → `Promise<void>`
将选中的模型加入 `this.models` 数组,并调用 `show_added_model` 显示。
#### ✅ `async open_input_widget(event)` → `Promise<void>`
打开输入表单弹窗。
使用 `Form` 组件加载 `input_fields` 定义的字段。
提交后触发:
- `handle_input(event)`
- 并自动关闭弹窗
#### ✅ `async handle_input(event)` → `Promise<void>`
处理用户提交的输入数据。
1. 调用 `show_input(params)` 显示输入内容
2. 遍历所有 `llmmodels`,调度执行各自的 `model_inputed(params)`
- 使用 `schedule_once(fn, 0.01)` 实现微任务延迟执行
---
## 📦 注册组件
```js
bricks.Factory.register('LlmIO', bricks.LlmIO);
```
允许通过工厂方式动态创建此组件。
---
## 🎯 使用示例
```js
var llmio = new bricks.LlmIO({
user_icon: '/static/icons/user.png',
tts_url: '/api/tts',
estimate_url: '/api/feedback',
input_fields: [
{ name: 'prompt', label: '输入提示', type: 'textarea' }
],
models: [
{
model: 'qwen-plus',
modelname: '通义千问',
url: '/api/llm/stream',
response_mode: 'stream',
icon: '/icons/qwen.svg'
}
]
});
```
---
## 🛠️ 注意事项 & 待优化点
| 问题 | 建议修复 |
|------|----------|
| `detectLaguage` 拼写错误 | 应为 `detectLanguage` |
| `this.oter_p` 拼写错误 | 应为 `this.other_p` |
| `lang='zh'` 错误地使用了赋值而非比较 | 应改为 `lang === 'zh'` |
| `objcopy` 函数未定义 | 应替换为 `JSON.parse(JSON.stringify(data))` 或引入深拷贝工具 |
| `Intl.LocaleDetector` 兼容性差 | 建议降级为基于关键词的语言识别算法 |
| `schedule_once(..., 0.01)` 不标准 | 建议改用 `setTimeout(fn, 0)``queueMicrotask` |
---
## 📚 附录关键类图UML 简化版)
```
+------------------+
| LlmIO |
+------------------+
|
v
+------------------+
| LlmModel[x] |
+------------------+
|
v
+------------------+
| ModelOutput |
+------------------+---> LlmMsgAudio (用于音频)
|
v
+------------------+
| UserInput |
+------------------+
```
---
## 📝 总结
本模块实现了完整的 LLM 交互链路:
1. 用户输入 → 表单收集
2. 多模型并发调用 → 支持流式/同步
3. 分句播放音频 → 提升听觉体验
4. 用户反馈机制 → 支持效果评估闭环
适合集成于智能客服、AI 助手、教育平台等需要多模型对比或语音播报的应用场景。
---
**文档版本v1.0**
📅 最后更新2025年4月5日
👨‍💻 编写Bricks Framework Team