当产品经理半夜发来一条“这个AI效果太好了,我们把它提上正式排期”的消息时,真正让AI变得丝滑的,往往不是某个神秘Prompt,而是背后一整套工程决策。“顶级AI该有的样子”在企业项目里可以被拆成四个可验证的指标:响应是否够快、回答是否靠谱、能不能真正调用业务工具、出了问题能不能快速定位。这篇博客会从一个基于Spring AI构建的内部智能服务助手出发,把实现一个“会干活”的AI Agent、接入流式输出、处理延迟与幻觉、理解credits消耗、完成生产化排障的过程完整讲清楚。
下面所有代码都用于说明工程思路,落地时请结合你使用的Spring AI版本、模型平台和公司内部框架做调整。
1. “丝滑”不是感觉,是四个可以被测量的工程指标
1.1 首字延迟:AI体验最容易被感知的数字
用户在对话框里按下回车后,看到第一个字出现的耗时,通常决定了产品经理对AI的第一印象。
大模型推理本身需要时间,完整的回答可能长达几秒甚至十几秒。如果前端一直转圈,等模型把整段内容生成完再一次性展示,用户会下意识觉得卡顿。流式输出(Streaming)把“等待完整答案”变成“边生成边展示”,让首字延迟从整个回答时长缩小到几百毫秒级别,体验立刻不同。
因此,“丝滑”不是玄学,而是一个可测量的指标:首字延迟、生成间隔、完整响应时间、超时率。这些指标背后关联模型选型、网络链路、提示词长度、工具调用次数和前端渲染策略。
1.2 上下文和提示词决定回答质量
同样一个模型,在A团队手里是“笨蛋AI”,在B团队手里却很聪明,差别通常不在模型能力,而在于给模型喂了什么上下文。
模型回答问题时只能基于系统提示词、用户消息、历史记录和工具返回结果。如果你想让AI回答公司内部政策,却只传入一句“请回答员工问题”,模型只能靠通用知识猜测。正确的做法是:
- 把业务背景写进系统提示词。
- 把相关文档或知识库结果拼到上下文里。
- 把历史对话按时间顺序截断到合理长度。
- 把工具返回数据明确标记为“事实依据”,要求模型不得编造。
这套流程决定了AI是“像内部助手一样回答问题”,还是“像一个不通业务的聊天机器人”。
1.3 工具调用决定AI能不能“干活”
如果AI只能聊天,产品经理的兴奋感通常维持不了太久。真正让人眼前一亮的AI,是能查订单、算价格、写周报、读数据库、调工单系统的那一种。
工具调用(Function Calling / Tool Calling)是把自然语言问题转成结构化动作的关键。例如用户说“帮我看看订单2001现在到哪了”,AI需要从这句话里提取出“2001”这个订单号,并决定调用查询订单接口。模型本身不直接访问系统,它只负责生成“应该调用哪个工具、传什么参数”的指令,真正执行还是由后端代码完成。
工程上要处理三件事:
- 把工具描述和参数结构告诉模型。
- 让模型在合适时机返回工具调用指令。
- 后端执行工具返回结果,再把结果交给模型组织成用户答案。
只有这一步做扎实,AI才从“会聊天”升级成“会干活”,这也是标题里“PM连夜找我”的核心原因。
1.4 稳定性与可观测性决定信任感
功能上线后,产品经理会在群里转发AI回答,如果其中一条答案明显错误,负面观感会被放大。稳定性和可观测性不是锦上添花,而是AI应用能不能被信任的前提。
你需要明确知道每次请求用了哪个模型、消耗了多少token、调用了哪些工具、生成了什么内容、哪一步耗时最长。没有日志和链路追踪时,AI回答错误几乎无法复现,因为模型输出有随机性,同一个问题在不同时间可能得到不同回答。
这里有一个很实际的工程判断:在项目初期就把日志规范、traceId、耗时统计、成本统计建好,而不是等上线出问题后再补。否则团队会对AI能力失去信心,产品经理的热情也会被一次线上事故浇灭。
2. 准备开发环境:Spring AI与大模型调用的最小配置
2.1 为什么选Spring AI
在Java技术栈里,Spring AI是一个面向AI应用开发的集成框架。它做的事情很像当年Spring Boot对Web开发做的事:把反复出现的模型调用、提示词管理、工具调用、结构化输出、向量检索封装成一套统一API。
如果你所在团队已经有Spring Boot项目,引入Spring AI的成本很低,不需要为了接入大模型单独写一套HTTP客户端和重试逻辑。Spring AI的另一个价值是抽象了模型平台差异:通过OpenAI兼容接口,可以对接多家云服务商,也可以切到本地部署的模型服务,业务代码改动较小。
当然,Spring AI本身还在快速演进,不同小版本之间的API变化并不少。实际项目中要固定版本,并在升级时重点回归“模型调用、工具调用、流式输出”三个核心链路。
2.2 Maven依赖与版本检查
下面是一个基于Maven的依赖管理示例,使用Spring AI BOM统一版本,避免多个包版本不一致。
<properties> <java.version>17</java.version> <spring-boot.version>3.3.1</spring-boot.version> <spring-ai.version>1.0.0</spring-ai.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> </dependencies>这段配置里的版本号只是示例,写到这里是想说明一个原则:Spring AI和Spring Boot之间有版本兼容要求,不要随意选最新版本。你在实际操作时,要去查看当前Spring AI官方文档确认对应版本。
2.3 application.yml 模型参数说明
把模型配置放到环境变量里,是生产环境的基本要求。API Key不要写死在代码或直接提交到Git仓库。
spring: application: name: ai-service ai: openai: api-key: ${OPENAI_API_KEY} base-url: ${OPENAI_BASE_URL} chat: options: model: ${AI_MODEL:gpt-4o-mini} temperature: 0.3 max-tokens: 800这些参数的含义可以整理成一张表,方便日常调优:
| 配置项 | 含义 | 常用建议 | 错误配置的表现 |
|---|---|---|---|
| api-key | 平台访问凭证 | 用环境变量注入 | 调用直接401或403 |
| base-url | 接口地址 | 云端或私有化网关地址 | 连接超时、unknown host |
| model | 模型名称 | 按任务复杂度选择 | 模型不存在或响应格式不同 |
| temperature | 采样随机性 | 工具类任务0.2-0.4 | 过高容易乱调用工具 |
| max-tokens | 最大输出token数 | 普通问答500-1000 | 回答被截断、结尾不完整 |
model参数的选择会影响延迟和成本,而temperature对工具调用可靠性影响最大。工具类场景建议把temperature调低,让模型更倾向于稳定的结构化输出,而不是发挥想象力。
2.4 本地模型与云端模型的取舍
本地部署AI是很多企业内部项目关注的方向。用Ollama等工具可以在本机快速拉起一个模型服务,也支持OpenAI兼容接口,适合开发阶段调试。例如本地服务地址可能是http://localhost:11434/v1。
本地部署的优势是数据不出内网、没有按次调用的费用、可以离线调试;劣势是GPU资源有限,模型规模通常不如云端模型,复杂推理能力和生成质量会有差距。
选型建议也很直接:
- 学习环境优先使用云端小模型或本地小模型,跑通链路最重要。
- 开发环境可以指向测试模型的独立服务,方便联调。
- 生产环境要结合数据合规要求选择云端服务或私有化部署,而且至少要准备主备两套通道,避免单点故障。
3. 实现一个可用的AI Agent:从聊天到工具编排
3.1 项目结构
为了便于理解,项目结构按分层组织:
ai-service ├── pom.xml ├── src/main/java/com/example/ai │ ├── AiApplication.java │ ├── config/ChatConfig.java │ ├── controller/ChatController.java │ ├── service/OrderQueryTools.java │ └── mapper/OrderMapper.java └── src/main/resources ├── application.yml └── prompts/system-prompt.txt一个关键设计是把“模型能力”和“业务能力”分开。OrderQueryTools只负责查询订单,ChatConfig只负责配置模型,Controller只负责接收用户请求并转发到ChatClient。这样当订单逻辑变化时,不需要改动AI调用代码。
3.2 注册业务工具
在一个真实项目中,AI要执行的业务操作很多,但并不是所有操作都适合开放给模型。以查询订单为例,实现的工具方法如下:
@Service public class OrderQueryTools { private final OrderMapper orderMapper; public OrderQueryTools(OrderMapper orderMapper) { this.orderMapper = orderMapper; } @Tool(description = "根据订单号查询订单状态,返回可读的订单信息") public String queryOrder( @ToolParam(description = "订单号,例如20250101001") String orderId) { Order order = orderMapper.selectByOrderId(orderId); if (order == null) { return "未找到订单,请确认订单号"; } return "订单" + orderId + "当前状态是" + order.getStatus() + ",金额是" + order.getAmount(); } }这里需要注意几点:
- @Tool描述要尽量明确,模型会根据描述决定何时调用。
- 参数描述要给出格式示例,减少模型传错参数的概率。
- 工具返回字符串要语义完整,为了让模型能直接组织成用户答案。
- 如果工具执行失败,不要直接抛异常,而是返回一段描述性文字,让模型可以基于结果做兜底回答。
3.3 提示词模板
提示词是用户和模型之间的“操作手册”。以查询订单助手为例,系统提示词可以写成下面这样:
你是企业内部的智能服务助手。 回答规则: 1. 如果用户想查询订单状态,必须调用queryOrder工具,不得猜测订单状态。 2. 当工具返回“未找到订单”时,直接告诉用户没有查到该订单,不要编造订单信息。 3. 语气简洁,使用中文回答。 4. 如果用户问题与业务无关,礼貌说明自己只处理订单相关咨询。这段提示词的作用是给模型划出明确边界。尤其在工具调用场景,必须强调“必须调用工具,不得猜测”。否则模型很可能根据训练数据里的常见订单号,编造一个看似合理的结果。
提示词文件可以放在prompts/system-prompt.txt,启动时加载到字符串。这样做的好处是后续运营人员可以独立调整提示词,不需要修改代码重新发布。
3.4 ChatClient配置与多轮对话
Spring AI中的ChatClient是统一入口。可以通过Builder注入系统提示词和工具:
@Configuration public class ChatConfig { @Bean ChatClient chatClient(ChatClient.Builder builder, OrderQueryTools tools) { return builder .defaultSystem(""" 你是企业内部的智能服务助手。 查询订单时,必须使用queryOrder工具,不得猜测订单状态。 如果工具返回结果包含“未找到”,就明确告诉用户没有这个订单。 """) .defaultTools(tools) .build(); } }多轮对话需要注意上下文管理。常见做法是给每个用户会话绑定一个sessionId,把最近N条消息存入Redis。每次调用模型时,把系统提示词、历史消息、当前问题和工具结果一起发送给模型。
不要把所有历史消息无限制塞进上下文,因为大模型的上下文窗口有限,而且历史消息越长,每次请求的token成本越高、首字延迟越大。生产环境建议限制历史消息条数或总token大小,超过后只保留最近几条摘要。
3.5 工具调用的执行链路
用户问“订单20250101001到哪了”后,实际执行链路是这样的:
- 用户消息发送到后端。
- Spring AI把系统提示词、工具定义、用户消息一起发给模型。
- 模型判断需要调用queryOrder,返回工具调用指令。
- Spring AI自动找到对应Java方法,传入参数并执行。
- 工具返回订单状态字符串。
- 模型拿到工具结果,生成最终回答“您的订单正在配送中”。
这个过程看似简单,但有个常见坑:模型可能会连续调用两个工具,或者第一次调用参数不完整。生产环境下要捕捉整个调用链路的日志,看清模型到底选择了哪个工具、传了什么参数、工具返回了什么内容。
4. 流式输出接入:把“等待”变成“对话”
4.1 SSE工作原理
流式输出在Web前端最常用的实现方式是SSE(Server-Sent Events,服务器发送事件)。它基于HTTP长连接,服务器可以持续向客户端推送消息,适合AI生成内容的场景。
WebSocket虽然也支持双向通信,但AI对话场景主要方向是服务器向客户端推送token,SSE实现更轻量,后端只需返回一个text/event-stream响应,前端用原生EventSource就能接收。
4.2 后端返回Flux
在Spring AI中,ChatClient提供了流式方法。下面代码演示了一个带消息参数的GET接口,返回类型是Flux,Spring会按SSE格式推送:
@RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient = chatClient; } @GetMapping(value = "/api/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestParam String message) { return chatClient.prompt() .user(message) .stream() .content(); } }这个接口把用户问题传给模型,模型每生成一小段文本,Flux就推送一次。前端不需要等待完整内容生成,而是实时渲染。
实际项目中,接口还会有sessionId、userId、来源渠道等参数。生产环境不要把敏感信息放到URL查询参数里,至少要使用POST请求或加密签名,并将流式接口放到网关鉴权后面。
4.3 前端接收逐字内容
前端使用EventSource接收后,可以把内容增量添加到页面上:
const source = new EventSource(`/api/chat/stream?message=${encodeURIComponent(input)}`); source.onmessage = (event) => { const part = event.data; // 把part追加到当前回答区域 answerElement.textContent += part; }; source.onerror = () => { source.close(); // 显示重试入口 };这里要注意编码问题:如果接口返回的SSE事件里有换行符,可能被解析成多个事件。实际项目中,通常不使用Spring的SseEmitter默认格式,而是统一约定返回JSON结构,例如每个事件都是data: {"delta":"你好"},前端再自行解析。这样对中文换行、特殊字符更可控。
4.4 超时、取消和异常处理
流式接口并不是一开到底,会遇到三类常见问题:
- 用户中途关闭页面,服务端还在生成内容。
- 模型生成时间过长,前端连接被网关断开。
- 生成过程中模型平台报错。
解决思路是:
- 前端监听页面关闭,主动中断EventSource。
- 后端给流式接口设置响应超时,并捕获流错误后发送一个错误事件。
- 网关层需要同步调整超时配置,不能只调后端代码,否则数据还没推完就被网关切断。
- 重要会话要记录“已生成内容”的断点,用户重试时可以继续而不是从头开始。
流式输出不是简单替换一个返回类型,而是要把前后端超时、异常、重连一起设计好,才算真正完成。
5. 生产级优化:延迟、幻觉、成本与排查
5.1 延迟优化
首字延迟和完整响应时间会影响用户体验。常见的优化手段如下:
| 优化项 | 做法 | 注意事项 |
|---|---|---|
| 模型选型 | 简单任务用小模型,复杂任务用大模型 | 小模型对复杂指令理解可能不足 |
| 精简提示词 | 删除多余描述,保留必要规则 | 不要为了提速去除工具调用约束 |
| 减少历史消息 | 只传最近N轮 | 丢失太早的上下文 |
| 开启流式 | 边生成边推送给前端 | 需要处理中断和超时 |
| 结果缓存 | 相同问题和上下文直接返回缓存 | 只适用于可复用问答,不适合实时查询 |
| 并发控制 | 给模型接口做信号量限流 | 避免调用打爆模型服务 |
这里最容易忽视的是提示词长度。系统提示词和工具描述都会作为输入token计算,工具比较多时,一次请求的输入token可能比用户问题本身大很多。建议每增加一个工具,都检查一下它会增加多少token,以及是否真的有必要让模型知道。
5.2 幻觉治理
幻觉是AI应用进入生产阶段最容易被挑战的问题。模型本质上是在预测下一个词,它对不确定的事实会给出“看起来合理但实际错误”的回答。
治理幻觉不能完全消灭,但可以明显降低概率:
- 接入RAG(检索增强生成),把知识库中相关内容作为上下文传给模型。
- 要求模型在回答中引用来源,例如“根据内部文档《考勤制度》第三章”。
- 当检索结果为空时,要求模型回答“知识库中没有找到相关信息”,而不是编造。
- 克制系统提示词里的引导性内容,不要把不确定的信息写成事实。
- 对高价值业务问题,设置一个后置校验步骤,用另一条提示词检查回答是否存在矛盾。
RAG的流程可以简化成三步:用户问题向量化,从向量库召回TopK文档,把文档拼接到系统提示词中再请求模型。它的核心价值是给模型提供事实依据,让模型从“凭记忆回答”变成“读资料回答”。
5.3 理解credits与成本配额
在AI平台中,credits通常指平台提供的调用额度,实际扣费取决于token数、图片张数、视频时长或按次调用。不同平台对credits的定义不一样,但核心逻辑都围绕“模型计算量”展开。
对开发者的提醒是,不要只关心“一次调用多少credits”,而要计算一次对话完整生命周期的总消耗。一次工具调用场景往往包含多轮请求:
- 模型解析用户意图并决定调用工具的请求。
- 工具执行后,模型整理最终回答的请求。
- 如果还带历史消息、RAG检索结果,这些都会作为输入token算一次费用。
因此控制成本的常用手段是:
- 限制上下文长度。
- 给每次请求设置max-tokens上限。
- 对非关键任务使用更便宜的小模型。
- 在后台统计每个用户、每个会话的消耗量。
credits和token的换算关系建议以你实际使用的平台文档为准,上线前要有一张成本预估表,避免月底账单超出预期。
5.4 可观测性与日志追踪
AI应用的可观测性比普通Web接口更复杂,因为一次用户请求可能对应多次模型调用,每次调用都可能有不同的输入输出。
发布到生产前,至少要记录以下字段:
| 字段 | 示例值 | 用途 |
|---|---|---|
| traceId | 8f3a2c9e | 串联用户请求和分析日志 |
| userId | 10023 | 按用户维度分析问题 |
| sessionId | chat_20250101_001 | 按会话维度复现上下文 |
| message | 用户输入 | 确认输入是否正常 |
| model | gpt-4o-mini | 定位模型版本问题 |
| toolCalls | queryOrder | 确认工具调用是否正确 |
| inputTokens | 1240 | 成本分析与限流决策 |
| outputTokens | 356 | 成本分析与生成长度 |
| latency | 2350ms | 延迟分析 |
| errorMessage | timeout | 定位异常 |
日志打印时要注意隐私合规,不要记录用户身份证号、手机号、地址等敏感字段。如果必须记录,需要做脱敏处理。
6. 常见问题排查:从现象到根因
6.1 响应慢,首字迟迟不出来
可能原因往往是模型复杂、输入token过多、网络链路慢、网关缓冲导致流式没有生效。
排查顺序:
- 查看后端日志中模型调用的开始时间戳和第一次返回时间戳,计算首字延迟。
- 统计本次请求的输入token数,看是否因为历史消息太长导致预处理慢。
- 检查是否真的走了流式接口,前端是否在等待后端把完整响应写完后才从缓存读取。
- 检查网络代理、负载均衡、网关是否开启缓冲,导致第一批token没有及时推给前端。
解决方向:精简提示词、缩短历史消息、更换更快模型、关闭网关对SSE响应的缓冲。
6.2 模型没有调用工具,直接编造答案
这是工具调用最经典的坑。现象是用户问订单状态,模型回答“您的订单正在路上,请耐心等待”,但后端日志里没有出现任何工具调用记录。
原因通常是:
- 系统提示词里没有强制要求调用工具,只写了“可以调用”。
- temperature设置过高,模型随机性增加,没有走稳定路径。
- 工具描述不清晰,模型没有把该问题映射到工具功能。
- 模型版本本身不支持复杂工具调用。
解决方式:在系统提示词中写“查询订单时必须使用queryOrder工具,如果查询不到,明确告知用户未找到”,并把temperature降到0.2-0.3。同时增加一个后置校验:如果模型回答中涉及订单状态,但本次请求没有工具调用记录,就返回“无法确认订单状态”。
6.3 工具调用成功了,但回答与工具返回结果不一致
比如工具返回“订单已签收”,模型却回答“订单还在配送中”。这种情况往往不是工具问题,而是模型在组织语言时没有严格基于工具输出。
处理思路:
- 提示词要求“只能基于工具返回结果回答问题,不要补充工具结果之外的信息”。
- 把工具返回结果包装成明显的结构化引用,例如“工具结果:订单已签收”。
- 在代码里做简单校验,如果模型回答不包含工具结果中的关键状态字段,可以降级为固定模板返回。
6.4 回答被截断,最后一句不完整
如果设置了max-tokens,模型生成到上限后会被强制截断。现象是内容讲得很顺,但结尾突然中断。
排查方式:在日志里记录outputTokens,如果每次截断时输出token都接近上限,说明max-tokens不够或者提示词要求模型输出过长内容。解决方式是提高max-tokens,或者把长文本生成拆成多步任务,不要指望一次调用完成。
6.5 流式输出中断或重复
前端收到完整内容后又重推了一次,通常是因为EventSource意外重连,服务端重新执行了请求。后端要对同一会话做幂等控制,例如记录请求的唯一requestId,前端重连时带上同一个requestId,服务端判断是否已经处理过。
中断还可能是生产环境代理服务器的空闲超时时间太短,长期没有数据推送时连接被断开。解决方式是在SSE流中添加心跳事件,每隔一段时间发送一个注释或空事件保持连接活跃。
下表汇总了以上问题:
| 问题现象 | 可能原因 | 检查路径 | 处理建议 |
|---|---|---|---|
| 首字延迟高 | 输入token多、模型复杂、网关缓冲 | 日志记录开始时间与首个token时间 | 精简提示词、开流式、换小模型 |
| 模型编造答案 | 工具调用约束弱、temperature过高 | 日志查看未产生工具调用 | 强制工具调用、调低temperature |
| 回答与工具结果冲突 | 模型没有严格遵循工具结果 | 对比工具返回与最终回答 | 增加“只能基于工具结果”约束 |
| 回答截断 | max-tokens过小 | 日志观察输出token接近上限 | 提高上限或拆分任务 |
| 流式重复 | EventSource重连 | 查看请求ID和服务端日志 | 增加幂等控制 |
7. 实战检查清单与扩展方向
7.1 从Demo到生产的检查清单
在把AI功能从本地Demo推到生产环境之前,可以对照下面的清单逐项检查:
- 是否配置流式输出,前端是否实时渲染。
- 是否设置了用户会话隔离,A用户不能看到B用户的历史消息。
- 历史消息是否有长度限制,是否做了token预算。
- 工具调用是否有权限控制,模型不能调用未授权的内部接口。
- 工具方法是否做了参数校验和异常兜底。
- 是否记录请求、响应、工具调用、token消耗、耗时日志。
- 是否对敏感数据脱敏,是否满足公司安全规范。
- 是否设置了成本预警,当每日调用量或credits消耗超过阈值时能否告警。
- 是否准备了兜底回答,当模型服务不可用时用户看到什么。
- 是否建立了评测集,用固定问题回归核心功能是否被破坏。
这些检查项里,最容易忽略的是评测集。没有评测集,你很难判断一次提示词改动是在变好还是变坏。建议维护一个包含20到50条典型问题的小集合,每次改动后自动或半自动跑一遍,把回答结果存下来进行对比。
7.2 扩展方向:RAG、多Agent与评估
当单Agent的对话与工具调用稳定后,可以继续往三个方向扩展。
RAG方向是给AI增加企业知识库能力。把内部文档分块、向量化、存入向量数据库,用户提问时先检索相关内容再交给模型。它适合FAQ、政策问答、产品手册等场景。落地的重点不是向量数据库选型,而是文档切分质量、召回阈值和引用展示。
多Agent方向是把单一AI助手拆成多个专职角色。比如一个Agent负责订单查询,一个Agent负责售后处理,一个Agent负责数据分析,由主Agent统一路由用户问题。这个方向复杂度明显上升,要处理Agent之间的上下文共享、结果冲突和任务状态同步,建议在单Agent已经稳定后再做。
评估方向是为AI回答质量建立自动化指标。最基础的是人工评估,让产品经理和业务人员对固定问题打分;进阶做法是使用模型作为裁判,对回答的相关性、准确性和完整性打分。这里的核心是要持续维护评测集,否则无法判断工程改动的效果。
真正让PM“连夜找你”的AI,一定不是靠某个神奇Prompt,而是靠工程上把延迟、幻觉、工具调用和可观测性这些基础问题逐个解决。建议从一个小范围业务场景开始,先跑通流式聊天和工具调用,再加入知识库与成本监控,最后用评测集守住质量底线。每一步都做扎实,AI应用的体验自然会被业务方看见。