← 返回文章列表
006 — AI · #逆向工程 #静态分析 #AI Agent · 2026-08-19 · 9 MIN READ

功能点提取:让 AI 认出任意技术栈里的功能入口

第一双眼睛看什么

功能点提取(skill1,feature-extract)是流水线的第一双眼睛。它的输入是一个页面的前端文件,输出是一份 function-list JSON,记录这个页面有哪些功能、每个功能的源文件和技术栈。后面六步全部围着这份 JSON 转:链路图按功能点逐个画,业务需求按功能点逐个翻译,模块归类按功能点计数对账。

这一步最重要的认知是分清「代码单元」和「功能点」。一个 Vue 文件里有几十个方法,绝大多数是零件:格式化日期、拼接参数、切换 loading 状态。功能点是入口:用户点一下按钮、页面加载时自动执行、定时器到点触发、另一个系统发来请求。零件不配拥有链路图,把零件当功能点提取出来,后面每一步都要为它画链路、写需求、做归类,整条流水线的工作量被无意义地放大。

反过来,漏掉一个入口更糟。功能点数量校验 N=M 防的是「提取了但没处理」,对「该提取而没提取」无能为力——上游漏了,下游连知道的机会都没有。所以提取规则的第一原则是宁可按明确规则分类,不许靠感觉取舍。

mermaid
flowchart TD
    A[读入页面前端文件] --> B[识别技术栈 - 三重信号]
    B --> C[遍历入口候选 - 事件/路由/定时/请求]
    C --> D{过滤排除规则}
    D -->|初始化/纯UI/无业务联动等| E[丢弃]
    D -->|通过| F[命名 - 按优先级取中文名]
    F --> G[按主入口/tab/行事件排序]
    G --> H[输出 function-list JSON]

先认栈,再动手

提取规则不是一套通用规则,而是按技术栈分派的七套。识别靠三重信号:文件扩展名(.vue、.jsp、.py)、目录结构(views、controller、api)、框架特征(el-table、@RequestMapping、jqGrid)。三重信号都指向同一结论才下判断,这层冗余是给混乱的老代码准备的。

技术栈入口特征
RuoYi + Vue2.vue 文件中带业务的 @click 等事件、mounted 等生命周期中的业务调用、路由、watch、跳转
Spring MVC + JSP/ExtJSController 的 @RequestMapping、JSP 内嵌 JS 函数、ExtJS 组件事件(grid.onbutton.on
Spring Boot + ThymeleafController 请求映射、模板内 th:onclick 绑定
Spring MVC + JSP + jQueryJSP 中 onclick 属性、$(function(){}) 内的业务初始化
Spring MVC + JSP + jqGridcolModel 定义、onSelectRowgridComplete、导航栏按钮事件
Python 脚本脚本主函数、if __name__ == "__main__"、类的主流程方法
定时任务 / 批处理@Scheduled 注解、main 方法、Quartz 任务类、crontab 配置

表格的用法是路由而非穷举。识别出栈之后,模型只按该栈的入口特征找功能点,不再用对其他栈的想象硬套。这份识别结果还有第二个去向:写进 JSON 的 技术栈 字段,链路图生成那一步直接读字段选模板,不做重复判断。识别一次,处处消费。这种「专用规则如何泛化成识别表」的改造过程,在技术栈中立化改造一篇有完整展开。

提取的另一半是排除

新人写提取规则,会把九成心思花在「怎么找全」,跑两遍就明白排除规则同样致命。排除清单有七类:

初始化与生命周期挂载(mountedcreated 只做数据加载而无业务动作)、纯 UI 状态切换(展开折叠、tab 高亮)、无业务联动的关闭取消、纯样式调整、纯数据格式化、重复实现的空壳函数、无实际逻辑的转发。每一条都能对应到一类真实的噪音:不加排除,一个列表页能提取出四十多个「功能点」,其中三十个是「格式化金额」「切换页签」。

排除规则的本质是给「业务价值」下可执行的定义:有输入、有处理、或有状态变更,才算功能。格式化金额没有输入输出状态,高亮页签没有,而「按合同编码查询台账」有。这个定义粗糙但可执行,比「提取有业务意义的功能」这种正确而无用的要求好得多。

最容易漏的两类功能点

实战里漏得最多的不是藏在角落的,而是看起来不像入口的。

表格行事件。列表页的每一行都能点,点开看详情、双击编辑、勾选后批量提交。这些交互挂在 @row-click@selection-changeonSelectRow 上,不在按钮区,扫按钮的模型天然看不见。规则明确要求行事件必须独立成功能点:选中查询明细是一个功能,批量删除选中是另一个功能,各画各的链路。

批处理步骤函数。一个 Python 报销影像脚本,主循环监视目录,到齐后解压、识别二维码、调接口回写状态。按「主函数一个功能点」的粗粒度提法,这条流水线会塌缩成一个巨大功能点,链路图画不下,业务规则写不清。规则要求主循环和每个业务步骤函数分别登记,主循环是入口,步骤函数挂在它的链路上。链路图生成一篇里那张七节点批处理链路图,靠的就是这步提取时的拆分。

中文名从哪来

产物要求每个功能点有中文名。对老系统,代码里往往只有一个 handleQuery,命名直译出「处理查询」这种词,下游业务文档跟着遭殃。命名按四级优先级取:

按钮文案或 label 属性最优先,btn text="导出台账" 直接给出答案;其次是注释和路由 meta 信息;再次是从代码语义推断,@row-click 配合后续的详情弹窗,推断为「点击行查看明细」;最后才是命名直译兜底。推断不是编造——推断出的名字要能对应到真实的代码行为,推断不出来就用原始命名,留给人工改名。

产物 schema:中立到只谈功能

function-list JSON 是七个 skill 之间的第一条接口,设计上刻意中立:不出现任何框架词汇,只描述「什么文件里有什么功能、这个功能大概算哪类业务」。

json
{
  "file_path": "src/views/money/pageList.vue",
  "技术栈": "RuoYi+Vue2+MyBatis",
  "页面名称": "台账列表",
  "功能点": [
    {
      "名称": "查询台账列表",
      "类型": "主功能",
      "源文件": "src/views/money/pageList.vue",
      "方法": "handleQuery",
      "相关接口": "api/money/pageList.js"
    }
  ],
  "统计": { "总功能点数": 23 }
}

两个字段值得注意。相关接口 是链路图跨边界追踪的接缝信号,在这里登记,下一步就顺着它跨进后端工程。统计.总功能点数 是给数量校验 N=M 预留的锚点——功能点列表是数组,数组声明本身可能被误计入数,独立字段让校验有第二来源可对账。

边界情况也定了规矩:一个文件遍历完没有任何功能点,不输出空文件,而是输出带 -无功能 标记的 JSON。空产物和「没跑」在下游是不可区分的,标记是给调度器的明示。

优先级排序与完成标志

功能点在 JSON 里不是随机排列,按固定优先级排:主功能入口在前,tab 切换次之,行事件第三,其余补充功能殿后。排序影响下游所有产物的阅读顺序,模块清单和功能目录都按这个序号走,人查文档时最关心的入口永远排在最前面。

提取完成后,控制台打印完成标志,输出文件:<完整文件名>。和所有步骤一样:中文冒号、不含路径,调度器捕获这一行才放行下一步。这套文件即接口的编排协议从第一份产物就开始生效。

第一步的杠杆率

功能点提取是杠杆率最高的一步。提取多了,后面六步的工作量线性放大;提取漏了,最终文档永远缺一块;提取错了颗粒度,中间所有步骤的复杂度全部失控。所以这一步的规则密度反而是全流水线最高的:七套识别表、七类排除、两类易漏点、四级命名优先级,全都在把「找功能」从模型的主观判断变成查表操作。

查表的本质是可复核。怀疑漏了功能点,对照七类入口特征逐类检查;怀疑多提了,对照排除清单逐条过。规则写得越死,人和模型之间的分歧越少。

下一步是沿着每个功能点追出完整调用链:链路图生成:跨边界调用链追踪与 mermaid 语义学

Comments