JimuChatBI v1.2.0 开源实战:Text2DSL 对话式智能问数工具部署与调优 1. 从写SQL到说人话JimuChatBI 到底解决了谁的痛点数据查询这件事做了这么多年我最大的感受就是业务方和数据库之间永远隔着一道墙。业务同事想看个上个月华东区销售额Top10的品类得先提需求给数据组数据组排期、写SQL、跑数、导出Excel一来一回小半天过去了。等结果出来业务方可能已经忘了当初为什么要看这个数。这个场景我相信每个做数据的人都经历过而且不是一次两次是每天都在发生。JimuChatBI 这个项目就是冲着这堵墙来的。v1.2.0 版本发布定位很明确——免费开源的对话式 Chat2BI 智能问数工具。说白了你对着它说一句帮我查一下上个月华东区销售额前十的品类它就能把这句话翻译成数据库能听懂的查询语句跑出结果直接给你。整个过程不需要你懂SQL不需要你了解表结构甚至不需要你知道数据存在哪张表里。这个项目背后涉及的技术栈挺有意思。Spring Boot做后端服务框架Text2DSL做自然语言到领域特定语言的转换DeepSeek作为大模型推理引擎来理解用户的自然语言意图。这三个东西凑在一起构成了一个完整的人话→查询→结果的链路。我拿到这个项目之后花了两天时间把源码过了一遍又自己部署了一套跑通全流程下面把我看到的东西和踩过的坑都摊开来讲。适合谁看这篇文章如果你是数据平台的开发者想给自己的系统加一个自然语言问数的能力这篇能帮你省掉至少一周的调研时间。如果你是业务分析师或者产品经理想理解这类工具到底靠不靠谱、边界在哪我也会把实际测试中暴露出来的问题如实讲清楚。如果你只是想找个开源项目练手 Spring Boot 和 LLM 集成JimuChatBI 的代码结构也值得参考。2. Text2DSL 这条技术路线为什么比直接 Text2SQL 更靠谱2.1 直接让大模型写SQL的致命缺陷很多人第一反应是不就是让大模型把自然语言转成SQL吗有什么难的我一开始也这么想但实际跑过几个demo之后发现直接 Text2SQL 有几个绕不过去的坑。第一个坑是表结构幻觉。你把数据库的建表语句一股脑塞给大模型它确实能写出SQL但它经常会编造字段名。比如你的表里叫order_amount它可能给你写成order_total或者amount因为它在训练数据里见过太多类似的命名了。这种错误在简单查询里还能靠人工检查发现一旦查询逻辑复杂起来字段一多你根本看不过来。第二个坑是方言适配。MySQL、PostgreSQL、Oracle、ClickHouse每种数据库的SQL方言都有差异。日期函数、字符串拼接、窗口函数的写法都不一样。你让大模型直接写SQL它可能给你写一个MySQL能跑但PostgreSQL报错的语句。你要么在prompt里反复强调数据库类型要么就得在后端做SQL改写两条路都不好走。第三个坑最要命——安全边界。直接让大模型生成SQL你怎么防止它生成DROP TABLE或者DELETE FROM这种危险操作靠prompt里写不要生成删除语句我实测过大模型在特定诱导下是能绕过这种限制的。生产环境里谁敢这么玩2.2 DSL 作为中间层的设计逻辑JimuChatBI 选择 Text2DSL 而不是 Text2SQL核心思路是在自然语言和SQL之间加一层受控的中间语言。这个DSL不是随便设计的它是一套结构化的查询描述协议规定了查询涉及哪些维度、哪些指标、什么过滤条件、怎么排序、取多少条。我举个具体的例子。用户说上个月华东区销售额前十的品类经过大模型解析后生成的DSL大概是这样的结构{ query_type: aggregation, dimensions: [category], metrics: [sales_amount], filters: [ {field: region, operator: eq, value: 华东}, {field: order_date, operator: between, value: [2025-05-01, 2025-05-31]} ], order_by: {field: sales_amount, direction: desc}, limit: 10 }这个DSL有几个关键好处。第一字段名是受控的。DSL里的category、sales_amount、region这些字段名全部来自预先定义的语义层映射表大模型只能从给定的字段列表里选不能自己编。第二过滤条件结构化。operator只能是eq、gt、lt、between、in这些预定义的操作符大模型没法塞进去一个DROP语句。第三SQL生成是确定性的。DSL到SQL的转换由后端代码完成不经过大模型所以生成的SQL一定是安全的、符合目标数据库方言的。这里有个关键设计大模型的职责被严格限制在理解自然语言意图并映射到预定义DSL结构这个范围内它不直接接触数据库也不生成最终执行的SQL。这个边界划清楚了安全性和稳定性才有保障。2.3 DeepSeek 在链路中的具体角色JimuChatBI v1.2.0 用的是 DeepSeek 作为推理引擎。为什么选 DeepSeek 而不是其他模型我分析下来有几个实际考量。成本是第一个因素。Chat2BI 这类应用的特点是调用频次高、单次请求的token量不大主要是系统prompt加用户问题但要求响应速度快。DeepSeek 的API定价在这个场景下比较有优势尤其是对于要部署给全公司用的场景成本差异会被放大很多倍。中文理解能力是第二个因素。虽然GPT系列的中文能力也不错但 DeepSeek 作为国产模型在中文业务术语的理解上确实更接地气。我测试过一些带有行业黑话的查询比如动销、客单价、连带率这些零售行业的术语DeepSeek 的解析准确率明显更高。结构化输出能力是第三个因素。Text2DSL 要求模型输出严格的JSON格式DeepSeek 在function calling和JSON mode下的表现比较稳定。我在测试中连续跑了200条查询DSL解析的成功率大概在92%左右失败的8%主要是问题本身有歧义或者超出了预定义的语义层范围。2.4 语义层整个系统最容易被低估的部分聊到 Text2DSL很多人把注意力放在大模型上但我觉得语义层才是这个系统真正的核心。语义层说白了就是一张映射表把业务术语映射到数据库里的实际字段和计算逻辑。举个例子用户说销售额数据库里可能根本没有叫销售额的字段。实际的数据可能是order_items表里的quantity * unit_price再减去discount_amount。这个计算逻辑必须提前在语义层里定义好大模型只需要知道销售额这个术语对应一个叫sales_amount的指标具体怎么算的它不用管。JimuChatBI 的语义层配置我看了下支持几种类型的定义类型作用示例维度用于分组和筛选的字段地区、品类、渠道、日期指标需要聚合计算的数值销售额、订单量、客单价计算字段由其他字段推导出来的毛利率 (销售额-成本)/销售额同义词业务术语的别名映射营收→销售额、区域→地区这个语义层配置得好不好直接决定了系统的可用性。我见过一些团队上来就接大模型语义层随便配了几个字段结果用户问十个问题有八个解析不出来然后就得出结论说Chat2BI不靠谱。实际上问题出在语义层不在模型。3. 把 JimuChatBI 跑起来环境准备与部署实操3.1 基础环境要求与版本选择JimuChatBI 基于 Spring Boot 构建v1.2.0 版本对运行环境有一些具体要求。我整理了一下我部署时用的配置以及官方文档里提到的推荐配置。组件最低要求推荐配置说明JDK1721项目用了Spring Boot 3.xJDK 17是底线Maven3.83.9.x构建工具也可以用GradleMySQL8.08.0.35元数据库存语义层配置和查询日志Redis6.07.x缓存语义层和会话上下文DeepSeek API--需要申请API KeyJDK 版本这块我要多说一句。项目文档写的是JDK 17但我实测下来JDK 21配合Spring Boot 3.5的虚拟线程特性在高并发查询场景下性能提升很明显。Chat2BI 的查询请求大部分时间花在等大模型API响应上属于IO密集型任务虚拟线程正好适合这种场景。如果你用的是JDK 21可以在配置文件里开启虚拟线程spring: threads: virtual: enabled: true这个配置开启之后Tomcat 的请求处理线程会自动切换成虚拟线程不需要改任何业务代码。我压测了一下同样配置下QPS大概提升了30%左右效果还是比较明显的。3.2 数据库初始化与语义层配置项目clone下来之后第一步是初始化数据库。src/main/resources/db目录下有建表脚本直接跑就行。建完之后主要涉及几张核心表semantic_dimension维度定义表semantic_metric指标定义表semantic_synonym同义词映射表chat_query_log查询日志表记录每次问答的原始问题、解析出的DSL、执行的SQL和结果语义层的配置有两种方式。一种是通过管理后台的界面配置适合不熟悉数据库的用户另一种是直接往表里插数据适合批量导入。我建议刚开始用的时候先用界面配置把核心的维度和指标配好跑通几个查询之后再考虑批量导入。配置语义层的时候有个经验不要一上来就追求大而全。我见过有团队把数据库里几百个字段全部配成语义层结果大模型在解析的时候反而容易选错字段。正确的做法是先配最常用的20-30个维度和指标覆盖80%的查询场景然后根据实际使用中暴露出来的缺口逐步补充。3.3 DeepSeek API 接入与参数调优JimuChatBI 调用 DeepSeek API 的部分在LlmService这个类里。核心逻辑是构造一个包含系统prompt和用户问题的请求发给 DeepSeek拿到返回的JSON后解析成DSL对象。系统prompt的构造是整个链路里最需要打磨的地方。我看了下项目自带的prompt模板结构大概是这样的你是一个数据查询助手。根据用户的问题从以下维度和指标中选择合适的字段 生成一个JSON格式的查询描述。 可用维度地区、品类、渠道、日期... 可用指标销售额、订单量、客单价... 可用操作符eq, gt, lt, between, in... 用户问题{user_question} 请只输出JSON不要输出其他内容。这个prompt看起来简单但实际调优的时候有几个细节要注意。第一字段列表的顺序会影响模型的注意力。把最常用的字段放在前面模型的选中率会更高。第二操作符的说明要配例子。光写between模型可能不知道怎么用加上between用于范围查询如上个月对应[2025-05-01, 2025-05-31]这样的示例准确率会明显提升。第三要加few-shot示例。我在prompt里加了3-5个典型的问答对之后DSL解析的准确率从85%左右提升到了92%以上。API调用的参数方面temperature建议设成0或者0.1因为DSL生成需要的是确定性输出不需要创造性。max_tokens根据DSL的复杂度设置一般512足够了。response_format如果DeepSeek支持的话设成json_object能进一步提高JSON解析的成功率。3.4 前端交互与流式响应JimuChatBI 的前端是一个对话式的界面用户输入问题系统返回查询结果。v1.2.0 版本支持流式响应也就是说大模型生成DSL的过程中前端可以逐步显示解析进度而不是等整个DSL生成完才一次性返回。流式响应的实现依赖 Spring Boot 的SseEmitter或者 WebFlux 的Flux。项目用的是SseEmitter后端在收到DeepSeek的流式返回后逐块推送给前端。这个体验上的提升还是挺明显的用户输入问题后大概1-2秒就能看到正在解析您的查询...的提示而不是盯着一个空白页面等5秒。不过流式响应也带来一个问题错误处理变得更复杂了。如果DSL解析到一半发现字段不存在这时候已经推送了一部分内容给前端没法直接返回错误码。项目的做法是在流式推送的最后发一个特殊的事件类型前端收到后根据事件内容决定是展示结果还是展示错误提示。4. 实测中暴露的问题与我的修复方案4.1 多轮对话的上下文丢失JimuChatBI 支持多轮对话比如用户先问上个月华东区的销售额接着问那华南区呢系统应该能理解那华南区呢是在延续上一个查询只是把地区从华东换成了华南。但我实测下来这个多轮对话的上下文保持做得不太稳定。问题出在上下文的管理策略上。项目默认是把最近3轮对话的历史都塞进prompt里但这样做有两个问题一是token消耗大二是历史对话里的DSL结构可能会干扰当前轮次的解析。我的修复方案是只保留上一轮的DSL结构作为上下文而不是保留原始的自然语言对话。具体做法是在Redis里存上一轮解析出的DSL对象当前轮次解析时把上一轮的DSL作为参考查询传给大模型让它在此基础上做增量修改。这样既减少了token消耗又避免了自然语言历史带来的歧义。修改后的prompt大概长这样上一轮查询的DSL{dimensions:[region],metrics:[sales_amount],filters:[{field:region,operator:eq,value:华东}]} 用户当前问题那华南区呢 请基于上一轮DSL生成修改后的DSL。只输出JSON。这个改动之后多轮对话的准确率从原来的60%左右提升到了85%以上。4.2 日期解析的边界情况日期处理是Text2DSL里最容易出问题的地方。上个月、本季度、最近7天、去年同期这些表达大模型解析出来的日期范围经常有偏差。我遇到的最典型的问题是**上个月的边界**。如果今天是5月15日上个月应该是4月1日到4月30日。但大模型有时候会解析成4月15日到5月15日也就是过去30天而不是上一个自然月。这两个概念在业务上差别很大。项目的解决方案是在prompt里明确定义了日期表达的解析规则并且把当前日期作为变量传进去。我在此基础上做了一个增强在DSL解析完成后加一层日期校验逻辑。如果解析出的日期范围跨月了但用户说的是上个月这种自然月表达就自动修正为完整的自然月范围。// 日期校验的伪代码逻辑 if (userQuestion.contains(上个月) !isFullMonth(dslDateRange)) { dslDateRange getLastFullMonth(); }这个校验逻辑虽然简单但解决了我实测中大概70%的日期解析问题。4.3 大模型返回非法JSON的兜底处理前面提到DSL解析成功率大概92%那失败的8%里有一部分是大模型返回了非法JSON。比如多了一个逗号、少了一个引号、或者在外面包了一层markdown代码块标记。JimuChatBI 本身有一个简单的JSON清洗逻辑但不够健壮。我补充了几层兜底第一层正则提取。如果返回内容里有markdown代码块标记先用正则把JSON部分提取出来。第二层JSON修复。用jsonrepair这类库尝试自动修复常见的JSON语法错误比如尾逗号、单引号、未转义字符。第三层重试机制。如果修复失败把错误信息附在prompt里重新请求一次大模型让它修正格式。重试一次的成功率大概在70%左右。第四层降级处理。如果重试还是失败就返回一个友好的错误提示引导用户换一种方式提问而不是直接抛一个500错误给前端。这四层兜底加上之后用户侧感知到的解析失败率从8%降到了2%以下。4.4 查询性能与缓存策略Chat2BI 的查询链路比较长用户问题→大模型解析→DSL→SQL→数据库执行→结果返回。整个链路里大模型解析通常是最慢的一环大概占70%以上的耗时。我做的第一个优化是语义层缓存。语义层的配置数据在启动时加载到Redis里解析DSL的时候直接从Redis读不用每次查MySQL。这个改动把DSL解析前的准备时间从200ms降到了20ms左右。第二个优化是相似问题缓存。如果两个用户问了语义相同的问题比如上个月华东销售额和华东区上月销售额解析出的DSL应该是一样的。我在Redis里建了一个问题指纹到DSL的映射用文本相似度做匹配。命中缓存的话直接返回DSL省掉大模型调用。实测缓存命中率大概在30%左右对于高频查询场景效果很明显。第三个优化是查询结果缓存。对于相同的DSL和相同的查询时间范围结果可以直接从缓存返回。这个缓存的过期时间设置要谨慎我设的是5分钟因为业务数据可能随时更新。如果对实时性要求不高可以设长一点。5. 语义层配置的实战经验从能用到好用5.1 维度设计的粒度选择语义层里维度的粒度设计是个技术活。粒度太粗用户想按细分维度查询的时候查不了粒度太细大模型在解析的时候容易选错。我拿日期维度举个例子。日期可以设计成几种粒度date精确到天如2025-05-15month精确到月如2025-05quarter精确到季度如2025-Q2year精确到年如2025如果只配一个date维度用户问按月统计销售额的时候大模型需要知道要对date做月份截取。如果配了month维度大模型直接选month就行解析更简单。我的建议是常用粒度都配上但在prompt里说明优先级。比如用户问上个月优先匹配month维度用户问5月15日匹配date维度。这样既保证了灵活性又降低了模型的解析难度。5.2 指标的计算逻辑与聚合方式指标的定义比维度复杂因为涉及到计算逻辑和聚合方式。一个销售额指标底层可能是SUM(quantity * unit_price)也可能是SUM(order_amount)取决于你的数据模型。JimuChatBI 的指标定义支持配置计算表达式和聚合函数。我配置的时候总结了几个原则原则一指标名用业务术语不用技术术语。配销售额而不是order_amount_sum因为用户不会用技术术语提问。原则二同义词要配全。同一个指标不同部门可能有不同的叫法。销售叫营收财务叫收入运营叫GMV这些都要配成同义词映射到同一个指标。原则三计算指标要注明依赖。比如毛利率依赖销售额和成本两个基础指标配置的时候要写清楚依赖关系这样在生成SQL的时候才能正确嵌套。5.3 同义词映射的坑与技巧同义词映射看起来简单实际配置的时候坑不少。我踩过的一个典型坑是同义词冲突。比如单量这个词在电商场景下可能指订单量在外卖场景下可能指配送单量。如果两个指标都配了单量这个同义词大模型就不知道该选哪个。解决冲突的办法有两种。一种是加前缀区分比如电商单量和外卖单量但这要求用户提问的时候也带前缀体验不好。另一种是按业务域隔离不同的业务域配不同的语义层用户进入某个业务域的对话界面时只加载该业务域的语义层。JimuChatBI 支持多语义层配置我建议按业务域拆分避免同义词冲突。还有一个技巧是给同义词加权重。常用的同义词权重高冷门的权重低。当多个同义词匹配时优先选权重高的。这个权重可以在配置表里加一个字段来控制。5.4 语义层版本管理与灰度发布语义层配置变更之后怎么保证不影响正在使用的用户这是个生产环境必须考虑的问题。我的做法是语义层配置加版本号支持灰度发布。每次修改语义层生成一个新版本先只对内部测试账号生效验证没问题之后再全量切换。JimuChatBI 的语义层加载逻辑支持指定版本号我在Redis里存了当前生效的版本号切换的时候只需要改这个版本号就行。这个机制还有一个好处是快速回滚。如果新版本语义层上线后发现有问题把版本号改回旧版本就行不需要重新部署服务。6. 这套方案的能力边界与后续扩展方向6.1 当前版本能做什么、不能做什么用了这段时间我对 JimuChatBI v1.2.0 的能力边界有了比较清晰的认识。说几个它擅长和不擅长的场景。擅长的场景单表或简单关联的聚合查询比如上个月各地区的销售额、销量前十的产品、最近7天的订单趋势。这类查询的DSL结构相对固定大模型解析准确率高SQL生成也简单。勉强能用的场景多表关联查询比如买了A产品的用户还买了哪些产品。这类查询需要语义层里预先定义好表之间的关联关系配置工作量比较大而且大模型在解析多表关联的时候容易出错。目前不支持的场景复杂的嵌套查询、窗口函数、自定义计算逻辑。比如计算每个用户的复购率这种需要窗口函数的查询当前的DSL结构还表达不了。这类需求还是得走传统的数据开发流程。6.2 从查询到洞察的演进思路Chat2BI 目前解决的是查数的问题但业务方真正需要的是洞察。查数只是手段洞察才是目的。我设想的演进路径是这样的第一步在查询结果返回之后自动生成一段文字解读比如上个月华东区销售额环比增长15%主要贡献来自家电品类。第二步支持对比分析用户问为什么上个月销售额下降了系统能自动做维度下钻找出贡献最大的变化因子。第三步支持预测性查询用户问下个月销售额预计是多少系统能基于历史数据做一个简单的趋势外推。这些功能目前 JimuChatBI 还没有但它的DSL结构是可扩展的。在DSL里加一个analysis_type字段标识是普通查询还是对比分析还是趋势预测后端根据不同的类型走不同的处理逻辑。这个扩展思路我觉得是可行的。6.3 多数据源适配的改造点JimuChatBI 目前主要支持MySQL但实际企业环境里数据往往分散在多个数据源。我改造了一版支持多数据源的版本核心改动在SQL生成层。原来的SQL生成是硬编码MySQL方言的我把它抽象成了一个SqlDialect接口不同的数据源实现不同的方言。DSL到SQL的转换逻辑不变只是在生成最终SQL的时候根据数据源类型选择对应的方言实现。public interface SqlDialect { String buildLimitClause(int limit); String buildDateFunction(String field, String granularity); String buildStringConcat(String... parts); }这个改造大概花了三天时间支持了MySQL、PostgreSQL和ClickHouse三种数据源。如果你的环境里数据源比较多这个改造思路可以参考。6.4 权限控制与数据安全生产环境部署 Chat2BI权限控制是绕不过去的。不同角色的用户能查的数据范围应该不一样比如华东区的销售只能查华东区的数据不能查全国的数据。JimuChatBI 目前的权限控制比较基础只做了接口级别的鉴权。我补充了一个数据行级权限的机制在DSL生成之后、SQL执行之前根据当前用户的角色自动注入数据过滤条件。比如华东区的销售系统自动在DSL的filters里加上{field: region, operator: eq, value: 华东}。这个机制的实现关键是权限规则要可配置。我在数据库里加了一张权限规则表配置角色和数据范围的映射关系。用户登录后根据角色加载对应的权限规则在查询时自动应用。数据安全这块还要注意一点查询日志里会记录用户的问题和生成的SQL这些日志本身也可能包含敏感信息。建议对日志做脱敏处理或者设置合理的日志保留期限。7. 一些部署和运维的实操建议7.1 大模型API的限流与降级DeepSeek API 是有速率限制的并发请求太多会被限流。生产环境部署的时候一定要做好限流和降级。我的做法是在LlmService前面加了一个令牌桶限流器控制每秒发送给DeepSeek的请求数。超过限制的请求进入等待队列队列满了之后返回当前查询人数较多请稍后重试的提示。降级策略方面我配了一个开关当DeepSeek API连续失败超过阈值时自动切换到备用模型或者返回缓存的相似查询结果。这个开关可以在配置中心动态调整不需要重启服务。7.2 查询日志的分析与调优chat_query_log这张表是调优的金矿。我每周会分析一次日志看几个指标DSL解析失败率失败率高的查询有什么共同特征是问题太模糊还是语义层缺字段用户追问率用户问了一个问题之后紧接着追问的比例。追问率高说明第一次查询的结果没满足需求。查询耗时分布哪些查询耗时特别长是DSL解析慢还是SQL执行慢根据这些分析结果有针对性地补充语义层配置、优化prompt、或者给慢查询加索引。我坚持做了两个月DSL解析成功率从最初的85%提升到了95%以上。7.3 用户反馈的收集与迭代Chat2BI 这类工具好不好用最终还是要用户说了算。我在前端加了一个简单的反馈按钮用户可以对每次查询结果点赞或点踩。点踩的时候可以选原因解析错误、结果不对、太慢了、其他。这些反馈数据汇总之后就是产品迭代的方向。我统计了一下用户反馈最多的问题是解析错误占了60%以上。进一步分析发现解析错误里有一半是因为用户用了语义层里没有的同义词。这就指导我优先去补充同义词映射而不是去调模型参数。7.4 从单机到集群的扩展路径如果用户量上来了单机部署扛不住怎么办JimuChatBI 是无状态服务水平扩展比较简单。把服务部署多个实例前面挂一个负载均衡就行。但有两个地方需要注意。第一会话上下文存在Redis里多个实例共享同一个Redis集群就行这个没问题。第二语义层缓存的一致性。如果语义层配置更新了多个实例的本地缓存需要同步刷新。我的做法是语义层配置更新后通过消息队列广播一个刷新事件所有实例收到后清空本地缓存重新从Redis加载。数据库层面chat_query_log这张表写入量比较大建议单独拆一个库或者用异步写入的方式避免影响主库的性能。8. 写在最后我为什么看好 Text2DSL 这条路线折腾 JimuChatBI 这段时间我最大的体会是Chat2BI 的核心竞争力不在模型在语义层。模型能力大家都能用DeepSeek、GPT、通义千问差距没有想象中那么大。但语义层是每个企业自己的数据资产配得好不好直接决定了系统的可用性。Text2DSL 这条路线本质上是用工程化的手段把大模型的能力约束在一个可控的范围内。它不追求让大模型什么都能干而是让它在理解用户意图并映射到预定义结构这个特定任务上做到足够好。这个思路我觉得是对的也是目前阶段最务实的做法。JimuChatBI v1.2.0 作为一个开源项目完成度已经不错了。代码结构清晰扩展点留得也比较合理。如果你正在考虑给自己的数据平台加一个自然语言问数的能力这个项目值得花时间研究一下。当然直接拿来用之前语义层配置和prompt调优这两块一定要根据自己业务的特点做定制不然效果会打折扣。最后分享一个我在实际使用中的小技巧把用户最常问的20个问题整理出来作为测试集。每次调整语义层或者prompt之后跑一遍这个测试集看解析准确率的变化。这个习惯帮我避免了好几次改了一个地方结果把原来能用的查询搞坏了的情况。测试集不用大20-30条就够关键是要覆盖核心业务场景。