资讯动态

对话式数据分析系统:用LLM将自然语言自动转为ECharts配置

发布时间:2026/10/22 18:57:33 来源:尧图企业网站定制
刚做完一个挺有意思的项目对话式数据分析系统。用户输入一句大白话比如“给我看看最近三个月各区域的销售额对比顺便标出环比增长率”系统自动把这句话转成ECharts配置前端直接渲染出图表。整个过程不需要拖拽组件不需要手写option全部靠LLM理解意图、映射数据、生成配置。我在这篇文章里把这套东西的完整思路、架构设计、核心代码、踩坑记录全部整理出来项目命名为“某对话式数据分析系统”以下简称“对话分析系统”。希望对正在做类似LLM应用落地的朋友有点帮助。这套方案最直接的价值有三个。第一数据分析门槛大幅降低业务人员不需要懂前端也不需要会写SQL只要会提问就能看数据。第二开发效率提升明显原来写一个图表配置需要查文档、调样式现在只要Prompt设计得好LLM生成的配置完成度能做到80%以上。第三对话上下文天然适合多维度的探索式分析用户可以从“总销售额是多少”一路追问到“华东区哪个品类增长最快”系统保持上下文连贯。适合看这篇文章的人主要是这几类做LLM应用开发的工程师想知道怎么把大模型输出跟具体业务场景做可靠对接前端开发者想了解ECharts配置的自动化生成思路数据分析师或数据产品经理想低成本实现一个智能BI的Demo。1. 先想清楚自然语言转图表配置难在哪在写代码之前我花了不少时间梳理这个问题的本质。表面上这是“把文字变成JSON配置”但实际操作中你会发现真正的难点根本不在配置生成上而是在“语义理解”和“数据对齐”这两层。1.1 传统方式的三个明显痛点传统的图表开发流程通常是前端工程师拿到需求查ECharts文档手写option联调数据接口最后调整样式。这个过程有几个老生常谈但一直没被很好解决的问题。第一需求沟通成本高。业务方说“做个好看的图”但“好看”的标准是什么柱状图还是折线图颜色用蓝色系还是渐变色要不要显示数值标签这些细节点如果每个都靠沟通确认效率非常低。第二图表配置的细节非常多。ECharts的option结构说简单也简单说复杂也复杂。一个坐标轴就有十多个配置项series里还有堆叠、平滑、动画、渐变色等用法。稍微没查文档就很容易写错属性名或者漏掉必要的配置项导致图表渲染异常。第三数据格式跟图表类型强耦合。饼图需要{name, value}结构折线图需要xAxis的category数据和series的data数组对齐。后端接口返回的数据往往不是现成的还需要前端做大量map、filter、reduce的转换操作。1.2 LLM介入之后流程发生了什么变化LLM介入后原来的“需求 → 设计 → 开发 → 联调”压缩成了“提问 → 出配置”。用户直接用自然语言描述想要看什么系统通过大模型解析意图、识别维度与指标再从数据源取数并聚合最后生成结构化ECharts配置。这个流程里LLM承担了最费脑子的部分意图理解、字段映射、图表类型选择、配置内容生成。人只需要做两件事一是把问题描述清楚二是在结果不满意时追加一句“换成玫瑰图”或“不要显示数值标签”。这是典型的“AI Copilot”模式不是替代人而是替代了人跟图表配置搏斗的过程。1.3 一个关键认知LLM输出的可靠性取决于约束强度这个项目做下来我最深的体会是LLM生成的自由文本能力很强但直接让它输出“JSON配置”时如果不对输出格式做强制约束结果会非常不稳定。可能出现多余的注释、Markdown代码块标记、错误的字段名、字符串和数值类型混乱等问题。所以整个系统的核心设计思想就一句话在自由语义和严格配置之间搭一座桥。LLM负责理解自然语言但它输出的不是裸配置而是经过Prompt约束、Schema校验、兜底修复三道防线之后才交给前端渲染。这座桥建得牢不牢直接决定了系统的可用性。2. 系统整体架构与模块划分这个系统的架构设计从一开始就确定了原则只做LLM能做好且适合做的事其他全部用传统代码兜住。整体分为四层各司其职。2.1 四层结构对话层、语义层、数据层、渲染层对话层负责接收用户输入、维护多轮会话上下文、记录用户对配置结果的修正反馈。语义层核心的Prompt编排与LLM调用模块把用户问题结构化为“意图 维度 指标 图表偏好”四要素。数据层负责从数据库中取数、按维度聚合、把结果转成ECharts需要的标准数据格式。渲染层前端拿到配置和数据后直接执行同时提供局部修改的入口。每一层都只跟相邻层通信不跨层调用。这样做的最大好处是任何一个模块出问题都能单独替换或降级。2.2 为什么选择ECharts而不是其他图表库这个项目选择ECharts是一个经过比较后的决定不是因为它最流行而是它最适合这个场景。对比之下Chart.js的配置简单但图表类型较少复杂统计图表支持一般。D3.js的定制性最强但学习曲线陡峭让LLM生成D3代码的难度远高于生成声明式配置。而ECharts恰好是声明式配置的典型代表本质上就是一个大的JSON对象这在“大模型生成配置文件”的场景里优势非常明显。另外ECharts的中文文档完善社区方案丰富遇到特殊图表需求比如桑基图、主题河流图时有现成案例。对于LLM应用来说训练数据里ECharts的示例浓度也相对够用模型对该配置结构的辨识度明显更高。2.3 数据源与指标体系的预设这个环节非常重要也是很多人在做类似项目时容易忽略的。直接让LLM去数据库里查表是不现实的你不可能让模型知道你的库里有什么表、字段叫什么、有没有权限限制。所以我在数据层做了一个“数据字典预设”的操作。预先在系统里定义好业务指标和维度的映射关系比如“销售额” → 字段total_amount聚合方式SUM“订单量” → 字段order_count聚合方式COUNT“区域” → 字段region分组维度“时间” → 字段order_date按月份格式化LLM不直接面对数据库它只面对这份数据字典输出“我要用哪些字段、怎么聚合、按什么维度分组”。然后由传统的代码模块把这个结构化描述翻译成SQL查询执行取数。这样做有三重好处安全性可控、生成的SQL不会乱来、字段歧义大幅降低。3. Prompt工程让LLM稳定输出ECharts配置Prompt是这个项目的灵魂。如果Prompt设计得不到位后面写得再漂亮也没用。这一节把我反复迭代出来的Prompt方案完整讲清楚。3.1 系统级Prompt的结构化编排我采用的System Prompt分为四个部分角色设定、任务定义、数据字典、输出格式要求。下面是一个精简但完整的示例你是一个数据分析助手。你的任务是根据用户提出的问题生成ECharts可视化配置。 可用的数据字段 - 日期维度order_date格式 YYYY-MM-DD - 区域维度region值华东、华南、华北、西南、西北 - 品类维度category值数码、家电、服饰、食品 - 指标total_amount销售额数值单位元 - 指标order_count订单量数值 使用规则 1. 从用户问题中提取需要展示的维度和指标。 2. 如果用户指定了图表类型优先使用指定类型 否则根据字段性质和关系推荐时间序列用line类别对比用bar占比用pie。 3. 输出必须是JSON对象包含两个字段sql和option。 sql字段包含维度和指标信息用query_columns表示。 option字段是ECharts的完整配置对象必须包含title、tooltip、xAxis、yAxis、series。 4. 不要输出任何解释性文字不要使用Markdown代码块标记只输出JSON。 本次会话开始。这个Prompt里面值得注意的细节是我明确说了“不要使用Markdown代码块标记”否则LLM很容易把JSON包在json里增加后端解析的额外动作和出错概率。同时把字段含义和可用值直接写进Prompt让LLM能根据用户描述精确匹配而不是自己瞎编。3.2 图表类型的自动推荐规则最开始的时候我让LLM完全自由决定图表类型结果发现它经常选得不对。比如用户问“各地区销售额情况”它给个雷达图视觉上好看但信息表达效率其实不高。后来我在Prompt里加了显式的推荐逻辑效果一下子稳定了很多。推荐规则参考了传统BI工具的选择逻辑用户问题出现“趋势”“变化”“走势”等关键词优先折线图。出现“对比”“排名”“Top”等关键词或明确提到“柱状图”优先柱状图。出现“占比”“比例”“百分比”或单一维度多指标对比优先饼图或环形图。出现“分布”“区间”“密度”等关键词优先直方图或散点图。这个规则本质上是在给LLM一个“决策路径”比让它完全自由发挥要可靠得多。3.3 Few-shot示例的加入时机初期版本没有加入few-shot效果不稳定但勉强能用。但遇到复杂一点的查询比如“按区域和品类两个维度交叉分析销售额”模型输出的SQL字段排列和ECharts配置就可能出现混乱。后来我在Prompt里加入了两个典型示例一个是简单的时间趋势图一个是带钻取的环形图。示例会提多个不同场景的few-shot让模型对不同复杂度的问题都能找到参照。注意few-shot不是越多越好通常2到3个覆盖不同难度级别就够了多了会拉长输入Token并增加成本。加完few-shot之后配置生成的成功率明显上了一个台阶。特别是涉及多指标并列展示的场景模型能正确输出两个yAxis和两个series的配置结构。3.4 用户反馈修正的上下文利用多轮对话是这个系统的亮点但开始我也没想清楚怎么管理上下文。如果每轮都把全部历史记录丢给LLMToken消耗和响应延迟都受不了而且很容易导致模型被无关信息干扰。后来采用的方案是维护一个结构化会话状态每轮只保留三部分信息用户最近的输入、上一轮的配置修正指令、系统上一轮输出的图表配置摘要。在Prompt中以“历史修改记录”的形式拼接让模型知道用户上一次做了什么调整。比如用户第一轮问“看看各区域销售额柱状图”系统生成柱状图第二轮说“改成玫瑰图并且加上百分比”历史记录里就保留“上一轮为bar图用户希望改为pie玫瑰图并显示百分比”。这样模型就知道是在原有基础上修改而不是重新生成一个全新的图表上下文连贯性就有了保障。4. 核心实现逻辑与关键代码细节这一节是全文最硬核的部分。我会把后端接口、LLM输出解析、数据聚合映射、前端渲染这几个核心环节的代码逻辑完整走一遍附上关键代码段和踩坑说明。4.1 后端接口接收自然语言并返回结构化配置后端我用Python FastAPI实现整体是一个轻量级的代理服务。核心接口就一个接收用户输入和会话ID返回ECharts配置和状态信息。示例代码如下from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Optional, List, Dict, Any import json import uuid app FastAPI() class QueryRequest(BaseModel): message: str session_id: Optional[str] None class QueryResponse(BaseModel): session_id: str echarts_option: Dict[str, Any] sql_info: Dict[str, Any] raw_response: str # 模拟会话存储 session_store: Dict[str, List[Dict[str, str]]] {} app.post(/api/query, response_modelQueryResponse) async def handle_query(req: QueryRequest): if not req.session_id: req.session_id str(uuid.uuid4()) history session_store.get(req.session_id, []) # 调用核心处理函数 result process_natural_language_query(req.message, history) # 保存会话历史 history.append({role: user, content: req.message}) history.append({role: assistant, content: result[raw_response]}) session_store[req.session_id] history[-10:] # 只保留最近10条 return QueryResponse( session_idreq.session_id, echarts_optionresult[option], sql_inforesult[sql_info], raw_responseresult[raw_response] )这个接口设计比较朴素但足够用。需要注意两点一是会话存储简单用内存字典实现了真实项目建议换Redis否则重启会话就丢了二是历史记录做了长度限制避免无限增长导致Token成本失控。4.2 LLM输出解析从文本到JSON的可靠路径这是整个项目里最坑的地方。LLM返回的内容偶尔会比较不可控有时会非常规整地直接输出一个JSON但也有时候会在JSON前后加一些自然语言描述甚至在键名里加了没必要的引号或者把布尔值写成字符串。我封了一个解析函数来处理这类问题def parse_llm_json_response(text: str) - dict: # 1. 直接尝试解析 try: return json.loads(text) except json.JSONDecodeError: pass # 2. 尝试提取JSON块兼容 json 或 包裹 import re json_pattern r(?:json)?\s*(.*?)\s* match re.search(json_pattern, text, re.DOTALL) if match: try: return json.loads(match.group(1)) except json.JSONDecodeError: pass # 3. 尝试从文本中截取第一个 { 到最后一个 } 之间的内容 start_idx text.find({) end_idx text.rfind(}) 1 if start_idx ! -1 and end_idx start_idx: try: return json.loads(text[start_idx:end_idx]) except json.JSONDecodeError: pass # 4. 最后兜底抛出明确异常 raise ValueError(f无法从LLM输出中解析JSON: {text[:200]})实际测试下来这个四级解析方案能覆盖绝大多数情况。我强烈建议不要只做第一级直接解析就完事因为LLM输出的不确定性决定了你必须在解析层面做足够防御。4.3 数据映射SQL信息转成真实查询结果前面Prompt里要求LLM输出sql字段这个字段不是真正的SQL语句而是我自定义的一个结构化查询描述。例如{ query_columns: { dimensions: [region], metrics: [{field: total_amount, agg: SUM}] }, filter: { date_range: 最近3个月 } }后端拿到这个结构之后再拼接成真的SQL去查数据库。这样做的好处是LLM不需要知道具体的数据库表结构它只需要理解字段语义真正的SQL生成由代码完成可靠性和安全性都有保障。import sqlite3 def generate_sql(sql_info: dict) - str: dims sql_info.get(query_columns, {}).get(dimensions, []) metrics sql_info.get(query_columns, {}).get(metrics, []) dim_str , .join(dims) if dims else 1 metric_str , .join([f{m[agg]}({m[field]}) AS {m[field]}_{m[agg].lower()} for m in metrics]) sql fSELECT {dim_str}, {metric_str} FROM sales_data GROUP BY {dim_str} if dims else fSELECT {metric_str} FROM sales_data return sql这里有一个实际项目中很容易踩的坑如果用户只问“总销售额是多少”没有指定维度那GROUP BY子句就不能加否则会按一行返回没有意义的结果。所以我在generate_sql函数里做了判断没有任何维度时直接查询总指标这个逻辑务必不要忽略。4.4 ECharts配置的二次校正与降噪LLM直接生成的option虽然整体结构正确但偶尔会有一些影响渲染的细节问题。我写了一个配置文件的后处理函数专门处理常见的“低级错误”。def normalize_echarts_option(option: dict) - dict: # 确保基本配置项存在 option.setdefault(tooltip, {trigger: axis}) # 检测series是否为空列表 if not option.get(series): raise ValueError(图表配置中series为空无法渲染) # 修正xAxis/yAxis格式ECharts允许对象或数组但必须保证data存在 if xAxis in option and isinstance(option[xAxis], list): if len(option[xAxis]) 1: option[xAxis] option[xAxis][0] # 确保series.data与xAxis.data长度一致这是一个高频问题 x_data option.get(xAxis, {}).get(data, []) if isinstance(option.get(xAxis), dict) else [] if x_data: for series_item in option.get(series, []): data_len len(series_item.get(data, [])) if data_len len(x_data): series_item[data] series_item[data][:len(x_data)] return option这里最值得说的是xAxis.data和series.data长度不一致的问题。由于LLM生成的series.data有时候会来自SQL查询结果而SQL结果和维度列表之间存在顺序或数量上的错位这个不一致会导致图表显示不全或坐标轴错位。写一个硬性的截断对齐逻辑能避开不少渲染异常。4.5 前端渲染与配置修正接口前端这一端我选择了Vue 3 ECharts 5的组件化方案。核心逻辑就是接收后端返回的option塞进ECharts实例监听窗口变化做resize。// ChartRenderer.vue (关键代码片段) template div refchartEl stylewidth: 100%; height: 480px;/div /template script import * as echarts from echarts; export default { name: ChartRenderer, props: { option: { type: Object, required: true } }, data() { return { chart: null }; }, watch: { option: { deep: true, handler(newVal) { if (!this.chart) return; this.chart.setOption(newVal, true); } } }, mounted() { this.chart echarts.init(this.$refs.chartEl); this.chart.setOption(this.props.option, true); window.addEventListener(resize, this.handleResize); }, methods: { handleResize() { this.chart this.chart.resize(); } }, beforeUnmount() { window.removeEventListener(resize, this.handleResize); this.chart this.chart.dispose(); } }; /script注意一个细节setOption的第二个参数我传了true表示notMerge。这个在多次更新配置时很重要。如果不传ECharts会做merge操作可能出现上一次图表残留的问题比如柱状图切换为折线图时旧的柱子还在。传true以后每次全量覆盖配置更新就是干净的。5. 模型选型与成本控制策略LLM是这个系统的核心动力源选什么模型、怎么控制成本直接影响项目能否从Demo走向落地。这一节聊聊我实测下来的选型感受和优化手段。5.1 不同能力等级模型的表现差异实测了几款当前主流的商用和开源模型简单对比一个直观感受轻量级模型或同等级小参数模型能处理简单配置生成但复杂一点就经常出错。特别是多指标、双y轴、数据聚合逻辑容易把配置结构写乱。中端模型整体可用在加了few-shot之后解决常见图表生成问题表现不错。是这个系统的主力模型。高端旗舰模型流畅度和理解力确实更好但成本高出不少。适合用于复杂查询、多轮对话语义纠偏的场景不适合作为默认全量调用。具体到项目里我是按问题复杂度做模型路由的。简单问题单指标、单维度走轻量模型复杂问题多指标、多维分析、图表修改走中高端模型。这样既保住了体验又控制了成本。5.2 成本估算实例假设一个中等复杂度的查询输入Prompt约1200 Token输出配置约600 Token。一次调用的总Token约为1800 Token。按目前的Token计价折算下来单次约为千分之几元。如果日均调用1000次一个月成本在几十元到上百元之间完全在可接受范围内。但如果未来做到企业级、日调万次甚至更多就需要引入更激进的缓存策略和语义分割方案。成本控制我建议一定要做Token统计每个会话、每次请求都记录Token消耗。有了数据才知道哪里该优化不能拍脑袋省成本。5.3 缓存策略相同问题不重复计算用户经常会问重复或高度相似的问题比如“帮我看一下华东区销售额Top10的品类”。这个查询第一次执行后结果短时间内一般不会变化。所以我在后端加了一个简单但有效的缓存层。缓存Key由两部分组成session_id message的哈希值。有效期设个5分钟到10分钟。缓存命中直接返回上次的结果不再调用LLM和数据源。实测命中率大约在20%到30%之间整体响应速度和成本都优化了不少。5.4 流式输出的权衡很多LLM应用现在都搞流式输出但在图表生成场景里我一开始也做了后来发现收益不大。图表配置的生成速度和流式展示的体验提升对用户来说感知并不强。因为用户最终看到的是图表渲染结果而不是一行一行地看JSON。所以后端直接用同步接口返回完整配置前端加一个加载动画简洁且稳定。只有在制作长报告或复杂洞察分析时流式输出的价值才会体现出来。6. 常见问题与排查技巧实录这个项目做了这么久踩过的坑能写满一页A4纸。这一节把最高频的问题和排查思路整理成一个速查表按“现象 → 原因 → 做法”的结构罗列方便遇到问题直接对号入座。6.1 图表渲染空白但控制台无报错遇到这个问题我一开始一度怀疑是ECharts版本问题排查了半天发现不是。后来定位到原因通常有两个一是series.data为空数组ECharts不渲染空数据但也完全不报错二是xAxis.data和series.data长度不匹配导致某些数据点被丢弃。排查步骤建议固定为先把LLM返回的JSON打印出来检查series和xAxis的data结构。其次在后端对数据长度做一致性校验发现不一致时提前修正。6.2 生成的图表类型不符合用户预期用户说“看下销售情况”模型给出了饼图但用户其实想看的是柱状图。这类问题本质上不是bug而是意图理解不够精准。解决思路有三个层次最简单粗暴的在Prompt中设定“当用户未明确指定图表类型时默认使用柱状图”。较推荐的做法把图表类型选择权交给一个单独的“意图分类”步骤先判断用户想要的图表类型再让LLM生成具体配置。体验最好的做法前端提供图表类型切换按钮用户不满意时一键切换切换时只调整series类型和对应配置不需要重新生成。实际项目中我采用了第二和第三层的结合既在对话层做了预分类也在展示层做了快速切换兜底。6.3 某些字段被LLM凭空捏造这个问题在所有LLM应用中都会出现图表配置场景也不例外。模型在生成查询字段时偶尔会“发明”一些数据字典里根本不存在的字段名。比如数据字典里只有total_amount单输出结果却可能出现profit_rate这种虚构指标。这种问题的根源是Prompt数据字典的语义约束不够严格。我采取了三层防御Prompt里加了硬性限制“只能使用可用字段不得新增字段”。后端解析时加字段白名单校验一旦发现未知字段立即抛异常要求模型重新生成。加了重试机制最多重试两次用模型自己的上一个错误输出来纠正它。def validate_sql_info(sql_info: dict, allowed_fields: set) - bool: dims sql_info.get(query_columns, {}).get(dimensions, []) metrics sql_info.get(query_columns, {}).get(metrics, []) for dim in dims: if dim not in allowed_fields: return False for m in metrics: if m[field] not in allowed_fields: return False return True白名单校验看起来多做了一步实际上避免了非常多后续的数据查询报错和渲染异常。6.4 多轮对话的上下文污染多轮对话是整个系统里维护成本最高的部分。我遇到过一种情况用户第一轮问的是“华东区的销售额”第二轮问“那华南呢”模型把两轮问题合并成了一个查询或者按第一轮的维度约束了第二轮的查询最终结果不符合预期。排查后发现问题出在上下文拼接的方式上。我把完整的历史记录拼接后一次性发给模型模型理解重点发生了偏移。后来改成“历史修改指令”模式只保留上一轮的图表配置和用户最新的修改要求情况明显好转。再进一步可以在每轮会话记录时带上“当前数据范围”摘要模型在生成新查询时就有明确的基准。6.5 常见问题速查表现象可能原因处理方案前端图表空白series.data为空或长度不匹配打印JSON检查data后端做长度校准图表类型不符预期意图解析失败或Prompt缺乏倾向性增加类型预分类前端提供手动切换字段不存在模型幻觉白名单校验异常时重试多轮问题答非所问上下文过载或始终以历史为主结构化会话状态只保留修正指令生成的配置偶尔带Markdown标记模型输出格式不稳定解析时兼容包裹删除非JSON内容响应延迟明显调用链路太长/模型太重走缓存、模型路由、去掉流式输出6.6 一个被很多人忽略的细节代码块包裹的冗余处理即便我在Prompt里已经明确说了“不要使用Markdown代码块标记”模型有时候还是会在输出中把JSON包在代码块里。前端直接传给后端解析时会失败。这个问题不复杂但很影响体验。我的建议是后端解析的时候做了一层“防呆处理”不管模型输出什么格式都尝试提取其中的JSON部分。也就是前面提到的解析函数第2层和第3层这两层代码看着多余实则在真实项目中能省下很多沟通和debug的时间。7. 这个项目的边界与扩展可能性个人实操回顾做到这个程度这个系统基本达到了“业务人员能自己提问、自己看图”的效果。但我也很清楚它的边界在哪。数据字典预设意味着增加一个新的业务指标需要手工维护字段映射。图表类型虽然覆盖面够用但遇到非常规图表比如关系图、树图、地图时生成效果依然不稳定。这些限制不是LLM本身的问题而是工程上选择了“稳定优先”的路子。从我实操的角度看如果后续要继续演进我会优先考虑两个方向。第一是接上真实的数据库Schema自动发现机制让数据字典能动态更新减少人工维护成本。第二是引入“用户行为反馈信号”比如用户强行切换了图表类型、修改了取数范围这些行为可以作为隐含标注数据用于后续微调Prompt或模型。这两个方向本质都是为了让系统从“固定配方”走向“自适应理解”。做出来之后这个对话式数据分析系统的落地空间还会更大。8. 最后的几个实操建议文章写完之前我再分享三个实用的经验总结都是踩过坑换来的教训。第一个建议不要把LLM的JSON输出当成理所当然。无论Prompt写得多么严格解析层都要做兜底。模型毕竟是概率模型它的输出天然存在随机性工程上必须设计好容错机制。用模板加校验加重试的方式把不确定性限制在可控范围内。第二个建议在画界面之前先把Prompt和数据结构跑通。我一开始最先做的是前端以为把ECharts配好就完事了结果后端LLM输出的格式频繁调整导致前端也跟着改了两三回。如果先花时间把Prompt、输出Schema、解析逻辑确定下来再去做前端开发效率会高很多。第三个建议尽量保留每个请求的完整日志。LLM应用的调试难度远高于传统应用因为很多问题不是代码逻辑错误而是模型输出的语义偏差。完整记录Prompt输入、模型原始输出、解析后结果、最终渲染状态排查问题时能省一半以上时间。日志字段用JSON格式存储后面用来分析模型错误模式也能派上大用场。这个项目目前已经作为内部工具投入使用业务同学用得还挺顺手。如果你也在做类似的事情希望里面的经验和代码片段能帮你少走几段弯路。有新的想法也欢迎交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑