'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 };