ERP数据查询技能
你可以通过HTTP接口查询ERP系统中的业务单据数据。所有请求需要在请求头中携带 X-MCP-SECRET 参数进行鉴权,该密钥与个人ERP账号一一对应,代表个人角色,MCP会根据角色查询相应的数据。
环境变量
本技能依赖以下环境变量,每个用户必须配置自己的密钥:
ERP_BASE_URL:ERP系统访问地址(如http://192.168.1.100:8080)APP_SECRET:你的MCP访问密钥(从ERP个人资料页面"生成MCP密钥"按钮获取)
⚠️ 重要:首次使用前必须配置
如果
ERP_BASE_URL或APP_SECRET未配置或为空,请停止执行,先引导用户完成配置:配置步骤(引导用户操作)
获取密钥:告诉用户登录 ERP 系统 → 个人资料页面 → 点击"生成MCP密钥"按钮 → 复制密钥
配置方式(二选一):
方式一:在对话中直接告诉 AI(推荐手机用户)
用户说:"我的ERP地址是 xxx,密钥是 xxx",AI 自动将配置写入
~/.codebuddy/settings.json的env字段。方式二:手动编辑配置文件
在
~/.codebuddy/settings.json中添加:{ "env": { "ERP_BASE_URL": "https://你的ERP地址", "APP_SECRET": "你的密钥" } }验证配置:配置完成后,调用
list_bill_types接口验证是否可用
所有 HTTP 请求中使用以下占位符(运行时自动从环境变量替换):
- 地址:
${ERP_BASE_URL} - 密钥:
${APP_SECRET}
可用工具
1. list_bill_types — 查询可访问的单据类型
查询ERP中当前用户可访问的所有单据类型。返回 billtypecode(单据类型编码)和 billtypename(单据类型名称)。
当用户询问的业务模块不明确时,先调用此接口查看有哪些可用单据类型。例如:用户说"销售合同",此接口可能返回 billtypecode="xsht", billtypename="销售合同"。
- 请求方式:GET
- URL:
${ERP_BASE_URL}/itf/common/mcp/listBillTypes.json - 请求头:
X-MCP-SECRET: ${APP_SECRET} - 参数:无
- 返回示例:
{
"success": true,
"data": [
{"billtypecode": "xsht", "billtypename": "销售合同", "ifdoc": false, "vmodulename": "销售管理"},
{"billtypecode": "cgdd", "billtypename": "采购订单", "ifdoc": false, "vmodulename": "采购管理"}
]
}
2. get_bill_fields — 查询单据字段元数据
查询指定单据类型的字段元数据。返回每个字段的 itemkey(字段编码)、showname(中文名)、datatype(数据类型)、listshow(是否列表显示)。
当需要知道"合同金额"对应哪个字段时,调用此接口查看。例如:用户说"金额",此接口可能返回 itemkey="ntotalamount", showname="合同总金额"。
- 请求方式:GET
- URL:
${ERP_BASE_URL}/itf/common/mcp/getBillFields.json?billtype=${billtype} - 请求头:
X-MCP-SECRET: ${APP_SECRET} - 参数:
billtype(必填):单据类型编码
- 返回示例:
{
"success": true,
"data": [
{"itemkey": "vbillcode", "showname": "单据编号", "datatype": 0, "listshow": true},
{"itemkey": "ddate", "showname": "签订日期", "datatype": 3, "listshow": true},
{"itemkey": "ntotalamount", "showname": "合同总金额", "datatype": 2, "listshow": true}
]
}
3. query_bill_list — 查询单据列表(含汇总行)
查询单据列表数据,返回记录列表和汇总行(summaryRow)。summaryRow 包含数值字段的合计值,如总金额。
支持 dateField + dateFrom + dateTo 进行日期范围过滤。支持 conditions 传入自定义查询条件(SQL WHERE片段,如 "billstatus=1")。
当用户问"XX金额是多少"时,调用此接口获取 summaryRow 中的合计值。返回数据中 fieldNames 是 itemkey → 中文名 的映射,帮助你理解字段含义。
- 请求方式:POST
- URL:
${ERP_BASE_URL}/itf/common/mcp/queryBillList.json - 请求头:
X-MCP-SECRET: ${APP_SECRET},Content-Type: application/json - 请求体:
{
"billtype": "xsht",
"pageNo": 1,
"pageSize": 50,
"dateField": "ddate",
"dateFrom": "2025-07-22",
"dateTo": "2026-07-22",
"conditions": "billstatus=1",
"orderBy": "ddate desc"
}
- 参数说明:
billtype(必填):单据类型编码pageNo:页码,默认1pageSize:每页条数,默认50,最大200dateField:日期过滤字段(itemkey,如"ddate")dateFrom:日期起(如"2025-07-22")dateTo:日期止(如"2026-07-22")conditions:自定义SQL条件(如"billstatus=1 AND ntotalamount>10000")orderBy:排序(如"ddate desc")
- 返回示例:
{
"success": true,
"data": {
"summaryRow": {"ntotalamount": 12345678.90},
"totalRecords": 56,
"records": [
{"vbillcode": "XSHT-2026-0001", "ddate": "2026-01-15", "ntotalamount": 50000.00, "billstatus": 1},
{"vbillcode": "XSHT-2026-0002", "ddate": "2026-02-20", "ntotalamount": 120000.00, "billstatus": 1}
],
"pageSize": 50,
"pageNo": 1,
"totalPage": 2,
"fieldNames": {"vbillcode": "单据编号", "ddate": "签订日期", "ntotalamount": "合同总金额", "billstatus": "单据状态"}
}
}
4. get_bill_detail — 查询单据明细
查询单据明细数据,返回完整的表头(HEADER)和表体(BODY)数据。当需要查看某张具体单据的详细信息时调用。
- 请求方式:GET
- URL:
${ERP_BASE_URL}/itf/common/mcp/getBillDetail.json?billtype=${billtype}&billid=${billid} - 请求头:
X-MCP-SECRET: ${APP_SECRET} - 参数:
billtype(必填):单据类型编码billid(必填):单据ID
- 返回示例:
{
"success": true,
"data": {
"billtype": "xsht",
"billid": "xxx-xxx-xxx",
"data": {
"HEADER": {"vbillcode": "XSHT-2026-0001", "ntotalamount": 50000.00, "ddate": "2026-01-15"},
"BODY": {"sale_order_b": [{"vbillcode_b": "XSHT-2026-0001-01", "nnum": 100, "nprice": 500.00}]}
},
"fieldNames": {"vbillcode": "单据编号", "ntotalamount": "合同总金额", "ddate": "签订日期"}
}
}
5. query_bill_summary — 聚合统计查询
对指定字段执行聚合统计(sum/count/avg/max/min),支持条件过滤。当用户问"总金额是多少""有多少张单据"时,可用此接口。也可以用 query_bill_list 的 summaryRow 获取合计,此接口更灵活。
- 请求方式:POST
- URL:
${ERP_BASE_URL}/itf/common/mcp/queryBillSummary.json - 请求头:
X-MCP-SECRET: ${APP_SECRET},Content-Type: application/json - 请求体:
{
"billtype": "xsht",
"aggregateField": "ntotalamount",
"aggregateType": "sum",
"dateField": "ddate",
"dateFrom": "2025-07-22",
"dateTo": "2026-07-22"
}
- 参数说明:
billtype(必填):单据类型编码aggregateField(必填):聚合字段(itemkey)aggregateType:聚合类型,默认sum。可选值:sum(求和)、count(计数)、avg(平均)、max(最大)、min(最小)dateField:日期过滤字段dateFrom:日期起dateTo:日期止conditions:自定义SQL条件
- 返回示例:
{
"success": true,
"data": {
"billtype": "xsht",
"billtypename": "销售合同",
"aggregateField": "ntotalamount",
"aggregateType": "sum",
"value": 12345678.90,
"fieldNames": {"ntotalamount": "合同总金额", "ddate": "签订日期"}
}
}
使用流程
第一步:确定单据类型
当用户提到"销售合同""采购订单"等业务名词时,先调用 list_bill_types 查看可用的单据类型,将用户的业务名词匹配到对应的 billtypecode。
例如:用户说"销售合同",在返回列表中找到 billtypename 包含"销售合同"的记录,获取其 billtypecode(如 "xsht")。
第二步:确定字段映射
当用户提到"金额""日期""客户"等字段名时,调用 get_bill_fields 查看该单据类型的字段列表,将用户的中文名匹配到对应的 itemkey。
例如:用户说"金额",在返回字段列表中找到 showname 包含"金额"的字段,获取其 itemkey(如 "ntotalamount")。
第三步:查询数据
- 用户问"XX金额是多少" → 调用
query_bill_list(设置日期范围),从返回的summaryRow中取合计值;或调用query_bill_summary(aggregateType=sum)直接获取聚合值。 - 用户问"列出最近XX的单据" → 调用
query_bill_list(设置日期范围和分页)。 - 用户问"某张单据的详情" → 先通过
query_bill_list找到billid,再调用get_bill_detail。 - 用户问"有多少张/数量" → 调用
query_bill_summary(aggregateType=count)。
第四步:分析数据
当用户要求"分析"时(如"分析销售合同的风险情况"),先调用 query_bill_list 获取足够的数据,然后你(LLM)根据返回的数据进行推理分析,输出分析报告。ERP不提供分析能力,分析由你完成。
第五步:可视化数据
查询结果默认应该用图表呈现,而不是仅输出表格。当返回的 records 含有可聚合的数值字段(金额、数量等)或可分组的维度字段(客户、状态、日期、物料等)时,主动调用 show_widget 工具渲染 Chart.js 图表,让用户像用 BI 工具一样直观看到数据。
详细规则和代码模板见下方「数据可视化」章节。
数据可视化
何时可视化(触发规则)
| 场景 | 是否可视化 | 说明 | |------|-----------|------| | 用户问"总金额是多少""有多少张" | ❌ 不画图 | 单一数值,文字回答即可 | | 用户问"列出最近 N 条"且 N ≤ 5 | ❌ 不画图 | 数据太少,表格更清晰 | | 返回记录 ≥ 3 条且含金额/数量字段 | ✅ 画图 | 至少画一张主图 | | 用户说"分析""趋势""分布""占比""排名""对比" | ✅ 画图 | 按意图选图 | | 用户说"画图""可视化""chart" | ✅ 画图 | 明确要求 | | 用户问"某张单据的详情" | ❌ 不画图 | 单据详情用文字/表格 |
图表类型选择
根据用户意图和数据形状选择:
| 用户意图 | 数据形状 | 推荐图表 | 示例问题 | |---------|---------|---------|---------| | 看占比/分布 | 1 个维度 + 1 个数值 | 饼图/环形图 | "各客户合同金额占比" | | 看排名/对比 | 1 个维度 + 1 个数值,维度 ≤ 15 | 水平柱状图 | "合同金额 TOP 10 客户" | | 看趋势 | 时间维度 + 1 个数值 | 折线图 | "近一年销售合同月度趋势" | | 看多维度对比 | 2 个维度 + 1 个数值 | 分组柱状图 | "各客户各月合同金额" | | 看状态分布 | 1 个状态维度 + 计数 | 环形图 | "销售合同状态分布" | | 看累计进度 | 时间 + 累计数值 | 折线图(填充) | "累计合同金额增长" |
默认策略:如果用户没有明确指定图表类型,且数据适合可视化,优先画一张水平柱状图(金额按维度排名),因为排名图信息密度最高、最实用。
数据转换规则
ERP 返回的 records 是字段 Map 数组,需转换为 Chart.js 数据集:
1. 按维度聚合(分组求和)
# Python 伪代码:按客户聚合合同总额
from collections import defaultdict
agg = defaultdict(float)
for r in records:
key = r.get('customer_name', '未知') # 维度字段
val = float(r.get('total_contract_amount', 0) or 0) # 数值字段
agg[key] += val
# 排序取 TOP N
sorted_items = sorted(agg.items(), key=lambda x: -x[1])[:10]
labels = [x[0] for x in sorted_items]
values = [x[1] for x in sorted_items]
2. 按时间分组(月度趋势)
from collections import defaultdict
monthly = defaultdict(float)
for r in records:
d = r.get('signingdate', '')[:7] # 'YYYY-MM'
if d:
monthly[d] += float(r.get('total_contract_amount', 0) or 0)
# 按月份排序
labels = sorted(monthly.keys())
values = [monthly[k] for k in labels]
3. 数值字段清洗
- ERP 数值字段可能返回字符串带千分位(如
"10,400,002.00"),需float(str(val).replace(',', '')) None/""/"null"统一当0处理
调用 show_widget 的规范
调用 show_widget 工具时:
- 先调用
read_me加载chart模块(首次画图前必须调用一次) widget_code传纯 HTML 片段(含<canvas>+<script>),不要包含<!DOCTYPE>、<html>、<head>、<body>title参数用简短中文描述(如"销售合同客户金额分布")loading_messages传 1-4 条中文加载提示
颜色规范
Canvas 无法读取 CSS 变量,必须用硬编码十六进制色值。从以下色板中选取(light 主题:50 填充 + 600 描边/文字):
| 色系 | 填充(50) | 主色(600) | 深色(800) | |------|---------|----------|----------| | 蓝 c-blue | #E6F1FB | #185FA5 | #0C447C | | 紫 c-purple | #EEEDFE | #534AB7 | #3C3489 | | 青 c-teal | #E1F5EE | #0F6E56 | #085041 | | 珊瑚 c-coral | #FAECE7 | #993C1D | #712B13 | | 粉 c-pink | #FBEAF0 | #993556 | #72243E | | 绿 c-green | #EAF3DE | #3B6D11 | #27500A | | 琥珀 c-amber | #FAEEDA | #854F0B | #633806 | | 红 c-red | #FCEBEB | #A32D2D | #791F1F | | 灰 c-gray | #F1EFE8 | #5F5E5A | #444441 |
多色系列(饼图/环形图按顺序循环使用):
const palette = ['#185FA5', '#534AB7', '#0F6E56', '#993C1D', '#993556', '#3B6D11', '#854F0B', '#A32D2D', '#5F5E5A'];
图表模板
模板 1:水平柱状图(金额排名 TOP N)
适用:合同金额按客户/物料/业务员排名。values 已按降序排好。
<div style="position: relative; width: 100%; height: 420px;">
<canvas id="erpChart" role="img" aria-label="合同金额按客户排名水平柱状图">各客户合同金额排名柱状图。</canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script>
const labels = /* ['客户A','客户B','客户C', ...] */;
const values = /* [10400002, 105787, 93000, ...] */;
const palette = ['#185FA5','#534AB7','#0F6E56','#993C1D','#993556','#3B6D11','#854F0B','#A32D2D','#5F5E5A'];
new Chart(document.getElementById('erpChart'), {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: '合同金额',
data: values,
backgroundColor: values.map((_, i) => palette[i % palette.length] + 'CC'),
borderColor: values.map((_, i) => palette[i % palette.length]),
borderWidth: 1
}]
},
options: {
indexAxis: 'y',
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: function(ctx) {
return ' ' + ctx.parsed.x.toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
}
}
},
scales: {
x: {
ticks: { callback: function(v) { return (v/10000).toFixed(0) + '万'; } },
grid: { color: 'rgba(0,0,0,0.06)' }
},
y: { grid: { display: false } }
}
}
});
</script>
模板 2:环形图(占比分布)
适用:客户占比、状态分布、币种分布。
<div style="position: relative; width: 100%; height: 320px;">
<canvas id="erpChart" role="img" aria-label="合同金额按客户占比环形图">各客户合同金额占比环形图。</canvas>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; font-size: 12px; color: #5F5E5A;">
<!-- 图例由 AI 根据数据生成,示例: -->
<span style="display: flex; align-items: center; gap: 4px;">
<span style="width: 10px; height: 10px; border-radius: 2px; background: #185FA5;"></span>客户A
</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script>
const labels = /* ['客户A','客户B','客户C'] */;
const values = /* [10400002, 105787, 93000] */;
const palette = ['#185FA5','#534AB7','#0F6E56','#993C1D','#993556','#3B6D11','#854F0B','#A32D2D','#5F5E5A'];
new Chart(document.getElementById('erpChart'), {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: values,
backgroundColor: values.map((_, i) => palette[i % palette.length]),
borderColor: '#fff',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '60%',
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: function(ctx) {
const total = ctx.dataset.data.reduce((a,b) => a+b, 0);
const pct = (ctx.parsed / total * 100).toFixed(1);
return ' ' + ctx.label + ': ' + ctx.parsed.toLocaleString('zh-CN') + ' (' + pct + '%)';
}
}
}
}
}
});
</script>
模板 3:折线图(时间趋势)
适用:月度/季度合同金额趋势。
<div style="position: relative; width: 100%; height: 320px;">
<canvas id="erpChart" role="img" aria-label="合同金额月度趋势折线图">合同金额月度趋势折线图。</canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script>
const labels = /* ['2025-01','2025-02','2025-03', ...] */;
const values = /* [50000, 120000, 80000, ...] */;
new Chart(document.getElementById('erpChart'), {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '合同金额',
data: values,
borderColor: '#185FA5',
backgroundColor: 'rgba(24,95,165,0.1)',
borderWidth: 2,
fill: true,
tension: 0.3,
pointBackgroundColor: '#185FA5',
pointRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: function(ctx) {
return ' ' + ctx.parsed.y.toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2});
}
}
}
},
scales: {
y: {
ticks: { callback: function(v) { return (v/10000).toFixed(0) + '万'; } },
grid: { color: 'rgba(0,0,0,0.06)' }
},
x: { grid: { display: false } }
}
}
});
</script>
模板 4:分组柱状图(双维度对比)
适用:各客户在各月份的合同金额对比。datasets 为每个维度一条数据集。
<div style="position: relative; width: 100%; height: 360px;">
<canvas id="erpChart" role="img" aria-label="各客户各月合同金额分组柱状图">各客户各月合同金额分组柱状图。</canvas>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; font-size: 12px; color: #5F5E5A;">
<!-- 图例由 AI 生成 -->
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
<script>
const months = /* ['2025-05','2025-06','2025-07'] */;
const palette = ['#185FA5','#534AB7','#0F6E56','#993C1D','#993556'];
const datasets = /* [
{ label: '客户A', data: [50000, 80000, 10400002] },
{ label: '客户B', data: [0, 105787, 93000] }
] */;
new Chart(document.getElementById('erpChart'), {
type: 'bar',
data: {
labels: months,
datasets: datasets.map((ds, i) => ({
label: ds.label,
data: ds.data,
backgroundColor: palette[i % palette.length] + 'CC',
borderColor: palette[i % palette.length],
borderWidth: 1
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
y: {
ticks: { callback: function(v) { return (v/10000).toFixed(0) + '万'; } },
grid: { color: 'rgba(0,0,0,0.06)' }
},
x: { grid: { display: false } }
}
}
});
</script>
多图组合
复杂分析场景可连续调用多次 show_widget,每次画一张图,图与图之间用文字过渡。例如分析销售合同时:
- 先画环形图(客户分布占比)
- 文字过渡:"下面是各客户的合同金额排名详情..."
- 再画水平柱状图(金额 TOP 排名)
- 文字过渡:"从时间趋势来看..."
- 最后画折线图(月度趋势)
不要把多张图塞进同一个 widget,每张图独立调用。
金额格式化
图表中的金额轴和 tooltip 统一用中文习惯:
- 万级:
(v/10000).toFixed(0) + '万' - 亿级:
(v/100000000).toFixed(2) + '亿' - tooltip 原始值:
v.toLocaleString('zh-CN', {minimumFractionDigits: 2, maximumFractionDigits: 2})
完整工作流示例
用户问"分析最近一年销售合同的客户分布和金额趋势":
list_bill_types→ 找到salesorderget_bill_fields→ 确认signingdate(合同日期)、customer_name(客户)、total_contract_amount(合同总额)等字段query_bill_list(pageSize=200,日期范围=最近一年)→ 获取 records- Python 脚本聚合:按客户分组求和、按月分组求和
read_me(modules=["chart"])→ 加载图表模块show_widget画环形图(客户占比)- 文字过渡
show_widget画折线图(月度趋势)- 文字总结分析结论
日期处理规则
- "最近一年" →
dateFrom= 今天减一年(YYYY-MM-DD),dateTo= 今天 - "本月" →
dateFrom= 本月1号,dateTo= 今天 - "本季度" →
dateFrom= 本季度第一天,dateTo= 今天 - "今年" →
dateFrom= 今年1月1日,dateTo= 今天 - 日期格式始终为
YYYY-MM-DD dateField通常是签订日期(如"ddate"),通过get_bill_fields确认具体字段名
返回数据理解
records:单据记录数组,每条记录是一个字段Map(key是itemkey)summaryRow:汇总行,包含数值字段的合计(如ntotalamount: 12345678.90)totalRecords:符合条件的总记录数fieldNames:itemkey → 中文名 的映射,帮助你理解字段含义value(query_bill_summary返回):聚合计算结果
注意事项
- 你只能查询到当前用户角色权限范围内的数据(由
APP_SECRET决定) - 如果查询结果为空,告知用户可能没有权限或没有符合条件的单据
- 分析类问题需要你基于数据自行推理,ERP不提供分析能力
conditions参数只支持简单的 SQL WHERE 条件(如"billstatus=1 AND ntotalamount>10000"),不支持子查询、注释、分号等- 金额类字段返回的是数值,需要你格式化为人类可读的形式(如
12345678.90→1234.57万元) - 如果用户没有指定单据类型,先调用
list_bill_types查看有哪些可用模块 - 如果用户没有指定日期范围,默认查询全部数据(不传
dateField/dateFrom/dateTo) - 所有请求必须携带请求头
X-MCP-SECRET,值为你的APP_SECRET - 可视化优先:查询返回 ≥3 条记录且含数值字段时,应主动用
show_widget画图,而非仅输出表格 - 画图前必须先调用
read_me(modules=["chart"])加载图表设计规范,否则可能违反样式规则 - 数值清洗:ERP 数值字段可能返回带千分位的字符串(如
"10,400,002.00"),画图前必须float(str(v).replace(',',''))转为数字 - Canvas 硬编码颜色:Chart.js 的 canvas 无法读取 CSS 变量,所有颜色必须用十六进制硬编码
- 多图分多次调用:不要把多张图塞进一个 widget,每张图独立调用
show_widget,图之间用文字过渡
微信扫一扫