@egova/egovis-sdk
v0.1.5
Published
Egovis 嵌入式智能助手 SDK
Maintainers
Keywords
Readme
Egovis 智能助手 SDK
@egova/egovis-sdk 用于在业务系统中接入 Egovis 智能助手,支持抽屉、页面内 Modal 和页面嵌入三种布局,并可向助手提供当前页面的业务上下文、快捷提问和宿主动作。
SDK 仅在浏览器环境中运行。Nuxt、Next.js 等服务端渲染项目应在客户端生命周期内加载 SDK。
安装
pnpm add @egova/egovis-sdk也可以使用 npm:
npm install @egova/egovis-sdk快速开始
import { create, type BusinessPageContext } from '@egova/egovis-sdk'
const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
display: {
theme: 'light',
starterTitle: '案件办理助手',
starterSubtitle: '分析当前案件并生成处置建议。',
conversationWidthPercent: 120,
themeToggleVisible: true,
fontSize: 100,
},
contextProvider: (): BusinessPageContext => ({
source: {
systemCode: 'case-center',
pageCode: 'case-list',
},
page: {
title: '案件统计',
path: location.pathname,
},
content: {
filters: { status: 'pending' },
selectedCaseIds: ['case-1'],
},
summary: [
{ key: 'total', label: '案件数', value: '12' },
],
questions: [
{ title: '分析趋势', content: '分析当前案件变化趋势' },
],
}),
})
assistant.on('error', error => console.error('智能助手异常', error))默认使用 drawer 布局,并在页面右下角显示系统 AI 图标。首次点击图标时加载并打开智能助手。
SDK 写入 iframe URL 的业务参数只有 projectId,其他配置通过 iframe 桥接发送。iframe 内独立完成登录;登录跳转不会销毁 SDK 创建的 iframe,登录成功后会继续完成桥接初始化。
使用新增展示字段前应先部署支持对应 Bridge 字段的新版 assistant.html,再升级业务系统中的 SDK。新版助手仍兼容旧 SDK 未携带新增展示字段的初始化消息。
初始化选项
| 选项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| assistantUrl | string | - | 必填,助手 assistant.html 的绝对 HTTP(S) 地址。 |
| projectId | string | - | 必填,当前 Egovis 项目 ID。 |
| mountTarget | string \| HTMLElement | document.body | SDK 挂载节点。 |
| layout | 'drawer' \| 'modal' \| 'embedded' | 'drawer' | 助手布局方式。 |
| trigger.label | string | - | AI 按钮文案;未配置时只显示系统 AI 图标。 |
| trigger.placement | 'left' \| 'right' | 'right' | AI 按钮位于页面左下角或右下角。 |
| trigger.hidden | boolean | false | 是否隐藏 SDK 内置 AI 按钮。 |
| trigger.draggable | boolean | true | 是否允许拖动 SDK 内置 AI 按钮。 |
| drawer.width | number \| px \| vw | 480 | 抽屉宽度,最小 360 px,最大为当前视口宽度。 |
| drawer.placement | 'left' \| 'right' | 'right' | 抽屉打开方向。 |
| drawer.resizable | boolean | true | 是否允许调整抽屉宽度。 |
| modal.width | number \| px \| vw | 90vw | Modal 默认宽度,数字按 px 处理。 |
| modal.height | number \| px \| vh | 90vh | Modal 默认高度,数字按 px 处理。 |
| modal.draggable | boolean | true | 是否允许拖动 Modal。 |
| modal.resizable | boolean | true | 是否允许调整 Modal 尺寸。 |
| modal.backdropVisible | boolean | true | 是否显示半透明遮罩;隐藏后仍会阻断宿主交互。 |
| interaction.snap | boolean | true | 是否为 Trigger、Drawer 和 Modal 启用吸附。 |
| interaction.showSnapIndicators | boolean | true | 是否显示吸附视觉反馈;不影响实际吸附。 |
| persistenceScope | 'origin' \| 'project' | 'origin' | 交互位置和尺寸按当前 Origin 或项目持久化。 |
| display.contextVisible | boolean | true | 是否在助手中显示页面数据卡片。 |
| display.starterTitle | string | 把想法变成成果,用 AI 高效完成工作 | 助手新对话欢迎区标题,最长 256 个字符;空白值回退默认文案。 |
| display.starterSubtitle | string | 您好,我是您的智能助手,可以帮您梳理思路、拆解任务、生成内容,让工作推进更简单。 | 助手新对话欢迎区副标题,最长 512 个字符;空白值回退默认文案。 |
| display.conversationWidthPercent | number | 100 | 对话内容和输入框共享宽度百分比,范围 80–140,步长 10。 |
| display.theme | 'light' \| 'dark' \| 'system' | 'light' | 助手初始主题;system 会在当前 iframe 会话内实时跟随操作系统主题变化。 |
| display.themeToggleVisible | boolean | true | 是否显示 assistant 页面右上角的主题切换按钮,宽屏和紧凑布局同时生效。 |
| display.fontSize | number | 跟随 iframe 宽度 | assistant.html 的 HTML 根字号,单位为 px,范围 80–120;显式配置固定覆盖,缺省时按 iframe width 自动计算。 |
| contextProvider | (scope: EgovisProjectScope) => BusinessPageContext \| Promise<BusinessPageContext> | - | 返回当前项目的最新业务上下文。 |
| actions | EgovisHostActionDefinition[] | [] | 注册宿主动作,供助手生成固定动作卡片。 |
| timeouts.loadMs | number | 10000 | iframe 文档首次加载超时时间,单位为 ms。 |
| timeouts.contextAckMs | number | 10000 | 上下文同步确认超时时间,单位为 ms。 |
| timeouts.actionExecutionMs | number | 10000 | 宿主动作 Handler 执行超时时间,单位为 ms。 |
三项超时仅接受有限正数;无效值统一回退到 10000 ms。
展示配置通过 Bridge 应用于当前 SDK iframe。宿主配置的对话宽度是每次加载的初始值;用户仍可在页面配置中临时调整当前会话宽度。display.theme 缺省为浅色主题,配置为 system 时会跟随操作系统主题实时变化;用户在助手内手动切换后仅影响当前 iframe 会话。fontSize 会直接写入 iframe 文档的 document.documentElement.style.fontSize,只影响 assistant.html,不会修改宿主页面字号。
业务上下文
字段说明
| 字段 | 必填 | 说明 |
| --- | --- | --- |
| source.systemCode | 是 | 来源系统编码,最长 128 个字符。 |
| source.pageCode | 是 | 来源页面编码,最长 128 个字符。 |
| page.title | 是 | 页面标题,最长 256 个字符。 |
| page.path | 否 | 以 / 开头且不包含 query 或 hash,最长 1024 个字符。 |
| subject | 否 | 当前业务主体,包含 type 和 title。 |
| summary | 否 | 页面摘要,最多 8 项,key 必须唯一。 |
| content | 是 | 提供给助手的结构化页面数据,根节点必须是 JSON 对象。 |
| questions | 否 | 快捷提问,最多 20 项,仅用于助手界面。 |
单个上下文序列化后最多 64 KB,最大嵌套深度为 16 层。上下文只能包含标准 JSON 值,不支持循环引用、函数、undefined、日期对象、访问器属性或稀疏数组。
对象键禁止包含 Token、Cookie、密码、密钥等敏感字段。业务系统应按白名单组装数据,并在传给 SDK 前完成脱敏。
更新上下文
页面路由、查询条件、表格数据或选中项发生变化后,重新获取当前页面数据:
async function handleSearch() {
await loadTableData()
await assistant.refreshContext()
}contextProvider 接收 { projectId, signal }。后续刷新、Provider 替换、项目切换或实例销毁会中止旧信号,并丢弃旧 Provider 的迟到结果。
如果业务系统已经持有完整的新上下文,也可以直接更新:
assistant.setContext(nextContext)每次更新都应提供完整上下文;summary、content 和 questions 会整体替换,不与上一次数据合并。用户发送消息时,助手会使用发送瞬间的上下文快照;questions 只用于快捷提问,不提交后端。
display.contextVisible: false 只隐藏页面数据卡片,不会清除或停止同步上下文。需要停止携带上下文时,应调用 clearContext()。
宿主动作
宿主动作让对话能够触发业务系统中的固定操作:
- SDK 将已注册动作的名称、描述、参数 Schema 和默认参数提供给助手。
- 当用户对话符合某个业务操作场景时,助手生成固定动作卡片。
- 用户点击卡片后,iframe 向 SDK 发起动作请求,SDK 调用对应 Handler 并回传结果。
助手不会获得 Handler 实现,也不会绕过用户点击直接执行宿主操作。
动作字段
| 字段 | 必填 | 说明 |
| --- | --- | --- |
| call | 是 | 动作唯一名称,必须符合 EGOVIS_XXX_XXX 格式。 |
| label | 是 | 动作卡片按钮文案。 |
| description | 是 | 描述适用场景,供助手选择动作。 |
| parameterSchema | 否 | 约束助手生成的卡片参数;省略时表示无参数。 |
| defaultArgs | 否 | 卡片默认参数;助手生成的同名参数会覆盖默认值。 |
| defaultButtonType | 是 | 默认按钮类型。 |
| allowedButtonTypes | 是 | 助手可选择的按钮类型范围。 |
| interaction | 否 | 配置点击后的终态和结果消息策略;省略时使用默认行为。 |
| handler | 是 | 用户点击卡片后执行的宿主函数。 |
import { create, type EgovisHostActionDefinition } from '@egova/egovis-sdk'
const actions: EgovisHostActionDefinition[] = [
{
call: 'EGOVIS_OPEN_CASE_LEDGER',
label: '打开案件台账',
description: '打开当前区域的案件台账页。',
parameterSchema: {
type: 'object',
properties: {
regionId: { type: 'string' },
},
required: ['regionId'],
additionalProperties: false,
},
defaultArgs: {
regionId: 'all',
},
defaultButtonType: 'info',
allowedButtonTypes: ['primary', 'info'],
interaction: {
disableAfterSuccess: true,
resultMessage: {
success: true,
failure: '案件台账打开失败,请稍后重试。',
},
},
handler: async (args, { projectId, signal }) => {
const response = await fetch(
`/api/projects/${encodeURIComponent(projectId)}/case-ledger`,
{ signal },
)
if (!response.ok) {
return {
status: 'failed',
errorCode: 'LOAD_CASE_LEDGER_FAILED',
message: '案件台账接口暂不可用。',
}
}
location.assign(`/case-ledger?regionId=${encodeURIComponent(String(args.regionId))}`)
return { status: 'success', message: '案件台账已打开。' }
},
},
]
const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
actions,
})
assistant.on('action-result', result => {
console.log('宿主动作执行结果', result)
})
assistant.on('action-execution', telemetry => {
console.log('宿主动作监控', telemetry)
})按钮类型支持 Element Plus 的 default、primary、success、warning、danger 和 info。
interaction.disableAfterSuccess 控制动作成功后是否将当前卡片按钮置为“已完成”并禁用;默认 false。该状态只影响当前消息卡片中的当前动作,不会禁用同名动作的其他卡片或其他布局实例。
interaction.resultMessage.success 和 interaction.resultMessage.failure 控制结果提示策略:false 表示不提示,true 表示使用 Handler 返回的 message,没有动态消息时回退为 SDK 默认文案,字符串表示固定覆盖文案。默认值为 { success: false, failure: true }。rejected、failed 和 timeout 均使用 failure 策略,unsupported 不展示结果消息。
parameterSchema 用于约束助手生成的动作卡片。SDK 执行时只校验参数是否为标准 JSON 对象且不超过 16 KB,Handler 必须自行校验最终参数、用户权限和当前项目状态。未知动作返回 unsupported,参数格式或大小不合法返回 rejected,Handler 抛错返回 failed,超过动作超时返回 timeout。
每次 Handler 执行都会收到独立的 { projectId, signal }。调用 replaceActions()、切换项目、执行超时或销毁实例会中止信号;unregisterAction() 和同名 Handler 替换不会取消已经开始的执行。
Handler 返回的 message 只用于当前动作结果提示,最大 240 个字符;空白字符串、控制字符、HTML 标签和非字符串值会被忽略。message 不会进入动作注册描述、请求参数或 action-execution 监控事件。
action-execution 事件只提供 projectId、call、status 和 durationMs,不包含参数、Handler 返回内容或异常对象。action-result 事件会携带基础结果字段;当 Handler 返回了有效动态消息时,事件载荷会额外包含 message。
布局
抽屉
const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
layout: 'drawer',
trigger: { label: 'AI 助手', placement: 'right' },
drawer: { width: 440, placement: 'right', resizable: true },
contextProvider: () => currentPageContext,
})抽屉首次打开时加载助手;关闭后保留对话状态,再次打开时重新获取页面上下文。Trigger 位置和抽屉宽度会根据 persistenceScope 持久化。
隐藏 SDK 内置按钮后,可以使用业务系统自己的入口:
const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
trigger: { hidden: true },
})
document.querySelector('#ai-button')?.addEventListener('click', () => {
void assistant.toggle()
})Modal
const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
layout: 'modal',
modal: {
width: '80vw',
height: '80vh',
draggable: true,
resizable: true,
backdropVisible: true,
},
interaction: {
snap: true,
showSnapIndicators: true,
},
})Modal 支持拖动、八方向缩放、位置与尺寸持久化,并在窄屏下自动切换为近全屏模式。关闭按钮、遮罩点击和 Escape 均可关闭 Modal;打开期间会保持焦点循环并锁定页面滚动。
modal.backdropVisible: false 只隐藏遮罩颜色,透明遮罩仍会阻断宿主页面交互。
页面嵌入
<div id="assistant-panel"></div>#assistant-panel {
width: 420px;
height: 100vh;
}const assistant = create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
mountTarget: '#assistant-panel',
layout: 'embedded',
contextProvider: () => currentPageContext,
})embedded 布局会填满挂载节点,因此挂载节点必须具有明确尺寸。该布局始终显示,close() 不会隐藏助手;嵌入区域尺寸由业务系统管理。
Drawer 和 Modal 使用 position: fixed。自定义 mountTarget 的祖先不应通过 transform、filter、perspective 或 contain 建立 Fixed Positioning Containing Block。
实例方法
| 方法 | 说明 |
| --- | --- |
| open() | 打开 Drawer 或 Modal,并重新获取当前页面上下文。 |
| close() | 关闭 Drawer 或 Modal;对 embedded 布局无效。 |
| toggle() | 切换 Drawer 或 Modal 的打开状态。 |
| refreshContext() | 重新执行 contextProvider。 |
| setContext(context) | 使用完整快照更新业务上下文。 |
| clearContext() | 清除当前业务上下文。 |
| setProjectId(projectId, options?) | 原子更新项目、上下文来源和宿主动作。 |
| registerAction(action) | 注册单个宿主动作。 |
| unregisterAction(call) | 按动作名注销宿主动作。 |
| replaceActions(actions) | 原子替换全部宿主动作。 |
| listActions() | 返回当前可公开的动作描述列表。 |
| getState() | 返回实例当前状态的只读快照。 |
| on(event, handler) | 监听 SDK 事件,返回取消监听函数。 |
| destroy() | 销毁 SDK 实例并移除页面元素。 |
业务页面卸载或微前端应用销毁时,必须调用 destroy()。
事件
| 事件 | 载荷 |
| --- | --- |
| open、close、ready | { projectId } |
| context-ack | ContextAckPayload |
| context-rejected | ContextRejectedPayload |
| action-ack、action-rejected | EgovisHostActionsAckPayload |
| action-result | EgovisHostActionResultPayload & { message?: string } |
| action-execution | { projectId, call, status, durationMs } |
| error | { code, message, projectId } |
const offReady = assistant.on('ready', ({ projectId }) => {
console.info('智能助手已就绪', projectId)
})
const offError = assistant.on('error', error => {
console.error('智能助手异常', error)
})
// 不再需要监听时取消订阅
offReady()
offError()iframe 首次触发 load 只表示文档已加载,不要求用户已经登录。ready 在助手登录并完成业务桥接后触发,表示助手业务已就绪。
切换项目
业务系统切换项目后,可以同时更新 SDK 的项目、上下文来源和动作:
await assistant.setProjectId(nextProjectId, {
contextProvider: ({ projectId, signal }) => loadProjectContext(projectId, { signal }),
actions: buildProjectActions(nextProjectId),
})| 选项 | 行为 |
| --- | --- |
| 不传 options | 复用当前 Provider 和动作;项目变化时清除旧快照。 |
| contextProvider | 替换 Provider;传 null 时移除 Provider 并清空上下文。 |
| context | 切换为直接快照模式;传 null 时清空上下文。 |
| actions | 原子替换动作;传 [] 时清空动作。 |
context 与 contextProvider 不能同时传入。SDK 会先完整校验本次更新,校验失败时保持原状态。setProjectId() 的 Promise 不等待 iframe 登录或桥接 ready,应通过 ready 事件确认远端业务就绪。
const state = assistant.getState()
// { projectId, open, ready, contextMode, actionRevision, destroyed }样式调整
按钮位置、抽屉方向和尺寸优先通过初始化选项配置。字体、颜色和层级可以通过 CSS 变量覆盖:
egovis-ai-assistant {
--egovis-assistant-font-family: var(--eg-font-sans);
--egovis-assistant-trigger-bg: #1677ff;
--egovis-assistant-trigger-color: #ffffff;
--egovis-assistant-z-index: 2000;
}IIFE/CDN 接入
<script src="https://cdn.example.com/egovis-sdk.iife.js"></script>
<script>
const assistant = EgovisSDK.create({
assistantUrl: 'https://ai.example.com/assistant.html',
projectId: 'project-100',
contextProvider: () => ({
source: { systemCode: 'case-center', pageCode: 'case-detail' },
page: { title: '案件详情', path: location.pathname },
content: { caseId: 'case-1', status: 'pending' }
})
})
</script>数据安全与跨域
contextProvider应按白名单组装数据,并在返回前完成脱敏。- 不要提供 Token、Cookie、密码、身份证明、完整手机号或其他认证凭证。
assistantUrl不应携带 Token 或其他敏感信息。- 宿主动作的
description、defaultArgs和 Handler 返回message均会进入助手 iframe,应避免包含敏感数据、内部错误堆栈或可用于越权操作的信息。 - 动作 Handler 必须在宿主侧重新校验参数、当前用户权限和项目归属;助手生成的卡片参数不能视为可信输入。
- 跨域部署时,助手服务端必须通过 CSP
frame-ancestors允许业务系统 Origin。 - 智能助手前端必须配置允许接入的业务系统 Origin,例如
VITE_ASSISTANT_HOST_ORIGINS=https://business.example.com。 - 生产环境使用 HTTPS,Origin 配置不得使用
*、路径、查询参数或片段。
