Skip to content

JS SDK 集成指南

JS SDK 提供完整的对话 UI 组件,适用于需要自定义交互、拦截请求或嵌入问数能力的场景。只需在页面中引入 SDK 文件并初始化即可。

前置要求

使用 JS SDK 需要一定的 JavaScript 开发经验。

快速开始

1. 获取 SDK 链接

在系统任意页面点击 Data Agent 展开对话侧边栏,点击右上角第一个图标按钮,在抽屉弹窗中获取 SDK 链接,例如:

<host>/assets/hengshi-copilot@<version>.js

注意

@<version> 是 SDK 版本号,请确保与系统版本一致。系统升级时也需要更新。

2. 引入 SDK 并初始化

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>AI助手</title>
  <script type="text/javascript" src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js" defer></script>
</head>
<body>
  <button id="trigger-ai">唤起AI助手</button>
  <div id="copilot-root" style="display: none;"></div>

  <script>
    const copilotConfig = {
      locale: 'zh-CN',
      draggable: {
        enable: true,
        minWidth: 440,
        minHeight: 440,
        position: { x: window.innerWidth - 480, y: 20 },
        size: { width: 440, height: window.innerHeight * 0.8 },
        onDragStop,
        onResize,
      },
      userConfig: {
        dataAppId: 130870,
        datasetId: 1,
      },
    };

    let copilotInstance = null;
    const button = document.getElementById('trigger-ai');
    const copilotRoot = document.getElementById('copilot-root');
    button.addEventListener('click', toggleCopilot);

    function onDragStop(event, position) {
      copilotConfig.draggable.position = position;
    }

    function onResize(event, position, size) {
      copilotConfig.draggable.position = position;
      copilotConfig.draggable.size = size;
    }

    function launchCopilot() {
      if (typeof Copilot === 'undefined') {
        (function(fn) { fn(launchCopilot); })(requestIdleCallback || setTimeout);
      } else {
        if (!copilotInstance) {
          copilotInstance = new Copilot(copilotRoot, copilotConfig);
          copilotRoot.style.display = 'block';
          button.textContent = '关闭AI助手';
        } else {
          copilotInstance.show();
        }
      }
    }

    function toggleCopilot() {
      if (copilotInstance) {
        const isVisible = copilotRoot.style.display !== 'none';
        if (isVisible) {
          copilotInstance.hide();
          button.textContent = '唤起AI助手';
        } else {
          copilotInstance.show();
          button.textContent = '关闭AI助手';
        }
      } else {
        launchCopilot();
      }
    }
  </script>
</body>
</html>

3. CDN 部署时指定后端 API Host

当 SDK 文件托管在 CDN 上时,SDK 默认会将 API 请求发送到 SDK 文件所在的 host。这通常不是后端 API 的实际地址,会导致请求错误。

此时请在引入 SDK 之前设置全局变量 window.__hs_sd_base_url__,显式指定后端 API 的 host:

html
<head>
  <script>
    window.__hs_sd_base_url__ = 'https://develop.hengshi.org';
  </script>
  <script type="text/javascript" src="https://cdn.example.com/assets/hengshi-copilot@<version>.js"></script>
</head>

注意

CDN host 仅用于加载 SDK 文件,API 请求 host 需通过 window.__hs_sd_base_url__ 明确指定。

4. 运行项目

启动或打开你的项目,点击按钮检查是否成功唤起对话框。

在 React / Vue 项目中集成

JS SDK 不依赖框架,在 React 或 Vue 项目中集成时,核心思路是 先加载 SDK 脚本,再初始化 Copilot 实例

React 集成要点

  • 在组件 useEffect 中加载 SDK 脚本
  • 在脚本加载完成后再创建 Copilot 实例
  • 组件卸载时移除脚本或隐藏实例

Vue 集成要点

  • onMounted 中加载 SDK 脚本
  • 在脚本加载完成后再创建 Copilot 实例
  • onBeforeUnmount 中进行清理

更完整的框架示例代码可参考本页后续的 renderChat 示例,或直接复用上文的 HTML 示例逻辑。

SDK 实例与方法

new Copilot(...) 的结果是一个 copilot 实例,你可以通过这个实例来操作 Copilot。

js
{
  copilotConfig: {
    config: {...}, // 大模型配置信息
    loading: false, // 是否正在加载
    onVerifyConfig: () => {}, // 验证配置信息
    onChangeProvider: () => {}, // 切换模型提供商
    // ...
  },
  context: {
    api: {...},
    endpoints: {...},
    // Copilot 状态
    isLoading: false,
    // 当前会话列表,数据格式参照
    // https://api.hengshi.com/conversation.html#conversation
    conversations: [],
    setConversations: () => {},
    currentConversationId: null,
    setCurrentConversationId: () => {},
    runningChatUid: null,
    setRunningChatUid: () => {},
    setChat: (id, chatUid, chat) => {},
    // 获取历史对话列表
    onFetchConversations: (offset = 0, limit = 10) => {},
    // 创建对话
    onCreateConversation: prompt => {},
    onFetchConversation: id => {},
    onClearConversations: () => {},
    onFetchPreviousConversations: () => {},
    // 创建问答
    onCreateChat: body => {},
    onDeleteChat: (id, chatUid) => {},
    onFetchSuggestions: refresh => {},
    onFetchFavorites: () => {},
    onCancelChatFavorite: (id, chatUid) => {},
  },
  // 控制对话框显示/隐藏
  show: () => {}, // 显示对话框
  hide: () => {}, // 隐藏对话框
}

SDK 配置参数

new Copilot(...) 的配置参数包含以下主要部分:

js
const copilotConfig = {
  locale: 'zh-CN', // 语言设置
  i18n: { // 本地化文案配置
    "zh-CN": {
      'copilot.question-reasoning': '问题理解',
      'copilot.question-answer': '数据表现',
      'copilot.question-reasoning-logic': '取数逻辑'
    },
    "en-US": {
      'copilot.question-reasoning': 'Question Understanding',
      'copilot.question-answer': 'Data Performance',
      'copilot.question-reasoning-logic': 'Data Logic'
    }
  },
  stylesheet: ':host > *, ::before, ::after { --brand: #4CAF50; }', // 自定义样式
  draggable: { // 对话框拖拽配置
    enable: true,
    minWidth: 440,
    minHeight: 440,
    position: {
      x: window.innerWidth - 480,
      y: 20,
    },
    size: {
      width: 440,
      height: window.innerHeight * 0.8,
    },
    onDragStop: onDragStop,
    onResize: onResize,
  },
  userConfig: { // 数据源配置
    dataSources: [
      {
        dataAppId: 130870,
        datasetId: 1,
      },
      {
        dataAppId: 130871,
        datasetId: 2,
      }
    ]
  },
  completionConfig: { // AI 对话完成配置
    meta: 'answer,chart,chartConfig', // 指定要渲染的内容组合
    openChartConfig: false, // 是否展开取数逻辑面板,默认为 true
    stylesheet: ':host > *, ::before, ::after { --brand: #4CAF50; }', // 对话完成后的自定义样式
    i18n: { // 对话完成后的本地化文案配置
      "zh-CN": {
        'copilot.question-reasoning': '问题理解',
        'copilot.question-answer': '数据表现',
        'copilot.question-reasoning-logic': '取数逻辑'
      }
    },
    onCompletionDone: (conversationId, uid) => {
      // AI 对话完成回调
      // conversationId: 当前对话 ID
      // uid: 当前完成的对话唯一标识
      console.log('AI completion done:', conversationId, uid);
    },
  },
};

配置说明

userConfig 数据源配置

userConfig 用于配置数据源,支持多个数据源配置。数据源配置支持两种模式,但在同一个配置中只能使用其中一种:

  1. 数据包与数据集模式:
  • dataSources: 数组,包含多个数据源配置对象
    • dataAppId: 数据包 ID
    • datasetId: 数据集 ID

示例:

js
userConfig: {
  dataSources: [
    {
      dataAppId: 130870,
      datasetId: 1,
    },
    {
      dataAppId: 130871,
      datasetId: 2,
    }
  ]
}
  1. 业务指标主题域模式:
  • dataSources: 数组,包含多个主题域配置对象
    • subjectId: 主题域 ID

示例:

js
userConfig: {
  dataSources: [
    {
      subjectId: 1,
    },
    {
      subjectId: 2,
    }
  ]
}

注意:每个 dataSources 配置中只能使用其中一种模式,即要么全部使用 dataAppId + datasetId 的组合,要么全部使用 subjectId。不能在同一个配置中混用两种模式。

i18n 本地化配置

i18n 配置用于自定义界面文案,支持多语言:

  • 按语言代码(如 'zh-CN'、'en-US')组织文案
  • 每种语言下可配置多个文案键值对
  • 预定义的文案键包括:
    • copilot.question-reasoning: 问题理解部分标题
    • copilot.question-answer: 数据表现部分标题
    • copilot.question-reasoning-logic: 取数逻辑部分标题

stylesheet 样式配置

stylesheet 用于自定义界面样式:

  • 接受 CSS 字符串
  • 可以通过 <style> 标签定义后获取
  • 支持 CSS 自定义属性(变量)
  • 主要样式变量:
    • --brand: 主题色

示例:通过 style 标签定义样式

html
<style id="copilot-css">
  /* 修改主题色 */
  :host > *, ::before, ::after {
    --brand: #4CAF50;
  }
</style>
<script>
  const css = document.querySelector('#copilot-css');
  const style = css.textContent;

  // 在 Copilot 配置中使用
  const copilot = new window.Copilot({
    // 其他配置...
    stylesheet: style,
  });

  // 或在对话完成配置中使用
  window.Copilot.renderChat(container, {
    // 其他配置...
    stylesheet: style,
  });
</script>

completionConfig 配置说明

completionConfig 用于控制 AI 对话的完成行为和展示方式:

  • meta: 字符串,指定要渲染的内容组合
  • openChartConfig: 布尔值,控制是否展开取数逻辑面板,默认为 true
  • stylesheet: 字符串,自定义对话完成后的样式
  • i18n: 对象,自定义对话完成后的文案
  • onCompletionDone: 函数,在 AI 对话完成时触发的回调函数,接收两个参数:
    • conversationId: 当前对话的 ID
    • uid: 当前完成的对话唯一标识

其他可选配置

以下为常用的可选配置项,按需使用:

配置项说明
closable是否显示关闭按钮,默认 false
draggable.bounds拖拽边界:windowparent
className / style对话框根容器的类名与样式
bodyClassName / bodyStyle对话框内容区域的类名与样式
header标题区域的自定义内容
systemMsg欢迎语文案
parser图表选择器配置(selectText / onSelect
chartTheme图表主题(与仪表盘主题一致)

登录认证

集成 SDK 时,可能需要先完成登录认证。支持以下方式:

  1. SSO 单点登录:如果您的系统已与衡石系统打通,无需额外登录。
  2. JWT 认证:通过以下方式完成登录认证:
js
fetch('https://develop.hengshi.org/api/auth/login-info?activeAuth=jwt-param&jwtParam=您的 JWT 参数')
  .then(response => {
    // 登录成功,继续使用 SDK
  })
  .catch(error => {
    // 登录失败,处理错误
  });

提示

请确保已在 “设置 → 组织管理 → 认证方式 → JWT 请求参数” 中完成配置,并将 您的 JWT 参数 替换为实际值。

控制对话框显示/隐藏

Copilot 实例提供了 showhide 方法来控制对话框的显示与隐藏,这比完全销毁和重新创建实例更加高效:

js
// 创建一个 Copilot 实例
const copilotInstance = new Copilot(copilotRoot, copilotConfig);

// 隐藏对话框(不会销毁实例,只是隐藏界面)
copilotInstance.hide();

// 显示对话框
copilotInstance.show();

// 切换显示/隐藏状态
function toggleCopilot() {
  if (copilotInstance) {
    // 可以通过检查 DOM 元素状态来判断当前显示状态
    const isVisible = copilotRoot.style.display !== 'none';
    if (isVisible) {
      copilotInstance.hide();
      button.textContent = '唤起AI助手';
    } else {
      copilotInstance.show();
      button.textContent = '关闭AI助手';
    }
  }
}

与完全销毁和重新创建实例相比,使用 show/hide 方法有以下优点:

  • 保持对话上下文和状态
  • 更快的响应速度(无需重新初始化)
  • 更少的资源消耗

通过 JS 调用 HTTP API

在上述 copilot 实例中,api 对象中提供了所有 HTTP API 的调用方法,你可以通过调用这些方法来与后端进行交互,如:

2.1 创建对话

js
const { body: { data: conversation }} = await copilot.api.requestCreateConversation({
  body: {
    title: '这里是本次对话的标题',
  },
}).value;
数据示例
js
conversation = {
  "id": 135,
  "title": "这里是本次对话的标题",
  "createdBy": 268,
  "createdAt": "2024-09-14 15:47:23",
  "updatedBy": 268,
  "updatedAt": "2024-09-14 15:47:23"
}

2.2 创建问答

js
const { body: { data: chat }} = await copilot.api.requestCreateChat({
  id: conversation.id,
  body: {
    prompt: '哪个导演拍的电影票房最高', // 这里是用户的问题
    userConfig: { // 参照上述 copilotConfig 中的 userConfig
      dataAppId: 129150,
      datasetId: 1,
    }
  },
  qs: {
    sync: true, // 是否同步执行
    timeout: 1000 * 60, // 超时时间,单位为毫秒
  },
}).value;
数据示例
js
chat = {
    "conversationId": 135,
    "prompt": "哪个导演拍的电影票房最高",
    "answer": "克里斯托弗·诺兰导演拍的电影票房最高。",
    "model": "gpt-4o",
    "uid": "08ff93ca-1972-4916-b884-35fab6f91c64",
    "temperature": 0,
    "createdBy": 268,
    "createdAt": "2024-09-14 15:47:23",
    "updatedBy": 268,
    "updatedAt": "2024-09-14 15:47:23",
    "responseAt": "2024-09-14 15:47:30",
    "isDelete": false,
    "isContextEnd": false,
    "suggestQuestions": [],
    "statusList": [
        "PENDING",
        "ANALYZE_REQUEST",
        "HQL_SELECT_FIELDS",
        "HQL_SELECT_FUNCTIONS",
        "GENERATE_HQL_QUERY",
        "DOING_HQL_QUERY",
        "DOING_SUMMARY",
        "DOING_QUESTION_SUGGESTING",
        "DONE"
    ],
    "userConfig": {
        "datasetId": 1,
        "dataAppId": 129150
    },
    "autoConfig": {
        "agentType": "HQL_AGENT"
    },
    "isCurrent": true,
    "usage": [
        {
            "completion_tokens": 24,
            "prompt_tokens": 4063,
            "total_tokens": 4087
        },
        {
            "completion_tokens": 5,
            "prompt_tokens": 1424,
            "total_tokens": 1429
        },
        {
            "completion_tokens": 49,
            "prompt_tokens": 3906,
            "total_tokens": 3955
        },
        {
            "completion_tokens": 16,
            "prompt_tokens": 299,
            "total_tokens": 315
        }
    ],
    "chartCreatedAt": "2024-09-14 15:47:30",
    "refineQuestion": "哪个导演拍的电影票房最高",
    "favorite": false,
    "charts": [
        {
            "appId": 129150,
            "datasetId": 1,
            "options": {
                "axes": [
                    {
                        "op": "{{1}}.{director}",
                        "uid": "uid1",
                        "fieldName": "director",
                        "kind": "formula",
                        "datasetId": 1,
                        "labelOrigin": "director",
                        "label": "director",
                        "isAggregate": false,
                        "value": "{{coffe_产品表}}.{director}",
                        "dataset": 1,
                        "fieldType": "string"
                    },
                    {
                        "op": "max({{1}}.{votes})",
                        "uid": "uid2",
                        "fieldName": "votes",
                        "kind": "formula",
                        "datasetId": 1,
                        "labelOrigin": "votes",
                        "label": "votes",
                        "isAggregate": true,
                        "value": "max({{coffe_产品表}}.{votes})",
                        "dataset": 1,
                        "fieldType": "number"
                    }
                ],
                "name": "Bar",
                "limit": 1,
                "sort": [
                    {
                        "op": "uid2",
                        "kind": "reference",
                        "direction": "desc"
                    },
                    {
                        "op": "uid1",
                        "kind": "reference",
                        "direction": "asc"
                    }
                ]
            },
            "dataAppId": 129150,
            "datasetIds": [
                1
            ]
        }
    ]
}

在已有 AI 对话工具中嵌入问数能力

除了嵌入完整的 AI 助手对话功能外,SDK 还提供了一个静态方法 renderChat,此方法可以让您在自己的大语言模型对话工具中集成的问数能力,支持两种使用模式:基于 prompt 的动态查询模式和静态渲染模式。

动态问答模式

这种模式下,您只需提供用户的问题(prompt),SDK 会自动完成数据查询、分析和可视化。这非常适合在您自己的 AI 对话工具中集成的数据分析能力。

html
<div class="conversation"></div>
<script src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js"></script>
<script>
  // 使用 prompt 配置,SDK 将自动完成数据查询和分析
  const chat = {
    prompt: "bug issue的模块分布情况",
    // type: "agent", // 可选参数,可以通过 type agent|workflow 指定 copilot 模式,默认情况下根据系统配置的 PREFER_AGENT_MODE 开关决定
    // conversationId: 1, // 可选参数,可以指定 conversationId 在已有的对话中继续
  };
  const container = document.querySelector('.conversation');
  // case 1: 不指定数据来源
  window.Copilot.renderChat(container, chat);

  // case 2: 指定数据来源
  // window.Copilot.renderChat(container, chat, {
  //   usePropsUserConfig: true,
  //   userConfig: {
  //     dataSources: [{
  //       dataAppId: 136789,
  //       datasetId: 10,
  //     }],
  //   },
  // });
</script>

静态渲染模式

这种模式适合展示已有的分析结果,您需要提供完整的对话数据结构。

html
<div class="conversation"></div>
<script src="https://develop.hengshi.org/assets/hengshi-copilot@<version>.js"></script>
<script>
  // 提供完整的对话数据结构进行静态渲染
  const chat = {
    "meta": "refineQuestion,answer,chart,chartConfig",
    "conversationId": 226,
    "uid": "00747081-9cc3-4c7c-b3de-735244498fd6",
    "prompt": "bug issue的模块分布情况",
    "answer": "bug issue在各模块中的分布较均匀,最多的模块有4个bug,其余大部分模块有2-3个bug。",
    "refineQuestion": "请提供bug issue在各模块中的分布情况",
    "options": {
      "charts": [],
      "chartsData": []
    },
  };
  const container = document.querySelector('.conversation');
  window.Copilot.renderChat(container, chat);
</script>

参数说明

Copilot.renderChat 方法接受三个参数:

  1. container: DOM 元素,用于渲染对话内容的容器

  2. chat: 对话数据对象,支持两种配置方式:

    a. 动态问答配置:

    • prompt: 字符串,用户的问题。提供此配置时,SDK 将自动进行数据查询和分析。
    • type: (可选) 字符串 agent | workflow,指定 copilot 模式
    • conversationId: (可选) 对话 ID,指定在已有对话基础上继续对话
    • meta: (可选) 字符串,指定要渲染的内容组合,可以是 refineQuestionanswerchartchartConfig 等字段的组合
    • openChartConfig: (可选) 布尔值,控制是否展开取数逻辑面板,默认为 true
    • onCompletionDone: (可选) 函数,在对话完成时的回调

    b. 静态渲染配置:

    • meta: 字符串,指定要渲染的内容,可以是 refineQuestionanswerchartchartConfig 等字段的组合
    • conversationId: 对话 ID
    • uid: 对话的唯一标识符
    • prompt: 原始问题
    • answer: AI 回答内容
    • refineQuestion: 精炼后的问题
    • charts: 图表配置数组
    • chartsData: 图表数据数组
  3. config: (可选) 配置项,通过 usePropsUserConfig 配合 userConfig 在动态问答模式下指定数据来源

衡石分析平台使用手册