用Web Component封装AI表单填充SDK,老系统也能轻松接入 老读者应该知道我折腾前端组件化有些年头了。前阵子有个朋友找我说他们的业务系统里有几十个表单天天有客服在那手动复制粘贴用户信息想接 AI 自动填充又不想把现有页面推倒重来。这需求太典型了——传统表单接 AI最怕三件事侵入性太强、耦合太重、部署太麻烦。我第一反应就是 Web Component 那套原生方案自定义一个标签往里一嵌完事。这篇就手把手拆一下怎么用 Web Component 封装一个通用的 AI 表单填充 SDK附上源码思路和踩坑记录。这篇文章适合这几类人看想把 AI 能力快速接到老表单上的前端工程师、想给团队沉淀通用能力的前端负责人、以及正在调研“能不能不改造现有页面上 AI”的架构师。不需要你精通 Web Component但最好有点原生 JS 基础。我会从“为什么选这个方案”一路讲到“核心源码怎么拆”“实际效果怎么落地”最后把常见坑一次性摊开。1. 为什么偏要用 Web Component 来做这件事先回答一个灵魂拷问AI 表单填充又不是没人做过后端接口返回数据、前端 JS 一把梭也能填充为什么非要封装成 Web Component 形式的 SDK1.1 传统方案的三个硬伤先说最常见的方案 A直接写一段全局脚本页面加载后遍历表单调用 AI 接口拿到结果往 input 里 set value。逻辑很简单但它有三宗罪。第一全局脚本不是模块化的你在这个页面改了字段逻辑那个页面不知道第二它和页面生命周期绑得太死页面一改结构脚本就容易挂第三AI 接口的 prompt 构造、错误处理、重试逻辑全都混在一起后期加需求就是改 bug 现场。方案 B 是 iframe 嵌入。有些团队会做一个独立的 AI 填充工作台用 iframe 嵌到表单旁边。好处是隔离得彻底但坏处也很明显iframe 加载成本高、通信靠 postMessage 容易漏消息、样式和布局很难自适应而且很多企业系统有 CSP 限制iframe 直接打不开。方案 C 是框架组件比如用 React/Vue 封装。这在新建项目里没问题可一旦遇到老系统——jQuery 页面、后端直出的模板、甚至混合了多框架的应用——组件库根本塞不进去。你要在 React 项目里塞一个 Vue 组件那是给自己找罪受。1.2 Web Component 恰好补上了这些短板Web Component 是浏览器原生标准由三个技术拼成Custom Elements 自定义标签、Shadow DOM 隔离样式、template 模板复用。它最大的特点是“技术栈无关”——你封装好的组件就是一个普通标签任何页面里都能用不管底下是 React、Vue、jQuery 还是原生 JS。它本身就是 SDK 的理想形态用ai-form-fill一个标签代表整个填充能力开发者只需要在页面上写一句ai-form-fill endpointxxx api-keyxxx/ai-form-fill再调一个 fill() 方法就完成集成了。这种心智负担远小于读文档、写初始化代码、手动管理事件监听。而且 AI 表单填充这个场景天然适合“局部增强”。它不是要重写整个表单而是在现有表单上叠加一层智能能力。Web Component 的 Shadow DOM 能把自己内部的结构和样式隔离起来不污染原表单同时通过自定义事件对外通信比全局事件总线清晰得多。下面这个表格可以更直观地看对比方案跨框架能力样式隔离集成成本老项目适配通信复杂程度全局脚本中差低中低iframe 嵌入高好中中高框架组件React/Vue低中中差中Web Component SDK高好低好低1.3 为什么 AI 填充特别适合“开口子”再深入想一层AI 填充不是简单地把接口数据铺到表单上它需要“理解表单”。表单里哪个字段是姓名、哪个是地址、哪个是身份证号这些肉眼能看出来的东西程序却需要靠 name、label、placeholder 反推。这意味着填充逻辑本身就需要一套独立的上下文感知机制。这段机制如果写在业务页面里每个页面都要复制一遍如果抽成独立 SDK就能沉淀成一个“表单理解中间层”。SDK 同时暴露一个干净的接口给页面——传页面上的表单节点它就负责找字段、构造上下文、请求 AI、回填结果。这就是服务化思想把脏活累活收进内部对外只给一个动作。所以从架构角度Web Component 是这类“局部智能增强型 SDK”的最优解之一既是稳定的容器又保持页面的绝对主导权。2. 动手前先理清Web Component 的四个关键点你如果没系统用过 Web Component我建议直接看这一节。这四件事 90% 的源码思路都建立在这上面。2.1 Custom Elements你怎么定义一个新标签Custom Elements 的核心是customElements.define()。你用class extends HTMLElement定义一个类里面实现生命周期回调然后注册到浏览器里就能在任何 HTML 里使用这个标签。生命周期里有几个关键函数connectedCallback组件被插入文档时触发适合做初始化、解析属性、绑定事件。disconnectedCallback组件被移除时触发适合清理解绑定时器、取消请求。attributeChangedCallbackobservedAttributes里列出的属性发生变化时触发适合响应式更新。adoptedCallback组件被移动到新文档时触发实际中较少用到。对 AI 填充 SDK 来说connectedCallback是核心——这里要把表单上下文扫描一遍方便后续 fill() 调用时快速匹配合适的字段。disconnectedCallback里要 AbortController 掉进行中的 AI 请求防止页面都关闭了请求还在跑。外部不需要知道你的类名只需要知道标签名。这就像你在 HTML 里新发明了一个元素但它在所有现代浏览器里都合法。2.2 Shadow DOM样式隔离与事件穿透的秘密Shadow DOM 给组件提供了一片“封闭的 DOM 树开关”。这棵树里的样式外面看不到外面的样式也钻不进来。因此你做填充反馈动画、状态提示条时怎么改样式都不用害怕和外层页面冲突。“事件穿透”是你最容易踩的坑之一。Shadow DOM 里触发的自定义事件默认是出得去的因为事件的 composed 属性默认为 true 时能跨过 Shadow DOM 边界但前提是你要给事件设置composed: true。如果忘了这个外层页面监听这个事件就永远收不到。我第一次写的时候就被坑过事件明明触发了外部页面怎么都监听不到查了半天才发现是composed的问题。此外Shadow DOM 里的 input、button 等控件参与外层表单提交的方式也有讲究。注意Shadow DOM 内的表单控件默认不属于外层的form你如果只是填充而不走原生表单提交问题不大但如果你希望填充后表单校验自动触发就得往原页面字段上派发input和change事件想办法让原表单感知到变化。2.3 template 和 slot内部的模板骨架template标签存放组件的内部骨架浏览器不会渲染它只有在被 clone 到 Shadow DOM 里时才生效。slot则是插槽允许外部往里塞内容——比如自定义一个“AI 填充中”的动画效果或者放一个手动触发按钮。实际封装里模板一般包含三块状态提示条请求中、成功、失败、填充进度指示器、以及一个可选的触发按钮。如果你把触发按钮也做成插槽页面甚至能把 SDK 的启动按钮放在任意位置通过事件方式触发填充。2.4 属性、方法、事件三角色组件和外界的互动靠“属性 方法 事件”三个角色。属性定义配置endpoint、model、api-key、selector等通过attributeChangedCallback响应变化。方法定义行为fill()启动填充、fillFrom(data)从一个已有的 JSON 数据源直接填充、reset()清空填充痕迹。事件定义通知fill-start、fill-progress、fill-success、fill-error、fill-ready都是 CustomEvent。这种“三角色”模型应该被严格遵循外部不要直接访问组件内部树组件不依赖外部任何全局变量。边界越清楚维护越省心。3. SDK 整体架构设计它到底该管什么不该管什么我见过很多失败的封装根本原因都是“管太宽”。一个 AI 表单填充 SDK如果连弹窗、按钮颜色、水印、UI 文案都管了那它很快就变成一套笨重的 UI 框架跟老系统打架。3.1 功能边界只做增强不重新造表单这个 SDK 的边界应该定义为它不渲染业务表单只在现有表单上做“感知 决策 填充 反馈”。具体来说感知自动扫描表单上下文找出候选字段及标签、placeholder、name、id。决策把字段上下文、用户输入的部分数据、必要的业务提示拼成 AI 请求交给服务端。填充把 AI 返回的结果按字段映射规则填回控件。反馈把填充状态用事件和状态条通知给页面和用户。它不该管的是用户权限认证、表单校验策略、业务数据存储。这些留在你的业务系统里SDK 只通过endpoint和api-key这种配置拿调用权限即可。这个边界看起来是取舍实际上是安全和可维护性的关键。把权限留在外部意味着即使 SDK 开源泄漏也不会直接暴露核心业务数据。3.2 配置与数据流静态配置、动态调用、实时事件一份配置文件会包含以下关键模板{ endpoint: https://api.example.com/ai/form-fill, model: form-fill-v2, apiKey: sk-xxx, selector: #billing-form, mapping: { customer-name: name.legal_name, customer-phone: contact.mobile, customer-address: address.full }, exclude: [csrf-token, captcha] }endpointAI 服务接口地址SDK 不做任何转发。model按需选择模型或服务版本。selector指定要填充的表单范围默认取组件所在文档的第一个 form。mapping把表单字段名映射到 AI 返回的 JSON 结构路径上这个字段是真正的“字段理解层”。exclude排除不需要填充的隐藏字段、令牌字段、验证码字段。数据流大概是用户触发 fill()→ SDK 读配置 → 采集表单上下文 → 组装请求 → 调用 AI 服务 → 解析返回 → 按 mapping 一一回填 → 触发事件。3.3 状态机设计别让组件在混乱的状态里翻车AI 请求有不确定性所以 SDK 内部要有清晰状态机。我用的状态是idle就绪 →preparing扫描表单 →fetching请求 AI →filling回填中 →done或error拿状态机管理能防住一个经典 bug用户连续点击多次 fill() 导致重复请求。SDK 内部只要state fetching时忽略新请求即可。另一个作用是状态条 UI 可以根据状态自动切换外部也能通过getState()方法查询。状态机的实现不复杂核心是一个setState(state)方法每次切换时更新内部状态、渲染 UI、广播事件。3.4 事件协议对外唯一可信的通知通道建议把事件协议设计成以下样子事件名触发时机detail 载荷fill-ready初始化完成后可立即调用 fill(){ state: ready }fill-start开始请求 AI{ fields: 字段列表 }fill-progress流式返回阶段逐段更新{ partial: ... }fill-success填充完成{ filledFields: [name,phone], result: {...} }fill-error填充失败{ code: AI_TIMEOUT, message: ... }fill-reset数据被清空或重置{ fields: [...] }事件命名用“动作结果”语义清晰。外部接的时候只用监听这几个事件就行不用管内部是怎么实现的。4. 核心源码思路拆解从类骨架到字段理解与智能填充前面是设计这里是真正的“源码思路”。我习惯用 TypeScript 编写最后打包成 ES Module 和 IIFE 两种格式。代码思路比最终代码更重要所以我会把关键逻辑切片讲清楚。4.1 类结构与生命周期模板先看整体骨架export class AIFormFill extends HTMLElement { static get observedAttributes() { return [endpoint, model, api-key, selector, mapping]; } private state: SDKState idle; private controller: AbortController | null null; private root: ShadowRoot; private fieldRegistry: FieldContext[] []; constructor() { super(); this.root this.attachShadow({ mode: open }); } connectedCallback(): void { this.render(); this.scanForm(); this.dispatch(fill-ready, { state: this.state }); } disconnectedCallback(): void { this.controller?.abort(); } attributeChangedCallback(name: string, oldVal: string, newVal: string): void { if (oldVal newVal) return; // 配置变化时重新扫描表单 if (name selector || name mapping) { this.scanForm(); } if (name endpoint || name api-key) { this.controller?.abort(); this.setState(idle); } } }几个关键设计shadowRoot用open模式方便调试工具检查内部状态。attributeChangedCallback监听配置变化selector 变了就重新扫描表单字段。disconnectedCallback里要abort()这是防止请求泄漏的兜底。不要依赖构造函数做初始化组件插入文档前属性可能还没就绪connectedCallback才是安全时机。4.2 表单字段采集它怎么“理解”一个表单AI 填充的前提是理解表单语义。SDK 需要扫描目标表单把它变成结构化上下文。每一步都得考虑哪些字段是必要的、哪些要排除、怎么把 label 和 placeholder 转化成 AI 能读懂的提示。interface FieldContext { name: string; type: string; // text / select / checkbox / radio / date ... label?: string; placeholder?: string; value: string; required?: boolean; options?: string[]; // 对 select、radio 而言 } function scanForm(form: HTMLFormElement | HTMLElement): FieldContext[] { const fields: FieldContext[] []; const nodes form.querySelectorAllHTMLElement( input, select, textarea ); nodes.forEach((node) { const input node as HTMLInputElement; if (input.disabled || input.readOnly) return; if (excludeSet.has(input.name || input.id)) return; fields.push({ name: input.name || input.id || extractInputName(node), type: getControlType(input), label: findLabel(input), placeholder: input.getAttribute(placeholder) || , value: input.value || , required: input.hasAttribute(required), options: extractOptions(input) }); }); return dedupe(fields); }字段采集有几个容易漏的细节排除 tokens:所有typehidden且 name 里含token、csrf、captcha的字段默认排除因为这种字段填充了反而坏事。label 识别:优先用label for...次选aria-label再不行就用包裹它的 label 文本。老系统里 label 经常和 input 不在同一个容器里得向上找两层。select 选项:采集 select 的 option 文本因为 AI 要判断返回的值合不合法避免填出不存在选项。采集成功后这些上下文会存储到fieldRegistry里供 fill() 调用。4.3 组装请求与调用 AI 服务的完整链路填充时SDK 要把上下文、用户预设信息、以及一条业务提示语拼成一个请求体。async fill(preset?: Recordstring, unknown): Promisevoid { if (this.state fetching) return; this.setState(preparing); const snapshot this.fieldRegistry.map((f) ({ name: f.name, label: f.label, type: f.type, placeholder: f.placeholder, value: f.value, required: f.required, options: f.options || [] })); const payload { model: this.model, prompt: 请根据以下表单上下文和用户信息用JSON返回需要填充的值。, formContext: snapshot, presetData: preset || {}, mappingRule: this.mappingRule }; this.setState(fetching); const result await this.requestAI(payload); this.performFill(result); }requestAI里用 fetch 加 AbortControllerprivate async requestAI(payload: unknown): PromiseFillResult { const controller new AbortController(); this.controller controller; const timeout setTimeout(() controller.abort(), 15000); try { const res await fetch(this.endpoint, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${this.apiKey} }, body: JSON.stringify(payload), signal: controller.signal }); if (!res.ok) { throw new Error(AI request failed: ${res.status}); } const data await res.json(); return normalizeFillResult(data); } finally { clearTimeout(timeout); this.controller null; } }这里有几个判断值得展开超时机制15 秒是一个合理默认值因为常规表单填充不是复杂对话15 秒足够如果超时主动 abort避免连接挂到天荒地老。Authorization 头这是最常见的转发方式。但注意如果 key 在前端暴露有安全顾虑建议让后端网关把请求转发给 AI 服务SDK 只调自家后端接口后端再调 AI。返回结构约定AI 返回的 JSON 必须是一个{ result: { fieldName: value } }结构这个约定要在 prompt 里跟 AI 讲清楚并在 SDK 里做一个校验函数字段名不在 mapping 规则里就丢弃。4.4 回填策略不是所有控件都 set value 那么简单拿到 AI 返回结果后真正的“手工活”才刚开始——要把数据填回不同控件。不同控件的填充方式完全不同必须分类型处理。function performFill(result: FillResult): void { const fields result.values || {}; const filled: string[] []; Object.entries(fields).forEach(([fieldName, value]) { const ctrl this.fieldRegistry.find( (f) f.name fieldName || this.mappingRule[fieldName] f.name ); if (!ctrl) return; const element document.querySelector([name${ctrl.name}]); if (!element) return; switch (ctrl.type) { case text: case email: case tel: case date: case textarea: setNativeValue(element, value as string); break; case select: fillSelect(element, value as string); break; case radio: setRadioByLabel(element, value as string); break; case checkbox: setCheckboxByValue(element, value as string); break; default: setNativeValue(element, value as string); } filled.push(fieldName); }); this.dispatch(fill-success, { filledFields: filled, result }); }setNativeValue的实现很关键因为直接el.value v不会触发 React/Vue 的受控组件更新。必须用原生 value setter 绕过框架拦截function setNativeValue(element: Element, value: string) { const proto Object.getPrototypeOf(element) as HTMLInputElement; const desc Object.getOwnPropertyDescriptor(proto, value); desc?.set?.call(element, value); element.dispatchEvent(new Event(input, { bubbles: true })); element.dispatchEvent(new Event(change, { bubbles: true })); }这是专门给受控组件用的招。直接赋值在现代前端框架里十有八九无效事件派发也不够必须把 setter 找出来调用。我在多个框架项目里验证过这个技巧有效。select 的填充还要考虑 option 匹配如果返回的中文文本和 option label 一致直接选中不一致建议用模糊匹配比如包含关系作为兜底。radio 同理。4.5 错误处理与降级策略AI 不稳定是常态错误处理必须分级。我的分层是错误类型处理方式触发事件网络层错误自动重试1次间隔2秒fill-error请求超时提示用户稍后重试不阻塞页面fill-errorAI 返回格式错误尝试提取带内容否则判定失败fill-error字段不匹配忽略但列出未识别字段fill-warning权限/鉴权失败不重试提示检查配置fill-error降级策略也很重要如果 AI 服务临时不可用保留用户手动填写的入口绝对不能让填充组件“卡死”用户操作。4.6 安全与隐私这个东西不能不提AI 表单填充一定会把字段上下文发给服务端隐私红线在于默认不采集密码、验证码、信用卡号如果你的表单里有这些字段宁可只发 name 和 label也绝不发 value。脱敏策略常见的有身份证、手机号、邮箱这类字段只给 AI 发标签、不发当前值或者只发打码后的值。数据保留期限建议在服务端做日志策略时明确客户端不落库。export 到日志SDK 可以提供一个logMasked()方法给外部自己决定怎么记录日志。这节是给团队做安全评审用的。安全能力不是上线后补丁而是 SDK 架构的一部分。5. 实操落地从零到一构建一个可复现的演示 SDK这里开始进入“附源码思路”的正题。我给出一个最小可复现工程你可以直接抄作业。5.1 项目结构与依赖项目用 Vite 做开发服务器和打包用 TypeScript 编写逻辑。目录结构ai-form-fill/ ├── index.html # 演示页面 ├── package.json ├── tsconfig.json └── src/ ├── ai-form-fill.ts # 核心组件 ├── field-scanner.ts # 字段扫描 ├── ai-client.ts # AI 请求客户端 ├── filler.ts # 回填逻辑 └── index.ts # 注册出口依赖项尽量少。不需要任何框架库Web Component 本身就是原生协议runtime 零依赖这个亮点一定要保持住。5.2 核心注册与打包出口src/index.ts的定义与注册import { AIFormFill } from ./ai-form-fill; if (!customElements.get(ai-form-fill)) { customElements.define(ai-form-fill, AIFormFill); } export default AIFormFill;判断customElements.get是个好习惯防止重复注册引发NotSupportedError。打包配置上Vite 的lib模式可以同时输出ES 模块格式dist/ai-form-fill.es.jsIIFE 格式dist/ai-form-fill.global.js方便老系统直接script引入文件名用ai-form-fill.global.js比较好语义清楚。5.3 演示页面一个带受控表单的 HTML做一个经典的演示页里面是一个老系统常见的纯 HTML 表单甚至是一个模拟 React 受控输入的场景!DOCTYPE html html head meta charsetUTF-8 / titleWeb Component AI 表单填充 Demo/title style .form-item { margin-bottom: 16px; } label { display: block; margin-bottom: 4px; font-weight: 600; } input, select { width: 320px; padding: 8px; } /style /head body form idorder-form div classform-item label forcustomer-name classfield-label客户姓名/label input idcustomer-name namecustomer-name placeholder例如张三 / /div div classform-item label forcustomer-mobile classfield-label手机号/label input idcustomer-mobile namecustomer-mobile placeholder例如13800138000 / /div div classform-item label forcustomer-address classfield-label收货地址/label input idcustomer-address namecustomer-address placeholder省市区街道 / /div div classform-item label fordelivery-time期望送达时间/label select iddelivery-time namedelivery-time option value请选择/option option valuemorning上午 9:00-12:00/option option valueafternoon下午 14:00-18:00/option option valueevening晚上 18:00-21:00/option /select /div /form ai-form-fill endpointhttps://api.example.com/ai/fill modelform-fill-v2 api-keyYOUR_KEY selector#order-form /ai-form-fill button idfill-btnAI 一键填充/button script typemodule import ./src/index.ts; const fillEl document.querySelector(ai-form-fill); document.getElementById(fill-btn).addEventListener(click, () { fillEl.fill({ /* 预设上下文 */ }); }); fillEl.addEventListener(fill-success, (e) { console.log(填充成功字段, e.detail.filledFields); }); fillEl.addEventListener(fill-error, (e) { console.error(填充失败, e.detail); }); /script /body /html这个演示的核心就是页面完全不知道 AI 是怎么调用的它只知道点按钮 → 调用 fill() → 得到 fill-success/fill-error 事件。5.4 接入老系统的三种姿势发布后不同系统有不同的接入姿势现代工程npm install在入口文件import ai-form-fill/dist/ai-form-fill.es.js然后页面直接写标签。老模板系统直接script src/path/ai-form-fill.global.js/script然后写标签。低代码平台有些平台支持自定义控件把这个标签注册进去配置 endpoint 和 mapping 即可。这三种姿势本质都是把 SDK 当作“黑盒增强器”。你不需要理解 AI 服务端怎么写的只需要理解“填了配置、看事件”这个模型。6. 常见问题与排查技巧实录这里记录几个真实项目中我和朋友遇到的问题都是文档里不会写的血泪坑。6.1 Shadow DOM 事件为什么监听不到症状外部页面addEventListener(fill-success)没有触发。排查思路检查自定义事件的bubbles和composed属性。很多新手构造 CustomEvent 时忘记写this.dispatchEvent(new CustomEvent(fill-success, { detail: payload, bubbles: true, composed: true }));composed: true是关键——它让事件能够穿越 Shadow DOM 边界进入外层 DOM。这一点我在前面已经强调但值得再次标注所有从 Shadow DOM 内部发出的 CustomEvent都建议显式设置composed: true。6.2 受控表单填充后刷新值但 UI 没更新症状明明 element.value 变了但页面的 React/Vue 数据模型还是旧值提交时是空。这个 90% 是框架拦截了 value setter。解决方案就是我在 4.4 节写的setNativeValue绕过框架直接调用原生 setter再派发 input 和 change 事件。这是整个 SDK 里最核心的一段兼容代码建议升级为独立工具函数并加注释。6.3 页面有 CSP 限制外部脚本加载不了这种问题多见于银行、政务类系统。CSP 通常会限制script-src你必须把 SDK 的脚本域名加入白名单。此外如果 CSP 里限制了connect-srcSDK 请求 AI 接口也会被拦。解决方案是请求域名加入白名单或者让后端网关转发同源请求。推荐后者因为网关转发还能隐藏企业内部的 AI 服务结构。6.4 AI 返回值和表单选项对不上比如 AI 返回“上午”但 select 的 value 是morning。我的建议是这样的三级匹配精确匹配 value 值。option 的 text 文本包含或等于返回文本。使用自定义映射字典例如{ 上午: morning }。不建议用模型去猜 value容易出现幻觉。在 mappingRule 里直接配好这种字典比让 AI 硬猜稳得多。6.5 选择器定位不准有的老系统一个页面有多个表单selector 不写导致 SDK 挂到了第一个 form 上。解决selector 支持三种形态CSS 选择器、HTMLFormElement 实例、以及auto默认找组件最近的祖先 form。监听方式我用的是document.querySelector加document.getElementById兜底适配老系统里五花八门的 ID。6.6 多实例冲突两个页面分别用两个不同配置的ai-form-fill会不会串不会每个实例的 state 和 controller 都是实例字段互不干扰。唯一要留心的是scanForm里如果用document.querySelector扫全局可能把别的表单也扫进来。所以scanForm必须严格限制在selector指定的表单范围。7. 从能用到好用扩展方向与我的个人体会核心 SDK 跑通以后有几个很值得扩展的方向也是我在真实项目里验证过的价值增量。7.1 流式填充与多模态支持如果 AI 服务支持 SSEServer-Sent EventsSDK 可以在fetching阶段监听流式输出实现“一边生成一边填”的体验。这在长表单上观感提升非常明显用户能看到姓名先填上地址一步一步出来。实现核心是改造requestAI用fetch读ReadableStream逐段解析 JSON再调用performFill处理部分结果。注意流式情况下字段间有依赖关系时比如先填城市再根据城市填区县需要声明依赖顺序不然会填得很乱。多模态支持则是指如果 AI 能直接接收截图SDK 可以允许用户拍一张手写表格照片AI 识别后填充。但这种模式对端侧算力要求高更合理的是只做“图片上传 → 服务端 OCRLLM → 返回填充结果”链路。7.2 云端配置中心一旦 SDK 接入多个系统字段映射、prompt 模板、版本选择如果都写在前端属性里改动会很痛苦。可以做一个云端配置中心SDK 只配置一个config-id启动时拉取并缓存配置。这样全公司几十个系统统一改一处配置就能全部生效。这个思路非常适合团队沉淀“智能表单能力中台”。7.3 表单校验联动填充完不等于填对了。SDK 可以暴露一个validateMappedFields()方法对 AI 返回的每个值做基础校验邮箱格式、手机号位数、必填字段是否为空。一旦发现问题可以在状态条里回显“手机号格式看起来不对请确认”。这能在体验上堵住 AI 幻觉的最后一道闸。7.4 我的个人体会最后分享一点真实感受。封装这类 SDK最难的不是 Web Component 的 API 用法而是“克制”。我刚开始写的时候总想把 loading 动画、成功弹窗、AI 推荐理由展示全塞进去结果项目越写越重接入方怨声载道。后来我把 UI 部分几乎砍光只保留一条 2 像素高的状态条和事件协议反而接入量翻了三倍——因为老系统的团队终于觉得“这玩意不会摔碎我的布局”。另外就是“AI 当服务”的思路SDK 里不要写任何具体业务 prompt只把表单上下文结构化交给服务端让服务端去拼 prompt。这样可以随时换模型、调策略SDK 几乎不用发版。我自己见过太多团队把 prompt 写死在前端最后模型一升级全乱套。还有一个经验这类 SDK 一定要有一个“演示盒子”最好是一个 800 行的 HTML 单文件包含常见字段类型和模拟接口。因为团队评审和试用时花三分钟看一个能跑的 demo比看一小时文档都管用。我每次交付这类项目都会把这个最小 demo 带在包里谁问就直接发过去。如果你也想在团队里推这件事我的建议是从一个非核心但痛点明显的表单开始比如订单备注补充、客户信息完善、地址标准化这种低频但价值高的场景。验证跑通之后再往更多系统铺开。技术选型不是越大越复杂越好能让一个老系统三天内接入 AI 能力本身就是这个方案最大的价值。