| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125 |
- 'use strict';
- /**
- * 智能问数智能体 · 工具注册表
- * ----------------------------------------------------------------------------
- * 清单(manifestText,注入系统提示)与派发(get)单一事实源。
- * 模型给出的工具名先在此求交(未注册即拒绝并回注可用清单,防幻觉工具名),
- * 参数再经各工具 sanitize 钳制。本期全部工具只读(湖查询/校验),无写操作。
- */
- const lake = require('./lake.js');
- const compare = require('./compare.js');
- const cross = require('./cross.js');
- const peer = require('./peer.js');
- const entity = require('./entity.js');
- const cloud = require('./cloud.js');
- const report = require('./report.js');
- const metric = require('./metric.js');
- const metrics = require('../metrics.js');
- const anomaly = require('./anomaly.js');
- const attribution = require('./attribution.js');
- const TOOLS = [
- {
- name: 'lake_overview',
- desc: '摸底当前主体:门店/连锁清单、湖内可用日期区间、各粒度行数、最近一次同步结果。分析类问题的第一步,先看有什么数据再取数。',
- argsDoc: '{}(无参数)',
- sanitize: lake.overviewSanitize,
- handler: lake.overviewHandler,
- },
- {
- name: 'query_report',
- desc: '查经营数据。mode=detail 日期×门店明细 / mode=shop 按门店汇总(排行/TopN)/ mode=date 按日期汇总(趋势)。区间自动钳制在湖内(截止默认昨天)。',
- argsDoc: '{"mode":"shop","from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名,可部分匹配"],"metrics":["grossAmt","validOrderCnt"],"sort":"grossAmt","sortDir":"desc","limit":10}(from/to/metrics 缺省=近30天/营业额)',
- sanitize: lake.querySanitize,
- handler: lake.queryHandler,
- },
- {
- name: 'compare_periods',
- desc: '同比/环比对比:本期 vs 等长基期。自动做门店集合对齐(开/闭店单列)、比率加权、缺失日清单、收入+支出=营业额恒等式校验。凡问「变化/对比/为什么涨跌」一律用它,不要自己相减。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","gap":0,"metrics":["grossAmt","validOrderCnt"],"shops":["门店名"]}(缺省=湖内最后7天 vs 紧邻前7天;gap=两期间隔天数,同比可用 gap 留空+自定 from/to 跨年)',
- sanitize: compare.compareSanitize,
- handler: compare.compareHandler,
- },
- {
- name: 'data_quality',
- desc: '数据体检:门店明细全零行、层间一致性(门店合计 vs 连锁 vs 公司)、恒等式、日期连续性、预留列状态、最近同步结果。回答前对数据可信度存疑时调用。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
- sanitize: lake.qualitySanitize,
- handler: lake.qualityHandler,
- },
- {
- name: 'cross_check',
- desc: '层间一致性核对(可单独抽查指定区间):按日核对 门店合计 vs 连锁合计 vs 公司行(营业额+有效订单),>0.5% 容差记不一致。环比/对比结论出来前建议抽查一次。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
- sanitize: cross.crossSanitize,
- handler: cross.crossHandler,
- },
- {
- name: 'raw_inspect',
- desc: '查某行 raw 原始字段(取数接口自带:环比差值/上期对比/同行均值排名),用于源侧交叉验证——本地算的数字 vs 接口自带的数字。字段按「包含匹配」取,命名以接口原样为准。',
- argsDoc: '{"date":"YYYYMMDD","shops":["门店名"],"keys":["gross_amt"]}(keys 缺省匹配营业额与单量相关字段)',
- sanitize: peer.rawSanitize,
- handler: peer.rawHandler,
- },
- {
- name: 'peer_benchmark',
- desc: '同行对标:门店自身值 vs 同行均值 vs 排名(来自全字段落库的 raw *_mall_avg/_rank)。凡问「对标/排名/同行」一律用它。',
- argsDoc: '{"field":"gross_amt","from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名"]}(field 蛇形命名,可用:valid_order_cnt/gross_amt/income_amt/expense_amt/exp_uv/order_user_cnt/clk_exp_rate/ord_clk_rate/per_actual_pay 等)',
- sanitize: peer.benchSanitize,
- handler: peer.benchHandler,
- },
- {
- name: 'cross_entity',
- desc: '跨主体(跨校/多账号)对比:湖内全部主体本期 vs 基期的营业额与有效订单。凡问「几家店/哪个食堂/跨校」类问题用它。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=当前主体湖内最后 7 天)',
- sanitize: entity.entitySanitize,
- handler: entity.entityHandler,
- },
- {
- name: 'aitable_verify',
- desc: '湖↔云只读比对:本地数据湖 vs 钉钉 AI 表格三表(行数 + 营业额抽样值)。凡结论涉及「云端数据是否同步/两边数据是否一致」时用它;云端未启用时优雅降级。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
- sanitize: cloud.cloudSanitize,
- handler: cloud.cloudHandler,
- },
- {
- name: 'metric_query',
- desc: '按指标查逐日序列(含派生指标如 7 日移动平均)。聚合口径由指标层决定:比率加权、客单价比值、移动平均都不许自己算。要「移动平均/平滑/去噪」视图时用它。',
- argsDoc: '{"metric":"gross_mov7","from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名"]}(metric 可用:grossAmt/validOrderCnt/incomeAmt/expenseAmt/expUv/orderUserCnt/clkExpRate/ordClkRate/perActualPay/gross_mov7/orders_mov7/uv_mov7)',
- sanitize: metric.metricSanitize,
- handler: metric.metricHandler,
- },
- {
- name: 'detect_anomalies',
- desc: '统计异常检测:加性指标逐日序列的 leave-one-out z-score(σ 下限防误报),|z|≥2 警告、≥3 不一致;区间含寒暑假会提示基线失真。凡问「哪天不对劲/异常波动」用它。',
- argsDoc: '{"metric":"grossAmt","from":"YYYYMMDD","to":"YYYYMMDD","k":2}(metric 可用 grossAmt/validOrderCnt/incomeAmt/expUv/orderUserCnt)',
- sanitize: anomaly.anomalySanitize,
- handler: anomaly.anomalyHandler,
- },
- {
- name: 'attribution',
- desc: '营业额归因(LMDI):把环比变化分解为 曝光效应/进店率效应/客单价效应,给出各因素贡献金额与主因。凡问「为什么涨跌/谁拉动谁拖累」在 compare_periods 之后用它。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名"]}(缺省=湖内最后 7 天;单店口径恒等式严格成立)',
- sanitize: attribution.attributionSanitize,
- handler: attribution.attributionHandler,
- },
- {
- name: 'build_report',
- desc: '生成经营分析 HTML 报告。deep=false 基础版(KPI/趋势/明细);deep=true 深度版(另含数据体检/统计异常/归因/同行对标章节)。用户要「报告/周报/导出」时调用,回答中提示点击「查看分析报告」。',
- argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","deep":false}(缺省=全湖区间)',
- sanitize: report.reportSanitize,
- handler: report.reportHandler,
- },
- ];
- function get(name) {
- return TOOLS.find((t) => t.name === name) || null;
- }
- function manifestText() {
- return TOOLS.map((t) => `- ${t.name}:${t.desc}\n 参数:${t.argsDoc}`).join('\n');
- }
- module.exports = { TOOLS, get, manifestText };
|