Logicform2API

Logicform2API 是一个实验性功能,用于对接用户自己的指标平台:将系统的 Logicform 与指标平台的参数对应,从而获取指标平台的返回值。

其工作流程为:自然语言提问 → 生成 Logicform → 将 Logicform 关键信息映射为 API 请求 → 返回指标平台数据

Logicform的关键信息例如:日期、维度等筛选条件,分组信息中的年、月、维度等

平台要求

要求筛选条件、分组等信息能够通过指标平台 URL 参数表达。

优缺点

优点:

  • 不局限于数据库,丰富了数据获取来源
  • 少量指标通过 API 获取,接入成本低、易于落地

缺点:

  • 若指标数量多,将大大增加工作量,此时建议将数据抽入数据库以正常方式接入本系统
  • 该逻辑直接获取指标平台结果,无法进行深度归因分析操作,仅能做快速查询

简单案例

下面通过一个简单案例辅助理解。

该函数判断当前查询是否包含【销售额】指标,并根据是否按【产品】维度分组返回不同结果:

  • 按产品分组时,返回目标结果数组
  • 未分组时,返回目标汇总金额

当用户问题中问到 销售额 或同时满足按 产品 分组时,将命中该代码逻辑

async (logicform, context) => {
  const { libs } = context;

  // 打印日志:记录当前传入的完整查询结构,方便调试查看查询条件、指标、分组信息
  libs.log('[LF2API] 被调用了: ' + JSON.stringify(logicform));

  // 判断查询聚合指标(preds)中,是否包含指标【销售额】
  const hasSalesPred = logicform.preds?.some(item => item.pred === '销售额');

  // 判断分组维度(groupby)中,是否包含维度【产品】
  const hasSalesGroup = logicform.groupby?.some(item => item._id === '产品');

  // 输出判断结果日志,调试用
  libs.log('hasSalesPred : ' + hasSalesPred);
  libs.log('hasSalesGroup : ' + hasSalesGroup);

  // 如果当前查询选择了【销售额】指标
  if (hasSalesPred) {
    // 同时选择了【产品】作为分组维度 → 返回按产品维度拆分的明细数据
    if (hasSalesGroup) {
      return [
        { _id: 0, 产品: { _id: "手机A" }, 总销售额: 888 },
        { _id: 1, 产品: { _id: "电脑B" }, 总销售额: 999 },
      ];
    }

    // 只选销售额指标、未选择产品分组 → 返回汇总总数据(不分产品)
    return [{ _id: '0', 总销售额: 2000 }];
  }

  // 注意:如果没有匹配销售额指标,函数无显式 return,默认走系统的 return,为不影响正常问答,最外层不需要return
};

得到结果如下图:

简单案例结果

实际案例

下面以一个实际场景为例,通过 API 获取指标平台的分娩量数据。

  1. 在系统中完成简单建模(最低要求:事件表,包含日期列、维度列、分析的指标列,不需要连接数据源)
  2. 通过自然语言提问相关维度及指标,获取 Logicform 逻辑
  3. 将 Logicform 的关键信息依据不同情况映射为 API 请求,获取最终数据

接口信息

样例说明

以下接口信息为样例,实际访问不通,仅用于配合下方实现代码理解接口对接方式。

请求 URL:

http://ip:8080/services/newborn/deliverynum?user=deepsee&pageSize=1000&pageNum=1&dateType=年&sdate=2026&edate=2026
  • /services/newborn/deliverynum:接口资源地址,不同 API 该地址不同
  • ? 之后的 URL Query 请求参数:参数名、参数值规则固定

请求方式:GET

请求参数:

参数名参数示例说明
userdeepsee身份用户标识
pageSize1000分页每页大小
pageNum1分页页码
dateType日期类型
sdate2026开始日期
edate2026结束日期

不同接口的资源地址会变化,但上述 Query 参数体系保持不变。

返回数据(JSON,HTTP 状态码 200):

返回数据的外层分页结构体格式固定,内部业务维度字段随接口变化。

{
  "data": {
    "pageNum": 1,
    "pageSize": 1000,
    "total": 6,
    "data": [
      {
        "deliverydate": "2026",
        "hosid": "深圳院区",
        "deliverynum": "190"
      },
      {
        "deliverydate": "2026",
        "hosid": "天津院区",
        "deliverynum": "344"
      }
    ]
  }
}

实现代码

async (logicform, context) => {
  const { libs } = context;

  libs.log('[LF2API] 被调用了: ' + JSON.stringify(logicform));

  // API 基础地址
  const baseUrl = 'http://ip:8080/services/services/newborn/deliverynum';

  // 从 logicform.query 获取起止时间字符串 YYYY-MM-DD HH:mm:ss
  const startDateStr = logicform?.query?.日期?.$gte;
  const endDateStr = logicform?.query?.日期?.$lte;

  // 截取年份(前4位),兜底默认 2026 防止取不到参数报错
  const sdate = startDateStr ? startDateStr.slice(0, 4) : '2026';
  const edate = endDateStr ? endDateStr.slice(0, 4) : '2026';

  libs.log('sdate = ' + sdate + ' & edate = ' + edate);

  // 判断查询聚合指标(preds)中,是否包含指标【分娩量】
  const hasdeliveryPred = logicform?.preds?.some(item => item.pred === '分娩量');

  // 判断是否按照院区分组
  const hasGroupByHosPred = logicform?.groupby?.some(item => item._id === '院区');

  libs.log('hasdeliveryPred = ' + hasdeliveryPred + ' & hasGroupByHosPred = ' + hasGroupByHosPred);

  if (hasdeliveryPred && hasGroupByHosPred) {
    const params = {
      user: 'deepsee',
      pageSize: 1000,
      pageNum: 1,
      dateType: '年',
      sdate,
      edate
    };

    libs.log('请求参数 params:' + JSON.stringify(params));

    try {
      // axios 请求写法:get(地址, { params }) 中 params 代表 url 查询参数
      const resp = await libs.axios.get(baseUrl, { params });

      libs.log('接口返回原始数据:' + JSON.stringify(resp.data));
      const apiList = resp.data?.data?.data || [];

      const result = apiList.map((row, idx) => {
        return {
          _id: String(idx),
          院区: { _id: row.hosid },
          分娩数量: Number(row.deliverynum)
        };
      });

      libs.log('组装完成返回数据:' + JSON.stringify(result));
      return result;
    } catch (error) {
      // 所有异常统一捕获打印
      libs.log('请求异常捕获!');
      if (error.response) {
        // 后端返回 500/400 等业务错误
        libs.log('异常响应内容:' + JSON.stringify(error.response.data));
        libs.log('响应状态码:' + error.response.status);
      } else {
        // 网络不通、超时、服务连不上
        libs.log('网络级异常信息:' + error.message);
      }
      // 异常兜底返回空数组,图表不会崩溃
      return [];
    }
  }

  // 本逻辑只针对特殊处理的 API 指标生效,避免影响对其他指标查询,最外层不需要兜底 return
};