跳到主要内容

进阶案例二:技术研发固化统计口径

内容概述

基础文档展示了最简单的查询场景。但实际项目管理中,不同角色的统计口径差异很大——产品经理要「各项目需求分布」,研发要「自己负责的需求状态」,老板要看「整体进度和延期风险」。每次让 AI 理解口径再实时写 SQL,效率低且容易出错。

本案例展示:当统计逻辑复杂、口径需要统一时,技术团队如何把 SQL 封装成接口,AI 加载 Skill 后一句话返回固定格式的统计结果。


业务场景:为什么需要固化统计口径

不固化的代价

当 AI 实时生成 SQL 时,每次问同样的问题,答案可能不同:

  • 第一次问"本周新增需求",AI 理解的是「创建时间」
  • 第二次问同样的问题,AI 可能用「更新时间」来定义"新增"
  • 第三个月业务调整口径,AI 的理解又变了

结果是:同一个指标,三个数字,管理层无法对比趋势,决策失去依据。

口径固化的价值

一致性:不管谁问、什么时候问、问几次,结果是同一个数字。这是管理报表的基本要求。

可审计:当老板问"这个数字怎么来的",可以追溯到具体的 SQL 文件,而不是"当时 AI 生成的"。

职责清晰:SQL 口径由技术团队管理,Skill 由 AI 执行,口径变更走变更流程,不依赖 AI 当场发挥。

对企业的意义

企业决策靠数据,数据不一致比没有数据更危险——错误的数字比没数字更容易把人带向错误的方向。

当统计口径被固化下来,它就不再是"一次性的查询结果",而成为企业的数据资产:

  • 新员工入职,查到的数字和所有人是一样的
  • 月报季报,连贯的口径让趋势分析有意义
  • 跨部门协作,同一个指标不会产生分歧

这不是技术问题,是管理问题。固化口径,是让数据真正成为决策依据的前提。


技术实现:开发 SQL 统计接口

整体架构分两层,职责分离:

┌─────────────────────────────────┐
│ 第一层:自定义 SQL(平台注册) │
│ 存 SQL 模板,#{param} 占位 │
└──────────────┬──────────────────┘
│ 获得 sqlCode

┌─────────────────────────────────┐
│ 第二层:Backend Function(脚本) │
│ 调用 sql.execute(sqlCode,params)│
│ 可加前置校验、后置格式化 │
└──────────────┬──────────────────┘
│ 获得接口路径

AI 调用接口

第一步:在平台注册自定义 SQL

技术团队在 Lovrabet 平台创建自定义 SQL,平台会分配一个 sqlCode:

-- sqlCode: get_requirement_stats
-- 参数:project_id(可选), time_range(本周/本月/本季度)

SELECT
p.name AS project_name,
COUNT(CASE WHEN r.status = "new") AS new_count,
COUNT(CASE WHEN r.status = "in_progress") AS in_progress_count,
COUNT(CASE WHEN r.status = "completed") AS completed_count,
COUNT(CASE WHEN r.status = "closed") AS closed_count
FROM yt_requirements r
JOIN yt_projects p ON r.project_id = p.id
WHERE r.updated_at >= #{start_date}
AND (#{project_id} IS NULL OR r.project_id = #{project_id})
GROUP BY p.name

第二步:开发 Backend Function 调用 SQL

BF 负责调用 SQL 并做后置格式化,不直接写 SQL 逻辑:

/**
* 需求统计接口
* 脚本名称: get_requirement_stats
* 所属应用: app-173e8652
*/
export default async function getRequirementStats(params, context) {
const { project_id, time_range } = params;

// 计算时间范围起始日期
const now = new Date();
let startDate;
if (time_range === "本月") {
startDate = new Date(now.getFullYear(), now.getMonth(), 1).toISOString();
} else if (time_range === "本季度") {
const quarter = Math.floor(now.getMonth() / 3);
startDate = new Date(now.getFullYear(), quarter * 3, 1).toISOString();
} else {
// 默认本周
const dayOfWeek = now.getDay();
startDate = new Date(now.setDate(now.getDate() - dayOfWeek)).toISOString();
}

// 调用平台已注册的自定义 SQL
const result = await context.client.sql.execute({
sqlCode: "get_requirement_stats",
params: {
start_date: startDate,
project_id: project_id || null,
},
});

if (!result.execSuccess || !result.execResult) {
throw new Error("SQL 执行失败:" + (result.errorMsg || "未知错误"));
}

// 后置处理:计算合计行
const rows = result.execResult;
const totals = rows.reduce(
(acc, row) => ({
new_count: acc.new_count + Number(row.new_count || 0),
in_progress_count:
acc.in_progress_count + Number(row.in_progress_count || 0),
completed_count: acc.completed_count + Number(row.completed_count || 0),
closed_count: acc.closed_count + Number(row.closed_count || 0),
}),
{
new_count: 0,
in_progress_count: 0,
completed_count: 0,
closed_count: 0,
}
);

return {
success: true,
data: rows,
totals: { project_name: "合计", ...totals },
};
}

注册路径

BF 注册后通过以下路径访问:

POST /api/endpoint/{appCode}/{scriptName}
// 完整路径:POST /api/endpoint/app-173e8652/get_requirement_stats

前置依赖

技术团队开发前需确认:

  • 应用 app-173e8652 已创建
  • yt_requirements(需求表)、yt_projects(项目表)已建立并有数据
  • 自定义 SQL get_requirement_stats 已在平台注册
  • 操作人有读取这些表的权限

Skill 封装:让业务人员一句话触发统计

接口开发完成后,把操作流程封装成 Skill,AI 加载后自动完成:识别统计意图 → 调用接口 → 格式化返回结果。

Skill 范本

---
name: get_requirement_stats
version: 0.1.0
description: "需求统计查询。触发词:帮我查一下本周需求、各项目需求情况、需求进度报表。"
---

# 需求统计查询

## 第一步:识别统计口径

根据用户输入,识别以下维度:
- 时间范围:本周 / 本月 / 本季度(默认本周)
- 项目范围:特定项目 or 全量
- 角色上下文:如果能获取用户所在项目或负责模块,自动加上筛选

如果用户没有明确说明,询问后继续。

## 第二步:调用统计接口

调用 get_requirement_stats 接口(/api/endpoint/app-173e8652/get_requirement_stats),传入:
- project_id(如有)
- time_range(本周/本月/本季度)

如果接口返回失败,告知用户失败原因,终止流程。

## 第三步:格式化返回结果

将接口返回的数据格式化为表格或列表,确保:
- 各项目分列显示状态分布
- 包含合计行
- 数字右对齐,文字左对齐

## 注意事项

- 不需要解释 SQL 逻辑,只需要呈现最终数字
- 如果某个项目没有数据,显示 0 而非空白
- 可以根据用户角色(老板/产品/研发)调整展示重点

使用示例

用户输入

帮我查一下本周各项目的需求完成情况

AI 自动完成

加载 Skill → 识别时间范围(本周)+ 项目范围(全量)→ 调用 get_requirement_stats 接口 → 格式化返回表格

返回结果示例

✅ 本周需求统计
━━━━━━━━━━━━━━━━━━
项目 新增 进行中 完成 关闭
━━━━━━━━━━━━━━━━━━
平台·官网与文档 3 2 5 1
Agent·运行态 7 4 3 0
Rabetbase-CLI 2 1 6 2
━━━━━━━━━━━━━━━━━━
合计 12 7 14 3

常见问题

Q:统计口径变了怎么办?

技术团队修改平台上的自定义 SQL 即可,BF 脚本和 Skill 无需改动,结果自动同步。

Q:需要新增统计维度怎么办?

在自定义 SQL 里增加字段,比如加上「延期需求数」「测试中需求数」,Skill 负责展示即可。

Q:可以查个人维度的统计吗?

可以,在自定义 SQL 里加上 WHERE r.assignee_id = #{user_id} 条件,Skill 支持识别「我的需求」等上下文。

Q:数据量大的时候接口会慢吗?

统计类接口建议在平台上加缓存(本周口径本周内不变),具体方案技术团队评估后决定。


下一步

当业务人员频繁需要某种统计时,产品 / 技术团队可以:

  1. 和业务方确认统计口径
  2. 技术在平台注册自定义 SQL,开发 BF 并注册到 Lovrabet
  3. AI 侧加载对应 Skill,一句话触发