registry.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. 'use strict';
  2. /**
  3. * 智能问数智能体 · 工具注册表
  4. * ----------------------------------------------------------------------------
  5. * 清单(manifestText,注入系统提示)与派发(get)单一事实源。
  6. * 模型给出的工具名先在此求交(未注册即拒绝并回注可用清单,防幻觉工具名),
  7. * 参数再经各工具 sanitize 钳制。本期全部工具只读(湖查询/校验),无写操作。
  8. */
  9. const lake = require('./lake.js');
  10. const compare = require('./compare.js');
  11. const cross = require('./cross.js');
  12. const peer = require('./peer.js');
  13. const entity = require('./entity.js');
  14. const cloud = require('./cloud.js');
  15. const report = require('./report.js');
  16. const metric = require('./metric.js');
  17. const metrics = require('../metrics.js');
  18. const anomaly = require('./anomaly.js');
  19. const attribution = require('./attribution.js');
  20. const TOOLS = [
  21. {
  22. name: 'lake_overview',
  23. desc: '摸底当前主体:门店/连锁清单、湖内可用日期区间、各粒度行数、最近一次同步结果。分析类问题的第一步,先看有什么数据再取数。',
  24. argsDoc: '{}(无参数)',
  25. sanitize: lake.overviewSanitize,
  26. handler: lake.overviewHandler,
  27. },
  28. {
  29. name: 'query_report',
  30. desc: '查经营数据。mode=detail 日期×门店明细 / mode=shop 按门店汇总(排行/TopN)/ mode=date 按日期汇总(趋势)。区间自动钳制在湖内(截止默认昨天)。',
  31. argsDoc: '{"mode":"shop","from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名,可部分匹配"],"metrics":["grossAmt","validOrderCnt"],"sort":"grossAmt","sortDir":"desc","limit":10}(from/to/metrics 缺省=近30天/营业额)',
  32. sanitize: lake.querySanitize,
  33. handler: lake.queryHandler,
  34. },
  35. {
  36. name: 'compare_periods',
  37. desc: '同比/环比对比:本期 vs 等长基期。自动做门店集合对齐(开/闭店单列)、比率加权、缺失日清单、收入+支出=营业额恒等式校验。凡问「变化/对比/为什么涨跌」一律用它,不要自己相减。',
  38. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","gap":0,"metrics":["grossAmt","validOrderCnt"],"shops":["门店名"]}(缺省=湖内最后7天 vs 紧邻前7天;gap=两期间隔天数,同比可用 gap 留空+自定 from/to 跨年)',
  39. sanitize: compare.compareSanitize,
  40. handler: compare.compareHandler,
  41. },
  42. {
  43. name: 'data_quality',
  44. desc: '数据体检:门店明细全零行、层间一致性(门店合计 vs 连锁 vs 公司)、恒等式、日期连续性、预留列状态、最近同步结果。回答前对数据可信度存疑时调用。',
  45. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  46. sanitize: lake.qualitySanitize,
  47. handler: lake.qualityHandler,
  48. },
  49. {
  50. name: 'cross_check',
  51. desc: '层间一致性核对(可单独抽查指定区间):按日核对 门店合计 vs 连锁合计 vs 公司行(营业额+有效订单),>0.5% 容差记不一致。环比/对比结论出来前建议抽查一次。',
  52. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  53. sanitize: cross.crossSanitize,
  54. handler: cross.crossHandler,
  55. },
  56. {
  57. name: 'raw_inspect',
  58. desc: '查某行 raw 原始字段(取数接口自带:环比差值/上期对比/同行均值排名),用于源侧交叉验证——本地算的数字 vs 接口自带的数字。字段按「包含匹配」取,命名以接口原样为准。',
  59. argsDoc: '{"date":"YYYYMMDD","shops":["门店名"],"keys":["gross_amt"]}(keys 缺省匹配营业额与单量相关字段)',
  60. sanitize: peer.rawSanitize,
  61. handler: peer.rawHandler,
  62. },
  63. {
  64. name: 'peer_benchmark',
  65. desc: '同行对标:门店自身值 vs 同行均值 vs 排名(来自全字段落库的 raw *_mall_avg/_rank)。凡问「对标/排名/同行」一律用它。',
  66. 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 等)',
  67. sanitize: peer.benchSanitize,
  68. handler: peer.benchHandler,
  69. },
  70. {
  71. name: 'cross_entity',
  72. desc: '跨主体(跨校/多账号)对比:湖内全部主体本期 vs 基期的营业额与有效订单。凡问「几家店/哪个食堂/跨校」类问题用它。',
  73. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=当前主体湖内最后 7 天)',
  74. sanitize: entity.entitySanitize,
  75. handler: entity.entityHandler,
  76. },
  77. {
  78. name: 'aitable_verify',
  79. desc: '湖↔云只读比对:本地数据湖 vs 钉钉 AI 表格三表(行数 + 营业额抽样值)。凡结论涉及「云端数据是否同步/两边数据是否一致」时用它;云端未启用时优雅降级。',
  80. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  81. sanitize: cloud.cloudSanitize,
  82. handler: cloud.cloudHandler,
  83. },
  84. {
  85. name: 'metric_query',
  86. desc: '按指标查逐日序列(含派生指标如 7 日移动平均)。聚合口径由指标层决定:比率加权、客单价比值、移动平均都不许自己算。要「移动平均/平滑/去噪」视图时用它。',
  87. 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)',
  88. sanitize: metric.metricSanitize,
  89. handler: metric.metricHandler,
  90. },
  91. {
  92. name: 'detect_anomalies',
  93. desc: '统计异常检测:加性指标逐日序列的 leave-one-out z-score(σ 下限防误报),|z|≥2 警告、≥3 不一致;区间含寒暑假会提示基线失真。凡问「哪天不对劲/异常波动」用它。',
  94. argsDoc: '{"metric":"grossAmt","from":"YYYYMMDD","to":"YYYYMMDD","k":2}(metric 可用 grossAmt/validOrderCnt/incomeAmt/expUv/orderUserCnt)',
  95. sanitize: anomaly.anomalySanitize,
  96. handler: anomaly.anomalyHandler,
  97. },
  98. {
  99. name: 'attribution',
  100. desc: '营业额归因(LMDI):把环比变化分解为 曝光效应/进店率效应/客单价效应,给出各因素贡献金额与主因。凡问「为什么涨跌/谁拉动谁拖累」在 compare_periods 之后用它。',
  101. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名"]}(缺省=湖内最后 7 天;单店口径恒等式严格成立)',
  102. sanitize: attribution.attributionSanitize,
  103. handler: attribution.attributionHandler,
  104. },
  105. {
  106. name: 'build_report',
  107. desc: '生成经营分析 HTML 报告。deep=false 基础版(KPI/趋势/明细);deep=true 深度版(另含数据体检/统计异常/归因/同行对标章节)。用户要「报告/周报/导出」时调用,回答中提示点击「查看分析报告」。',
  108. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","deep":false}(缺省=全湖区间)',
  109. sanitize: report.reportSanitize,
  110. handler: report.reportHandler,
  111. },
  112. ];
  113. function get(name) {
  114. return TOOLS.find((t) => t.name === name) || null;
  115. }
  116. function manifestText() {
  117. return TOOLS.map((t) => `- ${t.name}:${t.desc}\n 参数:${t.argsDoc}`).join('\n');
  118. }
  119. module.exports = { TOOLS, get, manifestText };