Skip to content

Feat/3.0.0 beta1 - #2270

Merged
dolphin0618 merged 6 commits into
releasefrom
feat/3.0.0-beta1
Aug 10, 2026
Merged

Feat/3.0.0 beta1#2270
dolphin0618 merged 6 commits into
releasefrom
feat/3.0.0-beta1

Conversation

@dolphin0618

Copy link
Copy Markdown
Collaborator

No description provided.

jieyuhuayang and others added 6 commits August 9, 2026 16:15
180 POC 会话 aa352cb4 复盘:主图并行委派 2 个调研子代理,日志显示
`graph=sub turn 29/30`——那是两个子代理**加起来**的数。

根因:deepagents 在建 task 工具时把子代理一次性编译
(subagents.py:584),每次 task 调用都重入同一个 runnable,而中间件的
`_turn_count` 是实例属性且从不重置,于是 N 次委派共吃一份 30 轮额度,
第二个子代理一上来就落在软着陆区。注释里只声称「主图与子代理各自独立」,
从未声称多次 task 调用共享——是隐性缺陷,不是设计意图。

改为按 LangGraph 节点 namespace 前缀分桶(有界 LRU,上限 128):
- 子代理 → 每次 task 调用一个桶,同一次调用跨轮次恒定、并发调用互不相同;
- 主图 → 强制单桶。主图 ns 形如 `model:<uuid>` 每轮都变且不含分隔符,
  一旦分桶预算每轮重置,是灾难性回归;
- 拿不到 runtime / ns 扁平 → 回落单桶 = 修复前行为,永不劣化。

同时:一次模型调用若只产出 write_todos(纯状态维护)不再扣预算。实测子代理
29 次调用里 10 次是这种空转。两段式记账——软着陆档位仍在调用前决定(它要改写
request),退款放在循环唯一的成功出口,所以瞬时重试/截断补写不会重复退款,
降级轮不退款。退款每桶封顶 10,否则 write_todos 死循环会让计数器永不前进、
软着陆阶梯永不触发,最后死在 GraphRecursionError。

新增 test_subagent_ns_contract.py:纯 langgraph 不调模型,钉住 namespace 的
三条性质。这是升级 langgraph/langchain 时唯一能提前炸出来的守门测试。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
180 POC 会话 aa352cb4 成功交付后仍显示「任务已完成 4/7」,库里还躺着
1 条 IN_PROGRESS + 2 条 NOT_STARTED,而会话状态是 COMPLETED。

linsight_execute_task 实际是 append-only 的「首次规划快照」:
- 模型从计划里剪掉的 todo 不产生任何事件。mapper 的注释写着「消失的 todo
  标记为 TERMINATED」,实现里却只有一行 `self.ctx.todos = new_projection`
  ——注释与实现不符;
- 三条正常完成路径只 finalize svid 伪任务,从不收口真实 todo 行;
- 位置对齐复用旧行 id 时不回写 task_data,标题永远停在第一版草稿。

三处修复:
1. `_set_tasks_failed` 更名 `_terminate_unfinished_tasks`(它写的是
   TERMINATED,旧名字既没描述动作也没描述状态),三条完成路径经
   `_converge_task_rows_on_completion` 接上。顺序是硬约束:必须在
   `_complete_session_pseudo_task` 之后,否则 sweep 会把会话自己那行也标掉。
2. `_diff_todos` 为消失项产出 TaskEnd(status="terminated"),已完成的跳过
   (剪掉计划不等于撤销已交付的工作)。**守门点**:`_handle_task_end` 里
   `_final_result` 必须收窄为「非 TERMINATED 才赋值」——否则一条被剪掉的
   todo 最后到达会经 `_handle_task_completion` 把整个会话判失败。
3. `_save_task_info` 对文案被改写的行刷新 task_data。

前端同步:后端改了前端也不会变——terminated 在 taskStatus 里既非 done 也非
running,渲染成和 not_started 一样的灰圈且仍进分母。TaskPanel 在
`completed && !terminated` 时过滤掉 terminated 行。用户终止/失败两条路径
的观感逐字节不变(有守门测试)。

另:recursion 地板跟随 write_todos 退款上调为 (turns+10)*4+20,并把
initdb_config 模板的 max_steps 提到 600 保持自洽——新增测试直接读模板断言
它不会自己触发「max_steps 低于所需」的告警。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
文件工具回显带前导斜杠的 /output/x,而代码执行器的 cwd 就是同一个工作区根,
盘上是 output/x。没人说过这件事,180 POC 会话为此付了两次代价:
正向 open("/skills/…") 直接 FileNotFoundError,花 3 次模型往返才摸清;
反向把执行器里看到的宿主路径喂给 read_file,前导斜杠被无脑剥掉后拼成
workspace/<svid>/root/.cache/… 这种废 key,收尾连续 4 次读图失败。

三件事:
1. 提示词:新增 path_namespace_rules() 供两个执行器共用,并把中文版说明注入
   主 system prompt。门控从「skills_present AND has_code_interpreter」双真
   改为只看 has_code_interpreter——原来那句唯一的解释藏在技能分支里,没选技能
   的会话(正是这次)根本看不到。保留 has_code_interpreter 门控是因为本模块
   处处遵守 prompt↔tool lockstep,未绑工具时不得提及工具名(有测试断言)。
   e2b 用 include_skills=False:沙箱 copy-in 快照发生在 skills 落地之前,
   那里确实没有 skills/,连举例也不能写。
2. normalize_workspace_path 增加可选 file_dir,识别执行器宿主路径前缀。
   收益不止「报错更友好」:read 会先查本地缓存 Path(file_dir)/rel,而执行器
   就写在那儿,所以折算之后那 4 次读图会直接**读成功**。
   只做可证明无歧义的剥离:要求完整 file_dir 前缀且带 / 边界。曾实现过
   「同父目录的兄弟任务目录也剥」,被自己的对抗用例证伪(前缀相似的兄弟被
   吞掉)后删除——改写路径必须无歧义,猜测只能进错误文案,不能进选文件。
3. advisory 区分读写:/skills、/uploads 是**读**区,open 抛 FileNotFoundError
   而已,什么都没丢,沿用写侧「文件被 DISCARDED」的措辞会让模型去追一个根本
   不存在的数据丢失问题。并且必须挂到失败返回路径上——绝对路径读必然
   exitcode≠0 提前 return,只扩正则会完全失效。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
180 会话 649ba617(2026-08-09):模型把一张 deck 截图交给 dashscope,回来的是

  400 {"code":"DOWNLOAD_FAILED", "message":"failed to download image, err: Get
       \"http://…/restful/data-uri/null/…\": read tcp 10.0.2.60:46976->
       10.86.10.104:6000: read: connection reset by peer"}

两端都是厂商自己内网的私有 IP——请求本身没问题,是**他们回取暂存对象时连接被重置**。
但 classify_behavior 把「plain 400」兜进 DEGRADABLE,而主图的 DEGRADABLE 是
re-raise,于是零重试、21 分钟的任务直接判失败。

新增 _TRANSIENT_TRANSPORT_SIGNATURES:当错误里带 Go/POSIX 的传输层失败措辞
(connection reset / refused、broken pipe、i/o timeout、unexpected EOF、
no route to host)时归 RETRYABLE。这些是传输层通用措辞不是厂商错误码,与本模块
既有的扁平签名集(_QUOTA_SIGNATURES / _RATE_LIMIT_SIGNATURES)同一范式,
不引入 per-vendor 分支。

刻意只认传输层失败:404 / 坏 URL / 不支持的格式这类取数失败每次重试都会失败,
必须继续走 DEGRADABLE,不能白烧三次退避。检查点放在 FAIL_FAST 分支之后,所以
欠费/鉴权错误即使正文里恰好带「connection reset」也不会被翻成重试(有守门测试)。

label 层同步:重试耗尽后给「服务繁忙」而不是通用未知卡片。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n a scrim

Daily mode played attachments through bare <video controls> while knowledge
preview had a built player — progress bar, playback rate, volume, PiP,
fullscreen. Same clip, two different players. MediaPlayer takes only kind and
src and has no knowledge-base coupling, so chat now uses it too.

It gains one optional prop: autoPlay. Opening a file in knowledge preview is
browsing and should stay put; opening a clip in chat is asking to watch it.
Off by default, so preview is unchanged.

URL resolution stays where it was — chat attachment links expire and have to be
re-minted from the stored path, which knowledge preview never needs.

The overlay is now a full-screen scrim with the player centred on it, matching
the shared Dialog's fixed inset-0 / z-[100] / black/40 so it layers like every
other modal. It portals to the body rather than routing: leaving the
conversation would tear down the composer and lose attachments staged but not
yet sent. Scroll lock is hand-rolled here — without it the wheel falls through
the scrim and scrolls the conversation away under the clip. No backdrop blur,
per the frosted-glass rule.
Sending an audio/video attachment with no speech in it killed the whole turn:
ASR returns nothing, KnowledgeMediaNoRecognizableAudioError propagates, and the
question the user typed alongside the file dies with it. Reported from 116,
where a batch of .avi/.aac/.m4a/.mkv/.flac test files all failed this way.

Silence is not a fault — the user attached a file, they did not ask for a
transcript. get_file_content now hands the model a placeholder saying the file
carried no recognizable speech, and the turn continues. Dropping the attachment
silently was the other option and is worse: the model would answer as if
nothing had been sent. The placeholder is English like the [file name] markers
around it; the model replies in the user's language.

The two genuine faults still raise: no ASR model configured, and transcription
service down. Those need fixing, not narrating.

Also fixes what the user actually saw. 12047 is the umbrella code for any
attachment-parse failure, but its Chinese copy asserted a cause —
「文件解析失败,并发过大,请稍后重试」— while en/ja only say parsing failed.
Every parse failure blamed concurrency, sending users to retry something that
retrying cannot fix. Now aligned with en/ja. Fixed in the packages/locales
source; generated artifacts rebuilt.
@dolphin0618
dolphin0618 merged commit f99bde0 into release Aug 10, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants