AI 不该出现在每一步:一条标讯数据链如何划分模型、代码与人的责任
真正有价值的 AI 架构,不是把模型塞进每一步,而是知道它不该出现在哪一步。
我在这条链路上最先学会的事,不是怎么把模型接进业务,而是模型不该出现在哪里。
这是一套面向垂直行业用户的信息产品:标讯(招标与中标公告)由公司付费从外部平台购买,再免费提供给用户,是核心引流内容。采集侧有一套自动化工作流,负责把外部数据结构化后写进来;入口、判重、入库和下游业务链由我负责。这个分工后面还会再提一次,因为它决定了哪些问题是我能修的,哪些不是。
下面记录的是这条链路上六次具体的取舍。它们共同指向一个结论:真正有价值的 AI 架构,不是把模型塞进每一步,而是知道它不该出现在哪一步。
图 1:从外部数据与采集到对外通道的端到端链路;模型位于输入模糊的边缘,核心由确定性代码和人来承担。
一、被静默吞掉的付费数据:先修可信度,再谈智能
2026 年 7 月,我开始改造每日标讯导入。当时存在两层浪费:一是结构浪费,外部平台的信息很丰富,我们的表结构接不住,用户只看到残缺版本;二是链路浪费,导入过程会静默丢行,花钱买来的标讯在入库那一刻消失,没有人知道。
根因在一个 Excel 解析框架(AutoPoi 1.4.0)的判行逻辑里。模板中有一个嵌套的“时间线”子表。框架判断“这一行是不是新主记录”时,默认只看第一列,也就是项目编号。当这一列为空、模板里存在嵌套子表、且上一条主记录已经解析成功时,三个条件同时成立,框架就把整行当成上一条的子表延续:只解析子表列,主表字段整行丢弃,而且不报错。
换一个判空列修不好它,因为这是判行语义错误,不是配置错误。正确的语义只有一句话:主表列全空、只有子表有值,才算延续行;任何主表列非空,就是一条新标讯。框架表达不了这个语义,只能自研解析或改掉嵌套模板。
我给自己定的目标不是“补这个 bug”,而是四个词:不丢、不重复、可见、信息完整。其中“可见”最关键——导入必须返回行级报告:文件总行数、解析数、入库数、跳过数、失败数及原因。一次导入到底丢了什么,必须能被看见,而不是藏在一条“导入成功”里。
还有一个前置约束:方案不能依赖“运营保证某一列永远必填”。源文件由外部平台导出,格式不由我们控制。所以先要真实样例,再设计解析,顺序不能反。
二、84.8%:把“数据有点脏”变成可定位的机制
2026 年 7 月底的一次历史数据治理中,开发库里 335 条记录,按业务逻辑唯一只有 51 条,重复 284 条,重复率 84.8%。
这个数字的价值不在于难看,而在于它可以被定位。按入库时间切片后,绝大部分重复集中在某一天上午连续四次导入;而那一天的上午,早于三层判重代码上线。旧逻辑只在项目编号上做精确等值比较,当时那批数据的来源链接列为空、部分编号带前导空格、四次导入几乎同时发起——每个请求查库时,都看不到其他请求尚未提交的行。
修复后的判重分三层:先比来源链接;再比标准化后的项目编号与公告子类型;最后退到名称、发布单位、日期与子类型四要素。修复之后再导入 4 条,零重复;之后对生产历史做了一次清洗,343 条收敛到 59 条。但我不想把这个故事讲成“已经根治”。当时没有用数据库唯一索引一刀切,因为来源字段不齐、历史脏数据与格式差异让单一键并不可靠;而“逐行查库、逐行插入”在并发导入下仍然存在可见性缺口。这个缺口被登记为技术债,而不是被一行成功日志盖住。
三、异常必须显形,例外交给人
源数据的问题不止重复。有的记录缺金额,有的公告类型根本不在我们的枚举里,有的列名换了形态:多一个空格、换一个别名、合并单元格。
后续一次改造把原则做进了系统:列名做归一与别名白名单,公告类型做值域归一,识别不了的内容不再被当成正常行吞下去,而是进入可见的异常清单;缺金额这类业务上可以接受的情况,允许人工豁免放行。
上线后的首批 4 条豁免,4 条精确命中,同一批未豁免的对照记录仍被拦截,审计字段对普通用户保持中性。机器负责发现异常,人负责处理例外——这两件事没有被合并成一句“导入成功”。
四、给 AI 开一扇门,而不是一把数据库钥匙
这里的 AI 指采集侧:协作方的自动化工作流抓取并整理标讯,再写入我们的系统。工作流本身主要由协作方实现,我负责的是它接入之后的入口、判重、入库和下游链路。这个边界要说清楚,否则后面的取舍会被误读成我一个人的功劳。
最省事的做法是给采集侧一个数据库账号。我没有这么做。
我们开放了三个端点:新增、更新、检查。约束有五层:
- 字段白名单:只接受 39 个固定业务字段,白名单之外的内容进不来;
- 幂等分流:同一条标讯重复推送时,由服务端按既定规则判定新增、更新还是跳过,而不是要求调用方自己保证;
- 审计与限额:每次写入落一条审计记录,带来源标记与独立密钥,每日调用有上限;
- 显式失败:参数不合规、密钥不对、超限,一律返回明确错误,不静默降级;
- 职责可分离:出了问题,是采集侧没采到、入口拒了,还是判重吞了,审计记录能分开回答。
接口在 2026 年 9 月上线,当天冒烟 10/10。三天后的生产巡检给了我最想要的那条证据:工作流已经连续多日真实写入,零重复、零碰撞。此前我甚至怀疑过“可能根本没有流量”,这个猜测被数据推翻了。
五、为什么实时匹配没有调用大模型
按企业画像匹配标讯,直觉方案是:用户查询时,把企业信息和标讯正文一起交给大模型判断相关性。我们没有这么做。
讨论后确认,这个场景不需要语义生成,只需要稳定的关键词归属。最终做法是硬编码词典加人工维护的标签,在标讯入库时就完成打标,标签写进扩展字段;用户查询时只走索引。上线时对约 3.5 万行历史中标档案做了一次性打标(数量为 2026 年 9 月统计时点),形成企业的标讯标签底池;新公告入库即筛即写消息,而不是等用户登录时现算。历史数据只进入画像池,不因为系统上线就向存量用户追推。
这个选择有明确代价:词典要维护,覆盖度和误判要持续治理,跨表述的语义理解肯定不如大模型。换来的是查询简单、在线成本可预测、故障面收敛在写入侧。对一条每天都要跑、而且跑错了会直接影响用户判断的链路,我更需要后者。
六、自动识别与自动对外之间,必须站一个人
系统能识别命中,不代表它有权把消息发出去。
推送通道扩展后,我们把自动直推改成了待发箱:机器命中的消息先落进待发队列,由人批准、删除、改文案或手工补录,同一个审核闸管理不同通道。上线当天验证了旧的自动直推通道不再新增记录,说明闸门确实生效。
这里也有必须承认的边界:当时部分通道的真实发送配置和首条实弹验证仍在挂账,我不能声称所有通道都已经大规模真实触达。审核闸的价值首先是责任可追溯,其次才是效率。
七、成本、边界,以及我对 AI 位置的判断
回头看,这条链路上的 AI 主要位于采集侧这类输入模糊、结果可被下游校验的环节。权威数据、实时判重、匹配打标和对外动作,则交给确定性代码和明确的人。
理由很朴素:模型擅长处理模糊输入,不擅长承担不可撤销的后果。让它直接写库、让它决定外发、让它每次查询都重新推理一遍,成本、延迟、故障面和责任会一起变模糊。
确定性系统同样有成本:规则要写,异常要维护,人会累。但它的行为可预测、可审计、可回滚,出了问题能找到那一行。
图 2:按正文叙事整理的六个阶段;仅标注正文明确出现的时间,未给出的阶段按顺序呈现。
最后一点,我认为比技术取舍更重要:工程链路打通,不等于需求被验证。工程让试错的成本更低,但不能替代真实需求。这句话不是修辞,是我在写完这条链路之后更愿意承认的一件事。
所以,“AI 应该放在哪里”不是一个架构审美问题,而是责任分配问题。判重、入库、外发这些会改变权威数据或影响真实用户的动作,需要能被人指名、复现和回滚;而模型适合待在那些输入模糊、结果可被下游校验的位置。分清楚这条线,比多接一个模型更有价值。