分析过程记录
认证要求「分析过程」证据。本文件记录这次分析实际怎么做的,包括走过的弯路和修正过程。
1. 任务与背景
- 岗位背景:销售,负责福建市场、运营商(移动/电信/联通/广电)行业整体。
- 业务线:网络安全产品、网络安全服务、网络攻防培训、AI 能力开发。
- 要回答的问题:销售资源有限,先投哪个标、先打哪个地市、用什么理由切入?
- 认证标准对应:用 AI 自助完成取数、分析、洞察与可视化,并对结论与口径负责。
分析拆成两条线,交叉使用:
| 宏观线 | 招投标线 | |
|---|---|---|
| 回答 | 哪个地市值得投入 | 哪里有真实需求、钱从哪出、谁在竞争 |
| 数据 | 公开统计公报 | 招投标公告原文 |
| 产物 | 地市优先级得分 | 商机优先级排序、竞争格局 |
2. 全流程与 AI 的参与方式
一条命令跑通全流程:
python src\run_all.py
| 步骤 | 脚本 | AI 做什么 | 人不放手的部分 |
|---|---|---|---|
| 0 抓取 | fetch_cmcc_notices.py |
(未用 AI)从中国移动采购与招标网公开接口按 12 个业务关键词检索、取并集抓公告全文 | 关键词与筛选规则由人定;逐条留存来源 URL + SHA-256 |
| 1 取数 | fetch_data.py |
从公开文本中抽取结构化字段 | 用确定性正则独立再抽一遍做交叉验证 |
| 2 清洗 | clean_data.py |
(未用 AI)类型规整、派生指标、质量校验 | 校验规则由人定义 |
| 3 分析 | analyze.py |
基于已算好的指标生成洞察叙述 | 指标与权重由人定义;AI 不参与计算 |
| 4 取数 | fetch_tenders.py |
从每份招投标公告原文抽取字段 | 同样用正则独立再抽一遍 |
| 5 清洗 | clean_tenders.py |
(未用 AI)字段归一化、招标/中标配对 | 配对规则由人定义 |
| 6 分析 | analyze_tenders.py |
基于已算好的指标生成投标建议 | 打分口径由人定义;AI 不参与计算 |
| 7 口径校对 | verify_caliber.py |
(未用 AI)交叉校验 + 回查 AI 输出数字 | 全部判定规则由人定义 |
| 8 可视化 | visualize*.py |
(未用 AI) | 图表选择由人决定 |
| 9 报告 | build_report.py |
(未用 AI) | 结论与建议由人把关 |
核心原则:AI 负责取数和表达,计算和判定一律由代码负责。 因此 AI 不可能编造出 一个"看起来很合理"的结论——它只能解释已经算好的数字。
3. 关键决策与取舍
- 地市优先级打分口径:把「客户值不值得投入」拆成客户规模(移动电话用户数,0.40)、 客户价值(人均 GDP,0.35)、数字化成熟度(城镇化率,0.25)。选这三个维度是因为它们都有 公开数据可查,可复现、可被质疑。
- 商机优先级打分口径:预算规模(0.45)+ 地市优先级(0.35)+ 业务线战略权重(0.20)。 业务线权重体现公司战略:AI 能力开发 1.2 > 网络安全服务 1.0 > 网络安全产品 0.9 > 网络攻防培训 0.8 > 其他 0.5。
- 权重是假设而非数据:权重写在
src/config.py的ASSUMPTIONS里显式登记, 并自动输出到《口径校对记录》C 节,避免"用假设冒充事实"。 - AI 输出必须可回查:AI 写出来的每个数字都要能在源数据里找到出处,否则标红交人工判断。
- 真实数据优先:程序优先读
data/raw/tender_notices/;该目录为空时回退到示例公告, 并在报告顶部强制插入醒目警示,防止示例数据被误当作真实结论引用。
4. 过程中真实遇到的问题与修正
这一节是本次分析最有价值的部分——它证明了"口径经过核对"不是一句空话。 下面 11 个问题全部是实际跑出来的,其中 5 个会直接导致错误结论。 前 6 个在示例数据阶段暴露,问题 7 之后的 5 个只有真实数据才能触发—— 示例数据太干净,根本碰不到。
问题 1:正则漏匹配,导致口径校验误报
- 现象:口径校验显示
C2(5G用户合计)无法校验、C6(地市个数)异常。 - 根因:确定性正则写得太死——
- 公开文本中「5G 移动电话用户」与「达到」之间换行了,正则里没有允许空白;
- 地市个数在文本里写的是阿拉伯数字
9,正则只匹配中文数字「九」。 - 修正:正则改为允许空白;地市个数同时兼容中文与阿拉伯数字。
- 结果:两项恢复为「通过」。这是正则的错,不是数据的错——如果不查,就会误判数据有问题。
问题 2:AI 输出数字核对存在漏检(严重)
- 现象:第一版校验显示「AI 文本中 31 个数字全部可回查源数据」,看起来完美。
- 根因:用于抓数字的正则用了
\b词边界,而中文汉字在 Python 正则里也是"词字符", 于是「得分分别为82.48」这种写法根本匹配不到\b,大量数字从未被检查—— "全部通过"是漏检造成的假象。 - 修正:改为全量抓取文本中的所有数字,再用显式声明的规则排除序号、年份等叙述性数字, 排除项也全部列出以便审计;判定容差收紧到 0.5%。
- 结果:核对覆盖面从"部分"变成"全部"。
- 教训:一个永远显示"通过"的校验,比没有校验更危险。
问题 3:招标/中标配对失败,导致"竞争对手 0 家"(严重,会直接得出错误结论)
- 现象:控制台输出「项目 5 个,在途商机 5 个,竞争对手 0 家」。 但 8 份公告里有 3 份是中标结果公告,不可能一个对手都没有。
- 根因:读 CSV 时用
fillna("")把缺失字段填成了空字符串, 后续projects["winner"].fillna(projects["win_winner"])就永远不生效—— 因为空字符串不是缺失值。结果中标公告的信息根本没并进项目表。 - 修正:改为把空字符串统一转成缺失值(
replace({"": pd.NA}))再做合并。 同时发现 pandas 3.0 下.astype(str)不会把缺失值转成字符串,"长亭" in w会抛异常, 一并改成先fillna("")再转字符串。 - 结果:项目 5 个、在途商机 2 个、竞争对手 3 家,与公告原文一致。
- 教训:如果一个"商机池"里有三分之一的项目其实已经中标了,销售会白跑一趟。 这类错误不会报错,只会安静地给出错误答案,必须靠"结果合不合理"来发现。
问题 4:校验指标本身失真,把"没有可比内容"算成了"不一致"
- 现象:AI 取数复核显示「24 / 32 个字段一致」,看起来有 8 处分歧。
- 根因:那 8 处其实是「双方均未抽到」——招标公告里根本没有中标金额, 正则和 AI 都正确地抽出了"无",却被统计成了不一致。
- 修正:把结果拆成三态——一致 / 双方均未抽到(无可比对内容)/ 不一致或缺失, 只把最后一类当作真正的问题。
- 结果:24 个一致,8 个双方均未抽到,0 个不一致。
- 教训:校验指标本身也会撒谎。指标难看时要先问"它统计的口径对不对", 而不是急着去修数据。
问题 5:锚点集合不全 + 值为 0 的锚点被跳过,把合法数字误报为"无依据"
- 现象:招投标 AI 洞察里有 2 个数字回查不到源数据(
1950万元、0万元)。 - 根因:
1950万元是 2026Q1 中标金额合计(1130 + 820),0万元是 Q3 中标金额合计, 都是合法计算结果——只是我建锚点集合时漏掉了「季度中标金额合计」这一项;- 更隐蔽的是,锚点匹配里写了
if not av: continue,值为 0 的锚点会被直接跳过, 导致 AI 文本里的0永远无法命中,必然被误报。 - 修正:补全季度中标项目数/中标金额锚点;把 0 值锚点改为"要求相等才算命中"而不是跳过。
- 结果:2 个数字全部回查成功。
- 教训:误报和漏报是一体两面。上一轮我修的是漏报(问题 2),这一轮就撞上误报。 校准一个校验器,必须同时盯住两个方向。
真实数据接入后新暴露的问题(问题 7 至 11)
前 6 个问题在示例数据阶段就出现了。换成 234 份真实公告后,又撞出一批新问题—— 这批更有说服力:它们靠构造得整齐的示例数据根本碰不到。
问题 7:有中标金额的行,业务类别全部丢失(严重,会直接得出错误结论)
- 现象:按类别统计中标金额时,所有类别都是 0,但竞争格局里明明有 777.55 万元。
- 根因:有 6 行能拿到中标金额,但它们的
category全是空值——因为项目表按 "招标公告 info + 结果公告 info" 配对后再取类别,而这几个项目只有结果公示、没有招标公告, 描述字段未能回填。 - 修正:让中标信息聚合时一并带上
w_project/w_operator/w_category/w_city/w_tender_no, 再按列fillna回填。 - 教训:"没有招标公告的项目"不是一个可以忽略的边缘情况——真实采购里结果公示 独立发布很常见,漏掉它就会把整个品类的战绩统计成零。
问题 8:同一项目重复发布公告,预算被高估(会直接得出错误结论)
- 现象:预算合计出现异常大值,与逐条审阅公告的直觉不符。
- 根因:有 4 组重复 key(同一项目重新发布公告)被当成不同项目重复计入,
合计多算 726.77 万元。重复组包括
FJYDZQ-ZM(FZ)-2025-648(362.78 万)、FJYDZQ-ZM(FZ)-2025-655(265.71 万)、FJYDZQ-ZM(FZ)-2026-064(83.70 万), 以及龙岩分公司的一条招募项目(14.58 万)。 - 修正:按公告日期排序后
drop_duplicates("key", keep="last"),保留最新一次发布。 - 教训:"同一个标被发了两遍"在真实平台上是常态(流标重发、延期重发), 只按项目名去重不够,必须把"取哪一份"的口径写死。
问题 9:预算金额的含税口径混用,合计值失去意义
- 现象:口径校验报出 3 处「AI 与正则都有值但不一致」。逐条查原文后发现: 原文写的是「235.89 万元(不含税),250.04 万元(含税)」——正则取不含税,AI 取含税, 两者都没"错",但两套口径混在同一列里做汇总,合计值就不成立了。 进一步抽查发現连 AI 自己也不一致:6 个双报价行里有 1 行取了不含税。
- 根因:提示词只要求"取预算金额",没有把税别口径写死。
- 修正:提示词改为一律优先取含税,并新增
budget_incl_tax字段显式记录税别; 新增口径校验 T8 项,把"仍有多少项只含不含税/未标注税别"摆到台面上。 - 结果:重跑后 6 个双报价行 全部取含税(0 行取不含税);T8 仍标注 15 项不含税、 65 项未标注,因为它们原文就只给了一个数,无法换算,也不应当偷偷换算。
- 教训:“取值口径不一致”不会报错,只会安静地把一个不能加总的数列加总。 这类问题只有把"每个数字的含义"写下来才看得见。
问题 10:实体安全项目把真正的安全商机挤下榜首(会直接得出错误结论)
- 现象:在途商机优先级的前两名是「某机场安全防范服务」(1931.70 万)和 「通信管线迁改及智能化改造工程」(1321.15 万)。
- 根因:抓取关键词是「安全」,会把实体安全项目一并捞进来。它们预算体量大, 按"预算规模 0.45 权重"打分自然排到前面,把真正的网络安全标挤了下去。
- 修正:商机打分前先按
is_cyber列过滤,只在长亭四条业务线口径内排序; 报告摘要也把「其他」类单列并排除出网络安全口径。 - 结果:榜首变为「泉州分公司南安某单位网络安全信息化建设」(723.66 万), 前八名全部落在四条业务线内。
- 教训:关键词召回永远会带回噪音,关键是噪音到达打分环节前要被挡住—— 而不是等销售看到榜单才发现榜首不是自己的生意。
问题 11:比对指标把"正则抽不到"误计为"AI 抽错"
- 现象:AI 取数复核最初显示 131 处不一致,看起来 AI 抽取质量很差。
- 根因:真实公告排版远比示例文本复杂(PDF 表格单元格被拆成多行、正文是 HTML 源码), 正则覆盖面有限。131 处里有 128 处是「AI 抽到、正则没抽到」——是正则能力不足, 不是 AI 编造。不加区分就统计,等于把工具的能力缺陷记在被核查方头上。
- 修正:把"不一致"按方向拆成三类分别统计:仅 AI 抽到 / 仅正则抽到 / 两者冲突。
- 结果:仅正则抽到 = 0(AI 从未漏掉正则能抽到的字段),两者冲突仅 3 处 (即问题 9 的含税口径),其余均为正则能力所限。
- 教训:与问题 4 同类——指标的口径比指标的高低更重要。
问题 12:单个关键词召回不全,漏掉主业标的
- 现象:初版只用一个检索词「福建 安全」(列表 181 条)。
- 根因:站方检索是标题关键词匹配,而等保测评、渗透测试、商用密码评估、AI 大模型 这类长亭主业项目的标题里常常没有「安全」二字,全部漏掉。
- 修正:按四条业务线设 12 个关键词分别检索,按公告 id 取并集。
- 结果:列表并集 181 → 252 条(保留 234 条),补回等保测评、渗透测试、商用密码、 大模型等标的。其中「攻防/演练/红蓝/应急响应/堡垒机/工控/保密」实测 0 命中, 也一并登记,作为「检索已穷尽」的证据。
问题 13:扩词后,通用 AI 平台把市场盘子撑到失真(会直接得出错误结论)
- 现象:加入「人工智能」后,预算合计从 8,188 万跳到 24,306 万, 「AI能力开发」占比 63%,在途商机 Top6 全部是 AI 平台类。
- 根因:某政务 AI 公共服务平台单体 1.17 亿,独占全盘 48%。它是通用 AI 平台建设, 与长亭"用 AI 做安全"是两回事,但按关键词与类别规则都会被算作 AI能力开发。
- 修正:把业务范围写死为「运营商客户采购 × 长亭四条业务线」,并给 AI能力开发 加 安全挂钩判据(项目名含安全类关键词);实体安全项目一并剔除。
- 结果:剔除 47 个非本案项目(实体安全 39 + 通用 AI 8),得 110 个项目、9,337.89 万元, 最大业务线回到网络安全产品(62.71%)。
- 教训:召回扩大必然带来噪音,而噪音的杀伤力与它的金额成正比。 一个 1.17 亿的错分类项目,足以让整份报告的市场结构结论全部作废。
问题 14:范围过滤插在去重之前,统计到的是"行数"而不是"项目数"
- 现象:加范围过滤后,口径校验 T1 报「118 vs 117」,差 1 个项目。
- 根因:过滤写在了按 key 去重之前,被剔除的统计是行数(含同一项目的重复公告), 而 T1 的不变式减的应是唯一项目数;另外两条剔除消息都被 T1 累加, 导致同一个项目被减了两次。
- 修正:把范围过滤移到去重之后;两类剔除只让 T1 解析最终那一条。
- 结果:T1 恢复通过(110 = 157 − 47)。
- 教训:与问题 4、11 同源——校验能不能对,取决于两边是不是在同一个口径上说话。
问题 6:AI 会把数字四舍五入,不能简单判为"编造"
- 现象:AI 写出「福州和厦门的人均 GDP 都超过 15 万元」,而源数据是 15.28 / 15.08 万元, 精确匹配回查不到,被标为"需人工复核"。
- 根因:这句话有依据,但是四舍五入后的近似表述。一律判为"无依据"会制造噪音, 让真正的问题被淹没。
- 修正:回查结果分为精确命中(偏差 ≤0.5%)和近似命中(AI 把源数据四舍五入后引用) 两档,近似项单独列出,不算作无依据,但也不与精确值混为一谈。
- 结果:两条线的数字全部可回查,且精确/近似分列,审计时一目了然。
5. 已知局限(须与结论同时引用)
- 样本仅覆盖中国移动:234 份公告全部来自中国移动采购与招标网,不含电信、联通、广电。 结论只能表述为「福建移动口径」,不能外推为福建全部运营商市场。
- 中标金额披露率低:110 个项目中仅 15 个披露了中标结果, 竞争格局与金额类结论只能作方向性判断。
- 检索仍可能漏标:标题不含 12 个关键词中任何一个的主业项目抓不到; 尤其在「等保测评」「渗透」「大模型」等口语化表述下可能漏召回。
- 宏观线 S2 为估算值:分运营商指标置信度低,不得对外引用。
- 缺关键字段:未纳入项目联系人、投标窗口期、资质要求等字段, 而这些直接影响"能否参与投标"。
- AI 分类存在边界不稳定:对「安全设施改造提升」这类模糊项目,
两次独立抽取给出过不同类别,须人工复核,详见
data/dataset_sources.md。 - 预算含税口径未完全统一:部分项目原文只给不含税金额或未标注税别, 跨项目加总时存在少量不可比性(口径校验 T8 项标注)。
- 「与长亭业务有关」是人为判定:实体安全与通用 AI 平台项目的剔除基于项目名关键词, 边界项目(如「安全设施改造提升」)建议人工确认。
6. 人机分工原则(可复用)
| 环节 | 交给 AI | 自己把关 |
|---|---|---|
| 取数 | 从非结构化文本抽取字段 | 用第二套方法交叉验证 |
| 分析 | 解释数字、组织语言 | 定义指标、权重、口径 |
| 结论 | 起草叙述 | 判断业务合理性、决定是否采纳 |
| 校验 | — | 设计判定规则,签字负责 |
7. 复现方法
python src\run_all.py
产物见 README.md。所有 AI 调用(提示词、返回原文、耗时)记录在
outputs/ai_logs/ai_calls.jsonl,可逐条追溯。
