12 KiB
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 |
⚠️ 注意:单引号
'的转义代码被注释,当前未启用。
示例
bricks.escapeSpecialChars('Hello "world"\n');
// 输出: 'Hello \\"world\\"\\n'
💬 用户消息组件:UserMsgBox
继承自 bricks.HBox,用于显示用户发送的消息。
继承关系
class UserMsgBox extends bricks.HBox
构造函数:constructor(opts)
参数 opts
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|---|---|---|---|---|
icon |
string | 否 | imgs/chat-user.svg |
用户头像 SVG 图标 URL |
prompt |
string | 是 | - | 用户输入的消息内容 |
msg_css |
string | 否 | 'user_msg' |
应用到消息体的 CSS 类名 |
内部结构
- 创建一个 SVG 头像图标(使用
bricks.Svg)。 - 使用
MdWidget显示 Markdown 格式消息。 - 添加左侧空白图标占位符(
BlankIcon),保持布局对齐。 - 消息排列顺序:
[BlankIcon] ← [消息内容] ← [用户头像]
示例实例化
new bricks.UserMsgBox({
prompt: "你好,请介绍一下你自己。",
icon: "/icons/user.svg",
msg_css: "custom-user-style"
});
🤖 LLM 消息组件:LlmMsgBox
继承自 bricks.HBox,用于展示 LLM 的回复,并支持流式和同步两种响应方式。
继承关系
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()
标记响应已开始,停止加载动画,移除运行指示器。
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)
行为
- 解析 JSON 数据。
- 若无
content字段或为空,忽略。 - 累加内容至当前消息。
- 更新
MdWidget内容。 - 触发
updated事件。
chunk_ended()
流式响应结束后,将最终内容保存为结构化消息并推入 messages 数组。
- 调用
escapeSpecialChars对内容转义 - 使用
apply_data填充模板 - 推入
this.messages
async set_prompt(prompt)
发起对 LLM 的请求。
参数
prompt: 用户输入文本(自动转义)
流程
- 将用户消息按模板格式化并加入
messages。 - 准备请求参数:
{ messages: this.messages, mapi: this.mapi, model: this.model } - 根据
response_mode执行不同请求策略:
| 模式 | 行为说明 |
|---|---|
stream |
使用 HttpResponseStream 发起 POST 请求,逐块接收并渲染 |
sync |
使用 HttpJson 同步获取完整响应,一次性渲染结果 |
| 其他 | 不做任何操作(预留扩展) |
示例调用
await llmMsgBox.set_prompt("请写一首关于春天的诗");
🧩 主对话框组件:LlmDialog
继承自 bricks.VBox,是完整的聊天对话界面容器,支持多模型同时响应。
继承关系
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 数组项结构
{
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)
主入口方法:发送用户消息并触发所有模型响应。
步骤
- 转义用户输入。
- 创建并添加
UserMsgBox到滚动面板。 - 调用
llm_request(prompt)并等待完成。 - 自动滚动到底部。
async llm_request(prompt)
为每个注册的模型创建一个 LlmMsgBox,并异步启动请求。
- 使用
schedule_once(fn, 0.1)延迟执行以避免阻塞 UI - 所有模型并行响应
事件机制
llm_answer 事件
当某个模型完成响应后,可通过监听此事件获取回答内容。
❗ 当前代码中未显式触发
llm_answer事件,需补充如下代码才能生效:
// 在 chunk_ended 或 sync 响应结束处添加:
this.fire('llm_answer', { content: txt });
建议增强事件通知能力。
🏗️ 工厂注册
bricks.Factory.register('LlmDialog', bricks.LlmDialog);
允许通过字符串标识动态创建组件:
var dialog = bricks.Factory.create('LlmDialog', options);
✅ 使用示例
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() |
| 📏 布局兼容 | 使用 Flexbox(HBox/VBox),确保父级容器设置正确尺寸 |
📚 总结
本模块提供了完整的前端 LLM 聊天对话解决方案,具备以下特性:
✅ 多模型支持
✅ 流式 & 同步响应
✅ Markdown 渲染
✅ 自动滚动与加载动画
✅ 可扩展模板机制
适合集成进低代码平台、AI 助手界面、多模型对比测试工具等场景。
文档版本:1.0
最后更新:2025年4月5日