门禁工程:人做人该做的事——工业/工贸CRM订单导入的LLM确定性范式
(平台提示:本文可能是商业推广软文)
摘要:把 LLM 接进一个已有 22 年沉淀的事务型 CRM,真正的难点不是让模型看懂表格,而是让一个概率组件产出的判决,安全地穿过解析、实体解析、引用完整性、幂等写入、后处理这五道事务边界。本文给出我们在超兔CRM的工业/工贸客户历史订单导入中落地的架构模式——门禁工程(Gate Engineering):
run / gate / ledger三件套契约、九道人工门禁、单调账本、认领优先于重发、错误可见性驱动的裁决反转。全文含可直接复用的协议代码、与 Saga / Outbox / Durable Execution 的同构对照、七个架构决策记录(ADR)与一张工程参数表。文中表名、字段名、接口路径与环境标识均已脱敏。
先说结论
一条结论,三句话:
概率层不写库,确定性层不做语义判断,人只在门禁处注入决策。
这句话拆开就是这个模式的三条硬边界:
| 层 | 职责 | 禁止事项 |
|---|---|---|
| 概率层(LLM) | 输出带置信度的候选:列语义映射、字典值归并 | 不产生任何写操作;不做多步自主规划 |
| 确定性层(代码) | 解析、归一、三级实体匹配、分组、查重、幂等写入、读回核对 | 不做语义等价判断(同义词、上下位、别名) |
| 人(门禁) | 在九个显式停点注入决策:形态、字典值、客户归属、产品拆分、放行、失败处置 | 不在流程中间被弹窗打断;不承担可自动化的判断 |
为什么这样切?因为三者的错误形态不同,而错误形态决定它该放在哪一层:
- 代码的错误是高置信、静默的——一条写死的别名规则把「负责销售」映射到了客户电话字段,它不会告诉你它不确定;
- 模型的错误是低置信、可标注的——它输出
conf: uncertain,能在门禁上被人一眼看到; - 人的错误是低频、可追责的——只在九个必须拍板的位置发生一次。
所以裁决优先级不是按「谁更准」排,而是按「谁的错误可观测」排。这一条后面会展开成 ADR-2。
把这三条边界画成数据流:概率层只向下游递候选,唯一位于写路径上的判断由确定性层做,人只在两层的「不确定汇合点」上出现。
120 组离线回归断言里,与模型直接相关的不到三成;剩下七成守的是同一件事——概率输出如何安全穿过事务边界。先把架构摆出来,再逐条讲。
一、问题定义:这其实是一个跨系统的一致性映射问题
业务侧的说法是「把销售台账贴进来」。工程侧的准确描述是五个连续的子问题,每个都有独立的失败模式:
| # | 子问题 | 失败模式 | 归属层 |
|---|---|---|---|
| 1 | 非结构化表格 → 规整行集 | 合并单元格、双行表头、多工作表混排 | 确定性 |
| 2 | 源列 → 目标字段(schema mapping) | 同名列语义不同、词表外列无归属 | 概率 + 规则 |
| 3 | 源值 → 目标值域(value mapping) | 客户自造字典项、边界同义 | 概率 + 规则 |
| 4 | 源实体 → 目标实体(entity resolution) | 一客多名、同简称多家、产品编号与品名错位 | 确定性 + 人工 |
| 5 | 实体图落库(referential integrity + idempotency) | 主单与明细的先后依赖、半成功态、重跑重复 | 确定性 |
第 5 项是最容易被低估的。CRM 里一张单据不是一行数据,而是一棵引用树:客户 → 主单 → 明细行 → 回款记录 → 交付计划 → 归属人。跨系统没有分布式事务可用,也不能要求客户侧开事务边界。所以这套架构的核心不是「保证原子性」,而是「让非原子写入可对账、可续跑、可收敛」——这是后面账本与读回核对存在的全部理由。
二、架构选型:控制流必须是确定的
最初我们把编排交给一个自主规划的智能体:先看数据再定策略,缺什么补什么。上线前压测暴露三个结构性问题,都指向同一个根因——控制流不确定,观测与重放就都做不了:
- 不可重放:同一输入两次执行产生不同步骤序列,线上问题无法在本地复现;
- 不可度量:一次导入几十轮模型调用,耗时与成本随数据形状漂移;
- 不可审计:决策链路是自然语言轨迹,出问题时没有结构化的中间产物可查。
我们据此把编排改为注册式静态流水线:阶段顺序、每步输入输出、失败处置在编译期就固定,模型只作为其中一个阶段内部的纯函数被调用。
设计判断:结构化判决用确定性编排,开放探索才用 Agent。选型的第一问不是「用不用 Agent」,而是「这个任务的控制流需要多少自由度」——判决类任务,自由度是负资产。
三、架构:run / gate / ledger 三件套
这套架构是在超兔CRM 22年事务型业务沉淀之上,为解决批量历史订单导入的一致性问题逐步形成的。
流水线由 11 个处理阶段与 9 道门禁组成。每个节点都实现同一个接口:
type State =
| { kind: 'pass' }
| { kind: 'pause'; decisionSpec: DecisionSpec } // 等人:声明要什么决策,不执行副作用
| { kind: 'block'; issues: Issue[] } // 阻断:不可继续,必须修正数据或环境
interface Stage {
id: string
run(ctx: Ctx): Promise<Partial<Ctx>> // 纯计算,或只读请求;不写业务数据
gate(ctx: Ctx): Promise<State> // 只判定,不产生副作用
ledger(ctx: Ctx): Promise<LedgerNote> // 放行后落一条幂等摘要
}
引擎本身只有二十来行:
async function run(ctx, fromId) {
for (let id = fromId; id; id = NEXT[id]) {
const stage = REGISTRY[id]
Object.assign(ctx, await stage.run(ctx))
const s = await stage.gate(ctx)
if (s.kind === 'block') return { status: 'blocked', issues: s.issues }
if (s.kind === 'pause') {
ctx.pending = { gate: id } // 只序列化「停在哪」,不保存调用栈
return { status: 'paused', gate: id, spec: s.decisionSpec }
}
await stage.ledger(ctx) // 只有放行才落账
}
return { status: 'done' }
}
async function resume(ctx, gateId, decision) {
ctx.decisions[gateId] = decision // 决策注入,而非回放
return run(ctx, NEXT[gateId])
}
11 个阶段与 9 道门禁的关系,收敛成一张原子逻辑图——三段确定性处理,中间夹一次概率判决与一次人工决策,最后落在「写入 + 读回」的闭环上:
3.1 暂停语义:没有持久化引擎的 Durable Execution
这套设计里最值得说的一点:pause 不保存调用栈,只保存「停在哪一道门禁」。续跑不是恢复现场,而是从注册表重入下一个阶段。
这带来一条强制性约束,我们把它写进了代码评审清单:
每个
run必须能从ctx完整重建自己的输入;任何跨阶段状态只能进ctx或账本,不能藏在闭包、DOM 或模块变量里。
代价是每步都要显式声明输入依赖;收益是暂停/续跑、刷新页面、换标签页、断网重连在架构上是同一件事——这也是后来「输入即新回合」这类产品行为能零成本实现的底层原因。对照来看,这相当于把 Temporal 一类 Durable Execution 引擎的语义,用一条静态注册表 + 会话级 ctx 序列化实现了,代价是没有跨进程的持久化保证,收益是零基础设施依赖。
3.2 与已知模式的同构关系
这套东西不是新发明,是把几个成熟模式的约束重新组合到「有人的决策点」这个场景里。显式对标,方便读者判断哪些部分可以复用:
| 本文机制 | 同构的已知模式 | 差异点 |
|---|---|---|
run / gate / ledger 三件套 |
编排式 Saga | 补偿动作不是自动回滚,而是「人工决策点 + 账本重放」 |
账本 pending → mark、只进不退 |
Transactional Outbox + 幂等消费 | 账本同时承担重放游标、审计日志、批次状态机三个角色 |
| 暂停 → 序列化停点 → 重入 | Durable Execution | 无持久化引擎,状态落在账本 + 会话存储 |
| 试写 2 单 + 读回比对 | 金丝雀 / 影子验证 | 试写数据即正式数据,靠账本可回收、可续跑 |
| 编号 → 全称 → 简称三级匹配 | deterministic-first resolution | 歧义不猜,直接升级为人工门禁 |
| 规则预判降级为参考 | 规则与模型融合 | 依据是错误可见性,不是准确率 |
| 错误三分类 | failure taxonomy | 分类结果直接映射三种处置通道,不允许静默 |
如果只带走一个结论:跨系统一致性不靠事务,靠「幂等键 + 单调账本 + 写入后读回」三件套。Saga 管的是编排顺序,Outbox 管的是消息不丢,读回管的是「我以为写成功了」。
四、五个关键机制
4.1 副作用登记制:让「系统替用户做的决定」全部可见
第一版流水线有个设计缺陷:粒度判定对「一单一行 + 探测到明细列」的组合提示「每行 = 主单 + 一条明细」,但这个承诺只在带明细模式下成立。缺省模式是「订单」,于是明细列被归入「本模式忽略」清单,产品分支整段跳过——五张单的明细静默归零。
我们没用「改缺省值」来修。用户可能真的只要主单,缺省值不该替用户决定。修法是在门禁上加一条显式告知:探测到明细列而当前模式不带明细时,亮黄标「明细列将被忽略」+ 一键切换到带明细模式。
这件事沉淀成一条可执行的规则,我们叫副作用登记制:
系统的每一个自动行为(自动填充、自动归并、自动取默认值、自动跳过),必须在「行为 / 风险 / 可见位置」三列里登记;没有可见位置的条目,不允许上线。
登记表做成了代码,13 条自动行为逐条登记,并带一个挂载点自查:如果某个确认页还没建,登记项必须显式标 PENDING,不允许写着「已可见」而实际渲染不出来。回归测试直接断言自查通过——纪律不进测试,等于不存在。
4.2 契约探针:把「文档说的」变成「线上测出来的」
订单与合同在主系统里是同一张单据表的两种形态,由形态字段区分。文档对形态的可写性描述不完整,我们的第一次实现按文档传参。接口返回了成功,而且响应体原样回显了我们传入的值——看起来完全正确。读回才发现:服务端把显式传入的合同形态强制改写成了订单。
这是本项目最贵的一次认知:响应体回显的是请求,不是落库事实。 之后我们对所有涉及系统行为的假设,采用「先探针、后编码」的流程:
| 假设 | 探针结论 | 对架构的影响 |
|---|---|---|
| 形态字段可直接写入 | 建单接口强制改写形态,读回定案四态 | 合同形态只能「先建后翻」:建完用批量编辑翻转,探针验证不伤明细 |
| 明细可事后追加 | 三条通路全灭(两条静默丢数据、一条建出无主孤儿行) | 明细必须内嵌在主单创建请求里,「先主单后补明细」被永久否决 |
| 读接口缺省返回全字段 | 只返回缺省字段集,需显式声明字段 | 所有读回调用改为显式字段集,比对器才有可比字段 |
第三条尤其典型:mock 层返回全字段,掩盖了真实接口的字段集裁剪;直到试写读回比对全部对不上,才发现比对器拿到的是空字段。探针不是调试手段,是架构输入。
设计判断:任何关于外部系统行为的结论,在写进代码之前先用线上最小实验证一次。文档、日志、历史记忆都会过期,探针不会。
4.3 写通道:认领优先于重发
测试环境连出两起重复单:同一条订单落库两次,编号相邻。根因是一条错误假设——把「接口返回错误」等同于「数据未写入」。
真实过程是:数据库瞬时故障,接口返回一个 HTML 错误页;写入层判定失败并重发;但服务端的 insert 已经提交,重发就产生了第二条。这类「半成功态」在任何非事务写通道里都存在,只在故障时暴露。
修复分三层,第三层是这次真正的方法论产出:
async function claimOrResend(row) {
// 1. 先认领:按业务组合键定向读回,确认上一轮是否已落库
const hits = await query({
subject: row.subject, amount: normAmount(row.amount), date: normDate(row.date)
})
if (hits.length === 1) return hits[0].id // 已写:认领,不重发
// 2. 无存量且批内唯一,才安全补发一次
if (hits.length === 0 && isUniqueInBatch(row)) return create(row)
// 3. 存量不清:阻断交人工,绝不猜
throw Err('VERIFY_AMBIG')
}
配套还修了一个「吞根因」的实现缺陷:批量写入接口整批失败时返回的是汇总文案(「数据共 N 条」)+ 一个逐条字段级原因数组。原解析逻辑看到失败标志就把数组整个扔了,只剩汇总文案冒充每条的原因——用户看到「试写失败 4 条」无法定位。改为逐条原因优先,失败列表直接给出「签约日期不合法」「客户为空」这类可行动文案。
设计判断:失败处置要覆盖三种态,而不是两种——成功、明确失败、不确定。前两种可以自动化,第三种必须交人;把它当失败处理,就会重复写;当成功处理,就会丢数据。
4.4 账本:单调状态机即幂等
导入工具最怕的不是失败,是失败后的重跑:网络断、权限不足、页面刷新、用户自己重来一次。哪些写过、哪些没写过,必须有一个权威记录。
账本记录结构(脱敏):
{
key: 'ACME|2023-05-12|12800.00|历史导入-OLD-0912', // 可读复合键
stage: STAGE.PAID, // 1 待写 → 2 已建 → 3 已翻转 → 4 已回款 → 5 已核对
docId: '13332', // stage ≥ 已建 才存在,供读回与后处理使用
attempts: 2,
lastError: null,
updatedAt: 1758...
}
三条约束:
// stage: 1 待写 → 2 已建 → 3 已翻转 → 4 已回款 → 5 已核对
function advance(rec, next, patch) {
if (next <= rec.stage) return rec // 只进不退:重跑天然幂等
return { ...rec, ...patch, stage: next }
}
- 只进不退:状态只能前进,失败不写失败态——失败行留在原阶段,重跑自然重捡。这一条从结构上杜绝重复建单,不需要额外的去重逻辑。
- 非哈希复合键:键由「客户归一值 + 日期 + 金额 + 主题」拼成,不做 hash。代价是存储变长、索引变宽;收益是线上排查时能把账本行和 Excel 行直接对上。可调试性优先于键长度。
- 写入口唯一:所有写入必须经
pending()领任务,禁止绕过。回款记录表没有天然防重指纹,账本是它唯一的幂等安全网。
断点续传的本质因此变了:不是恢复现场,而是状态机推进。已建主单不重发,卡在回款的行重跑只补回款,归属转移失败的客户不回滚已建档的客户档案——已提交的事实不回滚,只补缺口。
4.5 AI 层:五道护栏与一次裁决反转
模型在这个系统里只做两件事:列语义映射、字典值归并。这两件事的开发量,七成在护栏。
裁决反转(ADR-2)。 第一版按直觉设计:代码层做精确匹配和别名库,匹配不上才问模型。上线后出现一个反例——「负责销售」这一列被代码层的别名规则硬命中到客户电话字段,得到的是一个高置信的静默错误,模型连复核机会都没有。
反转后的顺序:代码层的所有命中(逐字、别名、模式归一)统一降级为预判,随要素包送入模型做全场复核;裁决顺序改为「模型判决 > 代码预判」,模型漏答或调用失败时回落预判。四条配套约束:
| 护栏 | 实现 | 解决什么 |
|---|---|---|
| 主键硬锁 | 客户全称等主键字段拒绝模型改判 | 允许错一列,不允许错主键 |
| 零调用快路径 | 全部列逐字命中且无子表字段时,不调用模型 | 确定性足够时不打扰概率层 |
| 调用预算 | 常规批次 LLM 调用 1 次封顶 | 重复调用不仅费钱,同输入两次判决不同会制造漂移 |
| 回落无损 | 漏答重试 ≤2 次,仍失败回落预判 | 降级只让清单变长,不让流程停摆 |
输出抢救。 模型偶发在 JSON 尾部多一个括号,整体解析失败会丢掉「判决全对、只差一个字符」的输出。我们加了一层字符串感知的括号配对扫描:
function salvageJson(text) {
const start = text.indexOf('{')
if (start < 0) return null
let depth = 0, inStr = false, esc = false
for (let i = start; i < text.length; i++) {
const c = text[i]
if (inStr) {
if (esc) esc = false
else if (c === '\\') esc = true
else if (c === '"') inStr = false
continue
}
if (c === '"') inStr = true
else if (c === '{') depth++
else if (c === '}' && --depth === 0) {
try { return JSON.parse(text.slice(start, i + 1)) } catch { return null }
}
}
return null
}
kind 四态台账。 每次调用落一个状态:ok / 全剔除(合理拒答) / 应答损坏 / 通道异常。这个分类救过一次误判:线上连续三次归并「失败」,旧台账无法区分,差点按通道故障排查;分类上线后真相是智能体在正确拒答——那个人名没有近似选项,禁猜是对的。
一次「全无建议」的定位。 门禁上 AI 建议列有 30%–50% 的出现率是空的,第一反应是超时。实测三轮,每次调用 2.4–9.4 秒返回,超时从未触发。真正的原因是归并工作流的人设对「边界同义」概率性翻转:同一输入三次里两次拒答,两个字段独立叠加,全空率正好落在 30%–50%。
修复不在代码,在人设:把判定从两档改为三档——等价记为 high、边界同义(上下位 / 行业惯称)强制 uncertain、禁止 reject、真无关才 reject。上线后八轮实测稳定出建议(修前 3 轮里 2 轮全拒),正确的拒答(无近似人名)八轮保持拒答。代码侧零改动。
设计判断:AI 集成的工作量在护栏不在提示词。硬锁、预算、抢救、回落、四态台账五件套齐备后,换模型档位、平台抖动、应答格式漂移,业务侧都无感。
五、验证:把验收嵌进写入路径
订单导入的失败代价不对称:漏导可以补,写错要人工清洗。所以全量放行前,流水线里内嵌一次实弹试写:
- 抽样:首现客户优先,取 ≤2 单;
- 真写:走正式写入通道,账本记「已建」(试写行就是正式数据);
- 读回:按系统回填的真实 id 拉主单与明细,分块 50 条;
- 逐字段比对:主题、金额、日期、客户、形态、编号、明细行序;
- 任一差异 → 拦截回列映射门禁,已建行不重发。
比对器把差异分两类:已知变换(系统生成的编号、格式规整)自动吸收;真差异(金额、日期、客户)如实呈现。我们在这道关口拦下过产品查重歧义、明细行序错位这类问题——它们等全量写完才发现,就是几百条脏数据加一次人工清洗。
离线侧是另一套独立体系:基线快照做夹具,Node 直跑编排层,回归零网络请求,浏览器与离线共用同一套推导函数。配套三个土办法,价值不低于断言数:
- 场景编号集:每个关键场景一个编号文件,来自真实业务数据脱敏,进回归、进验收、进复测。讨论因此有了共同语言——「120 组全绿」和「07 号数据过一遍」是两句话能说完的验收;
- DOM harness:门禁渲染层用本地静态页直驱,补 Node 覆盖不到的 UI 契约;
- 测试环境协议:免密入口与业务登录入口是两个独立数据库。一条数据在业务库全命中、在免密库六个客户全未命中,一度被当成匹配缺陷。规则写进测试须知:数据建在哪个库,就用哪个库的身份测。
六、工程参数表
把散在叙事里的量化锚点集中一页,方便同类项目对照取值:
| 参数 | 取值 | 设计理由 |
|---|---|---|
| 处理阶段 / 人工门禁 | 11 / 9 | 门禁只放「不可自动化的风险决策」 |
| 离线回归断言 | 120 组 | 覆盖全链 + 各阶段 + UI 契约 |
| LLM 调用预算 | 1 次 / 批(映射 + 归并合计) | 同输入重复调用会制造判决漂移 |
| 判决重试上限 | 2 次 | 覆盖偶发漏答,不做无限重试 |
| 试写样本 | ≤ 2 单,首现客户优先 | 覆盖最少见的实体,成本可控 |
| 读回分块 | 50 条 / 请求 | 兼顾请求时长与失败重试粒度 |
| 账本阶段 | 5 级单调 | 只进不退,失败不迁移 |
| 错误分类 | 3 类(可自愈 / 需人决策 / 不可修复) | 直接映射三条处置通道 |
| AI 归并稳定性 | 修前 3 轮 1/3 成功 → 修后 8/8 | 人设三档化,代码零改动 |
| 门禁 AI 建议全空率 | 修前 30–50% → 修后 0 | 同上,非超时问题 |
七、架构决策记录(ADR)
技术媒体评审最关心的是「你为什么这么选」。把七个关键决策按 Context / Decision / Consequence 三段式收敛:
| ID | 背景 | 决策 | 后果 |
|---|---|---|---|
| ADR-1 | 自主编排不可重放、不可度量 | 控制流静态化,模型降为阶段内纯函数 | 失去灵活性,换来可复现、可回归、可审计 |
| ADR-2 | 代码层别名命中出现高置信静默错判 | 裁决反转:模型优先,预判降级为兜底 | 多一次模型调用成本,换来错误的可观测性 |
| ADR-3 | 明细事后追加三条通路均不可用 | 明细内嵌主单创建请求,一次性写入 | 主单请求体积上限成为新约束,需前置校验 |
| ADR-4 | 错误页被当作「未写入」导致重复单 | 认领优先于重发;认领不清则阻断 | 失败路径多一次读请求,换来写通道幂等 |
| ADR-5 | 回款表无天然防重指纹 | 账本承担幂等,写入口唯一 | 账本成为单点,必须随阶段同步落盘 |
| ADR-6 | 自建客户价值口径与系统原生日批算并存 | 让位原生 RFM,只投基线(窗内中位数) | 放弃客户级打标自由度,换来口径统一 |
| ADR-7 | 错误分类混用导致处置方向错配 | 三类分流:可自愈 / 需人决策 / 不可修复 | 第三类单独成清单,不阻断整体流程 |
ADR-7 值得补一句:错误分类不是日志分级,而是路由。同一个 HTTP 500,可能是登录态失效(重登录即可)、可能是环境故障(等待恢复)、可能是业务校验失败(改数据),三者的处置动作互斥。我们把「结构化错误码优先于消息文本特征」写成了判定顺序——code 是事实,文本匹配只是兜底。
八、教训清单
| # | 结论 | 落地方式 |
|---|---|---|
| 1 | 控制流不确定性会同时毁掉重放、度量与审计 | 静态注册表 + 阶段契约 |
| 2 | 错误可见性比错误率更决定分层位置 | 模型优先裁决,规则兜底 |
| 3 | 外部系统行为只能靠探针确认 | 先探针后编码,结论写进 ADR |
| 4 | 写通道存在第三种态:不确定 | 认领 → 补发 → 阻断 三级处置 |
| 5 | 幂等的抓手是单调账本,不是缓存或记忆 | 只进不退 + 写入口唯一 |
| 6 | 验证要嵌进写入路径 | 试写 ≤2 单 + 读回逐字段比对 |
| 7 | 自动行为需要登记且必须可见 | 副作用登记表 + 挂载点自查进回归 |
| 8 | 合理拒答与通道故障必须分类 | kind 四态台账先于诊断 |
| 9 | 提示词约束是软约束,验收层才是硬约束 | 数值列硬拦放在解析后 |
| 10 | 定义了但没接线的纪律等于不存在 | 状态机三步接线 + 回归断言 |
第 9 条来自一次失败尝试:我们在提示词里写了「纯数值列不得映射为分类/状态」,复测依然出现误映射。最终把约束下沉到应答验收层——解析后对数值列的分类建议做确定性剔除。能被概率层忽略的约束,就不要只写在概率层。
九、这套模式的适用边界
诚实的边界说明,比适用范围更能说明一个模式的价值:
适用:跨系统、有引用树、写入不可回滚、错误代价不对称(漏写可补 / 错写要清)、且存在必须由业务方拍板的判断点。典型场景是客户数据迁移、历史单据导入、ERP/CRM 初始化——这些也是超兔CRM客户交付中最常见的几类数据集成任务。
不适用:
- 强事务要求的场景(账务记账、库存扣减),这里需要真正的分布式事务或单库事务,门禁只解决「人机协作下的一致性收敛」;
- 实时性要求毫秒级的链路,试写与读回的成本无法摊薄;
- 判断点可以被完全自动化的场景——如果门禁上没有人真正需要做决策,那九道门禁就是九次打扰,此时应该砍掉门禁,而不是保留形式。
一个具体的反例:RFM 计算我们最终没有自建一套客户级口径,而是改为向系统原生批算投喂基线(ADR-6)。原因很简单——系统已经有权威实现,自建第二套口径只会制造两个真相。门禁工程的第一步,是识别哪些东西根本不该由你来做。
十、结语
工具上线后的一个场景,比任何通过率都更能说明这套架构的价值:一位销售助理把 300 行台账粘进输入框,在四道门禁上各停不到十秒——确认形态、处理三个未命中客户(一个检索归档、一个就地新建、一行不导入)、扫一眼试写样例——然后离开。回来时导入完成,对账报告在等她。
这就是「概率层不写库、确定性层不做语义、人只在门禁注入决策」想抵达的地方:每一次自动化的推进,都能被一句人能听懂的话解释;每一次人的介入,都对应一个真的需要人承担的风险。
超兔CRM下一步会把确认清单再压短、让列映射记忆跨客户复用、把产品拆分的交互再砍一刀。但三条硬边界不会动——它们不是这个工具的遗产,是下一个工具的地基。
如果你的团队也在做「AI 写业务系统」这类功能,欢迎在评论区聊聊:你的门禁会设在哪几道?
技术标签:架构设计 / LLM 工程 / 数据集成 / 幂等性 / SaaS

浙公网安备 33010602011771号