feat(skill): 功能点自动计算技能进公共区all/——LLM语义识别+fp_calc.py规则引擎(IFPUG矩阵权重),四件套输出,全agent共用

This commit is contained in:
yumoqing 2026-09-09 08:01:08 +08:00
parent 682afb3729
commit 22f7520359
4 changed files with 1363 additions and 0 deletions

View File

@ -0,0 +1,140 @@
---
name: function-point-counting
version: 1.0.0
description: Use when 估算或复核软件功能点(FP/ILF/EIF/EI/EO/EQ/DET/FTR)。
trigger_conditions:
- 用户要求计算/估算/核算软件功能点、功能规模、FP
- 招投标场景需要按功能点计价或复核对方报的功能点数
- 需求评审需要列出全部功能点清单及计算依据
- 对已有功能点估算结果做防虚增审计(查按页面/按钮/API/表拆分的错误)
---
# 软件功能点自动计算IFPUG 口径LLM 语义 + 规则引擎计算)
源文档:《软件功能点自动计算原则》全文见 `references/principles-full.md`16 章)。
本技能是它的**可执行操作手册**;原则冲突时以 references 原文为准。
## 分工铁律(文档第 16 章)
- **LLM 只做语义理解**:①需求解析 ②功能候选识别 ③系统边界 ④ILF/EIF ⑤EI/EO/EQ ⑥去重 ⑦DET/RET/FTR 提取。
- **复杂度判定与 FP 加权(⑧⑨)必须交给规则引擎**`scripts/fp_calc.py`
禁止 LLM 心算 FP、禁止 LLM 直接报总数。LLM 输出结构化 JSONtype/det/ret/ftr/confidence/evidence
脚本查矩阵+权重表算复杂度与 FP并生成四件套报告。
- ⑩ 复核:把脚本输出交人工/规则复核后才定稿。
## 十步工作流
1. 读需求文档(全文,不跳章)。
2. **定应用边界**:本系统维护哪些数据、读哪些外部数据、哪些处理在外部完成。未定边界禁止往下算。
3. 列用户角色与外部系统。
4. 识别业务过程(业务动作+业务数据+处理目的+业务结果四要素齐才算候选)。
5. 识别数据对象 → 判 ILF/EIF见决策树
6. 识别事务 → 判 EI/EO/EQ见决策树
7. 去重(同功能跨页面/角色/终端/接口只算一次)。
8. 逐功能提 DET/RET/FTR标置信度信息不足进待确认不猜。
9. 写 input.json 跑 `scripts/fp_calc.py` 得复杂度与 FP。
10. 输出四件套 + 证据链,交复核。
## 分类决策树
**ILF**(数据功能·内部):数据在本边界内 ∧ 本系统维护 ∧ 用户可识别 ∧ 结构稳定。
**EIF**(数据功能·外部):数据他系统维护 ∧ 本系统只读引用 ∧ 对本系统业务有意义。
**EI**(事务·输入):边界外数据/控制信息进入 ∧ 引起业务处理或维护内部数据
(新增/修改/删除/提交/审批/填报/导入/AI 生成提交类)。
**EO**(事务·输出):输出含 计算|汇总|派生|转换|复杂处理|业务规则 之一。
**EQ**(事务·查询):输入条件→检索→直接展示,**无**计算派生。
**EO vs EQ 一句话**:结果里有"算出来的数"(总数/比率/均值/趋势)=EO只是把存的数据捞出来摆好=EQ。
## 功能独立性四问(全"是"才独立成点,否则并入所属业务事务)
独立业务目的?独立触发事件?独立处理逻辑?独立业务结果?
## 拆分禁令(五不按)
不按页面、不按按钮、不按 API、不按数据库表、不按 CRUD 机械拆。
例:人员管理/新增/编辑/详情 4 页 ≠ 4 功能POST/PUT/DELETE/GET /user ≠ 4 功能;
user/user_role/user_project/user_skill 4 表 ≠ 4 ILF按逻辑数据分组判
## 去重三不
同业务功能不因 ①多页面 ②多角色仅权限不同③多展现形式Web/移动/API重复计。
## 特殊场景
- 报表:纯查询=EQ含统计/计算/汇总/派生=EO复杂指标分析优先 EO。
- 工作流:不按节点数算;按 发起→输入→审批→状态变化→结果 的业务过程定 EI/EO/EQ/数据功能。
- 导入:接收外部数据+校验转换写入=EI。导出简单=EQ含汇总/计算/转换/派生=EO。
- 消息通知:流程内自动通知(如审批后发短信)=该流程的处理逻辑,**不**单独加点;
仅当通知本身是独立业务(用户主动群发管理)才算。
- 接口:一个 API ≠ 一个功能点;按接口背后业务判 EI/EO/EQ/ILF/EIF。
- AI 功能:一个 Prompt ≠ 一个功能点;按实际输入/处理/输出/引用数据分析
(如"AI 生成岗位描述"=用户提交需求(EI 输入)→调用生成→返回结果(EO/EQ 视有无派生))。
## 不确定信息(核心规则)
需求不足以定类型/边界/复杂度时**不得猜测**,输出三要素进待确认清单:
`状态:待确认 / 原因:缺少…… / 需要确认:……`
例:"系统支持人员统计分析"→ 问:统计哪些指标?含计算吗?含趋势吗?数据源?支持筛选?生成报表?
置信度high=描述明确可直接算medium=功能明确但 DET/FTR 部分缺失,可算但标假设;
low=只有模糊描述("具备XX管理能力")→ 进待确认,不给数。
## 证据优先与十条禁止system prompt 级强约束)
只算需求明确存在或可直接推导的功能。禁止为"完整"自补:用户管理/日志/权限/消息/字典/表/API/后台/配置。
1 禁止凭经验补功能 2 禁止按页面数 3 禁止按按钮数 4 禁止按 API 数 5 禁止按表数
6 禁止按代码量 7 禁止为抬高 FP 拆分 8 禁止重复计算 9 禁止缺证据强判(标待确认)
10 禁止修改需求事实(只识别需求,不创造需求)。
## 复杂度矩阵与权重IFPUG 4.xfp_calc.py 内置)
档位EI 的 DET 1-4/5-15/≥16、FTR 0-1/2/≥3EO·EQ 的 DET 1-5/6-19/≥20、FTR 0-1/2-3/≥4
ILF·EIF 的 DET 1-19/20-50/≥51、RET 1/2-5/≥6。
矩阵(行=FTR或RET档列=DET档
EI/ILF低低低/低中高/中高高EIF 第一行第三列=中)。
权重 UFPILF 7/10/15EIF 5/7/10EI 3/4/6EO 4/5/7EQ 3/4/6低/中/高)。
项目另有约定权重表时改 fp_calc.py 的 WEIGHTS/MATRIX不改流程。
## 规则引擎用法
```bash
python3 scripts/fp_calc.py input.json # markdown 四件套
python3 scripts/fp_calc.py input.json --json # JSON程序消费/复核)
```
input.json schema
```json
{"project": "名", "boundary": {"internal": [], "external": []},
"functions": [{"name": "", "type": "EI|EO|EQ|ILF|EIF", "desc": "",
"evidence": "需求章节+原文摘要", "det": 12, "ret": 1, "ftr": 2,
"confidence": "high|medium|low", "assumption": "可选"}],
"pending": [{"item": "", "reason": "", "ask": ""}],
"assumptions": [""]}
```
ret 仅 ILF/EIF 用(缺省 1ftr 仅 EI/EO/EQ 用(缺省 0。校验失败的功能进"输入校验错误"不计入。
脚本输出即文档第 14 章四件套:功能明细表 / 汇总ILF·EIF·EI·EO·EQ 计数+总 UFP/ 待确认项 / 计算假设。
## 证据链(每个功能点必须可回溯)
需求原文 → 功能识别 → 分类判断 → 复杂度判断 → FP 结果。
明细表每行带 evidence章节+原文摘要)与 calc_basis档位→复杂度→权重
没有证据支撑的功能不得进入最终计数。
## 典型案例标准答案(回归用)
| 需求描述 | 判定 | 要点 |
|---|---|---|
| 新增外包人员 | EI | 边界外输入+维护人员档案 |
| 按姓名/工号查人员 | EQ | 检索直展无计算 |
| 人员月度统计(流失率/到岗率/SLA | EO | 有算出来的比率 |
| 人员档案(本系统维护) | ILF | 四条件齐 |
| 甲方HR员工信息只读 | EIF | 他维护我引用 |
| 人员管理4个页面 | 1 组功能 | 不按页面拆 |
| 入场审批后自动发短信 | 不单独计 | 属审批流程处理逻辑 |
| Excel 批量导入人员 | EI | 校验转换写入 |
| "系统具备人员管理能力" | 待确认 | 低置信度不给数 |
| AI 生成岗位描述 | 按输入/处理/输出分析 | 一个 Prompt ≠ 一个点 |
## 防虚增审计模式(复核他人估算时)
逐条查:是否按页面/按钮/API/表/CRUD 拆?是否自补了无依据的通用功能?
是否同功能跨端重复?是否把流程内通知单列?是否缺证据强判?
命中即要求对方按本技能重算,并给出差异清单。

View File

@ -0,0 +1,973 @@
# 软件功能点自动计算原则
## 1. 总体计算原则
### 1.1 计算目标
软件功能点计算应以软件需求中**用户可识别的业务功能**为基本计算对象,通过识别数据功能和事务功能,确定软件功能规模。
功能点计算不得以:
* 页面数量
* 菜单数量
* 按钮数量
* API数量
* 数据库表数量
* 代码行数
* 类数量
* 微服务数量
* 开发人员数量
* 工作量人月
直接作为功能点数量。
---
### 1.2 用户可识别原则
只有能够被用户或外部系统识别、具有明确业务意义的数据处理功能,才能作为功能点候选对象。
大模型应优先识别:
> **业务动作 + 业务数据 + 处理目的 + 业务结果**
例如:
> “新增外包人员”
属于明确的业务功能。
而:
> “点击保存按钮”
不是独立业务功能。
---
### 1.3 业务完整性原则
一个功能是否独立计算,应以其是否形成**完整、独立的业务处理过程**为判断依据。
例如:
> 人员入场申请 → 审核 → 创建人员档案 → 分配项目
如果整体构成一个完整业务事务,应首先作为一个业务过程分析,而不能机械拆成多个功能点。
如果其中某个处理过程具有独立的业务目的、独立触发条件和独立数据处理,则进一步判断是否构成独立事务功能。
---
### 1.4 功能边界原则
大模型必须首先确定:
> **应用边界Application Boundary**
再进行功能点识别。
必须明确:
* 哪些功能属于本系统;
* 哪些数据由本系统维护;
* 哪些数据来自外部系统;
* 哪些处理由外部系统完成;
* 本系统与外部系统之间的数据交换边界。
不得在未确定系统边界的情况下直接计算功能点。
---
### 1.5 证据优先原则
大模型进行功能点计算时:
> **只能根据需求文档中明确存在或能够由明确业务描述直接推导的内容进行计算。**
禁止为了“完整”而自行增加:
* 用户管理
* 日志管理
* 权限管理
* 消息通知
* 字典管理
* 数据库表
* API
* 后台管理
* 系统配置
等没有明确需求依据的功能。
对于无法确定的功能,应标记:
> **待确认**
而不是自行判断为存在。
---
# 2. 功能分类原则
软件功能分为两大类:
## 2.1 数据功能
### 2.1.1 内部逻辑文件ILF
满足以下条件时可识别为ILF
1. 数据属于本应用边界;
2. 数据由本应用维护;
3. 用户能够识别该数据集合;
4. 数据具有稳定的业务逻辑结构。
典型示例:
* 人员档案
* 项目档案
* 合同信息
* 工时记录
* 绩效记录
---
### 2.1.2 外部接口文件EIF
满足以下条件时可识别为EIF
1. 数据能够被本应用识别;
2. 数据由其他应用维护;
3. 本应用仅引用或读取;
4. 该数据对本应用业务处理具有意义。
例如:
> 从甲方HR系统读取组织机构和员工信息。
如果HR系统负责维护数据而本系统只读取则属于EIF候选。
---
# 3. 事务功能分类原则
## 3.1 外部输入EI
当数据或控制信息从应用边界外进入系统并导致系统进行业务处理或维护内部数据时识别为EI。
典型场景:
* 新增人员
* 修改人员
* 提交入场申请
* 审批人员入场
* 填报工时
* 提交请假申请
* 创建项目
* 导入人员信息
---
## 3.2 外部输出EO
当系统向用户或外部系统输出信息,并且输出过程中包含:
* 计算
* 汇总
* 派生
* 数据转换
* 复杂处理
* 业务规则处理
等处理逻辑时识别为EO候选。
例如:
> 月度人员服务统计报表
如果系统计算:
* 人员数量
* 人员流失率
* 到岗率
* 工时
* SLA达标率
则属于EO候选。
---
## 3.3 外部查询EQ
当用户输入查询条件系统读取数据并直接展示结果且不存在明显的计算、派生或复杂业务处理时识别为EQ。
例如:
> 根据姓名、工号查询人员信息。
---
## 3.4 EO与EQ判定原则
这是大模型自动计算中最容易产生误判的地方。
### 判定为EQ
满足:
> 输入条件 → 检索数据 → 展示数据
且没有明显的数据计算或派生。
### 判定为EO
满足:
> 输入条件 → 数据处理/计算/汇总/派生 → 输出结果
例如:
**人员列表查询**
```text
姓名
工号
部门
岗位
状态
```
通常为EQ。
**人员服务统计**
```text
人员总数
平均工时
离职率
到岗率
SLA达标率
趋势分析
```
通常为EO。
---
# 4. 功能独立性判定原则
大模型不得仅依据页面、按钮或者菜单进行拆分。
每个候选功能必须判断:
### 4.1 是否具有独立业务目的
例如:
> “人员入场”
具有明确业务目的。
---
### 4.2 是否具有独立触发事件
例如:
> 用户提交人员入场申请。
属于明确触发事件。
---
### 4.3 是否具有独立处理逻辑
例如:
> 校验资料 → 检查岗位 → 检查资质 → 写入人员档案。
存在独立处理逻辑。
---
### 4.4 是否具有独立业务结果
例如:
> 形成正式入场人员记录。
具有明确业务结果。
---
### 4.5 功能独立性结论
满足上述条件的功能,可以作为独立功能候选。
否则应考虑并入所属业务事务。
---
# 5. 功能拆分原则
## 5.1 不按页面拆分
例如:
```text
人员管理页面
人员新增页面
人员编辑页面
人员详情页面
```
不能直接计算为4个功能。
---
## 5.2 不按按钮拆分
例如:
```text
新增
修改
删除
保存
提交
审核
```
不能直接认定为6个功能点。
---
## 5.3 不按API拆分
例如:
```text
POST /user
PUT /user
DELETE /user
GET /user
```
不能直接认定为4个功能点。
API是技术实现方式不是功能点本身。
---
## 5.4 不按数据库表拆分
例如:
```text
user
user_role
user_project
user_skill
```
不能直接认定为4个ILF。
应根据逻辑数据分组判断。
---
## 5.5 不按CRUD机械拆分
对于:
> 新增、修改、删除、查询
应根据实际业务事务判断。
如果它们属于同一个“人员档案维护”业务过程则需要进一步按照事务功能规则进行分析而不能简单按照4个按钮计算。
---
# 6. 数据功能复杂度计算原则
## 6.1 ILF/EIF分析维度
数据功能主要依据:
> **DET + RET**
进行复杂度判断。
### 6.1.1 DET
Data Element Type数据元素类型。
指用户可识别、不可再分的数据字段。
例如:
```text
姓名
工号
岗位
手机号
入场日期
离场日期
```
可以作为DET候选。
注意:
> 不同技术字段不一定对应不同DET。
---
### 6.1.2 RET
Record Element Type记录元素类型。
用于识别逻辑数据分组中的不同子结构。
例如人员档案中包含:
```text
人员基本信息
人员资质信息
人员项目经历
人员技能信息
```
需要根据逻辑关联关系判断RET而不能简单按照数据库表数量确定。
---
# 7. 事务功能复杂度计算原则
## 7.1 EI复杂度
EI主要依据
> **DET + FTR**
判断复杂度。
其中:
* DET输入/输出的数据元素;
* FTR事务功能引用或维护的数据功能。
---
## 7.2 EO复杂度
EO主要依据
> **DET + FTR**
同时考虑:
* 计算;
* 派生;
* 汇总;
* 数据转换;
* 业务规则;
* 输出逻辑。
---
## 7.3 EQ复杂度
EQ主要依据
> **DET + FTR**
同时确认:
> 不存在构成EO的计算、派生或复杂处理逻辑。
---
# 8. 功能点计算流程
大模型应严格按照以下顺序执行:
```text
需求文档
识别系统边界
识别用户角色及外部系统
识别业务过程
识别数据对象
识别ILF / EIF
识别EI / EO / EQ
确定功能边界
去除重复功能
判断DET / RET / FTR
判断功能复杂度
按照权重计算功能点
汇总
输出计算依据及置信度
```
---
# 9. 重复功能处理原则
大模型必须进行重复性检查。
以下情况不能简单重复计算:
### 9.1 不同页面调用同一业务功能
例如:
> PC端查询人员
> 移动端查询人员
如果底层业务功能相同,不因两个页面而重复计算。
### 9.2 不同角色执行同一业务功能
例如:
> 项目经理查询人员
> HR查询人员
如果业务处理和数据逻辑相同,仅权限不同,不应重复计算。
### 9.3 同一功能不同展现形式
例如:
> Web查询
> 移动端查询
> API查询
不能仅因展现渠道不同而重复计算。
---
# 10. 特殊功能计算原则
## 10.1 报表
普通数据查询:
> EQ
统计、计算、汇总、派生:
> EO
复杂分析、指标计算、业务规则处理:
> 优先按照EO分析。
---
## 10.2 工作流
工作流不能按照:
> 一个节点 = 一个功能点
计算。
应分析:
> 发起业务 → 数据输入 → 审批处理 → 状态变化 → 业务结果
根据实际业务过程确定EI、EO、EQ及数据功能。
---
## 10.3 文件导入
例如:
> Excel批量导入人员
如果系统接收外部数据并进行校验、转换、写入内部数据:
> EI候选。
---
## 10.4 文件导出
简单数据导出:
> 根据实际处理逻辑判断EQ/EO。
如果包含:
* 汇总
* 计算
* 转换
* 派生
通常应作为EO分析。
---
## 10.5 消息通知
不能因为:
> “系统发送短信”
就自动增加一个功能点。
应判断发送是否构成独立业务处理。
例如:
> 入场审批完成后自动发送通知
通常属于“人员入场审批”业务过程中的处理逻辑,不应机械增加一个独立功能。
---
## 10.6 接口
不能按照:
> 一个API = 一个功能点。
应根据接口背后的业务处理识别:
* EI
* EO
* EQ
* EIF
* ILF
---
## 10.7 AI功能
AI功能也不能简单按照
> 一个Prompt = 一个功能点。
应识别实际业务功能。
例如:
> AI生成招聘岗位描述
如果用户提交需求系统调用AI生成岗位描述并返回结果应按照实际输入、处理、输出以及数据引用情况分析。
---
# 11. 不确定信息处理原则
这是**大模型自动计算必须增加的一条核心规则**。
当需求描述不足以确定功能类型、边界或者复杂度时:
> **不得猜测。**
应输出:
```text
状态:待确认
原因:缺少……
需要确认:……
```
例如:
> “系统支持人员统计分析。”
不能直接计算功能点。
应该要求确认:
* 统计哪些指标?
* 是否包含计算?
* 是否包含趋势分析?
* 数据来源有哪些?
* 是否支持筛选?
* 是否生成报表?
---
# 12. 证据链原则
大模型计算每一个功能点,都应该保留:
> **需求原文 → 功能识别 → 分类判断 → 复杂度判断 → 功能点结果**
例如:
| 功能 | 类型 | 判断依据 | 复杂度 | FP |
| ------ | --- | -------------- | --- | -: |
| 新增人员 | EI | 接收人员信息并维护人员档案 | 中 | X |
| 查询人员 | EQ | 根据条件查询人员信息 | 低 | X |
| 人员月度统计 | EO | 汇总计算人员及工时指标 | 高 | X |
| 人员档案 | ILF | 本系统维护人员信息 | 中 | X |
| HR人员信息 | EIF | 外部HR系统维护本系统引用 | 低 | X |
**没有证据支撑的功能不得进入最终计数。**
---
# 13. 大模型自动计算的置信度原则
建议增加置信度:
### 高置信度
需求明确描述:
> “新增、修改、查询人员信息。”
可以直接计算。
### 中置信度
业务功能基本明确但DET/FTR等信息部分缺失。
可以计算,但标记假设。
### 低置信度
只有模糊描述:
> “系统具备人员管理能力。”
不得直接给出确定功能点,应进入待确认列表。
---
# 14. 功能点计算输出原则
大模型最终不应该只返回:
> **“系统共XXX个功能点。”**
而应该输出四个结果:
### 14.1 功能明细
每个功能:
```text
功能名称
功能类型
功能描述
需求证据
ILF/EIF
EI/EO/EQ
DET
RET
FTR
复杂度
功能点
置信度
计算依据
```
### 14.2 汇总结果
```text
ILFXX
EIFXX
EIXX
EOXX
EQXX
总功能点XXX
```
### 14.3 待确认项
列出所有:
> 因需求信息不足而无法确定的功能。
### 14.4 计算假设
明确列出:
> 大模型为了完成计算而采用的假设。
---
# 15. 大模型自动计算的核心禁止规则
这一部分我建议直接作为**System Prompt中的强约束**。
### 15.1 禁止凭经验补充功能
不得因为“通常系统都有”而增加功能。
### 15.2 禁止按页面数量计算
页面不是功能点。
### 15.3 禁止按按钮数量计算
按钮不是功能点。
### 15.4 禁止按API数量计算
API不是功能点。
### 15.5 禁止按数据库表数量计算
数据库表不是ILF/EIF。
### 15.6 禁止按代码量计算
代码量与FP不是同一计量维度。
### 15.7 禁止为了提高功能点而拆分
不得人为制造功能。
### 15.8 禁止重复计算
同一业务功能不得因角色、页面、终端、接口不同而重复计算。
### 15.9 禁止缺少证据时强行判断
信息不足时必须标记“待确认”。
### 15.10 禁止修改需求事实
功能点分析模型只能:
> **识别需求,不得创造需求。**
---
# 16. 推荐的大模型自动计算工作模式
如果你准备真正把它做成一个**AI功能点计算器**我建议不要让大模型“一次性从需求文档直接算出FP”。
应该设计成:
```text
需求文档
① 需求解析
② 功能候选识别
③ 系统边界识别
④ ILF / EIF识别
⑤ EI / EO / EQ识别
⑥ 功能去重
⑦ DET / RET / FTR提取
⑧ 复杂度判定
⑨ FP计算
⑩ 人工/规则复核
最终功能点
```
其中**①~⑦让大模型做语义理解,⑧~⑨最好由规则引擎计算**。
也就是说:
> **不要让大模型自己算数学。**
让LLM输出
```json
{
"type": "EI",
"det": 12,
"ftr": 2,
"complexity": "medium"
}
```
然后由程序根据标准权重表计算FP。
这样稳定性会高很多,也便于审计。
---
## 如果你要做成真正的“AI功能点计算引擎”
我建议最终架构甚至可以做成:
```text
需求文档
┌───────────────┐
│ LLM需求解析器 │
└───────┬───────┘
┌───────────────┐
│ 功能候选识别器 │
└───────┬───────┘
┌───────────────┐
│ 功能边界分析器 │
└───────┬───────┘
┌───────────────┐
│ FP分类判定器 │
│ EI EO EQ │
│ ILF EIF │
└───────┬───────┘
┌───────────────┐
│ DET/FTR/RET │
│ 提取与校验 │
└───────┬───────┘
┌───────────────┐
│ 规则计算引擎 │
│ Complexity/FP │
└───────┬───────┘
┌───────────────┐
│ 证据链/可解释性│
└───────┬───────┘
FP计算结果
```
**这比单纯写一个Prompt可靠得多。**
尤其如果你的目标是把它用于**招投标软件功能点自动核算**,我会进一步把这套原则整理成一份可以直接给大模型调用的 **《软件功能点自动识别与计算规则 V1.0》**,包括:
1. **标准术语定义**
2. **EI/EO/EQ/ILF/EIF判定决策树**
3. **DET/RET/FTR识别规则**
4. **低/中/高复杂度判定矩阵**
5. **功能拆分与合并规则**
6. **特殊场景规则**
7. **反作弊/防虚增规则**
8. **LLM Prompt**
9. **标准JSON输出Schema**
10. **2030个典型案例及标准答案**
这样就可以直接拿去做你的**AI功能点计算模块**,而不只是作为一段招标文件文字。

View File

@ -0,0 +1,219 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""功能点规则计算引擎 —— 《软件功能点自动计算原则》第16章落地
LLM 只做语义识别~复杂度判定与 FP 加权计算由本脚本完成
保证稳定可审计可复核
用法:
python3 fp_calc.py input.json # 输出 markdown 四件套
python3 fp_calc.py input.json --json # 输出 JSON供程序消费
cat input.json | python3 fp_calc.py - # stdin
输入 JSON schema:
{
"project": "项目名(可选)",
"boundary": {"internal": [...], "external": [...]}, # 可选,原样回显
"functions": [
{"name": "新增人员", "type": "EI",
"desc": "接收人员信息并维护人员档案",
"evidence": "需求3.2: 支持新增外包人员信息",
"det": 12, "ret": 1, "ftr": 2, # ret 仅 ILF/EIF 用; ftr 仅 EI/EO/EQ 用
"confidence": "high", # high/medium/low
"assumption": "DET 按表单字段估算(可选)"}
],
"pending": [{"item": "人员统计分析", "reason": "缺少指标清单", "ask": "统计哪些指标?"}],
"assumptions": ["..."]
}
复杂度矩阵与权重 = IFPUG 4.x 标准文档要求"程序按标准权重表计算"但未附表
本脚本以行业标准补齐如项目另有约定权重表改本文件 WEIGHTS/MATRIX 即可
"""
import json
import sys
# ── 复杂度矩阵(行=FTR或RET档, 列=DET档──
# 事务功能 EI: FTR 档 (0-1, 2, >=3) × DET 档 (1-4, 5-15, >=16)
EI_MATRIX = [["low", "low", "low"],
["low", "avg", "high"],
["avg", "high", "high"]]
EI_FTR_BANDS = [(0, 1), (2, 2), (3, 10 ** 9)]
EI_DET_BANDS = [(1, 4), (5, 15), (16, 10 ** 9)]
# 事务功能 EO/EQ: FTR 档 (0-1, 2-3, >=4) × DET 档 (1-5, 6-19, >=20)
EOEQ_MATRIX = [["low", "low", "low"],
["low", "avg", "high"],
["avg", "high", "high"]]
EOEQ_FTR_BANDS = [(0, 1), (2, 3), (4, 10 ** 9)]
EOEQ_DET_BANDS = [(1, 5), (6, 19), (20, 10 ** 9)]
# 数据功能 ILF: RET 档 (1, 2-5, >=6) × DET 档 (1-19, 20-50, >=51)
ILF_MATRIX = [["low", "low", "low"],
["low", "avg", "high"],
["avg", "high", "high"]]
# 数据功能 EIF: 同档位, 矩阵不同
EIF_MATRIX = [["low", "low", "avg"],
["low", "avg", "high"],
["avg", "high", "high"]]
DF_RET_BANDS = [(1, 1), (2, 5), (6, 10 ** 9)]
DF_DET_BANDS = [(1, 19), (20, 50), (51, 10 ** 9)]
# ── 标准权重表IFPUG 4.x, 未调整功能点 UFP──
WEIGHTS = {
"ILF": {"low": 7, "avg": 10, "high": 15},
"EIF": {"low": 5, "avg": 7, "high": 10},
"EI": {"low": 3, "avg": 4, "high": 6},
"EO": {"low": 4, "avg": 5, "high": 7},
"EQ": {"low": 3, "avg": 4, "high": 6},
}
CN = {"low": "", "avg": "", "high": ""}
def _band(v, bands):
for i, (lo, hi) in enumerate(bands):
if lo <= v <= hi:
return i
return len(bands) - 1
def complexity(ftype, det, ret, ftr):
"""返回 (complexity, 依据串)。"""
det = int(det or 0)
ret = int(ret or 1)
ftr = int(ftr or 0)
if ftype == "EI":
c = EI_MATRIX[_band(ftr, EI_FTR_BANDS)][_band(det, EI_DET_BANDS)]
why = "DET=%d(档%s) FTR=%d(档%s)" % (
det, _det_band_name(det, EI_DET_BANDS), ftr, _band_name(ftr, EI_FTR_BANDS))
elif ftype in ("EO", "EQ"):
c = EOEQ_MATRIX[_band(ftr, EOEQ_FTR_BANDS)][_band(det, EOEQ_DET_BANDS)]
why = "DET=%d(档%s) FTR=%d(档%s)" % (
det, _det_band_name(det, EOEQ_DET_BANDS), ftr, _band_name(ftr, EOEQ_FTR_BANDS))
elif ftype == "ILF":
c = ILF_MATRIX[_band(ret, DF_RET_BANDS)][_band(det, DF_DET_BANDS)]
why = "DET=%d(档%s) RET=%d(档%s)" % (
det, _det_band_name(det, DF_DET_BANDS), ret, _band_name(ret, DF_RET_BANDS))
elif ftype == "EIF":
c = EIF_MATRIX[_band(ret, DF_RET_BANDS)][_band(det, DF_DET_BANDS)]
why = "DET=%d(档%s) RET=%d(档%s)" % (
det, _det_band_name(det, DF_DET_BANDS), ret, _band_name(ret, DF_RET_BANDS))
else:
raise ValueError("未知功能类型: %s" % ftype)
return c, why
def _band_name(v, bands):
i = _band(v, bands)
lo, hi = bands[i]
return "%d-%d" % (lo, hi) if hi < 10 ** 9 else ">=%d" % lo
def _det_band_name(det, bands):
i = _band(det, bands)
lo, hi = bands[i]
return "%d-%d" % (lo, hi) if hi < 10 ** 9 else ">=%d" % lo
def calc(data):
funcs, errors = [], []
for i, f in enumerate(data.get("functions") or []):
t = str(f.get("type") or "").upper()
if t not in WEIGHTS:
errors.append("functions[%d] type 非法: %r" % (i, f.get("type")))
continue
det = f.get("det")
if det is None or int(det) < 1:
errors.append("functions[%d](%s) det 缺失或<1" % (i, f.get("name")))
continue
ret = f.get("ret", 1) or 1
ftr = f.get("ftr", 0) or 0
if t in ("ILF", "EIF") and f.get("ftr") is not None:
pass # 数据功能忽略 ftr
cx, why = complexity(t, det, ret, ftr)
fp = WEIGHTS[t][cx]
funcs.append({
"name": f.get("name") or ("functions[%d]" % i),
"type": t,
"desc": f.get("desc") or "",
"evidence": f.get("evidence") or "",
"det": int(det), "ret": int(ret), "ftr": int(ftr),
"complexity": cx, "complexity_cn": CN[cx],
"fp": fp,
"confidence": f.get("confidence") or "medium",
"assumption": f.get("assumption") or "",
"calc_basis": why + " → 复杂度%s → 权重%d" % (CN[cx], fp),
})
counts = {t: sum(1 for x in funcs if x["type"] == t)
for t in ("ILF", "EIF", "EI", "EO", "EQ")}
total = sum(x["fp"] for x in funcs)
return {"functions": funcs, "counts": counts, "total_fp": total,
"errors": errors,
"pending": data.get("pending") or [],
"assumptions": data.get("assumptions") or [],
"boundary": data.get("boundary") or {}}
def to_markdown(res, project=""):
out = []
if project:
out.append("# 功能点计算结果:%s" % project)
out.append("## 14.1 功能明细")
out.append("")
out.append("| 功能 | 类型 | 描述 | 需求证据 | DET | RET | FTR | 复杂度 | FP | 置信度 | 计算依据 |")
out.append("|---|---|---|---|--:|--:|--:|---|--:|---|---|")
for x in res["functions"]:
out.append("| %s | %s | %s | %s | %d | %d | %d | %s | %d | %s | %s |" % (
x["name"], x["type"], x["desc"], x["evidence"], x["det"], x["ret"],
x["ftr"], x["complexity_cn"], x["fp"], x["confidence"], x["calc_basis"]))
out.append("")
out.append("## 14.2 汇总结果")
out.append("")
c = res["counts"]
out.append("ILF%d EIF%d EI%d EO%d EQ%d" % (
c["ILF"], c["EIF"], c["EI"], c["EO"], c["EQ"]))
out.append("")
out.append("总功能点UFP%d" % res["total_fp"])
out.append("")
out.append("## 14.3 待确认项")
out.append("")
if res["pending"]:
for p in res["pending"]:
out.append("- %s:原因=%s;需确认=%s" % (
p.get("item"), p.get("reason"), p.get("ask")))
else:
out.append("- (无)")
out.append("")
out.append("## 14.4 计算假设")
out.append("")
if res["assumptions"]:
for a in res["assumptions"]:
out.append("- %s" % a)
else:
out.append("- (无)")
for x in res["functions"]:
if x["assumption"]:
out.append("- [%s] %s" % (x["name"], x["assumption"]))
if res["errors"]:
out.append("")
out.append("## 输入校验错误(未计入)")
out.append("")
for e in res["errors"]:
out.append("- %s" % e)
return "\n".join(out)
def main():
args = [a for a in sys.argv[1:]]
as_json = "--json" in args
args = [a for a in args if a != "--json"]
if not args or args[0] == "-":
data = json.load(sys.stdin)
else:
with open(args[0], encoding="utf-8") as f:
data = json.load(f)
res = calc(data)
if as_json:
print(json.dumps(res, ensure_ascii=False, indent=1))
else:
print(to_markdown(res, data.get("project") or ""))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,31 @@
{
"project": "IT人力外包管理平台样例",
"boundary": {
"internal": ["人员档案", "项目档案", "工时记录"],
"external": ["甲方HR系统组织机构与员工信息"]
},
"functions": [
{"name": "人员档案", "type": "ILF", "desc": "本系统维护的外包人员信息集合",
"evidence": "需求4.8 入场资料管理+15.1 人员档案管理", "det": 25, "ret": 3,
"confidence": "high"},
{"name": "HR人员信息", "type": "EIF", "desc": "甲方HR系统维护,本系统只读引用",
"evidence": "需求11.4 从HR系统读取组织机构和员工", "det": 12, "ret": 1,
"confidence": "medium", "assumption": "RET=1(仅员工主结构,组织树未明确)"},
{"name": "新增人员", "type": "EI", "desc": "接收人员信息并维护人员档案",
"evidence": "需求3.x 人员推荐与入场: 新增外包人员", "det": 12, "ftr": 2,
"confidence": "high"},
{"name": "查询人员", "type": "EQ", "desc": "按姓名/工号/部门/岗位/状态检索展示",
"evidence": "需求6.2 人员信息管理: 支持多条件查询", "det": 5, "ftr": 1,
"confidence": "high"},
{"name": "人员月度统计", "type": "EO", "desc": "汇总人数/流失率/到岗率/工时/SLA达标率",
"evidence": "需求16.x SLA指标体系+17.9 数据统计分析", "det": 22, "ftr": 3,
"confidence": "medium", "assumption": "DET按6类指标×字段估算"},
{"name": "填报工时", "type": "EI", "desc": "人员提交工时记录写入工时档案",
"evidence": "需求6.4 工时管理", "det": 6, "ftr": 2, "confidence": "high"}
],
"pending": [
{"item": "人员统计分析(模糊项)", "reason": "仅写'支持统计分析',指标/是否趋势/是否报表不明",
"ask": "统计哪些指标?是否含趋势分析?是否生成报表?"}
],
"assumptions": ["ILF人员档案 RET=3(基本信息/资质/项目经历,技能信息未明确单列)"]
}