本地 RAG 知识库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
wanghanlin 5c75cb3f71 fix(ai): 流式回答零内容时不再返回空回答,并标记 EMPTY_COMPLETION 2 weeks ago
..
assets 重构客服角色管理页面排版 2 months ago
src fix(sdk): 流式零内容时降级路径未渲染答案,界面永久停在「正在思考...」 2 weeks ago
tests feat(sdk): SDK 内置 Token 自动刷新,支持每请求前向宿主取 Token 2 weeks ago
CLAUDE.md feat(sdk): SDK 内置 Token 自动刷新,支持每请求前向宿主取 Token 2 weeks ago
README.md fix(sdk): 流式零内容时降级路径未渲染答案,界面永久停在「正在思考...」 2 weeks ago
package-lock.json feat(client): SDK 界面重构改用 TDesign Web Components 组件 2 months ago
package.json feat(client): SDK 界面重构改用 TDesign Web Components 组件 2 months ago
rollup.config.js feat(client): SDK 界面重构改用 TDesign Web Components 组件 2 months ago
tsconfig.json SDK 新增 6 项功能完善(类型导出/窗口配置/错误回调/测试/分组/通知) 3 months ago
vitest.config.ts SDK 新增 6 项功能完善(类型导出/窗口配置/错误回调/测试/分组/通知) 3 months ago

README.md

ChatbotSDK — 前端对接文档

P0 核心链路 ✅ | P1 体验增强 ✅ | P2 运营完善 ✅ | 版本:1.4.0 | 更新日期:2026-09-24


一、快速开始

最简接入(3 行代码)

<script src="https://your-domain.com/sdk/chatbot-sdk.min.js"></script>
<script>
  ChatbotSDK.init({
    integrateId: 1,                  // 必传:客服角色 ID(对应后端 roleId)
    requestDomain: 'https://your-domain.com', // 必传:后端地址
  });
</script>

引入后在页面右下角出现悬浮客服按钮,点击即可对话。

完整配置接入

<script src="/sdk/chatbot-sdk.min.js"></script>
<script>
  ChatbotSDK.init({
    // 必传
    integrateId: 'hr-portal-v2',      // 客服角色 ID(对应后端 roleId)
    requestDomain: 'https://ai.example.com',

    // 用户身份
    userId: 'zhangsan',

    // 鉴权(推荐 —— 详见上文「Token 鉴权与长会话」)
    token: initialToken,           // 首次从宿主后端换到的 SDK Token
    expiresIn: 7200,               // 该 Token 的有效期(秒)
    tokenStrategy: 'expiry',       // 'expiry'(默认)临期/401 才取;'always' 每个请求前都取
    tokenUrl: '/my-backend/sdk-token',  // SDK 直接 GET 它取 Token(与 getToken 二选一)

    // RAG 知识库检索
    enableRag: true,               // 启用 RAG 增强对话(自动走 /ai/chat/stream,enableRag=true)
    categoryId: 5,                 // 默认知识库分类
    showCategorySwitch: true,      // 显示知识库分类下拉

    // UI 定制
    title: 'HR 智能助手',
    width: 420,
    position: 'right-bottom',
    primaryColor: '#2563EB',
    showClear: true,
    quickReplies: ['如何重置密码?', '报销流程是什么?', '查询我的年假余额'],
    theme: 'light',              // 主题:'light' | 'dark'
    showTeaser: true,            // 是否显示首访提示气泡

    // 行为
    streaming: true,
    locale: 'zh-CN',       // 多语言(P2):zh-CN / en
    debug: false,
  });
</script>

Token 鉴权与长会话(重要)

后端 /ai/**、/feedback、/attachment/upload 由 SDK JWT Token 保护,Token 默认有效期 2 小时。 第三方页面往往一挂就是一整天,因此 SDK 内置了自动刷新,接入方无需自己盯过期时间:

触发方式 时机 说明
临期主动刷新 剩余寿命不足「总寿命 10% 与 5 分钟中的较小值」 需传 expiresIn,用户完全无感
每请求前取 每个受保护请求发出前 需 tokenStrategy: 'always',完全不依赖 expiresIn
401 兜底重试 请求收到 401 时 换一次 Token 后自动重试原请求,UI 上不会闪错误
手动推送 宿主自行换好后调 ChatbotSDK.setToken() 不想暴露取 Token 逻辑时使用
手动拉取 宿主调 ChatbotSDK.refreshToken() SDK 去调 getToken / tokenUrl 立即换新

该选哪种:

场景 推荐
宿主能拿到可信的 expiresIn(如自己后端返回了 expiresIn) tokenStrategy: 'expiry'(默认)—— 只在临期与 401 时才取,往返最少
宿主后端已自行缓存 Token、SDK 拿不到可靠 expiresIn,或不想让 SDK 推算过期时间 tokenStrategy: 'always' —— 每次请求前都取一次,由宿主决定何时真的换 Token。代价是每个请求多一次宿主后端往返(含每次对话的首 token 之前),且不再使用 expiresIn
什么都不配 只靠 401 兜底:每次失效都要先白跑一个 401

取 Token 的动作由宿主提供,推荐在宿主自己的服务端完成(服务端持 API Key 调 POST /open-api/auth/token),前端只把结果交给 SDK —— 这样 API Key 永远不会进入浏览器。 两种提供方式二选一(同时配置时 getToken 优先,tokenUrl 被忽略并告警):

// 方式 1:getToken 回调(最灵活,可复用宿主已有的请求封装 / 鉴权头 / 重试)
getToken: async () => {
  const res = await fetch('/my-backend/sdk-token');
  const data = await res.json();
  return { token: data.token, expiresIn: data.expiresIn };
}

// 方式 2:tokenUrl 地址(零胶水代码,SDK 直接 GET 它)
tokenUrl: '/my-backend/sdk-token'
// 最简写法:只给 Token —— SDK 不会自动续期,令牌过期后需宿主自己调 setToken() 更新
ChatbotSDK.init({ integrateId: 1, requestDomain: 'https://ai.example.com', token: 'xxx' });

// 推荐写法:Token + 有效期 + 取 Token 方式 → SDK 自动续期
ChatbotSDK.init({
  integrateId: 1,
  requestDomain: 'https://ai.example.com',
  token: initialToken,
  expiresIn: 7200,                       // 单位「秒」,与后端 expiresIn 字段一致
  tokenStrategy: 'expiry',               // 默认值;想「每次请求前都取」就改 'always'
  tokenUrl: '/my-backend/sdk-token',     // 或改用 getToken 回调(二选一)
  onError: (e) => {
    // auth_expired        = 刷新后仍鉴权失败(Token 彻底失效,需检查 API Key / 角色绑定)
    // auth_refresh_failed = 取 Token 失败(网络或宿主后端异常)
    console.warn('SDK 错误', e.code, e.message);
  },
});

约定与边界:

  • 取 Token 的两种形式:getToken 回调(优先)、tokenUrl 地址。tokenUrl 会兼容 { token, expiresIn } 与 { success, token, expiresIn, roles } 两种响应。
  • tokenUrl 的 Cookie 行为:请求使用 credentials: 'same-origin' —— 同源(宿主页面调自己后端, 最常见)自动带 Cookie;跨域则不带,避免把宿主站点的 Cookie 泄露给别的域。需要跨域带 Cookie 的场景请改用 getToken 自己发请求。
  • 「返回对象 { token, expiresIn }」可让 SDK 提前主动刷新;只返回字符串则只能靠 401 驱动重试 (功能正常,只是首个失效请求会多一次往返)。tokenStrategy: 'always' 下 expiresIn 不参与判断。
  • expiresIn 单位是秒,只做 24 小时上界钳制(防误传毫秒),不做下界 —— 传 expiresIn: 20 会真的在 18 秒后刷新。
  • 取 Token 失败不会打断对话:沿用现有 Token 继续发请求(由 401 兜底),并进入 60 秒熔断窗口, 窗口内不再重复取(避免宿主端点挂死时每个请求都多等一次超时)。refreshToken() 可强制绕过该窗口。
  • Token 只存内存,不写 localStorage/sessionStorage。页面刷新后由宿主重新 init() 传入。
  • 刷新失败不会吞掉错误:onError 会带 auth_refresh_failed / auth_expired 上报,同一错误码 30 秒内只报一次。

集成到现有项目

SDK 产物位于 client/dist/ 目录:

文件 大小 说明
chatbot-sdk.js ~5.6MB 未压缩开发版(含 sourcemap)
chatbot-sdk.min.js ~4.0MB 压缩生产版(gzip ~1.1MB)

部署路径: 将 .min.js 文件上传到后端 static/sdk/ 目录或任意 CDN。SDK 内置 TDesign Web Components 组件库(t-chat-* + 通用组件),按需 tree-shaking 后打包进 IIFE,无需额外加载组件库 CDN。


二、SDKConfig 完整参数

参数 类型 必传 默认值 阶段 说明
integrateId string | number ✅ — P0 集成标识 → 后端 roleId(客服角色 ID,决定 AI 人设和知识库范围)
requestDomain string ✅ — P0 后端 API 域名
userId string ❌ — P0 宿主用户标识 → 后端 accountId
token string ❌ — P0 SDK JWT Token(从 /open-api/auth/token 换取)。不传则受保护的 /ai/** 接口会 401
expiresIn number ❌ — P0 token 的有效期(秒,与后端 expiresIn 同单位)。传入后 SDK 会在临期前主动刷新
getToken function ❌ — P0 取 Token 回调:SDK 在临期、每请求前(always 模式)或收到 401 时调用它。推荐返回 { token, expiresIn }。详见上文「Token 鉴权与长会话」
tokenStrategy 'expiry' | 'always' ❌ 'expiry' P0 取 Token 的时机:'expiry' = 临期 / 401 才取;'always' = 每个受保护请求前都取一次(此时 expiresIn 不参与判断,代价是每请求多一次宿主后端往返)
tokenUrl string ❌ — P0 取 Token 的地址(与 getToken 二选一,同时配置时 getToken 优先)。SDK 会 GET 它并解析 {token,expiresIn} 或 {success,token,expiresIn,roles};请求用 credentials: 'same-origin'
roles Array<{id,key?,name?}> ❌ — P0 可用客服角色列表(与 token 配套),决定角色选择器可选项
enableRag boolean ❌ true P1 启用 RAG 知识库检索对话(走 /ai/chat/stream 接口,enableRag=true)
categoryId number ❌ — P1 默认知识库分类
showCategorySwitch boolean ❌ false P1 是否显示知识库下拉切换
title string ❌ "AI 智能助手" P0 弹窗标题
width number ❌ 500 P0 弹窗宽度(px)
position string ❌ "right-bottom" P0 悬浮按钮位置
primaryColor string ❌ "#4F46E5" P0 主色调
launcherIcon string ❌ 极光粒子图标 P0 悬浮按钮图标(可传 URL 或 SVG 字符串),默认使用极光粒子动画图标
launcherSize number | string ❌ 80 P0 悬浮按钮尺寸:数字=px(范围 36~96),或 CSS 长度字符串如 "4rem"/"5vw"/"clamp(48px,3vw,80px)" 用于响应式适配
showClear boolean ❌ true P0 是否显示清空按钮
quickReplies string[] ❌ [] P1 欢迎态快捷问题芯片,点击即自动发送
theme string ❌ "light" P2 主题模式:"light" / "dark"
showTeaser boolean ❌ true P1 首访提示气泡(延迟 1.5s 弹出)
teaserText string ❌ i18n 默认 P1 提示气泡文字,留空使用语言包默认值
streaming boolean ❌ true P0 是否启用 SSE 流式输出
locale string ❌ "zh-CN" P2 界面语言:zh-CN / en
debug boolean ❌ true P0 是否输出调试日志

三、公开 API

方法 阶段 说明
ChatbotSDK.init(config) P0 初始化 SDK
ChatbotSDK.destroy() P0 销毁实例(清理 DOM + 样式 + 事件)
ChatbotSDK.open() P0 打开聊天窗口
ChatbotSDK.close() P0 关闭聊天窗口
ChatbotSDK.toggle() P0 切换窗口显示/隐藏
ChatbotSDK.clearHistory() P0 清空当前会话
ChatbotSDK.setToken(token, expiresIn?) P0 运行时更新 Token(宿主自行换好后推送),无需 destroy + init 重建 DOM
ChatbotSDK.refreshToken() P0 让 SDK 调 getToken 回调立即换新 Token,返回是否换到了不同的 Token

四、P1 体验增强功能

4.1 SSE 流式打字机效果

默认开启(streaming: true),AI 回复逐字输出,支持:

  • 流式追加到气泡,实时滚动到底部
  • 流中断兜底:保留已接收内容 + 灰色提示
  • 无流内容时自动降级为同步请求,并把拿到的答案正常渲染出来(同时上报 onError 的 stream_empty);用户点「停止生成」时不会再多发一次请求

4.2 Markdown 渲染

AI 回复支持 Markdown 格式渲染,包括:

  • 代码块(深色背景 ``` 语法)
  • 行内代码(`code`)
  • 标题(## ### 等)
  • 粗体(**text**)、斜体(*text*)、删除线(~~text~~)
  • 有序/无序列表
  • 链接([text](url),仅允许 http/https 协议)
  • 引用块(> )
  • 水平线(---)

XSS 安全:Markdown 渲染由 TDesign Chat 组件内置 cherry-markdown 处理,HTML 标签默认被转义,仅保留安全的 Markdown 语法输出。

实现说明:渲染由 TDesign Chat 组件(<t-chat-item>)内置 cherry-markdown 承担,无需额外加载 marked.min.js。SDK 不再依赖自研 Markdown 渲染器,列表、代码块、表格等样式由 TDesign 统一保证。

4.3 知识库联动

方式一:直接启用 RAG(无需分类选择)

ChatbotSDK.init({
  integrateId: 'my-app',
  requestDomain: 'https://ai.example.com',
  enableRag: true,              // 所有对话自动走 RAG 增强接口
});

方式二:配合知识库分类下拉框

开启 showCategorySwitch: true 后,输入区上方出现知识库分类下拉框:

ChatbotSDK.init({
  integrateId: 'my-app',
  requestDomain: 'https://ai.example.com',
  enableRag: true,              // 启用 RAG
  showCategorySwitch: true,     // 显示分类下拉框
  categoryId: 5,                // 默认选中分类
});
  • enableRag: true 时,所有对话自动走 RAG 增强(/ai/chat/stream 接口,enableRag=true)
  • 选择分类后,后续对话也走 RAG 增强
  • 选择「全部分类」则走普通流式(enableRag=false)
  • 分类数据从 /category/tree 接口动态加载,支持树形缩进显示

4.4 RAG 引用来源展示

使用 RAG 对话后,AI 气泡底部自动展示引用来源卡片:

┌─────────────────────────────────┐
│ 📚 3 条参考来源               ▼ │
├─────────────────────────────────┤
│ 员工手册.pdf                    │
│ 员工可以享受年假...            │
│ 员工手册.pdf · 分块 #3 · 85%   │
│                                 │
│ HR制度.md                       │
│ 年假计算规则如下...            │
│ HR制度.md · 分块 #1 · 72%      │
└─────────────────────────────────┘
  • 默认折叠,只显示标题行,点击展开/折叠
  • 显示文档名称、摘要、来源文件、分块编号、相关度
  • 来源数据从 /ai/chat/sources 接口获取

五、P2 运营完善功能

5.1 多语言国际化

支持 zh-CN(中文)和 en(英文)两种语言:

ChatbotSDK.init({
  integrateId: 'my-app',
  requestDomain: 'https://ai.example.com',
  locale: 'en',  // 英文界面
});

国际化覆盖范围:

  • 弹窗标题、输入框占位符、发送按钮
  • 最小化/关闭按钮提示
  • 清空对话按钮及确认弹窗
  • Loading 状态文字
  • 所有错误提示(网络/超时/服务器/跨域等)
  • 知识库下拉框默认选项
  • RAG 引用来源标题
  • 历史会话面板文本

5.2 会话管理面板

弹窗头部新增时钟图标按钮,点击展开历史会话面板:

  • 展示当前用户的历史会话列表(从 /conversation/list 接口获取)
  • 支持导出会话(下载文本文件)
  • 支持删除会话(二次确认)
  • 空状态提示

5.3 控制台日志体系

SDK 全流程结构化日志,带 [ChatbotSDK] 前缀:

生命周期 日志内容
init() 初始化完成 integrateId={} requestDomain={}
sendMessage() 发送消息 integrateId={} length={}
收到回复 AI 回复 integrateId={} length={} duration={}ms
流式完成 流式回复完成 integrateId={} length={} duration={}ms
请求失败 请求失败 integrateId={} status={} message={}
clearHistory() 清空会话 integrateId={}
destroy() 销毁实例 integrateId={}
分类切换 切换知识库分类 categoryId={}
  • config.debug = false 关闭 info/warn 日志,error 始终输出
  • 内置性能计时器,自动记录 AI 回复耗时

六、对接的后端接口

P0 — 基础对话

GET /ai/chat            # 同步对话
GET /ai/chat/stream     # SSE 流式对话

P1 — 知识库联动

GET /ai/chat/stream     # RAG 增强流式对话(enableRag=true)
GET /ai/chat/sources    # RAG 引用来源
GET /category/tree      # 分类树(下拉框数据源)
GET /category/list      # 分类列表

P2 — 会话管理

GET  /conversation/list                  # 会话列表
GET  /conversation/{id}/messages         # 会话消息
DELETE /conversation/{id}                # 删除会话
GET  /conversation/{id}/export           # 导出会话

七、参数映射关系

核心映射(SDK → 后端):

SDK 入参 后端参数 说明
integrateId roleId 客服角色 ID(决定 AI 人设和知识库检索范围)
userId accountId 客户账号 ID(账号可绑定角色,绑定后服务端覆盖 roleId)
(自动管理) chatId 对话 ID(自动从 /conversation/list 获取或生成,格式 sdk_时间戳_随机串)

chatId 自动管理逻辑:

  1. SDK 初始化时,查询 /conversation/list?accountId={userId}&roleId={integrateId}
  2. 有匹配会话 → 使用最新会话的 conversationId 作为 chatId
  3. 无匹配会话 → 自动生成新 chatId(格式:sdk_时间戳_随机串)
  4. chatId 缓存在 localStorage(key: csk_chatId_{integrateId}_{userId})
  5. clearHistory() 会生成新的 chatId,开始全新对话

八、CSS 命名空间隔离

所有 DOM 元素的 class/id 均使用 csk- 前缀,不会与宿主页面样式冲突:

元素 ID/Class
悬浮按钮 #csk-launcher .csk-launcher
聊天弹窗 #csk-window .csk-window
消息区 #csk-messages .csk-messages
输入框 #csk-input .csk-input
发送按钮 #csk-send-btn .csk-send-btn
知识库下拉 #csk-category-select .csk-category-select
历史面板 .csk-history-panel
样式标签 style[data-csk-sdk]

z-index 分层:悬浮按钮 9998,弹窗 9999。


九、localStorage 缓存

缓存 key 格式:csk_history_{integrateId}

数据结构:

{
  "messages": [
    {
      "id": "uuid",
      "role": "user|ai",
      "content": "消息内容",
      "timestamp": 1700000000000,
      "sources": []
    }
  ],
  "updatedAt": 1700000000000
}
  • 消息上限 200 条,超出自动裁剪最早 50 条
  • 按 integrateId 隔离,不同接入方互不影响
  • init() 时自动恢复历史消息
  • RAG 引用来源同步缓存到 sources 字段

注意:SDK Token 不在此列 —— Token 只存内存,不写 localStorage/sessionStorage,避免被 XSS 或同域其它页面读取。页面刷新后请由宿主重新 init() 传入(或依赖 getToken 回调自动重取)。


十、错误处理

所有错误不抛异常、不阻塞宿主页面。控制台日志已关闭,错误统一通过 init({ onError }) 回调上报:

ChatbotSDK.init({
  // ...
  onError: (e) => console.warn(e.code, e.message, e.detail),
});
错误场景 中文提示 英文提示
网络断开 网络连接失败,请检查网络 Network connection failed
请求超时 请求超时,请稍后重试 Request timed out
HTTP 500 服务器异常,请稍后重试 Server error
跨域阻断 跨域请求被拦截... CORS request blocked...
HTTP 401 鉴权失败,请联系管理员 Authentication failed
HTTP 403 无访问权限 Access denied
流中断 回复被中断 Response interrupted

onError 的 code 字段(除下表外,其余为通用错误码):

code 含义 建议处置
auth_expired 刷新后仍返回 401,Token 彻底失效 检查 API Key 是否被吊销/过期、是否绑定了启用中的客服角色;修好后调 setToken()
auth_refresh_failed 换 Token 的 getToken 回调失败(宿主后端异常或网络问题) 检查宿主换 Token 接口;同一 code 30 秒内只上报一次,不会刷屏
config_invalid init() 传入的 token / expiresIn 等参数非法,该参数被忽略 按提示修正配置
stream_empty 服务端 SSE 流返回了零内容(只收到开场帧与收尾帧),SDK 已自动降级为同步请求并渲染答案 答案仍会出来(会慢一次往返)。若频繁出现,查后端:该模型是否流式输出为空(典型是 provider 的流式分片文本未被正确提取)。见 SDK-INTEGRATION.md

十一、技术栈

  • 语言:TypeScript → ES2017
  • 打包:Rollup → IIFE 格式
  • 压缩:Terser
  • UI 组件:TDesign Web Components 1.2.10(t-chat-* 聊天组件 + t-select/t-tag/t-button/t-alert 等通用组件)
  • Markdown:由 TDesign Chat 组件内置 cherry-markdown 渲染
  • 国际化:内置 i18n 字典
  • 浏览器兼容:Chrome 70+, Firefox 70+, Safari 13+, Edge 79+
  • 产物大小:.min.js ~4.0MB(gzip ~1.1MB),体积主要由内置的 TDesign Web Components 组件库贡献(按需 tree-shaking 后)

十二、开发与构建

# 进入 SDK 工程目录
cd client/

# 安装依赖
npm install

# 构建(输出到 dist/)
npm run build

# 开发模式(watch)
npm run dev

源码结构:

client/
├── src/
│   ├── index.ts      # 入口:window.ChatbotSDK 挂载
│   ├── types.ts      # TypeScript 类型定义
│   ├── config.ts     # 配置解析 + 参数校验
│   ├── logger.ts     # 日志模块(P2 增强:计时+生命周期)
│   ├── storage.ts    # localStorage 封装
│   ├── api.ts        # HTTP 封装 + SSE 流式 + P1 RAG + P2 会话
│   ├── token.ts      # Token 管理:临期主动刷新 + 401 兜底重试 + setToken/refreshToken
│   ├── dom.ts        # DOM 构建 + 拖拽 + P1 来源/分类 + P2 面板
│   ├── styles.ts     # CSS 注入 + 主题(--csk-* 与 --td-* 变量透传)
│   ├── chat.ts       # 对话核心 + P1 RAG + P2 i18n(Markdown 由 t-chat-item 内置 cherry-markdown 处理)
│   ├── i18n.ts       # P2 多语言国际化
│   └── utils.ts      # UUID、防抖、XSS 转义
├── dist/             # 构建产物
├── package.json
├── tsconfig.json
├── rollup.config.js
└── README.md

十三、功能路线图

阶段 状态 内容
P0 核心链路 ✅ 已完成 项目骨架、配置校验、样式注入、悬浮按钮+弹窗、HTTP 通信、对话核心、本地缓存、打包构建
P1 体验增强 ✅ 已完成 SSE 流式打字机、Markdown 渲染、知识库下拉切换、RAG 引用来源展示、UI 全配置化、弹窗拖拽
P2 运营完善 ✅ 已完成 多语言国际化(zh-CN/en)、控制台日志体系、会话管理面板(列表/导出/删除)
Token 自动刷新 ✅ 已完成 临期主动刷新 + 401 兜底重试、setToken / refreshToken 手动入口(v1.3.0);tokenStrategy: 'always' 每请求前取 Token + tokenUrl 地址取 Token(v1.4.0)
可选扩展 🔜 待定 知识库管理嵌入、更多语言、主题皮肤

十四、验证测试

单元测试(vitest) —— 覆盖配置解析与 Token 管理(含刷新竞态、自激循环等难以手工复现的场景):

cd client/
npm test        # 等价于 npx vitest run

浏览器端验证 —— 访问 http://localhost:9090/sdk/test.html 运行核心用例:

测试覆盖(10 个核心用例):

编号 阶段 功能
T1-T6 P0 全局加载、参数校验、DOM 创建、open/close、destroy 清理、实际对话
T7-T9 P1 SSE 流式接口、RAG 引用来源、知识库分类切换
T10 P2 多语言国际化

Token 自动刷新的手工验证步骤(setToken 生效、401 自动重试、临期主动刷新、destroy 清理)见 根目录 SDK-INTEGRATION.md 的验证章节。