新闻详情

新闻详情

首页 / 资讯中心 / 详情

自然语言界面只保留了表单五件事中的一件,工程真相是什么?

发布时间:2026/8/30 3:09:10
自然语言界面只保留了表单五件事中的一件,工程真相是什么?
“Forms did five things. Natural language kept one”这句话我第一次看到时觉得有点绕但结合最近在做的对话式表单、AI Agent 录入类项目再看突然就通了。传统表单页面里一个录入表单通常要承担“字段定义、用户引导、取值约束、数据校验、动作提交”这五类职责而引入自然语言界面后大模型真正承接住的只有“用户表达 - 结构化意图”这一件。其余四件并不是消失了而是被悄悄转移到了后端契约、JSON Schema、校验器和事务逻辑里。如果你正在做类似“聊天式申请单”“对话式报销单”“用大模型代替表单页面”这类功能这篇文章会把这句话拆开讲清楚并给出一个可运行的 FastAPI 示例让你看到自然语言界面背后仍然是表单时代沉淀下来的那套工程逻辑。1. 这句话到底在说什么先做一个概念对齐。在没有大模型的传统 Web 业务系统里一张“表单”绝不是一个简单的 HTMLform标签。它本质上是产品与数据库之间的“数据翻译层”。从用户输入到数据落库表单在一口气完成好几件事。1.1 表单的五件事我更愿意把表单的职责拆成下面五层第一件事声明字段与数据契约表单定义了这条业务数据有哪些字段每个字段是什么类型是否必填。比如请假单必须包含employee_id、start_date、end_date、reason日期类型是什么格式字符长度限制多少。这一层约定了前端和后端的接口契约也是数据进入系统的第一道门。第二件事引导用户把意图表达出来表单通过 label、placeholder、日期选择器、单选按钮等控件引导用户把脑子里的“我想请假”变成结构化的“假期类型 开始日期 结束日期 原因”。这是表单最容易感知、也最容易被忽视的部分。用户之所以能顺畅地填完表单是因为表单已经替用户把“如何表达”这个问题解决了一大半。第三件事限制取值候选集合下拉框、级联选择器、字典项本质上是在强行约束某个字段只能取有限的值。比如“请假类型”只能是年假、事假、病假因为下游审批流和薪资计算都只认这几类。这个能力叫做候选集控制很多表单问题、脏数据问题根源都是候选集没有约束好。第四件事数据校验与纠错必填校验、格式校验、范围校验、跨字段校验例如结束日期不能早于开始日期。传统表单在提交时会即时给出红色提示把大部分错误拦截在用户侧。第五件事提交动作绑定点击“提交”按钮本质上是发起一个明确的业务动作它不只是把 JSON 发给后端还会触发一条审批流、一条工单、一笔记账。提交动作需要处理重复点击、幂等、权限校验、审计日志等一系列问题。1.2 自然语言保留的那一件自然语言界面也就是现在常说的 Chatbot LLM 入口真正保留下来的能力是“第二件事”的升级版让用户用自然语言表达模糊意图再由模型把这句话映射成结构化字段。例如用户输入“下周一请一天年假带孩子去体检”表单时代的做法是让用户分别选日期、选类型、填原因自然语言界面的做法是让大模型自动解析出leave_type annual start_date 下周一 end_date 下周一 reason 带孩子去体检这一步做得足够好用户就感觉“好像不用填表了”。但也仅止于此。自然语言模型并不会自动保证字段合法不会自动约束枚举值更不会替你完成事务和审批动作。所以在工程上剩下四件事仍然原封不动地存在只是从页面 UI 转移到了后端代码和配置里。1.3 别急着说“表单已死”如果产品同学说“我们以后不做表单了全部用 AI 对话”技术同学一定要冷静下来。用户接触到的交互形式可以变化但一条业务数据从“自然语言”到“数据库记录”的链路并没有缩短它只是把原来很多靠前端控件实现的约束转移到了模型输出后的结构化校验上。甚至可以这样理解自然语言界面只是把表单的“外壳”换成了聊天窗口但表单的“骨架”仍然在后端以 Pydantic Model、JSON Schema、枚举、校验器、事务脚本等形式存在着。2. 从“表单页面”到“对话式录入”的架构变化为了后续动手先把两种架构下的数据流画出来。2.1 传统表单数据流传统场景下数据流是这样用户打开页面。前端渲染表单控件引导用户输入。前端执行部分校验比如必填、日期格式。用户点击提交JSON 按接口契约发送到后端。后端再次校验。业务逻辑执行。数据落库触发审批或工单。这里的核心特征是字段结构在代码里是写死的页面控件决定了用户只能按某种方式表达。2.2 自然语言界面数据流引入大模型后数据流变成这样用户在聊天窗口输入一句话。LLM 接收这句话结合系统提示词输出一个结构化 JSON。后端拿到 JSON 后仍然走同一个校验函数。校验通过后业务逻辑执行。数据落库触发审批或工单。如果校验失败把错误信息返回给模型让模型生成一段用户能看懂的解释再追问用户。对比之后就非常清晰模型只替换了“表达层”也就是把用户原话变成结构化字段。后面的字段契约、枚举约束、数据校验、业务动作绑定一个都不能少。2.3 表单职责在新链路中的位置我习惯用一张职责分配表来和团队对齐表单五件事传统实现位置自然语言界面实现位置字段与数据契约前端类型定义 后端 DTO后端 DTO / JSON Schema引导用户表达表单控件 / 占位提示LLM 系统提示词 模型意图理解限制候选集合下拉框 / 字典校验系统提示词注入枚举 后端枚举校验数据校验与纠错前端校验 后端校验后端 Pydantic / 校验器提交动作绑定提交按钮触发接口后端业务函数 / 服务层这张表也回答了一个常见问题“自然语言界面到底简化了什么”答案很明确简化的是用户输入成本而不是系统复杂度。系统的可靠性和安全性仍然需要那四件表单时代的工程能力来兜底。3. 完整实战基于 LLM 的请假申请助手下面用一套可运行的最小示例把上面的思路落地成代码。3.1 需求与功能边界我们要做的是一个“请假申请助手”用户在聊天框里输入自然语言请假需求。LLM 负责解析意图输出结构化 JSON。后端基于 Pydantic 模型执行字段、枚举、日期合法性校验。校验通过后写入模拟存储并触发一个审批流程占位函数。校验失败时返回 422 状态码附带错误字段信息供前端回显。这里我使用 FastAPI Pydantic 实现重点演示“表单五件事”里除“引导表达”以外的四件仍然必须显式出现在后端代码里。3.2 项目结构新建一个项目目录结构如下leave-chatbot/ ├── requirements.txt ├── app/ │ ├── __init__.py │ ├── main.py │ ├── schemas.py │ ├── llm_client.py │ └── repository.py └── README.md3.3 第一步固定数据结构与字段约束首先生成requirements.txt。需要注意这里不把版本写死因为 FastAPI、Pydantic 等库的版本迭代较快本文重点在思路实际安装时建议使用当前环境可用的稳定版本。fastapi uvicorn pydantic然后是核心数据结构app/schemas.py。这一层就是表单时代的“字段声明 候选集 校验规则”只是从 HTML 控件变成了 Pydantic Model。# 文件路径leave-chatbot/app/schemas.py from datetime import date from enum import Enum from pydantic import BaseModel, model_validator class LeaveType(str, Enum): annual annual sick sick personal personal class LeaveRequest(BaseModel): employee_id: str leave_type: LeaveType start_date: date end_date: date reason: str urgent: bool False model_validator(modeafter) def check_date_range(self): if self.end_date self.start_date: raise ValueError(end_date 不能早于 start_date) return self class ChatRequest(BaseModel): message: str user_id: str E1001这段代码做的事情就是表单时代的下拉框和前端校验employee_id是字符串对应契约里的员工编号。leave_type是LeaveType枚举不允许用户随便填。start_date、end_date是date类型传入非法字符串就会校验失败。check_date_range是跨字段校验等价于表单里的“结束日期不能早于开始日期”。在实际项目中employee_id还应该去员工表里关联确认员工是否存在。这里为了演示精简保留为一个字符串字段。3.4 第二步让 LLM 负责“自由文本到意图”的映射现在实现app/llm_client.py。这里的关键设计是不要直接让模型返回“用户原话的改写”而是让它返回一段可以直接被 Pydantic 消费的 JSON。这意味着模型必须严格按照提示词里给定的 JSON 结构输出字段名必须和设备端契约完全一致。# 文件路径leave-chatbot/app/llm_client.py import json # 当前日期由业务侧注入而不是让模型自己推断。 # 这里为演示简化实际项目中可用 date.today().isoformat()。 CURRENT_DATE 2025-06-23 SYSTEM_PROMPT f你是企业内部的请假申请助手。 请你把用户的请假需求解析为结构化 JSON并严格遵守以下规则 1. 只能输出 JSON不要输出任何解释性文字。 2. 字段固定为: employee_id, leave_type, start_date, end_date, reason, urgent 3. leave_type 只能是 annual, sick, personal 之一。 4. start_date 和 end_date 使用 YYYY-MM-DD 格式。 5. 如果用户没有明确说明 employee_id使用调用方传入的 user_id 作为默认值。 6. 如果用户提到“尽快”“加急”urgent 设为 true。 今天是 {CURRENT_DATE}。 用户给到的相对日期请换算成具体日期。 PROVIDER_MOCK True def parse_leave_intent(raw_message: str, user_id: str E1001) - dict: 调用大模型把自然语言转换为结构化 JSON。 生产环境建议使用模型平台提供的结构化输出能力 例如 JSON mode 或 function calling具体参数名以所用平台文档为准。 本文为便于本地运行先提供 mock 分支。 if PROVIDER_MOCK: return _mock_parse(raw_message, user_id) # 真实调用示例伪代码需按实际模型平台调整 # # messages [ # {role: system, content: SYSTEM_PROMPT}, # {role: user, content: raw_message}, # ] # # response chat_completion( # modelyour-model-name, # messagesmessages, # response_format{type: json_object}, # ) # text response[choices][0][message][content] # return json.loads(text) raise NotImplementedError(请接入实际大模型服务) def _mock_parse(raw_message: str, user_id: str E1001) - dict: 本地演示用的 mock 解析结果。 这里模拟模型已经正确把自然语言转换成了结构化 JSON。 if 病假 in raw_message: leave_type sick else: leave_type annual if 两天 in raw_message: end_date 2025-06-24 else: end_date 2025-06-23 return { employee_id: user_id, leave_type: leave_type, start_date: 2025-06-23, end_date: end_date, reason: raw_message, urgent: False, }这里最值得多说一句的是系统提示词它把“候选集”注入到了模型上下文里明确告诉模型leave_type只能取三个值。它把“当前日期”注入给模型避免模型对“下周一”这类相对日期产生歧义。它要求模型严格输出 JSON等于把“前端表单”里的字段名、类型都告诉给了模型。这就是自然语言界面里“引导用户表达”的具体实现方式。表单用控件引导自然语言界面用提示词和上下文引导。3.5 第三步后端仍然执行 schema 校验与枚举限制接下来写数据访问层app/repository.py。这里把校验和存储拆开是为了突出“模型输出不等于可信数据”。# 文件路径leave-chatbot/app/repository.py from datetime import date from .schemas import LeaveRequest # 模拟内存存储。生产环境请替换为真实数据库并配合事务管理。 # 千万不要在生产环境中使用内存列表作为唯一存储。 _db_records: list[LeaveRequest] [] def create_leave_request(payload: dict) - LeaveRequest: # 这一行是关键无论模型输出长什么样 # 最终都要通过 Pydantic 的完整校验。 req LeaveRequest(**payload) _db_records.append(req) # 这里可以继续触发审批流程、发送通知等业务动作。 trigger_approval_flow(req) return req def trigger_approval_flow(req: LeaveRequest) - None: # 生产环境这里会调用工作流服务创建审批单。 # 这里只做占位输出方便观察运行结果。 print(f[workflow] 已为 {req.employee_id} 创建审批单 f类型{req.leave_type.value}, 日期{req.start_date} ~ {req.end_date}) def list_records() - list[LeaveRequest]: return _db_recordsLeaveRequest(**payload)这一行会执行完整校验包括字段类型是否正确。leave_type是否在枚举范围内。日期是否能被解析为日期类型。结束日期是否早于开始日期。模型可能因为幻觉、上下文理解错误等原因产生非法数据但校验层不会因为“它是 AI 生成的”就放行。这一点是整篇文章的核心结论自然语言解决表达问题校验解决数据问题两者不能互相替代。3.6 第四步把可逆动作绑到存储层最后编写 FastAPI 入口app/main.py把聊天请求和结构化输出衔接起来。# 文件路径leave-chatbot/app/main.py from fastapi import FastAPI from fastapi.responses import JSONResponse from pydantic import ValidationError from .llm_client import parse_leave_intent from .repository import create_leave_request, list_records from .schemas import ChatRequest app FastAPI(title自然语言请假助手) app.post(/chat/apply-leave) async def apply_leave(chat: ChatRequest): # 第一步让 LLM 把自然语言转换成结构化 JSON。 parsed parse_leave_intent(chat.message, user_idchat.user_id) # 第二步交给后端做完整的表单式校验。 try: req create_leave_request(parsed) except ValidationError as exc: return JSONResponse( status_code422, content{ ok: False, error: 校验未通过, details: exc.errors(), }, ) return {ok: True, data: req.model_dump()} app.get(/leave-requests) async def get_leave_requests(): return {records: [r.model_dump() for r in list_records()]}这里有一个很典型的工程细节正常情况下用户输入“下周一请一天年假”LLM 解析出的日期可能是基于模型内部“知识”生成的不一定准确。所以更稳妥的做法是把当前日期明确注入系统提示词并且在解析结果返回后再次用业务规则校验。在真实项目里还会把模型生成的 JSON 和系统提示词中的 JSON Schema 做一次强校验。如果模型输出缺少关键字段或者字段类型不匹配就应该让模型基于错误信息重新生成而不是直接落库。3.7 运行与验证启动服务uvicorn app.main:app --reload --port 8000发送一条自然语言请求curl -X POST http://localhost:8000/chat/apply-leave \ -H Content-Type: application/json \ -d {message: 下周一请一天年假带孩子体检, user_id: E1001}由于 mock 分支只做演示上面的消息会解析为{ employee_id: E1001, leave_type: annual, start_date: 2025-06-23, end_date: 2025-06-23, reason: 下周一请一天年假带孩子体检, urgent: false }接口返回{ ok: true, data: { employee_id: E1001, leave_type: annual, start_date: 2025-06-23, end_date: 2025-06-23, reason: 下周一请一天年假带孩子体检, urgent: false } }再测试一个校验失败场景。修改llm_client._mock_parse让end_date早于start_date然后重新调用接口会得到类似下面的 422 响应{ ok: false, error: 校验未通过, details: [ { type: value_error, loc: [], msg: Value error, end_date 不能早于 start_date } ] }这个结果可以直观地看到模型再怎么自由发挥最终数据进入到业务层之前仍然会被表单时代的那套校验规则拦住。4. 高频问题与排查思路在实际接入自然语言界面时下面几个问题出现频率最高。问题现象常见原因解决思路模型返回 JSON 字段名和后端对不上系统提示词没有明确字段契约或模型对字段名理解有偏差在提示词中贴出完整 JSON Schema后端使用 Pydantic 或 JSON Schema 做强校验字段映射失败时让模型根据错误信息重试leave_type被解析成非法值枚举候选集没有注入提示词模型自行发挥把可选项用逗号或列表明确写进系统提示词同时在 Pydantic 枚举上兜底校验“下周一”这类相对日期解析错误模型不知道系统当前日期或日期计算有幻觉将当前日期写入系统提示词要求模型返回YYYY-MM-DD不要接受模型自编的日期缺少必填字段比如reason为空用户原话本身信息不足模型没有追问在提示词中加入“信息不足时先追问”的规则后端校验失败后把缺失字段返回给对话流让模型继续追问用户重复点击或重复提交产生多条数据没有做幂等控制生成请求 ID 或任务 ID在存储层做去重把“提交动作绑定”这一件事仍然放在后端完成模型输出包含额外字段导致校验通过但存储混乱模型返回了契约之外的内容在解析层忽略未知字段或者用 Pydantic 的extraforbid来严格拒绝多余字段这里需要特别提醒的是不要为了让模型“更聪明”就跳过校验。LLM 是概率模型它的输出天然存在不确定性。安全可靠的业务系统必须把校验放在业务写入路径上并且让校验结果能反馈给模型做二次修正而不是直接抛给用户一句半懂不懂的报错。5. 生产落地建议前面示例解决了核心流程但距离生产环境还差很多细节。下面给出几条工程建议。5.1 把“五件事”拆解到不同层不要试图在一个文件里塞下所有逻辑。建议这样分层提示词层负责“引导用户表达”和候选集提示只影响模型输出质量。Schema 层负责定义数据契约对应 Pydantic Model 或 JSON Schema。校验层负责格式、范围、跨字段、业务规则校验。服务层负责动作绑定、事务、审批流触发、幂等控制。持久层负责数据写入和审计。即使未来模型的交互形式又变了比如从聊天变成语音从语音变成多模态Schema 层、校验层、服务层仍然可以复用。把自然语言界面想象成一个“新外壳”而不是一套新业务系统整体架构会稳定很多。5.2 自然语言界面的安全与权限边界很多团队在做 AI 对话功能时只关心模型能不能懂人话却忽略了权限控制。这是比较大的风险点。用户通过聊天窗口提交的请求本质上和通过表单提交的请求是同一类东西。因此用户身份必须通过令牌或会话上下文获取不能信任模型输出的employee_id。上面示例里把user_id从调用方传入就是这个原因。服务端必须继续做越权校验比如用户 A 不能替用户 B 提交请假单。涉及删除、转账、发布等高风险操作时即使模型已经理解了意图系统也应增加二次确认门槛。所有对话输入和结构化输出都应记录审计日志便于事后追溯。注意AI 能力接入并不改变“最小权限原则”。对话式交互只是改变了用户输入方式并不会让用户获得额外权限。5.3 校验失败之后的“对话式兜底”传统的表单校验失败会显示“请填写结束日期”这类文本。自然语言界面如果直接抛出一段 JSON 错误用户体验会非常差。更好的做法是后端发现校验失败后把错误信息作为上下文传回给模型让模型生成一句用户能听懂的话继续追问用户。举例来说如果用户只说“我要请假”没有说时间模型第一次解析出的 JSON 可能缺少start_date。后端校验失败后可以将错误信息返回给模型让模型这样回答“好的我帮您提交请假申请。请问您计划从哪一天开始请假需要请多久”这样既保留了自然语言的交互体验又保证了后端数据契约不被破坏。也可以理解成表单的“红色错误提示”升级成了“AI 客服式追问”。5.4 性能、日志与可观测性自然语言界面通常会引入大模型调用这会带来新的问题延迟模型生成 JSON 比用户直接选下拉框慢得多。建议对结构化输出设置超时并在前端提供 loading 状态。成本不是每一次请求都需要调用大模型。如果用户已经提供了完整字段可以直接跳过解析环节。可观测性要记录模型返回的原始文本、解析后的 JSON、校验结果、重试次数。一旦线上出现数据异常能快速定位是模型问题、提示词问题还是校验规则问题。在实际项目中我会特别建议保留一个“纯表单模式”的备用入口。当大模型服务不稳定或者用户连续三次无法通过对话完成填写时自动回退到传统表单页面。这是非常实用的降级方案。6. 总结回到标题这句话Forms did five things. Natural language kept one.大模型确实把“引导用户表达”这件事做得比表单更自然。用户不再需要理解字段、日期格式、枚举选项只要说出自己的意图模型就能把事情的一部分表达出来。这是自然语言界面的核心价值。但这条链路里剩下的部分也就是数据契约、候选集约束、数据校验、提交动作绑定都没有也不能被“自然语言”替代。它们只是从可见的页面控件迁移到了不可见但必须存在的后端代码、Schema 和校验规则中。如果你正在做对话式表单或 AI Agent 业务流程建议你像上面示例一样先把字段契约、枚举、校验器、事务边界这些“表单骨架”写好然后再接入大模型。模型负责优雅工程负责可靠。下一步可以继续研究如何让模型在解析失败时基于校验信息自动修正、如何为每次请求生成全局幂等 ID、如何把对话式录入接入你自己公司的审批流引擎。这些方向本质上都是在“自然语言保留的那一件”之外重新把另外四件表单能力补回来。
网站建设 高端定制 企业官网