registry.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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 TOOLS = [
  17. {
  18. name: 'lake_overview',
  19. desc: '摸底当前主体:门店/连锁清单、湖内可用日期区间、各粒度行数、最近一次同步结果。分析类问题的第一步,先看有什么数据再取数。',
  20. argsDoc: '{}(无参数)',
  21. sanitize: lake.overviewSanitize,
  22. handler: lake.overviewHandler,
  23. },
  24. {
  25. name: 'query_report',
  26. desc: '查经营数据。mode=detail 日期×门店明细 / mode=shop 按门店汇总(排行/TopN)/ mode=date 按日期汇总(趋势)。区间自动钳制在湖内(截止默认昨天)。',
  27. argsDoc: '{"mode":"shop","from":"YYYYMMDD","to":"YYYYMMDD","shops":["门店名,可部分匹配"],"metrics":["grossAmt","validOrderCnt"],"sort":"grossAmt","sortDir":"desc","limit":10}(from/to/metrics 缺省=近30天/营业额)',
  28. sanitize: lake.querySanitize,
  29. handler: lake.queryHandler,
  30. },
  31. {
  32. name: 'compare_periods',
  33. desc: '同比/环比对比:本期 vs 等长基期。自动做门店集合对齐(开/闭店单列)、比率加权、缺失日清单、收入+支出=营业额恒等式校验。凡问「变化/对比/为什么涨跌」一律用它,不要自己相减。',
  34. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD","gap":0,"metrics":["grossAmt","validOrderCnt"],"shops":["门店名"]}(缺省=湖内最后7天 vs 紧邻前7天;gap=两期间隔天数,同比可用 gap 留空+自定 from/to 跨年)',
  35. sanitize: compare.compareSanitize,
  36. handler: compare.compareHandler,
  37. },
  38. {
  39. name: 'data_quality',
  40. desc: '数据体检:门店明细全零行、层间一致性(门店合计 vs 连锁 vs 公司)、恒等式、日期连续性、预留列状态、最近同步结果。回答前对数据可信度存疑时调用。',
  41. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  42. sanitize: lake.qualitySanitize,
  43. handler: lake.qualityHandler,
  44. },
  45. {
  46. name: 'cross_check',
  47. desc: '层间一致性核对(可单独抽查指定区间):按日核对 门店合计 vs 连锁合计 vs 公司行(营业额+有效订单),>0.5% 容差记不一致。环比/对比结论出来前建议抽查一次。',
  48. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  49. sanitize: cross.crossSanitize,
  50. handler: cross.crossHandler,
  51. },
  52. {
  53. name: 'raw_inspect',
  54. desc: '查某行 raw 原始字段(取数接口自带:环比差值/上期对比/同行均值排名),用于源侧交叉验证——本地算的数字 vs 接口自带的数字。字段按「包含匹配」取,命名以接口原样为准。',
  55. argsDoc: '{"date":"YYYYMMDD","shops":["门店名"],"keys":["gross_amt"]}(keys 缺省匹配营业额与单量相关字段)',
  56. sanitize: peer.rawSanitize,
  57. handler: peer.rawHandler,
  58. },
  59. {
  60. name: 'peer_benchmark',
  61. desc: '同行对标:门店自身值 vs 同行均值 vs 排名(来自全字段落库的 raw *_mall_avg/_rank)。凡问「对标/排名/同行」一律用它。',
  62. 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 等)',
  63. sanitize: peer.benchSanitize,
  64. handler: peer.benchHandler,
  65. },
  66. {
  67. name: 'cross_entity',
  68. desc: '跨主体(跨校/多账号)对比:湖内全部主体本期 vs 基期的营业额与有效订单。凡问「几家店/哪个食堂/跨校」类问题用它。',
  69. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=当前主体湖内最后 7 天)',
  70. sanitize: entity.entitySanitize,
  71. handler: entity.entityHandler,
  72. },
  73. {
  74. name: 'aitable_verify',
  75. desc: '湖↔云只读比对:本地数据湖 vs 钉钉 AI 表格三表(行数 + 营业额抽样值)。凡结论涉及「云端数据是否同步/两边数据是否一致」时用它;云端未启用时优雅降级。',
  76. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  77. sanitize: cloud.cloudSanitize,
  78. handler: cloud.cloudHandler,
  79. },
  80. {
  81. name: 'build_report',
  82. desc: '生成经营分析 HTML 报告(KPI 概览/逐日趋势/门店明细,可打印导出 PDF)。用户要「报告/周报/导出」时调用,回答中提示点击「查看分析报告」。',
  83. argsDoc: '{"from":"YYYYMMDD","to":"YYYYMMDD"}(缺省=全湖区间)',
  84. sanitize: report.reportSanitize,
  85. handler: report.reportHandler,
  86. },
  87. ];
  88. function get(name) {
  89. return TOOLS.find((t) => t.name === name) || null;
  90. }
  91. function manifestText() {
  92. return TOOLS.map((t) => `- ${t.name}:${t.desc}\n 参数:${t.argsDoc}`).join('\n');
  93. }
  94. module.exports = { TOOLS, get, manifestText };