bricks/docs/cn.old/llm_dialog.md
2025-11-19 12:30:39 +08:00

366 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.

# `bricks.js` 技术文档LlmDialog 模块
> **版本**1.0
> **模块名称**`LlmDialog`, `LlmMsgBox`, `UserMsgBox`, 工具函数 `escapeSpecialChars`
> **依赖框架**`bricks.js` 前端组件库(基于类的 UI 架构)
---
## 📚 概述
该模块提供了一套用于构建与 LLM大语言模型交互对话界面的组件系统支持多模型并行响应、流式/同步响应模式、Markdown 渲染以及消息内容转义。主要包含以下核心类:
- `bricks.escapeSpecialChars`:字符串特殊字符转义工具。
- `bricks.UserMsgBox`:用户消息展示组件。
- `bricks.LlmMsgBox`LLM 回应消息展示及请求处理组件。
- `bricks.LlmDialog`:主对话容器,管理整个对话流程。
此外,通过 `bricks.Factory.register` 注册了 `LlmDialog` 以便动态创建。
---
## 🔧 工具函数
### `bricks.escapeSpecialChars(s)`
对输入字符串中的特殊字符进行反斜杠转义,常用于防止 JSON 注入或确保文本安全输出。
#### 参数
| 参数 | 类型 | 描述 |
|------|--------|--------------|
| `s` | string | 待转义字符串 |
#### 返回值
- **类型**`string`
- 转义后的字符串,适用于嵌入 JSON 或 HTML 环境。
#### 支持转义的字符
| 字符 | 原始表示 | 转义后表示 | 说明 |
|------|----------|----------------|--------------------|
| `\` | `\` | `\\` | 反斜杠 |
| `"` | `"` | `\"` | 双引号 |
| `\n` | 换行 | `\n` | 换行符 |
| `\r` | 回车 | `\r` | 回车符 |
| `\t` | 制表符 | `\t` | Tab |
| `\f` | 换页 | `\f` | Form feed |
| `\v` | 垂直制表 | `\v` | Vertical tab |
| `\0` | 空字符 | `\0` | Null byte |
> ⚠️ 注意:单引号 `'` 的转义代码被注释,当前未启用。
#### 示例
```js
bricks.escapeSpecialChars('Hello "world"\n');
// 输出: 'Hello \\"world\\"\\n'
```
---
## 💬 用户消息组件:`UserMsgBox`
继承自 `bricks.HBox`,用于显示用户发送的消息。
### 继承关系
```js
class UserMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------|--------|------|----------------------------|------------------------------|
| `icon` | string | 否 | `imgs/chat-user.svg` | 用户头像 SVG 图标 URL |
| `prompt` | string | 是 | - | 用户输入的消息内容 |
| `msg_css` | string | 否 | `'user_msg'` | 应用到消息体的 CSS 类名 |
#### 内部结构
1. 创建一个 SVG 头像图标(使用 `bricks.Svg`)。
2. 使用 `MdWidget` 显示 Markdown 格式消息。
3. 添加左侧空白图标占位符(`BlankIcon`),保持布局对齐。
4. 消息排列顺序:`[BlankIcon] ← [消息内容] ← [用户头像]`
#### 示例实例化
```js
new bricks.UserMsgBox({
prompt: "你好,请介绍一下你自己。",
icon: "/icons/user.svg",
msg_css: "custom-user-style"
});
```
---
## 🤖 LLM 消息组件:`LlmMsgBox`
继承自 `bricks.HBox`,用于展示 LLM 的回复,并支持流式和同步两种响应方式。
### 继承关系
```js
class LlmMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|-------------------------|----------------------------------------------------------------------|
| `model` | string | 是 | - | 使用的模型名称(如 `gpt-3.5-turbo` |
| `mapi` | string | 是 | - | API 类型标识(可用于后端路由判断) |
| `url` | string | 是 | - | 请求后端接口地址 |
| `icon` | string | 否 | `imgs/user.png` | LLM 头像图标 URL |
| `msg_css` | string | 否 | `'llm_msg'` | 消息区域应用的 CSS 类 |
| `response_mode` | string | 否 | `'stream'` | 响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg` | object | 否 | `{role:'user', content:"${prompt}"}` | 用户消息模板,支持 `${prompt}` 插值 |
| `llm_msg` | object | 否 | `{role:'assistant', content:"${content}"}` | LLM 消息模板,支持 `${content}` 插值 |
#### 内部初始化逻辑
- 创建头像图标SVG
- 初始化 `MdWidget` 显示消息内容
- 加载 `BaseRunning` 动画组件(在响应中显示加载状态)
- 初始化消息历史数组 `this.messages = []`
#### 消息排列顺序
`[LLM头像] → [运行动画] → [消息内容] → [空白占位符]`
---
### 方法列表
#### `responsed()`
标记响应已开始,停止加载动画,移除运行指示器。
```js
this.run.stop_timepass();
this.remove_widget(this.run);
```
#### `user_msg_format()`
生成用户消息对象格式。
- 若设置了 `this.user_msg`,则返回其值;
- 否则返回默认模板:`{role: 'user', content: "${prompt}"}`
> 实际内容将在调用时通过 `bricks.apply_data()` 替换 `${prompt}`。
#### `llm_msg_format()`
生成 LLM 消息对象格式。
- 若设置了 `this.llm_msg`,返回其值;
- 否则返回:`{role: 'assistant', content: "${content}"}`
#### `chunk_response(l)`
处理流式响应中的每一个数据块chunk
##### 参数
- `l`: JSON 字符串片段(通常来自 ReadableStream
##### 行为
1. 解析 JSON 数据。
2. 若无 `content` 字段或为空,忽略。
3. 累加内容至当前消息。
4. 更新 `MdWidget` 内容。
5. 触发 `updated` 事件。
#### `chunk_ended()`
流式响应结束后,将最终内容保存为结构化消息并推入 `messages` 数组。
- 调用 `escapeSpecialChars` 对内容转义
- 使用 `apply_data` 填充模板
- 推入 `this.messages`
#### `async set_prompt(prompt)`
发起对 LLM 的请求。
##### 参数
- `prompt`: 用户输入文本(自动转义)
##### 流程
1. 将用户消息按模板格式化并加入 `messages`
2. 准备请求参数:
```js
{
messages: this.messages,
mapi: this.mapi,
model: this.model
}
```
3. 根据 `response_mode` 执行不同请求策略:
| 模式 | 行为说明 |
|-----------|---------|
| `stream` | 使用 `HttpResponseStream` 发起 POST 请求,逐块接收并渲染 |
| `sync` | 使用 `HttpJson` 同步获取完整响应,一次性渲染结果 |
| 其他 | 不做任何操作(预留扩展) |
##### 示例调用
```js
await llmMsgBox.set_prompt("请写一首关于春天的诗");
```
---
## 🧩 主对话框组件:`LlmDialog`
继承自 `bricks.VBox`,是完整的聊天对话界面容器,支持多模型同时响应。
### 继承关系
```js
class LlmDialog extends bricks.VBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|----------------|----------------------------------------------------------------------|
| `models` | array | 是 | - | 模型配置数组,每个元素定义一个 LLM 模型 |
| `response_mode` | string | 否 | `'stream'` | 全局响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg_css` | string | 否 | `'user_msg'` | 用户消息使用的 CSS 类 |
| `user_icon` | string | 否 | - | 用户头像图标路径 |
| `title_ccs` | string | 否 | `'llm_title'` | 标题栏 CSS 类(拼写疑似错误,应为 `title_css` |
| `height` | string | 否 | `'100%'` | 容器高度 |
#### `models` 数组项结构
```js
{
model: "gpt-3.5-turbo",
mapi: "openai",
url: "/api/openai/chat",
icon: "/icons/gpt.svg",
css: "gpt-response",
user_msg: { role: "user", content: "${prompt}" },
llm_msg: { role: "assistant", content: "${content}" }
}
```
---
### 方法列表
#### `show_models_info()`
遍历所有模型,调用 `show_model_info(model)` 在标题栏显示模型信息。
#### `show_model_info(model)`
在顶部标题区添加一个 HBox包含模型图标和名称。
- 使用 `Svg` 显示图标
- 使用 `Text` 显示模型名
- 存储引用到 `this.model_info_ws[model.model]`
#### `add_model(model)`
动态添加新模型配置,并立即显示在标题栏。
#### `delete_model(model)`
根据 `model.model` 名称删除模型及其视图。
#### `async set_prompt(prompt)`
主入口方法:发送用户消息并触发所有模型响应。
##### 步骤
1. 转义用户输入。
2. 创建并添加 `UserMsgBox` 到滚动面板。
3. 调用 `llm_request(prompt)` 并等待完成。
4. 自动滚动到底部。
#### `async llm_request(prompt)`
为每个注册的模型创建一个 `LlmMsgBox`,并异步启动请求。
- 使用 `schedule_once(fn, 0.1)` 延迟执行以避免阻塞 UI
- 所有模型并行响应
---
### 事件机制
#### `llm_answer` 事件
当某个模型完成响应后,可通过监听此事件获取回答内容。
> ❗ 当前代码中未显式触发 `llm_answer` 事件,需补充如下代码才能生效:
```js
// 在 chunk_ended 或 sync 响应结束处添加:
this.fire('llm_answer', { content: txt });
```
建议增强事件通知能力。
---
## 🏗️ 工厂注册
```js
bricks.Factory.register('LlmDialog', bricks.LlmDialog);
```
允许通过字符串标识动态创建组件:
```js
var dialog = bricks.Factory.create('LlmDialog', options);
```
---
## ✅ 使用示例
```js
var dialog = new bricks.LlmDialog({
height: '600px',
user_icon: '/icons/me.svg',
title_ccs: 'chat-title',
response_mode: 'stream',
models: [
{
model: 'gpt-3.5-turbo',
mapi: 'openai',
url: '/api/chat',
icon: '/icons/gpt.svg',
css: 'gpt-msg',
user_msg: { role: 'user', content: '${prompt}' },
llm_msg: { role: 'assistant', content: '${content}' }
},
{
model: 'claude-2',
mapi: 'anthropic',
url: '/api/anthropic',
icon: '/icons/claude.svg',
css: 'claude-msg'
}
]
});
document.body.appendChild(dialog.dom_element);
// 发送消息
dialog.set_prompt("解释什么是机器学习");
```
---
## 📝 注意事项与改进建议
| 项目 | 说明 |
|------|------|
| 🔐 安全性 | `escapeSpecialChars` 有助于防止注入,但建议结合 DOMPurify 或其他 sanitizer 进一步防护 XSS |
| 🧩 单引号转义 | 被注释掉,若需兼容 SQL 或属性字符串场景,建议启用 |
| 🎯 事件缺失 | `llm_answer` 事件未实际触发,建议在 `chunk_ended()` 中补全 |
| 🖼️ 图标资源 | 依赖 `bricks_resource()` 函数解析静态资源路径,需确保其存在 |
| ⏳ 异步控制 | `schedule_once(..., 0.1)` 是一种 Hack可考虑改为 `queueMicrotask` 或 `Promise.resolve().then()` |
| 📏 布局兼容 | 使用 FlexboxHBox/VBox确保父级容器设置正确尺寸 |
---
## 📚 总结
本模块提供了完整的前端 LLM 聊天对话解决方案,具备以下特性:
✅ 多模型支持
✅ 流式 & 同步响应
✅ Markdown 渲染
✅ 自动滚动与加载动画
✅ 可扩展模板机制
适合集成进低代码平台、AI 助手界面、多模型对比测试工具等场景。
---
> 文档版本1.0
> 最后更新2025年4月5日