
1. 从一份表单说起为什么我们需要一个Schema生成Agent做过中后台系统的朋友大概率都经历过这样的场景产品经理甩过来一份需求文档里面密密麻麻列了几十个字段每个字段的类型、校验规则、联动逻辑、默认值、是否必填都写得清清楚楚。你打开编辑器开始一个字段一个字段地手写JSON Schema或者表单配置。写了半小时眼睛花了手也酸了结果测试的时候发现某个字段的maxLength写成了maxlength某个嵌套对象的required数组漏了一个字段某个枚举值的enum和enumNames顺序对不上。然后就是一轮又一轮的调试和返工。这个场景太常见了。表单开发本质上是一个高重复、低创造性、但极易出错的活儿。字段定义、校验规则、联动逻辑这些东西逻辑上并不复杂但量大、琐碎、容错率低。一个中等规模的表单五六十个字段手写Schema加上调试大半天就没了。而且这种工作很难沉淀——下次换个项目字段变了又得从头来一遍。FormAgent这个项目要解决的就是这个问题。它的核心思路很直接既然表单Schema本质上是一种结构化的描述那能不能让机器来生成输入一份自然语言描述或者一份非结构化的字段清单输出一份规范、可直接使用的Schema。这就是FormAgent的定位——一个专注于Schema生成的智能代理。这篇文章我会从设计思路、核心技术点、实操流程、踩坑经验几个维度把FormAgent这个项目拆开来讲。适合谁看如果你正在做低代码平台、中后台表单引擎、或者任何需要批量处理Schema的场景这篇内容应该能给你一些可以直接抄作业的思路。如果你只是好奇Schema生成这件事到底怎么做才靠谱也可以往下看我会尽量用大白话把里面的门道讲清楚。2. 整体设计思路为什么是Agent而不是模板引擎2.1 从模板引擎到智能生成的演进逻辑最早做Schema生成大家用的都是模板引擎的路子。定义一套模板字段名、类型、校验规则用占位符替换跑一遍脚本就出来了。这种方式在字段结构高度一致的场景下很好用比如你有一百个结构相同的CRUD表单只是字段名不同那模板引擎确实能解决问题。但现实情况往往没这么规整。字段类型是混合的——有字符串、有数字、有日期、有枚举、有嵌套对象、有数组。校验规则是多样的——有的字段必填有的字段有长度限制有的字段有正则约束有的字段依赖其他字段的值做联动。字段之间的关系也是复杂的——A字段的值决定了B字段是否显示C字段和D字段不能同时为空。这些逻辑用模板引擎表达起来非常吃力模板会变得极其臃肿维护成本反而更高。FormAgent选择用Agent的方式来做核心考量就是Schema生成这件事本质上是一个理解意图结构化输出的任务而不是一个填空替换的任务。自然语言描述里包含了大量的隐含信息比如用户年龄这个字段有经验的开发者知道它应该是数字类型、有合理的范围限制、可能还需要考虑是否必填。这些隐含信息模板引擎捕捉不到但一个设计良好的Agent可以。2.2 核心架构拆解输入解析、意图理解、Schema组装FormAgent的整体架构可以分成三层。第一层是输入解析层负责把各种形式的输入统一成结构化的中间表示。输入可能是自然语言描述、可能是Excel表格、可能是数据库表结构、也可能是另一份不规范的JSON。这一层的任务是把这些异构输入翻译成统一的字段描述列表。第二层是意图理解层这是Agent的核心。它要做的不仅仅是识别字段名和类型还要理解字段之间的语义关系。比如开始时间和结束时间这两个字段有经验的开发者知道它们之间应该有一个结束时间不能早于开始时间的校验规则。这种规则在原始输入里可能没有明确写出来但Agent应该能够推断出来并自动补全。第三层是Schema组装层负责把理解后的字段信息组装成目标格式的Schema。这里涉及到格式适配的问题——不同的表单引擎用的Schema格式不一样有的用JSON Schema标准有的用自定义的配置格式。组装层需要根据目标格式做转换同时保证生成的Schema是合法的、可用的。2.3 为什么选择这样的技术路线选择Agent路线而不是纯规则引擎还有一个很重要的原因可扩展性。规则引擎的问题是每遇到一种新的字段类型或者新的校验规则就得加一条规则。规则越加越多系统越来越复杂最后变成一团乱麻。而Agent的方式是把理解和生成分开理解层用模型来做生成层用规则来做。模型负责处理多样性和模糊性规则负责保证输出的规范性和准确性。这样分工之后系统的扩展性就好很多——新增一种字段类型只需要在生成层加一个模板理解层不需要大改。另外一个考量是容错性。纯规则引擎遇到没见过的输入模式很容易直接报错或者生成错误的Schema。Agent的方式可以通过模型的泛化能力对不规范的输入做一定程度的容错处理。比如输入里写的是年龄岁模型能理解这指的是年龄字段单位是岁而不是把岁当成字段名的一部分。3. 核心细节解析Schema生成的关键技术点3.1 字段类型推断从看起来像到确实是字段类型推断是Schema生成的第一步也是最基础的一步。看起来很简单——姓名是字符串年龄是数字生日是日期。但实际操作中边界情况非常多。比如手机号这个字段它本质上是字符串还是数字从语义上讲手机号是一串数字但它不应该被当成数字类型处理因为手机号不参与数学运算而且前导零的问题会让数字类型丢失信息。再比如金额字段它应该是数字类型但需要考虑精度问题——用浮点数还是用定点数如果目标Schema支持multipleOf或者precision这样的约束就需要在生成时加上。FormAgent在类型推断这块采用了一个语义优先格式校验的策略。首先通过字段名和描述做语义推断得到一个候选类型列表。然后通过字段的示例值或者格式描述做二次校验确定最终类型。比如字段名是创建时间描述里写了格式YYYY-MM-DD HH:mm:ss那类型就确定为string格式为date-time。如果描述里写的是时间戳那类型就是integer或者number。这里有一个实操中很容易踩的坑不要把看起来像数字的字段一律推断为数字类型。像身份证号、银行卡号、手机号、订单号这类字段虽然内容都是数字但都应该用字符串类型。判断标准很简单——这个字段会不会参与数学运算不会的话就用字符串。3.2 校验规则提取从自然语言到结构化约束校验规则的提取是Schema生成中最考验理解能力的部分。自然语言描述里的校验规则往往不是显式写出来的而是隐含在描述里的。比如用户名字段描述是4到20个字符只能包含字母和数字。这里隐含了三个校验规则最小长度4、最大长度20、正则表达式^[a-zA-Z0-9]$。Agent需要把这些自然语言描述转换成结构化的约束。再比如确认密码字段描述是必须与密码字段一致。这是一个跨字段的校验规则在JSON Schema里需要用const或者自定义关键字来表达。Agent需要识别出这是一个字段间的依赖关系并生成相应的约束。FormAgent在处理校验规则时采用了一个规则库模型补全的方式。规则库覆盖了常见的校验模式比如长度限制、数值范围、正则匹配、枚举值、必填等。模型负责处理规则库覆盖不到的情况以及从模糊描述中提取规则。两者结合既保证了常见场景的准确性又保证了特殊场景的覆盖率。实操心得校验规则的提取有一个很容易忽略的点——默认值的处理。很多字段在描述里没有明确写默认值但从业务逻辑上应该有一个合理的默认值。比如状态字段默认值通常是启用或者草稿。Agent在生成Schema时应该根据字段的语义推断一个合理的默认值而不是留空。留空的话表单初始化时会出现未定义的状态容易引发bug。3.3 字段联动与依赖关系的处理字段联动是表单开发中最复杂的部分也是Schema生成中最难自动化的部分。联动关系通常有三种显示联动A字段的值决定B字段是否显示、可选联动A字段的值决定B字段是否必填、值联动A字段的值决定B字段的可选值范围。在自然语言描述里联动关系往往描述得很模糊。比如如果用户选择了企业类型则需要填写企业名称和统一社会信用代码。这句话里隐含了一个显示联动和一个可选联动。Agent需要识别出如果...则...这样的条件结构并把它转换成Schema里的条件约束。JSON Schema标准里条件约束用if-then-else来表达。但很多表单引擎不支持这个标准而是用自定义的联动配置。FormAgent的做法是在中间表示层用统一的条件结构来描述联动关系然后在组装层根据目标格式做转换。这样不管目标格式是什么联动关系的表达都是一致的。3.4 嵌套结构与数组的处理策略嵌套结构和数组是Schema生成中容易出错的地方。嵌套对象需要处理层级关系数组需要处理元素类型和长度约束。比如联系人列表字段它是一个数组每个元素是一个对象包含姓名、电话、关系三个子字段。在Schema里这需要用type: array加上items来描述。如果数组有长度限制比如最多添加5个联系人还需要加上maxItems: 5。嵌套结构的处理难点在于层级关系的维护。如果输入是扁平的字段列表Agent需要根据字段名的命名规则或者描述里的层级信息把扁平结构还原成嵌套结构。比如联系人[0].姓名、联系人[0].电话这样的字段名需要被识别为同一个数组元素下的子字段。4. 实操过程从零搭建一个Schema生成流程4.1 输入准备什么样的输入质量决定什么样的输出质量Schema生成的质量很大程度上取决于输入的质量。输入越规范生成的Schema越准确。但现实情况是输入往往是不规范的。所以实操的第一步是尽可能把输入整理得规范一些。如果输入是自然语言描述建议按照字段名描述的格式来组织每个字段一行。描述里尽量包含类型、长度、格式、是否必填、默认值、校验规则这些信息。比如用户名字符串4-20个字符只能包含字母和数字必填 年龄数字18-120之间的整数选填默认值18 邮箱字符串符合邮箱格式必填如果输入是Excel表格建议列头明确写上字段名、类型、描述、是否必填、默认值、校验规则这些列。列头越明确Agent解析起来越准确。如果输入是数据库表结构那是最理想的因为类型和约束都是现成的。但需要注意数据库类型和Schema类型的映射关系。比如VARCHAR(255)对应string加上maxLength: 255INT对应integerDATETIME对应string加上format: date-time。注意事项输入里如果有敏感信息比如真实的用户数据、内部系统地址、具体的业务规则建议先做脱敏处理。Schema生成只需要字段的结构信息不需要具体的值。4.2 中间表示层的设计统一异构输入的关键中间表示层是FormAgent的核心设计之一。它的作用是把各种异构输入统一成一种格式方便后续处理。中间表示层的结构大概长这样{ fields: [ { name: username, label: 用户名, type: string, required: true, constraints: { minLength: 4, maxLength: 20, pattern: ^[a-zA-Z0-9]$ }, default: null, description: 用户登录名 } ], dependencies: [ { type: visibility, source: userType, target: [companyName, creditCode], condition: userType enterprise } ] }这个中间表示层的好处是不管输入是什么格式最终都转换成这个结构。后续的校验规则补全、联动关系推断、Schema组装都基于这个结构来做。这样就把输入解析和Schema生成解耦了新增一种输入格式只需要加一个解析器不需要改动生成逻辑。4.3 Schema组装从中间表示到目标格式Schema组装是最后一步也是直接面向使用的一步。组装的过程就是把中间表示层的数据转换成目标Schema格式。以JSON Schema为例一个字段的组装逻辑大概是这样的function buildFieldSchema(field) { const schema { type: field.type, title: field.label, description: field.description }; if (field.default ! null field.default ! undefined) { schema.default field.default; } if (field.constraints) { Object.assign(schema, field.constraints); } return schema; }对于必填字段需要在父级的required数组里加上字段名。对于嵌套结构需要递归组装。对于联动关系需要转换成if-then-else或者目标格式支持的条件语法。组装完成后还需要做一轮校验。校验的内容包括Schema是否符合目标格式的规范、字段名是否有重复、required数组里的字段是否都存在于properties里、联动关系引用的字段是否存在。这一轮校验能拦截掉大部分低级错误。4.4 生成结果的验证与迭代Schema生成出来之后不能直接就用需要做验证。验证分两步结构验证和功能验证。结构验证是检查Schema本身是否合法。如果目标是JSON Schema可以用ajv这样的校验库来验证Schema的合法性。如果目标是自定义格式需要写一个校验函数来检查必填项和格式约束。功能验证是检查Schema在实际表单引擎里的表现。把生成的Schema导入到表单引擎里看看字段是否正常渲染、校验规则是否生效、联动逻辑是否正确。这一步往往能发现一些结构验证发现不了的问题比如某个校验规则在特定表单引擎里不支持、某个联动条件的语法不对。迭代的过程就是根据验证结果调整生成逻辑。比如发现某个字段类型推断错了就调整类型推断的规则发现某个校验规则没提取出来就补充规则库。迭代几轮之后生成准确率会明显提升。5. 常见问题与排查技巧实录5.1 类型推断错误的典型场景与修正方法类型推断错误是最常见的问题我整理了几种典型场景和对应的修正方法问题场景错误推断正确推断修正方法手机号字段numberstring在规则库里加入手机号/电话/联系方式关键词强制推断为string金额字段stringnumber加入金额/价格/费用关键词推断为number并加上precision约束日期字段stringstring format加入日期/时间关键词推断为string并指定format布尔字段stringboolean加入是否/启用/开关关键词推断为boolean枚举字段stringstring enum识别描述里的可选值/枚举/选项关键词提取枚举值修正的核心思路是建立关键词到类型的映射表并在模型推断之前先用规则做一轮预判。规则能覆盖的场景用规则规则覆盖不了的再用模型。这样既保证了常见场景的准确性又保留了处理特殊场景的能力。5.2 校验规则丢失的排查思路校验规则丢失的表现是生成的Schema里缺少某些约束导致表单校验不生效。排查的思路是从输入到输出逐层检查。先检查输入解析层原始描述里的校验规则有没有被正确提取出来可以在中间表示层打印出来看看如果中间表示层里就没有这条规则那问题出在解析层。解析层的问题通常是规则库覆盖不全或者模型没有识别出这条规则。再检查组装层中间表示层里有这条规则但组装后的Schema里没有。那问题出在组装层可能是组装逻辑漏掉了某个约束字段或者目标格式不支持这个约束。还有一种情况是规则被提取了但表达方式不对。比如描述里写的是不能超过100提取成了maximum: 100但目标表单引擎用的是max而不是maximum。这种问题需要通过格式适配来解决。5.3 联动逻辑生成失败的常见原因联动逻辑生成失败通常有三个原因。第一个原因是条件结构识别失败。自然语言里的条件描述有很多种表达方式如果...则...、当...时...、在...情况下...如果规则库只覆盖了如果...则...那其他表达方式就会被漏掉。第二个原因是字段引用解析错误。联动条件里引用的字段名需要和Schema里的字段名对应上。如果输入里用的是中文标签用户类型而Schema里用的是英文字段名userType那联动条件里的引用就对不上。解决方法是建立一个标签到字段名的映射表在生成联动条件时做转换。第三个原因是目标格式不支持。有些表单引擎的联动配置能力有限不支持复杂的条件组合。这种情况下需要在生成时做降级处理把复杂的联动拆成多个简单的联动或者提示用户手动配置。5.4 性能优化大批量Schema生成的效率问题当需要生成的Schema数量很大时性能就成了一个问题。比如一个系统有几百个表单每个表单几十个字段一次性全部生成耗时会比较长。优化的思路有几个。第一个是缓存对于相同的输入直接返回缓存的结果避免重复计算。第二个是批量处理把多个表单的字段合并成一个批次一次性送给模型处理减少调用次数。第三个是分级处理先用规则做一轮快速处理规则处理不了的再送给模型这样能大幅减少模型的调用量。实测下来分级处理的效果最明显。大部分字段的类型推断和校验规则提取都能用规则搞定只有少数字段需要模型介入。这样整体耗时能降低百分之六七十。6. 工具选型与工程化落地建议6.1 模型选型不同场景下的取舍Schema生成这个任务对模型的要求是理解准确、输出稳定、成本可控。理解准确指的是能正确识别字段类型和校验规则输出稳定指的是同样的输入每次生成的Schema应该一致成本可控指的是大批量生成时费用不能太高。在实际选型时我建议根据场景来定。如果输入比较规范字段类型和校验规则都写得很明确那用规则引擎就够了不需要模型。如果输入是自然语言描述有一定的模糊性那需要一个理解能力较强的模型。如果输入是混合的既有规范的部分也有模糊的部分那用规则模型的混合方案。实操心得模型输出的稳定性是一个很容易被忽略的问题。同样的输入模型可能这次生成maxLength: 20下次生成maxlength: 20。这种不一致在批量生成时会造成很大的麻烦。解决方法是在组装层做一轮标准化把所有约束字段名统一成目标格式要求的写法。6.2 规则库的维护与扩展规则库是Schema生成系统的基础设施它的质量直接决定了生成结果的准确性。规则库的维护有两个关键点覆盖度和可扩展性。覆盖度指的是规则库能覆盖多少种常见的字段类型和校验规则。这个需要在实际使用中不断积累。每遇到一种新的字段类型或者校验规则就补充一条规则。积累到一定程度规则库的覆盖度就上来了。可扩展性指的是新增规则的难易程度。规则库的设计应该让新增规则尽可能简单最好是配置化的不需要改代码。比如用YAML或者JSON来定义规则新增规则只需要加一条配置。6.3 与现有表单引擎的集成方式FormAgent生成的Schema最终要导入到表单引擎里使用所以集成方式很重要。集成方式大概有三种直接导入、API对接、插件集成。直接导入是最简单的方式生成的Schema导出成文件手动导入到表单引擎里。适合一次性生成、少量表单的场景。API对接是把FormAgent封装成一个服务表单引擎通过API调用来获取Schema。适合需要动态生成Schema的场景比如用户自定义表单。插件集成是把FormAgent做成表单引擎的一个插件在表单引擎内部直接调用。适合深度集成的场景用户体验最好但开发成本也最高。选择哪种方式取决于具体的使用场景和团队的技术能力。我的建议是先从直接导入开始验证效果之后再考虑更深入的集成方式。6.4 版本管理与变更追踪Schema是有版本的。业务变了字段可能要增删改Schema也要跟着变。如果没有版本管理很容易出现这个Schema是什么时候改的、改了什么、为什么改都说不清楚的情况。版本管理的基本要求是每次生成的Schema都保存一个版本记录生成时间、输入内容、生成结果、修改记录。这样在出问题的时候可以回溯看看是哪个版本引入的问题。变更追踪是在版本管理的基础上记录Schema的具体变更内容。比如从v1到v2新增了哪些字段、删除了哪些字段、修改了哪些约束。这些信息对于排查问题和评估影响范围很有帮助。7. 我踩过的坑与实战建议7.1 不要过度依赖模型规则才是基本盘刚开始做FormAgent的时候我一度想全部用模型来做觉得模型理解能力强什么都能处理。实际跑下来发现模型在处理常见场景时确实没问题但一到边界情况就容易出幺蛾子。比如把订单号推断成数字类型把是否启用推断成字符串类型。这些错误在批量生成时会被放大造成大量返工。后来调整了策略把规则作为基本盘模型作为补充。规则能处理的场景坚决用规则保证准确性和稳定性。规则处理不了的再用模型。这样调整之后生成准确率从百分之八十多提升到了百分之九十五以上。7.2 输入格式的规范化比模型调优更重要有一个阶段我花了很多时间在调优模型上试图让模型更好地理解不规范的输入。效果有但提升有限。后来换了个思路把精力放在输入格式的规范化上。给用户提供一个输入模板要求按照模板来填写。结果生成准确率大幅提升而且用户的学习成本也不高。这件事给我的启发是在Schema生成这个场景里输入的质量比模型的性能更重要。与其花时间让模型理解混乱的输入不如花时间让输入变得规范。7.3 生成结果一定要人工审核不要直接上线Schema生成的结果不管准确率多高都建议人工审核一遍再上线。原因很简单Schema的错误往往不会在生成阶段暴露而是在表单使用阶段才暴露。比如某个字段的校验规则写错了用户填了不合规的数据表单没有拦截数据就脏了。这种问题的修复成本很高。人工审核的重点是字段类型是否正确、校验规则是否完整、联动逻辑是否合理、默认值是否合适。审核一遍下来一个中等规模的表单大概需要五到十分钟比从头手写Schema还是快很多。7.4 建立反馈闭环让生成系统越用越准Schema生成系统不是一次性的工具而是一个需要持续优化的系统。建立反馈闭环是关键。每次人工审核发现的问题都应该反馈到规则库或者模型调优里。比如发现某个字段类型推断错了就在规则库里加一条规则。发现某个校验规则没提取出来就补充规则库。反馈闭环建立起来之后系统会越用越准。刚开始可能需要审核修改的地方比较多用了一段时间之后大部分生成的Schema都能直接使用人工审核只是走个过场。7.5 小团队落地的最小可行方案如果团队规模不大资源有限我建议从最小可行方案开始。不要一上来就搞大而全的系统先解决最痛的问题。最小可行方案可以是这样先用规则引擎处理最常见的字段类型和校验规则覆盖百分之七八十的场景。剩下的百分之二三十用人工处理。等规则库积累到一定程度再考虑引入模型来处理更复杂的场景。这个方案的优点是启动成本低见效快。规则引擎的开发量不大一两个星期就能跑起来。跑起来之后先在小范围试用收集反馈逐步完善规则库。等规则库比较完善了再考虑扩展。8. 后续可以扩展的方向FormAgent目前主要解决的是从描述到Schema的生成问题。在这个基础上还有几个可以扩展的方向。第一个方向是反向生成。给定一个已有的表单自动提取出Schema。这个在接手老项目的时候很有用可以快速把老表单的配置提取出来迁移到新的表单引擎里。第二个方向是Schema的智能校验。不仅仅是校验Schema的格式是否合法还要校验Schema的业务逻辑是否合理。比如检查是否有字段的校验规则互相矛盾、是否有联动条件永远不可能满足、是否有字段的默认值不符合校验规则。第三个方向是多表单的Schema管理。当一个系统有几百个表单的时候Schema的管理就成了一个问题。哪些Schema是相关的、哪些Schema可以复用、Schema变更的影响范围是什么这些都需要工具来支持。第四个方向是和表单渲染引擎的深度集成。生成的Schema直接驱动表单渲染用户在界面上看到的表单就是Schema生成的。这样可以把Schema生成、表单渲染、数据收集打通形成一个完整的闭环。这些方向我都在陆续尝试有些已经有了一些进展有些还在探索阶段。Schema生成这个领域看起来简单实际上有很多细节可以打磨。把细节做好了工具的价值才能真正体现出来。