要把 Laya 接进现有决策流程,先别问 Laya 怎么调用,先问哪一步的自回归生成可以被一次性决策输出替代。通常替换点落在流程末端:上游已准备好上下文和规则,下游只消费最终决策字段,不读中间 token。若某一步依赖逐 token 校验、前缀拼接或中间状态回调,先保留,只做旁路对照。替换后必须加适配层和回滚开关,不能直接改下游解析逻辑。
判断方向是:先做流程标注,再按下游消费方式决定可替换点。通常只把最终决策输出且下游不读中间 token 的位置换成 Laya 一次性输出,涉及流式校验、逐 token 拼接、中间状态回调的环节先不动。操作上先加适配层和对照记录,验证下游字段能解析、原流程结果可留存;边界是高风险或强实时流程先旁路运行,不直接切主链路。
在现有流程里标出所有依赖逐步生成的位置
第一步是标注流程,不讨论接口。每个位置记录输入、输出、下游消费方式,以及是否读取前序 token。建议同时在自回归解码和流式中间处理处打印 token 序号、增量事件和下游回调名,用日志确认哪些位置真的逐 token 工作。
| 位置 | 输入 | 输出 | 下游消费 | 依赖前序 token |
|---|---|---|---|---|
| 提示拼装 | 上下文、规则 | 完整 prompt | 送入解码器 | 否 |
| 自回归解码 | prompt、采样参数 | 逐步 token | 流式回调、增量拼接、提前终止 | 是 |
| 流式中间校验 | token 增量 | 校验事件、状态 | UI、路由阈值 | 是 |
| 结果解析映射 | 完整文本 | JSON、决策字段 | 规则引擎、执行器 | 否 |
| 后置校验补偿 | 解析结果 | 修正决策 | 执行器、审计 | 否,可整次重试 |
| 审计回放 | token 与最终结果 | 轨迹 | 审计、问题定位 | 可能依赖,先不动 |
判断哪些位置可以换成一次性决策输出
可替换与不可替换的分界,不在 Laya 能否生成完整答案,而在下游是否依赖生成过程。逐条打勾,全部满足再考虑替换。
- 下游只消费最终结构化字段,不监听 token 流。适用场景:决策标签、动作参数、路由结果。操作:旁路把适配层输出喂给下游。验证:下游无解析错误且分支可解释。边界:下游有基于首 token 的快速失败时不能直接替换。
- 没有基于前 N 个 token 提前终止或改路由的逻辑。操作:搜索流式回调、stop 条件、前缀判断。验证:临时关闭流式回调后仍能完成一次决策。边界:强实时交互流程先保留原环节。
- 不需要流式展示或流式校验。适用场景:后台批量、离线评估、异步任务。操作:把原流式入口改成一次性返回。验证:调用方只接收最终结果时不报错。边界:前端依赖打字机效果时要补最终事件。
- 失败可整次重试,不依赖部分结果。操作:适配层捕获空结果后重试一次。验证:构造空输出,确认下游不消费半截数据。边界:原流程已写部分状态时,重试前先清理。
- 涉及中间状态回写、逐 token 计费或前缀缓存复用的位置先保留。操作:在标注清单标为不可替换。验证:检查日志是否出现逐 token 计费或前缀缓存记录。边界:强行替换可能造成审计断点或缓存失效。
优先级从最后一步决策生成开始,不要一上来替换流式中间层。下游只拿标签或参数时,替换末端生成改动最小;下游拿 token 流做二次判断时,该位置继续保留。
为可替换位置写适配层,把输出转成原有结构
适配层目标不是重写下游,而是把 Laya 一次性输出伪装成原自回归结果。先固定输入输出映射,再写函数骨架。下面用通用写法,实际入口按环境中的 SDK 或网关替换。
def laya_once_adapter(task, context, schema):
request = {
'task': task,
'context': context,
'output_schema': schema,
'mode': 'single_shot'
}
raw = call_laya(request) # 按实际环境替换
normalized = {
'decision': raw.get('decision'),
'params': raw.get('params', {}),
'raw_text': raw.get('text', ''),
'finish_reason': raw.get('finish_reason', 'stop'),
'events': [{'type': 'final', 'payload': raw.get('decision')}]
}
if not normalized['decision']:
raise ValueError('empty decision')
return normalized
映射表用来检查下游是否需要改。通常只改适配层,不改规则引擎和执行器。
| 原自回归输出 | Laya 一次性输出 | 适配层动作 | 下游改动 |
|---|---|---|---|
| 逐步 token 序列 | 完整文本 | 取最终文本字段 | 不改,若下游只做拼接 |
| 增量事件流 | 无天然增量 | 补发一个 final 事件 | 下游需能处理单次事件 |
| 决策标签 | decision 字段 | 直接映射 | 不改 |
| 动作参数 | params 对象 | 类型与默认值归一 | 不改,缺字段走原默认 |
| 中间状态回调 | 无 | 不伪造中间状态 | 若下游依赖,该位置不可替换 |
| 结束原因 | finish_reason | 映射为原枚举 | 不改 |
适配层对空决策、字段类型不符、枚举值未知直接抛错,不要塞默认值让下游误判。这样失败时能定位是替换点问题,而不是规则引擎问题。
用一组历史任务做对照运行并记录差异
替换前先旁路运行,不要直接切主链路。选一组历史任务,原流程和 Laya 一次性输出各跑一遍,原流程结果完整留存,再喂给同一套下游。记录模板如下,差异分类先粗后细。
任务ID:
输入摘要:
原流程输出留存路径:
原流程关键字段:
Laya 输出留存路径:
Laya 关键字段:
适配层输出:
下游消费结果:
差异分类:字段缺失 / 类型变化 / 语义偏移 / 顺序变化 / 下游报错 / 无差异
是否阻塞替换:是 / 否
备注:
差异分类至少覆盖:字段缺失指适配层输出缺少下游必填字段;类型变化指字符串变对象或数字;语义偏移指标签或参数含义与原流程不同;顺序变化指原流程依赖 token 或事件顺序而替换后顺序消失;下游报错指解析、校验或规则执行失败。原流程结果保留原始输出和映射后结果两份,便于区分模型差异还是适配层差异。验证下游可消费时,重点看解析器、规则引擎、执行器三层是否都能走完。若某类任务反复出现同一差异,先回到第 2 步检查该位置是否真的可替换,不要用适配层硬抹平语义差异。
设定回滚条件与触发方式
替换和回滚成对设计。触发条件在旁路阶段就写进配置或监控规则。
回滚触发条件
- 适配层抛错或关键字段缺失连续出现,且无法在短时间内定位。
- 下游解析失败率超过你设定的观察阈值,或规则引擎出现新增异常分支。
- 抽样对照发现不可接受的语义偏移,影响执行动作。
- 端到端时延超出下游超时预算,且重试后仍不稳定。
- 审计或合规要求必须保留逐步 token 链路,而当前替换点无法提供。
操作步骤
- 关闭替换开关,例如把 decision_engine 从 laya_once 改回 original_autoregressive,或关闭 feature flag。
- 清理适配层临时状态和本次请求缓存键,确保下一次请求走原流程。
- 保留 Laya 输出、适配层日志和下游消费日志,不要覆盖原流程结果。
- 热加载或重启配置,确认入口路由回原自回归流程。
验证回退成功的方法
- 发起一条历史任务,确认原流程输出结构、字段和下游消费结果与替换前一致。
- 检查日志中不再出现 Laya 一次性调用,或出现 original 路径标记。
- 观察一类高风险任务,确认下游没有新增解析错误或空值分支。
- 保留一个观察窗口,确认没有残留 Laya 分支继续写入中间状态。
回滚不是失败,而是替换流程的一部分。开关、日志和原流程结果留存到位,先旁路、再小范围、再决定是否扩大,通常比一次性切主链路更可控。