多 Agent 数据分析图表工具分层暴露方案总结
1. 项目背景
在多 Agent 数据分析系统中,项目内包含 26 种数据分析图表工具,例如折线图、柱状图、饼图、散点图、热力图、箱线图、桑基图等。
如果将 26 个图表工具全部平铺暴露给大模型,每个工具都包含完整的 description、参数 schema 和调用示例,会带来明显问题:
- 大量占用大模型上下文窗口;
- 相似图表工具之间容易误选;
- 工具列表扩展后维护成本高;
- 模型推理链路变长,调用稳定性下降;
- 无法清晰区分"图表意图判断"和"具体工具执行"。
因此,需要对图表工具进行二次封装,设计一套分层暴露和动态加载机制。
2. 核心项目难点
2.1 工具数量多导致上下文膨胀
26 个图表工具如果直接作为 tools 暴露给大模型,每个工具都需要携带说明、参数、字段约束和示例,很容易占用大量上下文。
这会导致模型真正用于理解用户问题和数据语义的上下文空间被压缩。
2.2 相似图表之间容易混淆
很多图表语义接近,例如:
- 折线图、面积图、多折线图;
- 柱状图、条形图、分组柱状图、堆叠柱状图;
- 饼图、环图、堆叠图;
- 散点图、气泡图、热力图。
如果一次性暴露全部工具,模型容易在相似工具之间摇摆。
2.3 用户意图需要结合数据结构判断
用户请求中的关键词并不总是准确表达图表类型。
例如:
text
帮我比较今年和去年的销售额变化趋势
其中同时包含:
- 比较:可能指向柱状图类;
- 变化趋势:可能指向折线图类。
因此不能只依赖用户文本,还需要结合数据字段 profile,例如是否存在时间字段、类别字段、数值字段等。
2.4 分类成本和准确率需要平衡
纯规则分类速度快、成本低,但泛化能力有限。
纯 LLM 分类效果更好,但每次调用都会产生 token 成本和延迟。
因此更合理的方案是使用级联分类架构:
text
规则触发词 → Embedding/小模型分类 → LLM 极简兜底
2.5 工具 Schema 需要按需加载
完整的图表参数 schema 只有在确定具体图表类型后才需要加载。
例如,只有模型最终选择了 line_chart,才需要加载折线图的完整参数定义。
这样可以避免所有图表 schema 一次性进入上下文。
2.6 渲染工具需要统一治理
底层可以有 26 个真实 renderer,但不应全部直接暴露给大模型。
更好的方式是提供一个统一入口:
text
render_chart(tool_name, arguments)
由后端根据 tool_name 路由到真实图表渲染器,并统一做参数校验、错误处理和执行治理。
3. 最佳实践架构
推荐采用:
text
级联分类器 + 多层 Tools 动态加载
整体链路如下:
text
用户请求 + 数据字段 Profile
↓
级联分类器
↓
得到 chart_family
↓
加载该图表组下的候选工具
↓
选择具体 chart_tool
↓
按需加载 chart_tool 的完整 schema
↓
生成图表参数
↓
调用统一 render_chart
↓
路由到底层真实 renderer
4. 图表分类设计
可以先将 26 个图表工具按照分析意图进行分组。
示例分类:
| 图表组 | 适用场景 | 示例图表 |
|---|---|---|
| trend | 趋势变化、时间序列、走势、波动 | 折线图、面积图、多折线图 |
| comparison | 类别对比、排名、最高、最低 | 柱状图、条形图、分组柱状图 |
| distribution | 分布、频率、范围、异常值、四分位 | 直方图、箱线图、密度图 |
| relationship | 变量关系、相关性、两个指标之间的关系 | 散点图、气泡图、热力图 |
| composition | 占比、构成、比例、份额 | 饼图、环图、堆叠图 |
| geo | 地理空间、区域分布 | 地图、区域热力图 |
| advanced | 特殊业务分析链路 | 桑基图、漏斗图、树图 |
5. 级联分类器设计
5.1 第一层:规则触发词
为每个图表组维护触发词。
示例:
text
trend:
趋势、变化、随时间、时间序列、走势、波动、增长、下降
comparison:
比较、对比、排名、最高、最低、Top、前十、差异
distribution:
分布、频率、范围、异常值、四分位、集中、离散
relationship:
关系、相关、相关性、影响、两个指标
composition:
占比、比例、份额、构成、组成
不建议简单地"命中即返回",更好的方式是打分并计算置信度。
当 top1 和 top2 的得分接近时,进入下一层分类。
5.2 第二层:Embedding 或小模型分类
如果规则分类不确定,可以使用 Embedding 分类或小模型分类。
冷启动阶段更建议先使用 Embedding 分类:
text
每个 chart_family 准备若干典型请求样例;
对用户 query 和样例做 embedding;
计算相似度;
选择相似度最高的图表组。
当积累了足够多真实用户请求和图表选择日志后,再考虑训练 BERT/RoBERTa 等小模型分类器。
5.3 第三层:LLM 极简兜底
当规则和 Embedding 都不确定时,使用一次极短 prompt 的 LLM 分类。
Prompt 中只包含:
- 图表组名称;
- 每个图表组的一句话描述;
- 用户请求;
- 数据字段 profile。
要求模型只返回结构化结果:
json
{
"family": "trend",
"confidence": 0.82,
"reason": "用户关注过去 12 个月的销售额变化,且数据包含时间字段和数值字段。"
}
6. 多层 Tools 动态加载设计
不直接暴露 26 个图表工具,而是暴露少量索引工具。
推荐工具:
text
list_chart_tools(family)
get_chart_tool_schema(tool_name)
render_chart(tool_name, arguments)
可选工具:
text
list_chart_families()
如果系统已经通过级联分类器得到 chart_family,则可以不再让大模型调用 list_chart_families()。
6.1 list_chart_tools
根据图表大类返回该类下可选工具。
例如:
json
{
"family": "trend",
"tools": [
"line_chart",
"area_chart",
"multi_line_chart"
]
}
6.2 get_chart_tool_schema
只有当模型选择了具体图表后,才返回完整参数 schema。
例如:
json
{
"tool_name": "line_chart",
"description": "展示时间或有序维度上的趋势变化。",
"required": {
"x": "date | ordinal field",
"y": "number field"
},
"optional": {
"series": "category field",
"smooth": "boolean"
}
}
6.3 render_chart
统一渲染入口。
示例:
json
{
"tool_name": "line_chart",
"arguments": {
"x": "month",
"y": "sales",
"series": "region"
}
}
后端根据 tool_name 路由到真实 renderer:
text
line_chart → render_line_chart
bar_chart → render_bar_chart
pie_chart → render_pie_chart
7. 架构收益
7.1 降低上下文占用
模型初始只需要看到少量索引工具,不需要同时看到 26 个图表工具的完整说明和 schema。
7.2 提升工具选择稳定性
先通过图表组缩小候选范围,再在组内选择具体工具,可以减少相似图表之间的误选。
7.3 降低调用成本
高置信度场景可以由规则或 Embedding 直接完成分类,不需要每次都调用 LLM。
7.4 提升可维护性
新增图表时,只需要更新图表注册表和对应 schema,不需要修改主推理流程。
7.5 方便评估和监控
可以分别评估:
- 图表组分类准确率;
- 具体图表选择准确率;
- schema 参数生成成功率;
- render_chart 渲染成功率;
- 用户重试率和澄清率。
7.6 评估指标拆解
图表组分类准确率
评估级联分类器是否把用户请求分到了正确的大类。
例如:
text
用户请求:查看过去 12 个月销售额变化趋势
期望图表组:trend
实际图表组:trend
结果:正确
该指标主要衡量:
text
规则触发词、Embedding/小模型分类、LLM 兜底分类是否能正确识别用户分析意图。
计算方式:
text
图表组分类准确率 = 正确分类的请求数 / 总请求数
可以按分类来源进一步拆分:
text
rule 分类准确率
embedding 分类准确率
llm_fallback 分类准确率
这样可以判断哪一层分类器最容易出错。
具体图表选择准确率
评估模型在已确定图表组后,是否选择了正确的具体图表。
例如:
text
chart_family = trend
候选工具:line_chart、area_chart、multi_line_chart
用户请求:展示不同地区销售额随月份的变化
期望图表:multi_line_chart 或 line_chart + series
实际图表:line_chart + series
结果:正确
该指标主要衡量:
text
模型在组内工具选择时,是否能区分相似图表。
计算方式:
text
具体图表选择准确率 = 具体图表选择正确的请求数 / 已完成图表组分类的请求数
如果该指标低,说明:
- 组内工具说明不够清晰;
- 相似图表边界不明确;
- 需要增加 when_to_use 和 avoid_when;
- 需要加入更明确的数据字段约束。
Schema 参数生成成功率
评估模型在拿到具体图表 schema 后,是否能生成符合要求的参数。
例如折线图 schema 要求:
json
{
"x": "date field",
"y": "number field",
"series": "optional category field"
}
模型生成:
json
{
"x": "month",
"y": "sales",
"series": "region"
}
如果字段存在、类型正确、必填项完整,则认为成功。
计算方式:
text
Schema 参数生成成功率 = 参数校验通过次数 / 参数生成总次数
该指标主要衡量:
text
schema 设计是否清晰,模型是否能正确将用户意图映射到字段参数。
如果该指标低,通常需要优化:
- 字段类型描述;
- required/optional 边界;
- 参数示例;
- 错误反馈和重试机制。
render_chart 渲染成功率
评估统一渲染入口是否成功执行。
即:
text
模型已经选择 tool_name 并生成 arguments 后,render_chart 是否能成功返回图表结果。
计算方式:
text
render_chart 渲染成功率 = 渲染成功次数 / render_chart 调用总次数
失败原因可以进一步分类:
- 参数缺失;
- 字段不存在;
- 字段类型不匹配;
- 数据为空;
- renderer 内部异常;
- 前端渲染失败;
- 图表配置不兼容。
该指标主要衡量:
text
工具执行链路的工程稳定性。
用户重试率和澄清率
用户重试率衡量用户是否需要重新表达需求。
计算方式:
text
用户重试率 = 同一任务中用户重新提问或修正图表请求的次数 / 总任务数
例如:
text
第一次:帮我画销售额变化
第二次:不是这个,我想看不同地区的对比
这说明前一次图表意图可能理解错了。
澄清率衡量系统主动向用户提问的频率。
计算方式:
text
澄清率 = 系统发起澄清问题的次数 / 总请求数
例如:
text
你是想看随时间变化的趋势,还是不同类别之间的对比?
澄清率不是越低越好,也不是越高越好。
合理目标是:
text
高置信度场景自动执行;
低置信度或多组冲突场景再澄清。
这两个指标主要衡量:
text
用户体验和系统自动决策边界是否合理。
8. 简历表述
版本一:简洁版
text
设计并实现多 Agent 数据分析场景下的图表工具动态路由机制,将 26 类可视化工具从平铺式暴露改造为"意图分类器 + 分层工具注册表 + 按需 Schema 加载 + 统一渲染路由"的架构,降低大模型上下文占用并提升工具选择稳定性。
版本二:工程细节版
text
负责数据分析 Agent 的可视化工具治理,将 26 个图表工具按趋势、对比、分布、关系、构成等维度进行二次封装;通过规则触发词、数据字段 Profile、Embedding/小模型分类和 LLM 极简兜底的级联分类策略,先定位图表大类,再动态加载对应工具说明和参数 Schema,避免一次性暴露全部工具造成上下文膨胀。
版本三:偏架构版
text
构建图表工具分层暴露与动态加载架构,将大模型从直接面对 26 个工具的平铺调用模式,改造为"图表意图路由 → 组内工具选择 → Schema 按需加载 → 统一渲染执行"的分层决策链路,提升多 Agent 数据分析系统的工具调用准确性、可扩展性和上下文利用效率。
9. 面试表达
可以这样介绍:
text
我们项目里有 26 种数据分析图表,如果全部作为 tools 暴露给大模型,会有两个问题。
第一是上下文占用很高,因为每个图表都要带 description、参数 schema 和示例。
第二是工具选择不稳定,因为很多图表语义很接近,比如折线图、面积图、多折线图,或者柱状图、条形图、堆叠柱状图。
所以我做了一个分层工具路由机制。
第一层不是直接暴露所有图表,而是先做图表意图分类,判断用户是要看趋势、对比、分布、关系还是占比。
这个分类不是完全依赖大模型,而是用级联方案:
先用规则触发词快速命中;
如果规则不确定,再结合数据字段 profile 或 Embedding 分类;
最后才用一个极短 prompt 的 LLM 分类兜底。
分类完成后,只把对应图表组下的工具暴露给大模型。
比如判断是趋势类,就只加载折线图、面积图、多折线图的简短说明。
模型选中具体图表后,再按需加载这个图表的完整参数 schema,然后通过统一的 render_chart 路由到真实渲染工具。
这个架构的好处是把"图表意图分类"和"具体工具调用"拆开了。
前者追求快和低成本,后者追求 schema 准确和执行稳定。
最终既减少了 token 消耗,也降低了大模型工具选择错误的概率。
10. 一句话总结
text
这个方案本质上是把大模型从"直接工具执行者"改造成"分层决策者",把高频、确定性的路由逻辑前置到规则和小模型中,把复杂语义判断留给 LLM,从而在成本、上下文占用和工具调用稳定性之间取得平衡。