ai-assistant-plugin
| Install | |
|---|---|
npm install ai-assistant-plugin |
|
| Latest Version: | 1.0.6 |
| Vue: | 2 |
| License: | MIT |
| Last Updated: | Sep 18, 2026 |
| Links: | npm |
ai-assistant-plugin
Vue 2 的 AI 助手浮层插件,无 Element UI 依赖,纯 Vue 2 + 原生 CSS。对接周报生成(PMS AIOps)的流式 SSE 接口,支持主动唤起、多轮对话、结果回填。
功能特性
- 🪟 右下角浮层:非模态,默认 630×560 贴右下角,不遮挡页面操作
- 🖱️ 拖拽移动:按住头部标题栏拖动
- 📐 拖拽缩放:右边缘 / 下边缘 / 右下角三个手柄调整宽高
- 💬 多轮对话:消息气泡,可连续追问
- ⚡ 结果操作:一键「插入 / 替换 / 复制 / 重新生成」
- 📌 固定侧边栏:可固定为右侧全高面板
- 🏢 外部数据透传:唤起时传入
externalData,随请求体带给后端 - ⌨️ 输入框自适应:随内容撑高,最高 120px,超出滚动
一、安装
已构建为编译产物(dist/),源码不发布,使用方无需额外配置:
npm install ai-assistant-plugin
二、引入并初始化
在项目入口(如 main.js):
import Vue from 'vue'
import AiAssistant from 'ai-assistant-plugin'
Vue.use(AiAssistant, {
apiPrefix: '/api', // 宿主代理前缀,默认 '/api'
token: 'Bearer 你的token', // 必填。完整凭据,原样作为 Authorization 发出
title: 'AI 助手'
})
Vue.use 的第二个参数会作为全局默认值保存,之后每次 open(config) 都以 { ...默认值, ...config } 合并。因此这里能配的项和 open(config) 完全一样。
安装后所有组件内均可通过 this.$aiAssistant 访问。
三、主动唤起
this.$aiAssistant.open({
externalData: { bizType: 'order', bizId: '10086' },
context: this.name,
onApply: ({ text, mode }) => {
this.name = mode === 'replace' ? text : (this.name || '') + text
}
})
open(config) 参数
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
title |
String | 'AI 助手' |
面板标题 |
context |
String | '' |
上下文(如输入框现有内容),会拼进发给 AI 的消息 |
baseUrl |
String | — | 完整接口地址。给了就整段替换,优先级最高 |
apiPrefix |
String | '/api' |
宿主代理前缀,拼在内置路径前。传 '' 表示不挂前缀 |
token |
String | 必填 | 完整凭据(含 Bearer 前缀),原样作为 Authorization 发出,插件不再拼前缀 |
externalData |
Object | null |
外部数据,非空时作为 body.externalData 透传给后端 |
onApply |
Function | null |
「插入 / 替换」的回填回调,参数 { text, mode } |
接口地址如何决定
baseUrl 传了 → 就用 baseUrl(整段替换,apiPrefix 不参与)
baseUrl 没传 → apiPrefix + '/pms-aiops/v1/agent/pms/wr/stream'
实际效果:
| 配置 | 实际请求地址 |
|---|---|
| 都不传 | /api/pms-aiops/v1/agent/pms/wr/stream |
apiPrefix: '/gateway' |
/gateway/pms-aiops/v1/agent/pms/wr/stream |
apiPrefix: '' |
/pms-aiops/v1/agent/pms/wr/stream |
baseUrl: 'http://10.0.0.9:8080/x' |
http://10.0.0.9:8080/x |
代理要求:默认地址是相对路径,需要宿主项目为
apiPrefix配好代理转发到真实服务。以/api为例:devServer: { proxy: { '/api': { target: 'http://真实服务:端口', changeOrigin: true, pathRewrite: { '^/api': '' } // 剥掉前缀,转发 /pms-aiops/v1/... } } }代理没配好时的现象:dev server 常把未匹配路由回退成
index.html(200 + HTML)。插件读到 0 个事件,于是静默返回空内容——不报错、也不卡在「生成中」,只是没有回答。遇到这个现象先查代理,不是插件故障。
四、完整 API
| 成员 | 类型 | 说明 |
|---|---|---|
$aiAssistant.open(config) |
方法 | 主动唤起,返回组件实例 |
$aiAssistant.close() |
方法 | 关闭面板(会先中止进行中的请求) |
$aiAssistant.isOpen |
属性 | 是否打开中(只读) |
$aiAssistant.destroy() |
方法 | 销毁全局实例并移除 DOM |
具名导出(不使用 Vue.use 时):
import { AiAssistant, generate } from 'ai-assistant-plugin'
AiAssistant—— 组件本体,可单独注册或嵌入页面generate(params)—— 底层 SSE 调用函数,签名{ message, sessionId, baseUrl, apiPrefix, token, externalData, signal, onEvent },返回Promise<string>(累积的 markdown),onEvent收到每个原始 SSE 事件对象
五、接口约定
插件向 baseUrl(或 apiPrefix + 内置路径)发起请求:
POST <接口地址>
Content-Type: application/json
Authorization: <token>
Accept: text/event-stream
其中 <token> 就是传入的完整凭据(含 Bearer 前缀),插件原样发出,不做拼接。
请求体:
{
"input": "用户输入",
"sessionId": "agent-session-<token 去掉 Bearer 前缀>",
"externalData": { "bizType": "order" }
}
sessionId默认由 token 拼成('agent-session-' + token 去掉 Bearer 前缀),同一 token 复用同一会话,用于保持多轮上下文externalData仅当传入非空对象时才出现在请求体里;传{}或不传都不会带上该字段
响应为 SSE(text/event-stream),事件按 type 字段区分:
事件 type |
说明 |
|---|---|
THINKING_BLOCK_START |
模型开始思考 |
TEXT_BLOCK_DELTA |
正式回答增量,取 delta 字段累积 |
AGENT_END |
终止事件,收到即结束本次请求 |
其它(TOOL_CALL_* / TOOL_RESULT_* / AGENT_RESULT) |
会透传给 onEvent,内置 UI 当前不展示 |
插件读到流关闭(
done)或收到AGENT_END都会收尾并主动释放连接。这一点是必要的——服务端发完AGENT_END后通常不会主动断流,只等断流会导致界面一直卡在「生成中」。
六、回填说明(onApply)
用户点击「插入」或「替换」时触发 onApply({ text, mode }):
onApply: ({ text, mode }) => {
if (mode === 'replace') {
this.value = text
} else {
// 简化示例:追加到末尾;实际项目可结合 selectionStart/End 插到光标处
this.value = (this.value || '') + text
}
}
mode === 'replace':整体替换mode === 'insert':插入
七、交互说明
| 操作 | 方式 |
|---|---|
| 移动面板 | 按住头部标题栏拖动 |
| 调整宽度 / 高度 / 宽高 | 拖拽右边缘 / 下边缘 / 右下角手柄 |
| 固定侧边栏 | 点头部固定图标 |
| 关闭 | 点头部 × ,或按 Esc |
| 发送消息 | 输入框按 Enter(Shift+Enter 换行) |
| 停止生成 | 生成中点击红色停止按钮 |
| 重新生成 | 回答下方的 ↻ 按钮 |
八、目录结构
ai-assistant-plugin/
├── src/
│ ├── index.js # 入口(install + $aiAssistant API)
│ ├── components/
│ │ └── AiAssistant.vue # 核心浮层组件
│ └── services/
│ └── chat.js # SSE 接口调用
├── dist/ # 构建产物(发布用)
│ ├── ai-assistant.common.js # main 指向
│ ├── ai-assistant.umd.js
│ └── ai-assistant.umd.min.js
├── package.json
└── README.md
九、开发与发布
npm run build # 改完源码必须重新构建——私仓消费的是 dist,不是 src
npm publish # 升级 package.json 的 version 后发布
⚠️ 产物必须保持在 ES2019 以内(别破坏 webpack 4 兼容)
使用方里有 webpack 4 的项目,而 webpack 4 依赖的 acorn 最高只支持 ES2019。产物里一旦出现 ?.(可选链)或 ??(空值合并)这类 ES2020 语法,使用方构建直接失败。
这个坑很隐蔽——源码干净、本机编译成功、产物自己跑也没事,只有使用方会炸。已经踩过三次,来源各不相同:
| 来源 | 表现 | 对应措施 |
|---|---|---|
依赖 marked |
v18 自带 31 处 ?.、4 处 ?? |
transpileDependencies: ['marked'] 交给 babel 转译 |
| 构建链缺 babel | 本工程 devDependencies 缺 @vue/cli-plugin-babel,导致上一条是空转,依赖原样进产物 |
已补装该依赖 |
| 压缩器 terser | 把 x == null ? a : x 优化成 x ?? a,在压缩阶段二次引入 |
terserOptions.ecma: 2019 锁住 |
所以下面三样都不要动:
- 不要改
.browserslistrc——它压到Chrome 79 / FF 73 / Safari 13 / Edge 79(最后一批还不支持?.的版本),是逼 babel 转译 ES2020 语法的开关 - 不要拿掉或调高
vue.config.js里的terserOptions.ecma,也不要去掉transpileDependencies - 不要删
babel.config.js,删了 babel 就完全不参与构建
另外别在 src/ 里直接写 ?. / ??。现在有 babel 兜底不会出错,但每处可选链展开成三元都很啰嗦,会白白撑大产物(当前 153KB 里相当一部分就是转译 marked 的代价)。
改完必须自检——这是唯一可靠的验收方式,三个产物都要通过:
node -e "
const acorn=require('./node_modules/acorn'), fs=require('fs');
for (const f of ['dist/ai-assistant.common.js','dist/ai-assistant.umd.js','dist/ai-assistant.umd.min.js']) {
try { acorn.parse(fs.readFileSync(f,'utf8'),{ecmaVersion:2019,sourceType:'script',allowReturnOutsideFunction:true}); console.log(f,'OK'); }
catch(e){ console.log(f,'FAIL',e.message); }
}"
babel.config.js里的useBuiltIns: false是有意为之:库不该自带 polyfill(既撑大体积——实测从 367KB 降到 153KB,又会污染使用方的运行环境)。它不影响语法降级,语法目标仍由.browserslistrc控制。代价是老浏览器缺失的运行时 API 需由使用方自行 polyfill(如 marked 用到的Array.prototype.at())。
注意
- token:源码不内置任何凭据(避免敏感信息随包发布),
token为必填项。未传时generate会直接抛错「缺少 token」,错误会显示在对话面板上。请由接入方从自身的登录态/环境变量读取后传入,不要硬编码进仓库。 - peerDependency:仅
vue@^2.6.0,不依赖 Element UI。 - 运行时依赖:
marked,用于渲染回答中的 markdown。
Related Packages
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.6 | ^2.6.0 | MIT | |
| 1.0.5 | ^2.6.0 | MIT | |
| 1.0.4 | ^2.6.0 | MIT | |
| 1.0.3 | ^2.6.0 | MIT | |
| 1.0.2 | ^2.6.0 | MIT | |
| 1.0.1 | ^2.6.0 | MIT | |
| 1.0.0 | ^2.6.0 | MIT |