Repository navigation
[Bug] update_goal 反复报 stale 错:模型拿着过期 revision 无限重试,不自愈 #8955
Replies: 3 comments
|
源码级核对(本机 ① function hasRoundCap(value) { return value !== void 0 && value !== 0; }注释原文是 "rather than a strict-schema zero filler" ⇒ 0 被有意当成"严格 schema 的占位零"。 ② 「传错 id」与「revision 过期」共用一条 —— 属实,机制在这里 if (state === null) throw new GoalError("no current goal", "GOAL_NOT_FOUND");
if (ref.id !== current.id || ref.revision !== current.revision) throw new GoalError(`stale goal ref …`, "GOAL_STALE_REVISION");它是对比而不是查找 ⇒ id 写错时同样落进第二条。 ③ 但「给不同错误码」这条已经部分实现:错误带 code( 怎么解决(按代价从低到高)A. 调用侧硬规则(零改动,今天就能生效) B. 自愈中间件(能根治"反复撞墙",附一个已离线验证的原型) export const name = 'goal-stale-heal';
const STALE_RE = /stale goal ref "([^"]+)" revision (\d+); current is "([^"]+)" revision (\d+)/;
export function apply(ctx) {
ctx.on('tools/post-execute', async (exec, result, next) => {
const decision = await next();
if (!result || !result.isError) return decision;
const text = String(result.content ?? result.value ?? '');
const m = STALE_RE.exec(text);
if (!m) return decision;
const [, sentId, sentRev, curId, curRev] = m;
return {
kind: 'block',
feedback: [
'目标引用已过期(goal revision 是 CAS 乐观锁,每次成功操作 +1)。',
`请原样重发刚才那次调用,只把两个参数换成报错给的当前值:`,
` goal_id = "${curId}"`,
` revision = ${curRev}`,
`(你刚才发的是 goal_id="${sentId}" revision=${sentRev};其余参数不变。)`,
'已经过期的那份参数别再试第二种格式 —— 换格式不会让它变新。',
].join('\n'),
};
});
}本机离线验证过 6 个用例(stale 改写 ✓ / 无关错误透传 ✓ / 成功结果透传 ✓ / 尊重 next() 的决定 ✓ / C. 工具侧(要改内核,才算真修) 至于你问的复现范围:我这边答不了(本会话没建过 goal,不便改动会话状态来造)。 |
你的诊断结构上成立——而错误信息里已经把答案给出来了,这正是可修之处1.
|
|
复现(DSH,2026-10-23 本机会话):
建议:
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
现象
正常会话里(工具链完整、没切模型、没丢工具、非极简 preset),
update_goal100% 失败,只会一直撞同一条:revision是 CAS 乐观锁,每次成功的 create/edit/pause/resume/complete/block 都 +1。模型攥着会话早期读到的旧值重发,就永远撞同一面墙 —— 这正是「目标更新一直报错、一直报错」的真实形态。而且它倾向于认为「是参数格式不对」,于是反复换格式重试,而不去读错误里那半个答案。自愈路径其实是有的:照抄报错里的
current is "<id>" revision <N>原样重发,一次就通。最小复现
三个我认为值得改的点
①
goal_id写错与revision过期返回同一条错误。传一个根本不存在的 id,回来的还是
stale goal ref ... current is "<真实id>" revision N。调用方无法区分「抄错 id」和「版本旧了」,而这两种情况的正确动作不同。②
max_goal_rounds: 0被静默当成「没传」。工具层
hasRoundCap(value) { return value !== void 0 && value !== 0; },于是 0 既不报「值非法」也不改上限,而是掉进goal edit requires objective and/or maxGoalRounds—— 错误信息指向了一个完全无关的原因。③ 失败反馈没有把模型推向自愈。
stale 报错里其实已经带着正确答案,但它是夹在长句里的自然语言。建议直接返回结构化的当前 ref(
{ current: { id, revision } }),或给出可直接复制的重试参数。建议
not-found与stale revision,给出不同错误码max_goal_rounds为 0 或负数时显式报非法值,不要退化成「未提供」get_goal再update_goal,复制精确 id+revision」写成硬规则(README 提过,但实测不足以让模型照做)与已有讨论的区别
#8331(goal 工具中途全部变 unknown tool)、#2027(极简 preset 缺
update_goal导致跑满maxGoalRounds)讲的都是工具不在。本贴是工具都在、人类正常对话,update_goal仍然 100% 失败。环境:DSH 0.2.0-rc.2 / Windows / profile
desktop。想确认一下:是否有人在没有模型切换、没有工具丢失的会话里也复现过同样的持续 stale?这能把「设计如此」和真 bug 区分开。
All reactions