智能预处理与 Agent 编排:先把逻辑讲清楚,再让 AI 真正提速
智能预处理与 Agent 编排:先把逻辑讲清楚,再让 AI 真正提速
wwxdsg这篇文章想复盘的,不只是一次预处理能力升级。
我更想讨论一个更底层的问题:
为什么很多团队一边说“让 AI 提效”,一边又被 AI 拖慢节奏?
我现在的答案很直接:
不是 AI 不够强,而是任务边界和编排逻辑先天就没讲清楚。
这次升级真正解决的,不是“多识别几个脏表”
做数据分析助手时,预处理很容易被理解成一个局部功能。
比如:
- 识别宽表
- 删除合计行
- 处理特殊缺失值
- 规范列名里的单位
表面看,这些都像小事。
但一旦系统进入 Agent 链路,问题就不再只是“识别到了没有”,而会立刻升级成另一组系统问题:
- 这件事由谁判断
- 判断完什么时候生效
- 是自动应用还是先让用户确认
- 用户确认前,后续分析到底能不能继续
- 下一轮分析是否要沿用这次的处理结果
也就是说,预处理一旦进入 Agent 模式,它就不再只是一个数据清洗函数。
它变成了一个有状态的分支节点。
而这次升级,本质上是在把这个分支节点从“能跑”改造成“可解释、可确认、可复用、可回滚”。
这条链路其实经历了三次明显的改版
如果把这次演化过程拆开看,大致可以分成三步。
版本一:规则检测器 + 本地 fix
最早的思路其实很工程化:
- 写一组规则检测器
- 识别宽表、表头偏移、合计行、重复列名等问题
- 命中后调用确定性 fix 函数
这套思路的优点很明显:
- 可控
- 可测
- 低幻觉
- 出错时容易定位
但它的上限也很明显。
规则检测器擅长识别“格式像什么”,却不擅长判断“在当前任务下该不该处理”。
比如:
- 宽表一定要转长表吗
- 列名里的单位应该自动提取,还是只做提示
- 表头偏移是元数据行,还是业务说明的一部分
这时候,纯规则系统开始显得僵硬。
版本二:规则检测 + LLM 评估 + 试做复核
于是第二版开始让 LLM 参与决策。
流程变成了:
- 先用规则检测器找候选问题
- 再让 LLM 评估每个问题是
auto / ask / inform / skip - 对部分问题先试做一版
- 再用一次 LLM 复核试做结果是否可靠
这一版已经比最早期强很多。
它开始具备一种真正有产品意义的能力:
把结构问题翻译成产品动作。
也就是:
- 哪些问题系统可以直接处理
- 哪些问题必须打断用户确认
- 哪些问题只需要提示,不要自动动数据
但这版仍然有两个核心缺点。
第一,链路太绕。
识别一次、评估一次、试做一次、复核一次,成本很高。
第二,状态语义不够干净。
最危险的问题是:
待确认 fix 和已确认 fix 的边界不够硬。
如果 pending 也会被后续分析偷偷吃进去,那用户眼里看到的是“待确认”,系统内部却已经把它当成“已生效”。
这种系统最容易出现“看似可交互,实则状态不可信”的问题。
版本三:单次 LLM 诊断 + JSON Schema 约束 + 确定性执行
这次升级最终落地的,是第三版思路:
- 不再把 LLM 当成自由发挥的清洗器
- 让它只负责识别问题、生成受约束参数、决定处理级别
- 所有输出都必须落在
issue_type + params + decision + confidence这个结构里 - 真正动数据的,仍然是确定性的 fix 函数
也就是说:
LLM 负责判断,代码负责执行。
这看上去像一句很朴素的话。
但它其实是整次升级里最关键的边界。
因为只有这样,系统才能同时得到两种好处:
- 保留 LLM 的语义判断能力
- 保留工程系统的确定性和可验证性
这次升级真正落下来的技术元素是什么
如果只讲“要把逻辑理清”,这篇文章会显得很空。
真正让这次升级落地的,其实是一组非常具体的技术元素。
我最后把整条链路收敛成了下面这个结构:
User Query |
这套结构里,重要的不是“多了一层”。
而是每一层都只做一件事。
1. 用 Schema 把 LLM 从“自由文本生成器”收敛成“受约束诊断器”
这次预处理升级最核心的技术动作,不是换了哪个 prompt。
而是把模型输出强行压进一套稳定契约:
issue_typedecisionparamsconfidenceuser_message
再加上 JSON Schema 和列名、行号、dry-run 校验。
这样做的意义是:
模型可以判断,但不能随意发明系统里不存在的动作。
它不再有机会输出一个后端根本接不住的“聪明答案”。
2. 用确定性 fixer 保住系统的可测试性
我没有让 LLM 直接“修改 DataFrame”。
真正执行数据变换的,仍然是硬编码的 fix 函数。
比如:
- 删合计行
- 宽表转长表
- 特殊缺失值归一
- 转置恢复
这样做的好处非常工程化:
- 可单测
- 可回放
- 可 debug
- 可解释
这也是为什么我一直强调:
LLM 负责决定“要不要做”,代码负责决定“怎么做”。
3. 用显式状态机替代隐式约定
这次升级里我最重视的,其实不是识别率。
而是状态机。
如果没有显式状态,系统就会开始依赖默认约定。
而默认约定往往是最危险的东西。
现在这条链路里,至少有三类状态是清楚的:
- confirmed fixes
- pending confirmations
- suggested fixes
这三类状态分别对应不同的产品动作和不同的数据流权限。
一旦状态机被写进代码,很多以前看起来“容易说不清”的事,都会自动变清楚。
4. 用 preprocess_fingerprint 保证 plan 和 execute 看的是同一版数据
这是一个非常典型的架构细节。
如果 plan_analysis() 和 execute_analysis() 各自重新读文件、重新做预处理,那么系统理论上随时可能出现:
- 规划阶段基于版本 A
- 执行阶段基于版本 B
这种问题不会天天发生。
但一旦发生,用户看到的就是最难排查的“偶发错乱”。
所以我补了共享 artifact 解析路径,并把关键输入折叠成 preprocess_fingerprint。
这件事的价值不是性能优化,而是:
把“同一份数据版本”从默认假设变成显式约束。
5. 用兼容归一化把系统从“切换新协议”改成“平滑迁移”
预处理 issue 契约升级以后,一个很现实的问题是:
- 流式 SSE 已经开始发新字段
- 历史消息里还存着旧字段
如果不做兼容层,用户刷新一次页面,系统就会像换了一个脑子。
所以这次还有一个不太显眼、但非常关键的技术动作:
把历史 payload 统一归一化成同一套 issue 结构。
很多系统并不是死在新功能做不出来。
而是死在“新旧世界并存时没有过渡层”。
6. 用“真脏表工厂”而不是纯 mock 测试预处理
这次我后来专门补了一层真实场景回归。
它不是只 mock 一些 issue payload。
而是会真实生成:
- 宽表 + 合计行的 CSV
- 列名带单位 + 特殊缺失值的 CSV
- 表头偏移的 XLSX
- 转置后的 CSV
然后走真实读盘、真实 prepare()、真实 fix 重放。
这层测试的意义很大。
因为预处理最怕的一件事,就是:
你以为自己在测真实表格,其实只是在测一组自己定义的数据结构。
真正难的不是识别问题,而是把状态归属讲清楚
如果只看功能表,预处理升级像是在做“识别能力增强”。
但真正决定系统质量的,反而不是识别本身,而是状态怎么流动。
这次我最后理顺的,是下面这几个边界。
如果再往上抽一层,这其实就是几条很典型的架构原则:
- 契约先于实现
- 显式状态优于隐式约定
- 单一职责优于混合责任
- confirmed data path 必须和 proposed data path 分离
这些原则听起来很抽象。
但一旦落到预处理链里,它们就会变成非常具体的系统规则。
1. auto、ask、inform 分别属于什么状态
这三个词如果只存在于 prompt 里,系统很快就会乱。
必须把它们落成真正的状态语义:
auto:直接进入 confirmed fixesask:进入 pending confirmations,阻塞后续分析inform:进入 suggested fixes,不阻塞,只有用户点apply才进入 confirmed
这件事一旦讲清楚,前后端动作也自然就清楚了:
confirmrevertapply
如果这层不先定义,AI 很容易写出“能交互但逻辑互相打架”的实现。
2. 待确认 fix 不能参与正式分析
这是这次升级里最需要强行纠偏的一点。
在旧链路里,pending 有机会被一起重放到数据引擎里。
这意味着:
- UI 说“请确认”
- 后端却已经拿它去做正式分析
这是很典型的“提示词语义”和“系统语义”不一致。
所以我把规则改得非常硬:
只有 confirmed fixes 才能进入分析链。
待确认 fix 只存在于卡片和 session state 里,不得偷偷生效。
3. 历史消息和流式消息必须共享同一套 issue 契约
这一点也很容易被忽略。
如果实时 SSE 发的是一套字段:
issue_typeuser_messageparamsconfidence
但历史消息里存的还是旧字段:
typesummaryfix_paramsllm_confidence
那用户刷新一次页面,就会得到两套世界观。
所以这次我做了两件事:
- 前端切到新字段契约
- 后端在读历史消息时做兼容归一化
这不是“兼容旧数据”这么简单。
这是在保证:
系统对同一个状态的解释,在时间维度上保持一致。
4. 同一份文件不能在 plan 和 execute 阶段重复做两次预处理
这也是典型的 Agent 编排问题。
如果 plan_analysis() 和 execute_analysis() 各自重新加载文件、重新预处理一次,就会带来两个风险:
- 重复计算
- 计划阶段和执行阶段看到的不是同一份数据版本
所以这次我在数据引擎里补了共享 artifact 解析路径。
它会把:
- 原始文件
- 当前 preprocess config
- 当前 query
共同折叠成一个可复用的预处理产物。
再配合 preprocess_fingerprint,就能让 plan 和 execute 至少知道自己看到的是不是同一版数据。
这类设计不是 AI 自己会“顺手想到”的。
它必须先由人把系统不变量讲清楚。
为什么 Agent 编排还得人来做
这次升级之后,我反而更确定了一件事:
Agent 编排最难的部分,不是写代码,而是定义责任。
AI 很擅长做这些事:
- 按明确枚举补 schema
- 按确定接口补 API
- 写 fix 函数
- 改前端 action 流
- 扩测试覆盖
但 AI 不擅长替你做这些决定:
- 哪些状态应该长期保存
- 哪些状态只是一次性建议
- 哪些动作会阻塞后续分析
- 哪些结果必须显式失败
- 哪些中间态可以缓存,哪些绝不能缓存
换句话说:
AI 擅长实现边界,人的工作是先画出边界。
如果边界没画清楚,就会出现一种很常见的低效:
- 你让 AI “优化预处理”
- 它帮你补了一堆代码
- 但 pending 和 confirmed 继续混着用
- 前端 action 和后端语义继续错位
- 测试越写越多,逻辑反而越乱
这时候不是 AI 没有产出。
而是它产出的是一堆建立在错误前提上的高质量代码。
高质量地做错事,往往比低质量地做错事更危险。
为什么含糊不清的任务会拖慢 AI,而不是加速 AI
这是我这次最大的感受。
很多人以为,给 AI 一个宽泛任务,能换来“更大的自主性”。
比如:
- “把预处理做智能一点”
- “把 Agent 编排优化一下”
- “让流程更自然”
这类话听起来像方向,实际上对工程实现几乎没有约束力。
当任务含糊时,AI 往往会发生三件事:
第一,它会自动脑补需求。
第二,它会把局部最优当成系统最优。
第三,它会在没有明确状态机的前提下,把不同语义揉在一起。
最后你得到的,不是“高效自动化”。
而是:
- 一轮又一轮返工
- 一层又一层兼容补丁
- 越来越重的上下文负担
所以真正高效的做法,从来不是一句“你自己发挥”。
而是先把这些问题想清楚:
- 系统里有哪些状态
- 每个状态由谁产生
- 每个状态何时生效
- 哪些状态允许跨轮复用
- 哪些状态绝对不能默默流入后续分析
当这五个问题清楚以后,AI 的效率会突然变高。
因为它终于不需要替你猜系统设计了。
它只需要帮你实现系统设计。
这也是我现在越来越认同的一句话:
Agent 编排不是提示词工程,首先是系统设计。
提示词只是其中一层。
真正的骨架,是:
- 状态机
- 协议
- 缓存
- 幂等
- 回归样本
这些东西不先定下来,AI 越强,返工速度反而越快。
这次升级后,我对“AI 提效”的理解反而更保守了
以前我也会更乐观地想:
只要模型够强、上下文够长、工具够多,系统自然会越来越聪明。
但这次做完以后,我的结论反而更保守,也更实用:
AI 不是替代架构思考,而是放大架构思考。
如果你的逻辑是清楚的,AI 会把效率放大很多。
如果你的逻辑是含糊的,AI 也会把这种含糊放大很多。
所以真正的提效顺序应该是:
- 人先把状态、边界、责任讲清楚
- 把枚举、schema、动作协议和测试目标定清楚
- 再把这些明确任务交给 AI 并行推进
这样 AI 才是加速器。
否则,它更像一个会高速放大歧义的执行器。
写给所有正在用 AI 做工程的人
如果你想让 AI 真正帮你提效,我现在最推荐的不是“学更多 prompt 技巧”。
而是先学会做三件事。
1. 把问题写成状态机,而不是写成愿望
不要说“让体验更顺”。
要说:
ask阻塞inform不阻塞confirm进入 confirmedrevert从 pending 移除
只有这样,AI 写出来的东西才会稳。
2. 把能力写成受约束的契约,而不是模糊描述
不要说“让模型返回结构化结果”。
要说:
issue_typeparamsdecisionconfidence
再配上 schema、校验器和 deterministic executor。
这时候模型才是在你的系统里工作,而不是在系统外自由发挥。
如果再往技术上说得更硬一点,这一步至少应该包括:
- 明确枚举
- 参数 schema
- 引用校验
- dry-run 验证
- 失败回退策略
只有把这些层补齐,模型输出才真正配叫“工程输入”。
3. 先定义回归样本,再谈智能升级
这次我专门补了一组“真脏表工厂”测试,而不是只写 mock。
因为现实世界里的表格问题,从来不是抽象的。
它们往往就是:
- 宽表加合计行
- 单位混在列名里
- Excel 头两行是说明文字
- 一整张表是转置过来的
如果没有这类真实样本,任何“智能预处理”都很容易只是在 demo 上显得聪明。
更进一步说,真正高价值的测试不是“函数能跑”。
而是:
- 一份真实脏表进入系统
- 一组状态在链路里流转
- 最终只有该生效的 fix 生效
这才是 Agent 系统真正该验证的东西。
结语
这次智能预处理升级,表面上是在重构一个数据清洗分支。
但对我来说,它更像是一次对 Agent 工程方法的重新确认:
真正决定效率的,不是你把多少任务交给 AI,而是你在交给 AI 之前,到底有没有先把逻辑理清。
AI 当然能写很多代码。
但能不能把这些代码变成稳定、可信、可演化的系统,最后仍然取决于:
- 你有没有先想清楚架构
- 你有没有先讲清楚边界
- 你有没有把模糊任务压缩成可验证任务
当这些前提成立时,AI 确实会非常快。
当这些前提不成立时,AI 不会救你。
它只会更快地把混乱实现出来。