sync: complete remaining files (source dhk-1)
This commit is contained in:
1 parent
42f2a26fa8
commit
da6521b6cb
79 files changed
+2468
-459
No files matched your search
@@ -44,6 +44,8 @@
|
||||
|
||||
### 2.2 规划师对话(LLM,替换现行规则路由)
|
||||
|
||||
- 必须调用独立内置智能体 `report-collaboration-intent` 的真实 LangGraph thread,并原样消费 `messages-tuple` SSE;禁止用 `ainvoke()` 等待完整 JSON 后再包装成一话。
|
||||
- 流式正文、思考内容和工具步骤直接进入工作台现有 `MessageList`,与普通智能体问答采用同一套步骤条 / Reasoning 展示。
|
||||
- 模型:`report_collaboration.planner_model`(回退 `coordinator_model` → 默认模型,复用现有 model_resolver 冻结机制)。
|
||||
- 提问是领域化的:写市场分析就问「关注整车还是供应链 / 要不要竞品对比 / 给谁看 / 篇幅」,而非固定模板问句。
|
||||
- 澄清轮次上限沿用 `max_clarification_rounds`;用户随时可以说「直接开始」按默认假设推进。
|
||||
@@ -54,7 +56,7 @@
|
||||
|
||||
### 2.4 工作流生成与迭代
|
||||
|
||||
- 用户确认摘要后,规划师生成**一份**工作流方案(不是三选一):以工作流卡(成员构成、各节点使命、预计时长/成本)+ 右侧流程图预览呈现。
|
||||
- 规划师判断意图信息足够后,立即按已确认意图自动生成**一份**工作流方案(不是三选一),不再要求用户额外点击“确认意图/生成方案”;方案以工作流卡(成员构成、各节点使命、预计时长/成本)+ 右侧流程图预览呈现。
|
||||
- 生成约束(保证可执行):**节点类型受词表约束** — `research / verify / analyze / write / review`;自由的是结构(几条线、并行还是串行、核验深度)与每个节点的使命/角度/工具。
|
||||
- 对话式修改:「把价格策略单独拆一条线」「加一个政策风险分析」→ 规划师修订后出新版工作流卡(带变更说明),再确认。
|
||||
- 「开始执行」→ 冻结需求 + 工作流(含模型/预算快照,沿用现有 freeze 机制)。
|
||||
|
||||
@@ -229,23 +229,27 @@ sequenceDiagram
|
||||
|
||||
```http
|
||||
POST /api/report-collaboration/sessions
|
||||
POST /api/report-collaboration/sessions/{sessionId}/messages
|
||||
Content-Type: application/json
|
||||
|
||||
{ "text": "请写一份…", "target_node_id": null, "agent_run_id": null }
|
||||
POST /api/intent/init
|
||||
POST /api/intent/stream
|
||||
POST /api/report-collaboration/sessions/{sessionId}/intent-turns
|
||||
```
|
||||
|
||||
- `useReportCollaborationSession.send()`:当 `session.status` 不是 `running` / `awaiting_input` / `reviewing` 时走 `sendMessage`。
|
||||
- 报告页面固定调用内置 `report-collaboration-intent` 智能体;`/api/intent/stream` 原样透传 LangGraph `messages-tuple`,不是等待模型完成后包装响应。
|
||||
- 正文 token、provider reasoning 和工具调用步骤实时写入当前 `MessageList` 气泡;终态后 `/intent-turns` 只负责持久化镜像与状态交接,不再调用模型识别意图。
|
||||
- 旧 `/sessions/{sessionId}/messages` 在 `empty/clarifying` 阶段返回 `409 USE_STREAMING_INTENT`,防止退回阻塞式 `ainvoke` 路径。
|
||||
- 会话状态:`empty` → `clarifying` → `planning` → `proposal_ready`。
|
||||
- 澄清问题以 `CollaborationMessage` 进入时间线;业务卡通过 `MessageList` 的 `businessCardSlot` 渲染(`additional_kwargs.report_collaboration_card`)。
|
||||
- 意图智能体输出 `INTENT_READY` 后,`POST /intent-turns` 自动创建并完成 `plan_request`;前端无需再展示“确认意图”或“生成候选方案”按钮。
|
||||
|
||||
### 5.3 阶段 2:候选方案
|
||||
|
||||
```http
|
||||
POST /api/report-collaboration/sessions/{sessionId}/plan-requests
|
||||
# 正常路径:由最后一轮 POST /messages 自动触发规划
|
||||
POST /api/report-collaboration/sessions/{sessionId}/plans/{planId}/select
|
||||
```
|
||||
|
||||
- `POST /plan-requests` 仅保留给“重新生成”与兼容调用,不是首次规划的必经步骤。
|
||||
- 规划师尚未判定 `ready` 时直接调用该兼容接口会返回 `422 REQUIREMENT_NOT_READY`,防止跳过意图确认。
|
||||
- **预览方案**(`previewPlanId`):纯前端,不调 API。
|
||||
- **采用方案**:`select` 写 `selected_plan_id`,**不**创建 run。
|
||||
- 409:方案 revision 过期 → 前端 `refreshSnapshot`,提示用户重选。
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# 日常使用复查(2026-09-14)
|
||||
|
||||
范围:ji 分支上继续检查知识库管理、助手知识库、我的项目、项目问答和官方入口。沿用本地隔离后端数据目录 `.runtime/ji-acceptance`,浏览器检查 464×642 与 1440×900 两种尺寸;未向生产库写入验收项目。
|
||||
|
||||
## 发现与修复
|
||||
|
||||
**首屏白屏的直接业务触发点也已修复:** `LlmWikiKnowledgeSelector` 在懒加载/未加载知识目录时,每次渲染创建新的空数组,推荐问题 effect 随之回写父页状态,形成无限更新。将目录空结果和默认助手库选择稳定化后,重新从登录进入新对话成功,欢迎引导、输入框及知识空间下拉均可操作(截图 06)。仅升级依赖并不能消除这个业务循环;上述修复与依赖兼容更新共同保留。
|
||||
|
||||
1. **首次进入:高影响,已修复依赖。** 本轮首次登录后出现整页空白,控制台为 ScrollArea `Maximum update depth exceeded`。安装版本 1.2.10 使用不稳定引用回调;升级并锁定 1.2.18,同步 package-lock,核对源码使用稳定 setState 引用。冷启动仍复现组合引用循环,进一步发现各基础组件带入 Slot 1.2.3/1.2.4;统一锁定 Slot 1.3.3,npm 与 pnpm 均配置覆盖间接依赖。依据 [Radix 发布记录](https://www.radix-ui.com/primitives/docs/overview/releases) 的 React 19 无限重渲染及 Slot 引用修复。
|
||||
2. **新建项目:已修复。** 手机表单出现两个菜单按钮(截图 01)。由外层提供菜单时不再显示内部按钮;官方入口也去掉无效的内部侧栏按钮。
|
||||
3. **项目历史:已修复。** 我的项目侧栏链接跳到旧项目空间(截图 02),导致返回目的地变化。现在保留我的项目路径,普通聊天与旧项目空间保持原路由。手机点历史后菜单自动收起。官方侧栏原本把项目聊天打开为普通聊天,现按项目归属进入项目工作区;`/official/workspace/projects/groups` 根路径回到普通问答项目列表。
|
||||
4. **项目设置与失败恢复:已修复。** 默认智能体更新后,同步新对话选择器;项目对话权限/归属加载失败、普通问答项目列表失败均有重试入口。
|
||||
5. **助手知识检索:已修复。** 连续检索或切换库时,以递增请求序号隔离过期成功/失败/结束回调,防止旧结果覆盖新库;新检索清除旧结果;空输入禁止提交。无结果原本只有小标签(截图 03),现在说明可调整关键词并提供浏览目录按钮。输入框补充可访问名称。
|
||||
6. **窄屏输入栏:已修复。** 464px 截图中发送箭头被工具栏挤出右边缘(截图 07);工具区允许换行、发送区不收缩,截图 08 确认按钮完整可见。项目问答网络错误也增加“重新加载对话”,只重建读取状态,不自动重发消息。
|
||||
|
||||
## 复测记录
|
||||
|
||||
- 具名项目创建成功;新建项目对话,通过真实模型收到“项目问答回归通过”;刷新保留消息与项目归属。
|
||||
- 修复工具栏后,在窄屏实际点击发送并收到“窄屏发送通过”;首屏知识空间菜单完成加载、选择助手库后显示已选择 1 个,未再出现循环白屏。
|
||||
- 手机项目历史链接确认留在 `qa/projects/groups`;桌面官方 groups 根路径跳回正确列表,官方项目历史进入相同项目。
|
||||
- 官方和常规项目切换时偶发网络 `Failed to fetch`,刷新/重新挂载恢复历史消息;没有将其描述为已排除的网络问题,新增恢复按钮未进行独立网络故障注入测试。
|
||||
- 助手知识库列表、阅读、检索无结果、返回入口实际操作;空查询按钮禁用。检索竞态由代码检查修复,未使用浏览器网络故障注入。
|
||||
- 后端 22 个相关文件重新运行:**273 passed**,涵盖知识库管理/导入/检索、Wiki、编码与向量、项目归属隔离、策略持久化及调用边界。
|
||||
- 前端项目与策略测试:**33 passed**,新增项目历史保持入口、旧项目和普通聊天/创建向导回归。
|
||||
- 生产构建通过。全仓类型检查仍有 **59 个既有错误**,与上一轮错误列表一致;不宣称全仓类型检查通过。
|
||||
|
||||
## 证据与边界
|
||||
|
||||
本轮截图与日志存放在 `frontend-web/.runtime/daily-usage-audit/`:`01-create-before.png`、`02-history-before.png`、`03-knowledge-search.png`、`04-official-desktop.png`、`05-official-project-after.png`(网络失败状态)、后续 after 截图,以及 tests/build/typecheck 日志。后端日志为 `.runtime/ji-acceptance/daily-regression.txt`。
|
||||
|
||||
上一轮真实文件入库、向量命中、方案生成与保存的验证详见 `ji-knowledge-projects-acceptance.md`。本轮不重复付费生成完整方案;没有进行生产全量语料质量评估、负载测试、断网故障注入或完整无障碍合规审计。已检查可见导航、表单名称、窄屏显示和主要失败恢复,不能据此承诺所有模块没有缺陷。代码未提交、推送或部署。
|
||||
@@ -0,0 +1,37 @@
|
||||
# ji 分支知识库与我的项目验收
|
||||
|
||||
日期:2026-09-14。基线:`ji` / `f62a512a`,已从 origin fast-forward 更新。
|
||||
|
||||
## 修复范围
|
||||
|
||||
1. 简洁模式进入我的项目保留功能导航,覆盖列表、策略工作台和问答项目;移动端有菜单展开入口。
|
||||
2. 策略结果卡片的模型思考前置;检查意图、评判、方案、展开视图保持过程在正文前。
|
||||
3. 普通问答改为真实项目分组:先填写项目名称创建项目,再在项目中创建多条问答。服务端持久归属与权限校验沿用 analysis-projects;不自动迁移或标记历史普通聊天。官方入口支持对应 groups 子路由。
|
||||
4. 修复方案列表要求预先选中策略导致无法进入的问题;任务变化后已有方案仍可查看;请求序号避免旧请求失败覆盖成功结果。
|
||||
5. 修复知识库管理目录缓存导致上游删除后仍显示的问题;保留检索端缓存。
|
||||
6. 项目编辑保留知识库绑定,助手知识库补充普通用户空态。
|
||||
|
||||
## 自动化
|
||||
|
||||
- 后端 22 个相关测试文件:273 passed(知识库、Wiki、向量、编码器、项目归属、策略配置与持久化、边界等)。
|
||||
- 前端策略与项目测试:31 passed。新增用例覆盖未选择策略、已有方案变更任务、未完成评判及全部策略失败。
|
||||
- `tests/test_knowledge_live_embedding.py`:真实接口 1 passed;默认跳过,设置 `KNOWLEDGE_TEST_EMBEDDING_KEY` 后运行。默认服务 SiliconFlow、模型 BAAI/bge-m3;可用 `KNOWLEDGE_TEST_EMBEDDING_URL` / `KNOWLEDGE_TEST_EMBEDDING_MODEL` 更换。
|
||||
- `pnpm build`:通过。
|
||||
- `pnpm typecheck`:未完全通过,59 个错误位于此次未改动文件(nextra 缺失、Markdown 插件类型、旧 JSX 类型及旧问答页等);修改文件无类型错误。不能将生产构建通过等同于全仓类型检查通过。
|
||||
|
||||
## 真实接口与浏览器
|
||||
|
||||
测试使用 `.runtime/ji-acceptance` 独立数据目录,保留测试数据便于复查,未操作业务数据库已有项目。
|
||||
|
||||
- BAAI/bge-m3:中英文 3 条输入,均返回 1024 维有效向量。
|
||||
- 实际检索服务:相关正文排名、指定库范围、草稿排除、概览查询通过。
|
||||
- 两个项目 A/B:A 创建两条对话,B 列表为零;跨项目读取 A 的对话返回 404。
|
||||
- 浏览器:简洁模式完整菜单可见;新建具名普通问答项目;项目内真实发送并收到回答;第二条对话独立;回列表仍是一张项目卡,刷新后保留。
|
||||
- 方案整理:真实模型返回 1539 字符正文,含 agent_done / all_done,流正常结束。
|
||||
- 方案入口:完成评判但尚未勾选策略的测试项目出现在方案列表。浏览器进入后勾选策略、生成 2755 字符正文、保存成功;服务端校验 synthesisStatus=done 且 solutionSavedAt 非空,刷新后正文与导出按钮恢复。
|
||||
- 官方入口:`/official/workspace/projects/groups/new` 正常展示具名问答项目表单,官方侧栏保留。
|
||||
- 助手知识库:创建、重命名、列表、拒绝 .py 上传通过;上传测试文本任务 completed;生成 Wiki 页面,向量检索命中 1 条,详情 HTTP 200。
|
||||
|
||||
## 限制
|
||||
|
||||
真实模型测试使用最小合成读书活动/设备资料,未对生产全量知识语料进行质量评估。API 密钥仅用于测试进程,不提交配置。仓库原有未跟踪 docx 与 packages1.zip 保持不变。未提交、推送代码,也未执行离线发布。
|
||||
@@ -0,0 +1,105 @@
|
||||
# 本地问答、资料库与技能并发验收
|
||||
|
||||
测试日期:2026-09-28 至 2026-09-29 凌晨。环境:Windows、约 8GB 物理内存、Python 3.12、Node 22、Gateway 单进程、SQLite、默认 MiniMax-M2.5。前端 Vite 5174,Gateway 8001。模型并发限流与 Redis 分布式门控未启用。
|
||||
|
||||
本次保留并验收了工作区原有的资料库索引、问答收尾、沙箱超时和流式恢复修改,另外修复了压测复现的技能刷新重复校验,以及真实地图问答暴露的内部待办提醒显示问题。测试结果不能等同于 Linux 多进程生产环境容量。
|
||||
|
||||
## 版本范围
|
||||
|
||||
开始测试时本地为 `3a07c476`,没有 `cognitive-situation-map`。随后拉取远程,发现 `origin/master` 已前进至 `3107f2cc`,包含该技能的初始化及 v2.6.0 精简运行时。保留本地修改后合入远程,合并提交为 `c9effe83`;最终绘图与多用户测试以合入后的代码为准。
|
||||
|
||||
旧本地版本里 `page-report-generation` 的地图仅支持中、美、日、台,因此其“不支持菲律宾”的反馈不能用于评价最新的 `cognitive-situation-map`。
|
||||
|
||||
## 真实问答
|
||||
|
||||
使用独立普通测试账户,阶梯间顺序运行,逐条检查预期测试关键词与结果、SSE `end`、持久化 `success`,结束后删除本轮生成的测试线程。1/3/5/10 路检查正文包含“测试”和数字 4;20 路增强为检查“测试正常”及明确的“2加2等于4”。这属于简单问答验收,不是复杂内容质量评测。
|
||||
|
||||
| 同时问答用户数 | 成功 | 首段答案中位耗时 | 流结束中位耗时 | 流结束最慢 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 1 | 1/1 | 5.792s | 6.439s | 6.439s |
|
||||
| 3 | 3/3 | 2.214s | 3.245s | 3.287s |
|
||||
| 5 | 5/5 | 2.885s | 3.702s | 4.373s |
|
||||
| 10 | 10/10 | 2.920s | 4.796s | 5.416s |
|
||||
| 20 | 20/20 | 4.364s | 10.622s | 11.135s |
|
||||
|
||||
累计 39/39 通过。20 用户整批耗时 11.829s,同期 health 12/12 成功、中位 13ms、最大 371ms。这里是一次阶梯短时突发,并未证明百用户持续长任务容量。
|
||||
|
||||
取消接口返回 204,58ms 后状态为 `interrupted`,测试线程删除返回 200。同期健康探测 20 次均成功,中位 9ms,最大 3.044s;说明仍存在数秒调度尖峰,不能仅凭平均值宣布无阻塞。
|
||||
|
||||
早期多个磁盘基准同时运行时,单路首段答案为 33.853s,日志显示同步 agent 构建 28.344s,health 也有 5s 超时。这批明确有本机资源争用,不纳入稳定容量结论。开发脚本启动的服务进程还曾消失,没有拿到退出或 reload 日志,原因未确认;期间的连接失败剔除。后续使用无热重载的固定进程复测。
|
||||
|
||||
## 资料库和知识库
|
||||
|
||||
- 资料库/知识库相关回归 186 项通过;资料库新存储 Memory/SQLite 专项另有 8 项通过,覆盖用户隔离、分页、原子换代、并发租约和旧任务围栏。
|
||||
- 真实测试账户看到 2 个共享知识库;选中的库包含 668 份文档、168 个 Wiki 页面。首次 hybrid 查询 3.58s,返回 5 条。
|
||||
- 排除合成磁盘负载后,空资料库、知识库目录和 hybrid 查询分别测试 1/4/8 并发,每档 8 次,总计 72/72 HTTP 200,零异常。
|
||||
|
||||
| 接口 | 8 并发中位耗时 | 8 并发最大耗时 |
|
||||
|---|---:|---:|
|
||||
| 资料库列表(空测试账户) | 165ms | 224ms |
|
||||
| 知识库目录 | 517ms | 557ms |
|
||||
| hybrid 检索(相同问题、5条结果) | 426ms | 441ms |
|
||||
|
||||
这组重复使用同账号和查询,包含热缓存效果;空账户列表不代表有大量文件的资料库。独立测试 SQLite 另生成了 101 个合成用户、10 万条资料元数据,480 次查询全部成功;其中单用户 5 万条资料关键词查询 P95 为 1.08s、首页 P95 为 130ms,但此轮同时有其他 IO 测试,只作为规模观察值。
|
||||
|
||||
资料库索引按 5 分钟过期刷新;进程异常退出时正在同步的租约最多等待 30 分钟恢复。大库关键词查询仍使用多字段包含匹配,不能据当前测试证明生产规模检索始终快速。
|
||||
|
||||
## 大技能与并发刷新
|
||||
|
||||
文件体积大和小文件数量多分别测试,使用隔离临时目录,不改真实技能内容。
|
||||
|
||||
| 场景 | 结果 |
|
||||
|---|---|
|
||||
| 真实技能副本,269 文件 / 2.75MiB,稳定缓存 64 并发 | 全成功,约 1.05s;早期混合负载观察值 |
|
||||
| 同副本改 1 个资产,32 并发,修复前混合负载 | 27 成功、5 个 30s 锁等待超时,复现问题 |
|
||||
| 独占配对:同副本改 1 个资产,32 并发,旧逻辑 | 32/32 成功,P95 13.700s,最大 14.842s |
|
||||
| 独占配对:同场景,新逻辑 | 32/32 成功,P95 / 最大 3.963s,P95 降低约 71% |
|
||||
| 128MiB 资产、34 文件,新逻辑 | 冷初始化 5.54s;变更后 32 并发 P95 0.584s,全成功 |
|
||||
| 约 160 技能、5120 个小文件(修复前规模观察) | 冷加载执行者约 107s,31/32 等待者超时;热校验约 18s |
|
||||
|
||||
修复让同一进程内、相同权限配置的一批重叠请求共享一次刷新及最终校验。等待者仍检查当前缓存身份、权限哈希和能力标记;失败或技能下线时不返回旧视图。新增 8 项回归连同原有 62 项,共 70 项通过。
|
||||
|
||||
**剩余风险:** 数千个小文件的单次 Windows 元数据扫描仍昂贵,本次修复没有消除这项成本,也未证明跨进程刷新能合并。不能把 32/64 个技能调用线程等同于 32/64 个真实模型用户。
|
||||
|
||||
实际调用 `gongwen-assistant` 读取技能并输出短通知,10.672s 内正常结束,无工具卡住。但答案把要求的 10月8日改成10月15日:通信/技能读取通过,内容日期遵循失败,不能记为整体语义验收通过。
|
||||
|
||||
## 绘图与界面
|
||||
|
||||
普通问答已实际生成菲律宾地图底图及马尼拉、吕宋北部、巴拉望、棉兰老四个点位,点位弹窗可打开。使用明确标注的虚构数据,未进行时事真实性评估。初次模型额外要求确认消息内地图,回复确认后完成,属于可见的额外交互成本。
|
||||
|
||||
这次真实对话发现 `todo_completion_reminder` 被当作用户问题展示。修复后,后端待办提醒带 `hide_from_ui`,前端也按两个历史内部名字过滤聊天和问题导航;真实用户引用 `<system_reminder>` 的文本仍保留。后端 3 项、前端新及关联分组 12 项通过。
|
||||
|
||||
最新 `cognitive-situation-map v2.6.0` 已经通过真实问答完整调用:读取技能和菲律宾参考 JSON、写入场景、调用技能自带 `node render.mjs`、检查文件、`present_files` 交付,42.481s 完成,SSE 正常结束并保存 `success`。产物标题为“菲律宾冲突认知态势图(功能验收示意)”,包含 4 个事件、4 个标注、3 个行为体和 2 条连线,明确标注所有事件与等级均为测试数据。输出 `status=ok`、无恢复和警告。模型曾发起两次过宽的目录 glob,被技能访问隔离拒绝后自行改用指定技能目录完成,没有循环卡住。
|
||||
|
||||
同一最新技能的直接渲染为 624ms;8 个并发独立渲染进程全部成功,范围 650–949ms。围栏/截断/缺失字段/约 600KiB 场景输入的恢复、非空目录拒绝覆盖、脚本结束标签转义均完成专项检查。真实 Windows LocalSandbox 虚拟路径经 PowerShell 调用 Node 成功(含冷 shell 7.85s)。CSS、JS、菲律宾底图和旗帜资源均返回 200;2.93MB 底图一次请求耗时 208ms。8 个渲染进程不等于 8 个同时生成长内容的模型用户。
|
||||
|
||||
**最新 HTML 的视觉验收未通过:** 测试账户能在会话中看到生成文件卡片,文件接口返回 200,源码模式能显示 HTML、正确资源地址和页面结构,但 Codex 内嵌浏览器的预览面板空白;DOM 中 iframe 的 `src` 为 `blob:http://127.0.0.1:5174/...`,可访问树仍为 `about:blank`。普通地图组件的可见性不能替代这个 HTML 的验收,渲染器返回成功也不能证明最终视觉正确。已保留空白预览截图,未将此项计为通过。
|
||||
|
||||
进一步在内存中执行真实 `prepareGeneratedHtml`:11,269 字节输入变为 14,597 字节,原有 3 个脚本逐字保留、DOM id 与外链 URL 均不变,新增兼容脚本语法有效。暂未发现生成器或预处理破坏 HTML 的证据;故障停在预览加载/Blob 导航阶段,具体原因未确认,不将其归为已知浏览器限制,也不宣称已修复。
|
||||
|
||||
界面还复现了 TDesign 新手引导闲置时的 `reading 'element'` 异常:`current=-1` 时其 resize 监听仍读取不存在的步骤。简洁模式与智能体两处包装组件已改为只有有效步骤才挂载 Guide。浏览器复测了文件面板开关、侧栏切换、手动启动、下一步、跳过,未再出现新的同类异常;完成/跳过的持久化语义保持不变。
|
||||
|
||||
## 合并后回归
|
||||
|
||||
- 完整 harness:274/274 通过,53.07s;包含前述技能及 Todo 子集,不重复累加。
|
||||
- 模型并发配置专项:10 项通过;种子技能资源:4 项通过。
|
||||
- 前端流式恢复专项:9 组共 44 项通过;待办可见性及相关消息分组:12 项通过。
|
||||
- 全量前端构建和类型检查最初并行运行时,系统空闲内存降到约 17MB,因此主动停止该轮类型检查,改为串行执行;该轮中断不作为代码失败。
|
||||
- `pnpm build` 通过(10m25s,包含前期资源争用,有大 chunk 警告)。其后新增的两个 Guide 挂载条件已通过单独语法检查和真实浏览器回归;另补齐问题导航抽取后的类型导入,不改变生成代码行为。
|
||||
- 最终 `pnpm typecheck` 仍未通过,共 73 项原有诊断;与测试前本地 `hang-typecheck.log` 的 75 项逐条比较,减少了消息分组测试的 2 项,没有新增诊断。本轮发现的 `QuestionItem` 类型导入遗漏已经修正,复跑后消失。剩余问题包括 `nextra` 依赖、旧 JSX 类型和既有组件/测试类型,未扩大范围处理,不能标记全量类型检查通过。
|
||||
|
||||
## 本地证据和复测入口
|
||||
|
||||
详细证据仅保留在 `.runtime/test-results/20260928-local-qa/`,不提交模型凭证、问答日志、测试数据库、构建 ZIP 或技能临时大目录:
|
||||
|
||||
- `qa-concurrency-results.json`:逐轮时间、结束事件、答案检查和清理结果。
|
||||
- `qa-concurrency-20-results.json`、`harness-full.xml`:20 个独立用户问答和完整 harness 回归。
|
||||
- `knowledge-stable-recheck.json`、`knowledge-library-report.md`:知识库与资料库稳定复测及限制。
|
||||
- `skills-benchmark-paired.json`、`skills-findings.md`:同机旧/新刷新配对;“旧逻辑”在隔离进程禁用新合并函数回放,不改服务源码。
|
||||
- `philippines-html.json`、`large-skill.json`:旧页面技能能力缺口与公文技能真实调用。
|
||||
- `philippines-map-ui.png`:普通地图真实浏览器渲染。
|
||||
- `cognitive-philippines.json`、`cognitive-skill-probe.json`:最新技能真实完整问答及渲染器专项。
|
||||
- `cognitive-preview-ui.png`:最新 HTML 在本轮内嵌浏览器出现空白的实际截图。
|
||||
- `frontend-build.log`、`frontend-typecheck-final.log`:全量构建与最终类型检查。
|
||||
|
||||
提交的回归入口包括 `packages/harness/tests/test_skill_view_refresh_coalescing.py`、`test_skill_view_waits.py`、`test_sandbox_command_liveness.py`、`test_run_completion_timeout.py`、`test_todo_reminder_visibility.py`,以及前端 `control-timeout`、`stream-terminal-recovery` 和 `todo-reminder-visibility` 测试。
|
||||
@@ -0,0 +1,358 @@
|
||||
# 技能并发与流式修复:配置、前端包与验收说明
|
||||
|
||||
日期:2026-09-27(2026-09-28 补充高并发运行配置)。
|
||||
|
||||
本说明对应本次提交的修改,包含模型流式兼容与恢复、React #185 错误处理和已完成的最小技能示例,
|
||||
以及可选的技能视图复用、版本缓存、模型计时日志队列和 Gateway 线程池扩容。
|
||||
部署前通过 `git log -1` 核对代码提交;仅修改配置或替换旧包不能确认已部署本次修复。
|
||||
|
||||
## 1. 先确认需要更新什么
|
||||
|
||||
- **后台代码也要更新**:四个新开关需要新版后台读取。只换前端 dist 或只把字段写进旧版 config.yaml 不会实现后台优化。
|
||||
- **前端**:使用本说明末尾的 ZIP,里面是完整 `dist/`,不是源码或 node_modules。
|
||||
- **仓库运行配置已开启优化**:两份 config.yaml 的四个开关均已显式设为 `true`。但已有服务器的持久化/外挂配置不会被仓库自动覆盖,仍需手工同步。
|
||||
- 开启线程池开关后可将上限调整为 100,但不提高模型服务商配额、不启用多 worker。
|
||||
|
||||
## 2. .env 需要修改哪些项
|
||||
|
||||
后台启动所使用的 `.env` 保持或修改为:
|
||||
|
||||
```dotenv
|
||||
# Gateway 进程数,不是线程池大小;当前内存流/运行管理器必须单进程。
|
||||
GATEWAY_WORKERS=1
|
||||
|
||||
# 默认就是 0;显式写 0 可防止误用不安全的多进程覆盖开关。
|
||||
DEERFLOW_ALLOW_UNSAFE_MULTIWORKER=0
|
||||
|
||||
# Gateway asyncio 线程池上限;还必须打开下一节的 asyncio_thread_pool_enabled。
|
||||
DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE=100
|
||||
```
|
||||
|
||||
适用于已更新的 `scripts/start_backend-online.sh` 和 `offline-backend-20260512/start_backend.sh`。
|
||||
若通过自定义服务直接运行 Uvicorn,也应确认实际命令是单 worker;仅修改 .env 不会改变硬编码的启动命令。
|
||||
启动环境中已导出的同名变量可能覆盖 .env,需一并核对。
|
||||
|
||||
前面三个技能/日志优化没有独立环境变量,它们配置在下一节的 `config.yaml`。
|
||||
线程池上限变量现已真正接入;修改 .env 后必须重新启动后台,Docker 应通过现有启动脚本重新创建容器,
|
||||
单纯 `docker restart` 不会更新容器创建时注入的环境变量。
|
||||
其余模型地址、密钥、数据库连接等保留服务器原值,不用本机 .env 覆盖生产文件。
|
||||
|
||||
### 100 个线程与环境变量优先级
|
||||
|
||||
`GATEWAY_WORKERS=100` **不是**把工具线程池调成 100,而是启动 100 个进程,当前部署不要这样设置。
|
||||
|
||||
`DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE=100` **已经实现**,兼容别名 `DEERFLOW_IO_THREADS=100`,
|
||||
两者任选其一即可。开启线程池开关后,按以下顺序取第一个非空值:
|
||||
|
||||
1. `DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE`(正式变量)
|
||||
2. `DEERFLOW_IO_THREADS`(兼容别名)
|
||||
3. `config.yaml` 的 `runtime_performance.asyncio_thread_pool_size`(默认 100)
|
||||
|
||||
有效范围为整数 **1..1024**。选中环境变量的值非法时启动失败并提示,不会静默退回默认小线程池。
|
||||
开关关闭时不读取环境变量来改变线程池,保留 Python 原默认。已有正式变量时,修改别名不会覆盖它。
|
||||
线程按需创建,100 是上限,不会预先常驻启动 100 个线程。
|
||||
|
||||
## 3. config.yaml 新增配置
|
||||
|
||||
在实际加载的 `config.yaml` **顶层**配置这一段,不能缩进到 `models`、`sandbox` 或 `skills` 内,
|
||||
也不要重复定义两个 `runtime_performance`。下面是程序在字段缺失时的兼容性默认值;
|
||||
当前仓库的两份可运行配置已使用后文的高并发开启值:
|
||||
|
||||
```yaml
|
||||
runtime_performance:
|
||||
# 一次工具调用内只获取一次已验证技能视图,默认 false。
|
||||
skill_view_reuse_enabled: false
|
||||
|
||||
# 版本未变时复用技能视图,默认 false。
|
||||
skill_view_cache_enabled: false
|
||||
|
||||
# 模型计时日志使用独立队列,不争抢耗时工具线程,默认 false。
|
||||
model_timing_nonblocking_enabled: false
|
||||
|
||||
# 是否覆盖 Gateway asyncio 默认线程池,默认 false。
|
||||
asyncio_thread_pool_enabled: false
|
||||
asyncio_thread_pool_size: 100
|
||||
```
|
||||
|
||||
如决定启用本次四项优化并将线程池上限设为 100,改为:
|
||||
|
||||
```yaml
|
||||
runtime_performance:
|
||||
skill_view_reuse_enabled: true
|
||||
skill_view_cache_enabled: true
|
||||
model_timing_nonblocking_enabled: true
|
||||
asyncio_thread_pool_enabled: true
|
||||
asyncio_thread_pool_size: 100
|
||||
```
|
||||
|
||||
四个开关独立生效,可以单独开关。关闭/省略对应项时,对应的性能优化走原实现。
|
||||
这四项不控制模型协议兼容修复或前端错误处理;全部关闭不等于回退所有代码。
|
||||
为了让已有模型实例和所有后台进程配置一致,修改后重启后台;回退也是改回 `false` 后重启。
|
||||
|
||||
### 确认没有改错配置文件
|
||||
|
||||
- 已设置 `DEER_FLOW_CONFIG_PATH` 时,以它指向的文件为准;Docker 内该值必须是容器内可访问路径。
|
||||
- 本仓库包含 `offline-backend-20260512/config.yaml` 和 `offline-backend-20260512/backend/config.yaml`;
|
||||
两份已同步为高并发开启值,但服务器要修改的仍是启动时实际加载/挂载的那一份。
|
||||
- 启动日志 `Loaded AppConfig from ...` 可确认路径,`RUNTIME_PERFORMANCE` 可确认实际开关值。
|
||||
|
||||
## 4. 开启后做了什么,没做什么
|
||||
|
||||
### 单次调用复用
|
||||
|
||||
同一次 `read_file`、`ls`、`glob`、`grep`、`bash` 等工具执行期间,技能检查、沙箱初始化、
|
||||
路径替换和输出脱敏共用一份已验证视图;退出调用后清理作用域,下一次调用重新验证。
|
||||
不会把一个问答最初的技能启停状态一直沿用到结束。
|
||||
|
||||
### 技能版本缓存
|
||||
|
||||
每次核对启停 JSON 内容、已知源/目标文件和目录的元数据及标记。
|
||||
版本一致时不重新发现/解析全部技能、不复制文件、不持有全局刷新锁。
|
||||
新增、编辑、删除、启停及支持文件变化触发重建,并保留跨进程锁合并并发刷新。
|
||||
|
||||
没有采用会延迟下线的 TTL 缓存;下一次工具调用能发现下线或无效配置并拒绝访问。
|
||||
已经开始执行的工具不因此被强行中断。
|
||||
缓存仅用于标准 `LocalSkillStorage`,自定义存储实现/子类保守回到原刷新路径。
|
||||
同一 Gateway 进程内,仅在多个热命中调用真正重叠时合并为一次元数据校验;
|
||||
该批调用结束后结果立即丢弃,新调用仍重新校验。启停文件哈希不同的调用不会加入旧校验,
|
||||
校验异常会对该批调用统一 fail-closed,且不会阻止后续调用重试。
|
||||
|
||||
热路径仍有 O(文件数) 元数据校验,慢磁盘/网络挂载仍可能耗时;这不是“所有文件操作零开销”,
|
||||
也没有把受锁保护的重建变成无锁写入。
|
||||
|
||||
### 模型计时日志
|
||||
|
||||
仅轻量计时/入队在回调中执行,日志 I/O 由独立线程消费,不占工具共享线程池或事件循环。
|
||||
队列上限 4096 条;日志端长期阻塞时会丢弃诊断并在恢复后汇总告警,不无限积压或阻塞模型输出。
|
||||
退出时最多等待 0.5 秒尽力刷新,异常退出可能丢失尾部日志。
|
||||
|
||||
这是 `ModelTimingCallback` 的优化,不会改变其他回调(如 RunJournal、可选追踪插件)、
|
||||
子智能体调度池、数据库池或模型上游的连接/速率限制。不能据此保证 100 个会话同时生成。
|
||||
|
||||
### Gateway 工具线程池
|
||||
|
||||
开启 `asyncio_thread_pool_enabled` 后,在后台启动时、LangGraph 初始化之前设置当前
|
||||
Gateway 事件循环的默认执行器。同步工具、该循环的 `asyncio.to_thread` 等可使用最多 100 个槽位。
|
||||
关闭时完全不替换默认执行器;退出时由拥有该事件循环的服务运行器清理。
|
||||
|
||||
这不是 AnyIO 的同步 HTTP 路由线程池,也不改变子智能体专用池或独立事件循环的默认池。
|
||||
线程池扩大只能减少可并行 I/O 的排队,不能让同一把技能锁同时被多人持有,也不会增加模型端的生成配额。
|
||||
|
||||
## 5. 日志检查
|
||||
|
||||
确认加载日志类似:
|
||||
|
||||
```text
|
||||
RUNTIME_PERFORMANCE skill_view_reuse_enabled=True skill_view_cache_enabled=True model_timing_nonblocking_enabled=True asyncio_thread_pool_enabled=True asyncio_thread_pool_size=100
|
||||
```
|
||||
|
||||
线程池最终是否生效,以另一条启动日志为准(进程 id 随实际启动变化):
|
||||
|
||||
```text
|
||||
GATEWAY_ASYNCIO_EXECUTOR enabled=true max_workers=100 source=DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE pid=12345
|
||||
```
|
||||
|
||||
如果仍显示 `enabled=false max_workers=python_default`,说明线程池开关没打开或改错了配置文件,
|
||||
不是已经调整到 100。改环境变量后若日志仍取旧值,检查是否重建了 Docker 容器及实际 env-file。
|
||||
|
||||
查看以下日志:
|
||||
|
||||
| 日志 | 用途 |
|
||||
| --- | --- |
|
||||
| `GATEWAY_ASYNCIO_EXECUTOR` | 实际线程池开关、最终上限、配置来源和进程 id;关闭时显示使用 Python 默认,不虚报 100 |
|
||||
| `SKILL_VIEW_CACHE status=refreshed` | 发生重建;包含 `lock_wait_ms`、`refresh_ms`、`total_ms` 和条目数 |
|
||||
| `SKILL_VIEW_CACHE status=joined` | 等待期间其他线程/进程已完成重建,本次直接复用 |
|
||||
| `SKILL_VIEW_CACHE status=hit` | 缓存命中;正常命中为 DEBUG,超过 500ms 的慢命中为 INFO |
|
||||
| `SKILL_VIEW_CACHE status=failed` | 刷新失败,旧能力不可继续使用,需要排查配置/磁盘 |
|
||||
| `LLM_CONNECT_START/OK`、`LLM_REQUEST_DONE/FAILED` | 模型调用生命周期、首块和总耗时 |
|
||||
| `LLM_CALL_RETRY/FAILED`、`OPENAI_TOOL_STREAM_SUMMARY` | 模型重试/兼容处理诊断 |
|
||||
| `LLM_TIMING_LOG_DROPPED` | 日志队列曾满;检查日志磁盘/采集端速度 |
|
||||
|
||||
如需查看正常缓存命中,可短时设置顶层 `log_level: debug`;排查后恢复 `info`。
|
||||
首块耗时不等于纯网络连接等待,仍包括上游排队、模型处理等阶段。单次 curl 成功也不能证明并发容量足够。
|
||||
本次日志不记录技能正文、模型正文或 API 密钥。
|
||||
|
||||
## 6. 前端替换与回退
|
||||
|
||||
1. 先备份服务器当前前端目录和 `runtime-config.js`,并备份实际后台 .env/config.yaml。
|
||||
2. 校验下载 ZIP 的 SHA-256 与本说明记录一致,再解压到一个新的发布目录。
|
||||
3. ZIP 包含 `dist/`;Web 根目录应指向其内部,确保根下直接能找到 `index.html`,不要误放成 `dist/dist/`。
|
||||
4. **保留/恢复服务器自己的 `runtime-config.js`**。核对生产后台地址、认证和嵌入参数;不要把构建机默认值当生产配置。
|
||||
也不要覆盖生产 .env 的密钥与数据库配置。恢复 runtime-config 后其单文件哈希自然与原包不同。
|
||||
5. 更新后台代码并按需设置四个开关,使用现有部署方式重启后台,再切换前端发布目录。
|
||||
6. 刷新浏览器缓存后检查普通问答、智能体问答、技能示例、读取技能、生成文件以及离开后重新打开正在生成的会话。
|
||||
7. 如需回退性能优化,后台关闭相应开关并重启,无需删除技能文件或技能缓存目录。
|
||||
如需回退模型协议或前端修复,须恢复对应的已备份代码/前端发布目录,单独关闭性能开关不够。
|
||||
|
||||
正式 Linux AMD64 离线镜像/发版仍通过独立的 `knowledge-base-bj/deerflow-offline-deployment`
|
||||
仓库流程执行;本次前端 ZIP 不是完整后台离线镜像。
|
||||
|
||||
## 7. 本地验证结果与限制
|
||||
|
||||
### 后台
|
||||
|
||||
- 提交前最终专项:技能/线程池/日志/启动配置及技能老回归 **166 项通过**;模型适配、错误分类、
|
||||
诊断失败保护、SSE 响应头、会话计时与真实 transport **123 项通过**(两组不重叠,共 289 项)。
|
||||
对应修改模块与测试 Ruff 通过,harness 未增加对 app 的反向依赖。后续没有执行生产模型并发压测。
|
||||
- 本次技能复用/缓存/配置、日志回调与相关技能目录、大文件安全专项:**79 项通过**;
|
||||
又补充 1 项开关关闭/重新开启的回退测试,通过缓存文件全部 13 项回归,合计 **80 项**。
|
||||
- 模型工具参数流/推理流、错误恢复、SSE 传输和启动 worker 校验另 **76 项通过**;相关专项共 **156 项通过**。
|
||||
- 后续新增线程池接入与配置专项 **45 项通过**(与前述配置测试有重叠,不相加充当独立总数):
|
||||
覆盖默认关闭、两个环境变量优先级/别名、非法值拒启、启动顺序,以及实际 **24 / 100 个本地阻塞任务同时执行**、
|
||||
退出后线程全部清理;新模块和相关测试 Ruff 检查通过。另一次联合技能/日志/启动配置回归 **88 项通过**。
|
||||
该测试只验证本地线程池,不是 100 路模型压测。
|
||||
- 线程池接入后再次尝试后台全量测试(首次失败即停止):**604 通过、2 跳过、1 失败**,
|
||||
仍停在修改前已复现的 `test_llm_metrics.py::test_middleware_records_success_with_speed`,不是全量通过。
|
||||
- 冷启动 8 线程并发只构建一次;真实第二进程复用持久化清单、不再发现技能。
|
||||
- 下线/无效配置、源文件变更、同长保留 mtime 编辑、目标篡改、Windows junction 保护均有回归覆盖。
|
||||
- 真实 LangChain 异步模型测试证明:默认工具线程池占满且日志处理器阻塞时,启用计时日志优化仍可开始、输出 token 并结束;未调用真实模型服务。
|
||||
- 修改后尝试全量测试,设置失败 5 项停止:**871 通过、2 跳过、5 失败**,并非全量通过。
|
||||
失败包括 `test_llm_metrics.py` 的 3 项记录时序断言、既有 Wiki `auto_sync` 配置与测试预期不一致、
|
||||
`test_model_management.py` 的 thinking 参数预期不一致。首项在本轮修改前即已复现;
|
||||
本轮没有修改指标中间件、模型工厂或 Wiki auto_sync 设置,不为过测试擅改这些业务配置。
|
||||
- 本次针对 #185/MD 的追加回归:模型真实 SDK 回放、工具参数、思考字段、日志、配置、线程池、
|
||||
错误中间件和传输联合 **145 项通过**。其中新增 SDK 回放 19 项,覆盖 8 种写文件形态,
|
||||
确认 JSON 尚未闭合时部分 MD 已传出、最终工具名称和参数正确;没有连接线上模型。
|
||||
|
||||
### 合成性能对比
|
||||
|
||||
Windows 11 / Python 3.12.13,本机临时目录:150 个技能、450 个文件,各模式预热一次后串行 10 次。
|
||||
旧刷新平均约 **3133ms**,缓存命中平均约 **806ms**(本次约 3.89 倍);
|
||||
`load_skills` 从 10 次降为 0 次。临时数据已清理。
|
||||
这不是生产 Linux 性能结论,也不是多人访问模型的承载压测。
|
||||
|
||||
### 前端与交付包
|
||||
|
||||
- 提交前最终前端专项 **60 项通过**(原 57 项,加同 reader 续接/新页面重播与迟到事件隔离 2 项、
|
||||
最小技能示例 1 项);真实生产 React 浏览器错误边界测试再次通过。
|
||||
- 最终全量类型检查仍有既有 **75 个诊断**,本次变更文件无新增诊断;记录在
|
||||
`.runtime/frontend-final-typecheck-20260927.log`。不要把构建成功等同全量类型检查通过。
|
||||
- 前端本次流式恢复、React #185 分类、技能示例、流式预览及相关权限等 **32 项专项通过**;
|
||||
修正新增测试文件一处 `.ts` 导入导致的 TS5097 后,相关 16 项再次通过。
|
||||
- 最新追加前端专项 **57 项通过**,含真实 SDK SSE → 文件参数 → MD 预览;旧 16ms 设置在连续流中
|
||||
没有通知刷新,修复后在 JSON 尚未闭合时持续更新。真实 Chrome + 生产 React 中用循环 layoutEffect
|
||||
触发 #185,验证静态友好兜底、无自动重挂、点击重新载入后恢复。
|
||||
- 最新 `pnpm build` **通过**:Vite 13,853 个模块,8 分 49 秒;存在大于 500KB 的 chunk 提醒,不是构建失败。
|
||||
构建日志 `.runtime/frontend-final-build-20260927.log`;本次修复重新打开会话的游标边界后已重新打包。
|
||||
- 前一版临时 localhost 静态 HTTP 烟测 **通过**:入口 HTML 及引用的主 JS/CSS/runtime 等 5 个资源均 200,
|
||||
响应字节 SHA-256 与磁盘一致,没有缺失资源;烟测服务已关闭。
|
||||
- 最新 ZIP 中 **886 个文件**均逐文件比对大小和 SHA-256,与本次 dist 一致;入口/CSS 的 99 个本地资源引用全部存在。包内无 `.env`,
|
||||
入口为 `dist/index.html`,包含 `dist/runtime-config.js`。
|
||||
- 全量 `pnpm typecheck` **未通过**:复测还有 **75 个诊断、涉及 29 个本次未修改文件**,
|
||||
包括现有 nextra / `@codemirror/state` 依赖、旧测试及 legacy QA 类型问题;
|
||||
本次新增测试文件的 TS5097 已修正并复测,本次前端变更文件无类型诊断。
|
||||
完整本地日志为 `.runtime/qa/frontend-delivery-typecheck-20260927.log`;
|
||||
不能把生产构建通过表述成整个项目类型检查通过。
|
||||
追加修复后本次产品变更文件无全量类型诊断,新增测试及依赖专项 TypeScript 检查为 0 诊断。
|
||||
- 附加 legacy Chrome CSS 检查 **未通过**,报三个本轮未修改文件
|
||||
`legacy-html-preview.test.ts`、`legacy-html-preview.ts`、`SolutionProjectLanding.tsx` 中的现代颜色函数字样;
|
||||
没有报本次 dist CSS 缺失回退,也不能据此宣称完成旧浏览器端到端兼容验收。
|
||||
|
||||
交付 ZIP(仓库根目录):
|
||||
|
||||
```text
|
||||
deerflow-frontend-dist-20260927-master-final.zip
|
||||
```
|
||||
|
||||
- ZIP 大小:35,869,210 字节(约 34.2 MiB)。
|
||||
- 此包替代 `deerflow-frontend-dist-20260927-191207-185-stream-fix.zip` 及更早版本;旧包保留,但不包含最终的游标作用域修复。
|
||||
- 解压后文件总大小:66,347,693 字节。
|
||||
- ZIP SHA-256:
|
||||
|
||||
```text
|
||||
7E4C1525BDEB686161FFF5AED12D36087407BBF4F472B87E78CA04DADD1DF371
|
||||
```
|
||||
|
||||
Linux 可用 `sha256sum deerflow-frontend-dist-20260927-master-final.zip`;
|
||||
Windows 可用 `Get-FileHash -Algorithm SHA256 .\deerflow-frontend-dist-20260927-master-final.zip`。
|
||||
|
||||
未连接生产环境进行真实多人模型问答端到端验收,不能宣称所有线上卡顿已被实测消除。
|
||||
|
||||
## 8. 关于 20 人同时使用
|
||||
|
||||
当前不能承诺 20 人同时问答一定流畅。本次验证了本地代码路径和构建产物,并没有验证你的两台服务器、
|
||||
上游模型地址/API key 配额、数据库/磁盘及实际技能耗时在 20 路负载下的容量。
|
||||
|
||||
部署新版后台、明确开启所需开关后,先在测试环境按 5 → 10 → 20 个独立会话逐级测试:
|
||||
|
||||
1. 纯聊天和包含技能读取/写文件的问答分开测试,保持提示长度和输出规模接近真实业务。
|
||||
2. 每级记录成功/失败/超时数、首字和总耗时的分位数,以及技能 `lock_wait_ms/total_ms`。
|
||||
3. 同步查看模型端 429/503、排队数量/首 token 延迟,以及应用 CPU、内存、磁盘和数据库等待。
|
||||
4. 若上游只能并行处理少量请求,应用扩线程无法增加上游容量;应控制并发或扩容模型服务。
|
||||
|
||||
20 个在线用户、20 条运行中的问答和 20 个正在模型端生成的请求不是同一指标。
|
||||
|
||||
## 9. #185 与 MD 流式追加修复
|
||||
|
||||
用户已确认思考模式正常;本节针对仍出现的 #185 和 MD 预览卡住。
|
||||
|
||||
- 四个直接流入口统一使用 `STREAM_RENDER_BATCHING=true`,避免安装版本 SDK 的数字 16ms
|
||||
尾部防抖不断推迟 UI 刷新;保留异步批处理,不改成每个片段同步强制渲染。
|
||||
- #185 流错误不会再按模型异常直接显示原始错误串;同一轮最多一次自动恢复,重复失败暂停重连,
|
||||
不主动取消后台任务,不循环重新发送问题。友好提示 30 秒去重。
|
||||
- 真正渲染/commit 阶段的 #185 由页面错误边界接住,进入静态提示,只能手动重新载入;
|
||||
不吞其他错误。控制台 `react_ui_render_error` 保留组件堆栈,帮助定位未知的线上循环组件。
|
||||
- 恢复游标与当前页面的消息累积器绑定:同一页面断线续接不重复拼接工具参数,离开后重新打开或
|
||||
切换会话则完整重播,不能复用全局旧游标跳过正文前缀;旧 reader 的迟到事件也不污染新页面。
|
||||
- 后端兼容重复工具名称,避免 `write_filewrite_file` 使工具路由和预览识别失效;
|
||||
`OPENAI_TOOL_STREAM_SUMMARY repeated_tool_names` 可确认是否命中。也修复了空 `error:{}` 被误判失败。
|
||||
- 诊断日志写入失败不会覆盖真实模型异常、取消或成功结果。错误脱敏后仍在内存中识别上下文超限,
|
||||
并识别 `invalid_api_key` 等结构化认证错误,防止本应修剪上下文或停止重试的请求反复排队。
|
||||
- **实时预览和物理落盘不同**:模型还在生成参数时展示虚拟 MD 内容;参数完整、工具校验通过后,
|
||||
`write_file` 才正式写沙箱文件。此次不把未完成的 JSON 直接写进最终文件。
|
||||
|
||||
本地已经复现并修复上述具体缺陷,但没有线上原始流/组件堆栈,不能保证它们解释全部生产故障。
|
||||
|
||||
## 10. `threads` 创建慢:数据库路由与阶段耗时
|
||||
|
||||
`POST /api/threads` 会先查/写应用数据库的 `threads_meta`,再调用检查点存储写入空 checkpoint;
|
||||
它不需要请求模型,也不会在创建时遍历所有旧问答。
|
||||
|
||||
当前仓库部署模板是 `database.backend: mysql`,同时 `checkpointer.type: postgres`、
|
||||
`checkpointer.use_postgres: false`。如果服务器保持这些值且没有 `DEER_FLOW_USE_PG_CHECKPOINTER`
|
||||
覆盖,实际是 **MySQL 元数据 + SQLite 检查点**,清理 PostgreSQL 不会加速这条链路。
|
||||
本地开发配置则是 SQLite。线上以实际加载的配置和环境覆盖为准,不凭数据库连接串是否存在判断。
|
||||
|
||||
新建/搜索接口新增 `GATEWAY_OPERATION_TIMING` 日志和 `Server-Timing` 响应头,
|
||||
浏览器 Network 的响应头也能看到,不必先拿服务器日志:
|
||||
|
||||
- `metadata_lookup` / `metadata_create`:应用元数据查询/写入。
|
||||
- `checkpoint_create`:检查点创建,包含 saver 锁/连接池等待和数据库写入,不能等同为纯 SQL 时间。
|
||||
- `metadata_search`:会话列表的元数据查询。
|
||||
- `handler_total`:进入处理函数后的耗时;**不含进入函数前的事件循环/鉴权/代理排队**。
|
||||
|
||||
模型图构建仍保持原来的任务上下文。新增 `AGENT_BUILD_TIMING`:超过 500ms 以 WARNING 记录,
|
||||
正常为 DEBUG。已在本地复现同步慢工厂阻塞同事件循环的其他请求,但不能直接裸搬到线程:
|
||||
延迟工具注册上下文需回传,MCP 初始化存在跨事件循环约束。此处目前是定位日志,不是已经消除阻塞。
|
||||
|
||||
本机安装的 SQLite saver 和 PostgreSQL saver 都存在实例级串行锁;后者使用连接池也不代表
|
||||
同一个 saver 自动并行处理所有检查点。扩大 asyncio 线程池不解除此锁。
|
||||
尚未取得线上分阶段耗时,因此不能承诺新建 10 秒已经根治,也不能承诺归档即可解决。
|
||||
新增计时与真实 SQLite 锁测试、已有分析项目/浏览器内部创建入口联合 **43 项通过**,
|
||||
其中计时专项 14 项;这是本地回归,不是线上延迟验收。
|
||||
|
||||
## 11. 30 天已结束会话的 PostgreSQL 归档 SQL
|
||||
|
||||
归档脚本单独交付,不进入本源码仓库,也不在启动时执行。使用 `pg_checkpoint_archive.sql` 搬移,
|
||||
`pg_checkpoint_restore.sql` 按提交批次恢复;两者需与 `pg_checkpoint_common.sql` 放在同一目录。
|
||||
应用数据库在 MySQL / PostgreSQL 的候选只读导出分别为
|
||||
`mysql_checkpoint_candidates.sql` / `pg_checkpoint_candidates.sql`。
|
||||
|
||||
按用户确认,候选仅包括 30 天未更新、所有运行已结束的会话,排除正在运行、待处理和等待用户输入的会话。
|
||||
PG 检查点没有应用会话终态字段,因此不能只按 `checkpoint.ts` 猜测,必须先从应用元数据导出候选 ID。
|
||||
脚本仅在确认实际启用 PG、全部写入服务已停、备份可恢复及双读回退关闭后允许提交。
|
||||
默认是预览/回滚;提交时三张原始表的全部 namespace 一起搬入新归档 schema,逐行校验后才删源行,
|
||||
恢复时不覆盖同 ID 的新数据,归档副本保留。不删除沙箱文件。
|
||||
|
||||
**范围与风险**:这是 PG 检查点冷归档,不是完整应用归档。它不搬 MySQL `threads_meta/runs`,
|
||||
因此旧会话列表入口可能仍在,但归档正文需要恢复后才能读取/续聊。归档到同一库的新表也不保证减少总磁盘占用。
|
||||
SQL 的验证记录与具体执行命令见单独交付包的 README;部署前仍须在生产备份恢复的隔离副本演练。
|
||||
不要跳过预检直接在生产提交。如果线上仍为 SQLite,这组 PG SQL 不适用。
|
||||
|
||||
独立包 `deerflow-db-archive-20260927.zip` 含 SQL、README 和复验测试,不含数据库数据、连接凭据或数据库二进制。
|
||||
已在 PostgreSQL 16.15 + 安装的 LangGraph checkpoint 3.1.2 原始建表结构上通过 13 项真实执行和 14 项静态检查;
|
||||
未执行真实 MySQL 候选导出,详见独立包 README。ZIP 共 9 个文件、20,983 字节,SHA-256:
|
||||
|
||||
```text
|
||||
E90A189DA7D7032527EED7CEC9B51DE1A30FB6D48B4382CE06AF1C287B670F59
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# 问答停在执行命令或读取技能:排查与修复
|
||||
|
||||
日期:2026-09-28。
|
||||
|
||||
本次检查针对普通问答和智能体问答:页面停在工具步骤,上游模型没有收到下一轮请求。
|
||||
以下是代码审查和受控回归确认的触发路径;尚未收到具体生产对话 ID 和现场日志,不能据此认定某次现场故障只有一个根因。
|
||||
|
||||
## 已修复的等待路径
|
||||
|
||||
| 位置 | 原来的触发条件 | 当前行为 |
|
||||
| --- | --- | --- |
|
||||
| LocalSandbox 命令执行 | `subprocess.run` 超时仅终止外层 shell,后代仍持有输出管道;Windows 超时收尾继续无期限 `communicate` | 独立进程组、关闭交互输入、临时文件采集输出;超时清理进程树并限制回收等待 |
|
||||
| 技能视图刷新 | 进程锁、跨进程文件锁和等待并发校验结果均没有统一等待上限 | 等待最多 30 秒后抛出明确工具错误;不删除正在执行的刷新,不使用未经校验的旧视图 |
|
||||
| AIO 沙箱 | 上次命令未返回时,后续命令或文件操作无限等待同一把锁 | 锁等待 30 秒后失败;不抢占旧请求的锁,不自动重复执行命令 |
|
||||
| 文件分段读取 | 超长单行先读完整行才裁剪;FIFO/设备文件可能一直等数据 | 达到当前行输出预算即返回;本地读取用非阻塞打开并检查普通文件,远端分段读取同步限制 |
|
||||
| 前端结束恢复 | 后端已 success/error,但旧 SSE 仍开着,SDK `isLoading` 一直为真;整段 SSE 期间的 `sendInFlight` 又把同一轮结束快照丢掉 | 核对轮次及运行 ID 后清理重连标记、关闭本地 reader、应用结束快照;不因此取消后端或覆盖新一轮 |
|
||||
| 前端恢复查询 | `/runs`、检查点或历史请求悬挂,占着 single-flight 恢复槽 | 普通 SDK 请求设置 30 秒超时;恢复失败仍保留后续检查机会,SSE 不受此总时长限制 |
|
||||
| 后端结束收尾 | 图已返回,但检查点检查、状态写入、日志刷新或标题同步永远不返回,导致 `end` 发不出去 | 终态检查、终态状态写入分别有 10 秒预算;后续元数据收尾共享 10 秒预算,独立 finally 发出 `end` |
|
||||
|
||||
技能锁超时走原有工具错误处理链,模型可收到失败结果并决定后续操作;不会把“超时”伪装成成功。
|
||||
元数据收尾超时只发生在图已经结束后,不给整个研究/子智能体执行增加统一短时限。
|
||||
终态检查点超时会报告运行失败,不能直接当成模型成功。
|
||||
|
||||
## 配置和上线
|
||||
|
||||
本次需同时更新后端代码和前端构建产物,重启后端后生效。只刷新页面或调线程数不能替代修复。
|
||||
本次未操作生产服务、未发布前端包;正式离线发版仍使用独立的 `deerflow-offline-deployment` 仓库。
|
||||
|
||||
本地命令保留原来的 600 秒默认执行时限,新版允许在实际加载的配置文件中调整:
|
||||
|
||||
```yaml
|
||||
sandbox:
|
||||
# 合并到已有 sandbox 段中,保留 use、mounts 等既有字段。
|
||||
command_timeout_seconds: 600
|
||||
```
|
||||
|
||||
该字段只控制 LocalSandbox 命令执行,必须为有限正数。未配置仍为 600 秒;不要把它误认为模型请求超时。
|
||||
命令输出每个流最多读入 1 MiB 头尾内容,再经过既有工具输出裁剪。
|
||||
运行中的命令约每 100ms 检查累计捕获输出,超过 16 MiB 请求终止;这是采样护栏,不是磁盘硬配额。
|
||||
正常退出后的显式后台进程需要自行重定向输出和管理生命周期。
|
||||
|
||||
前端已有的无业务事件检查窗口仍为 90 秒;这是启动后台状态核对的时机,不是宣判任务失败的时限。
|
||||
30 秒控制请求超时不影响合法的长时间 SSE 和工具执行。
|
||||
|
||||
## 仍需现场证据区分的情况
|
||||
|
||||
- 磁盘/NFS 本身阻塞的刷新执行者仍可能卡在操作系统 I/O;本次限制的是其他调用的等待,不宣称能终止任意内核 I/O。
|
||||
- 自定义技能脚本自身请求外部服务不返回,仍会等到本地命令时限;建议技能内各 HTTP 请求也设置连接/读取超时。
|
||||
- 整个图中的第三方异步工具、模型前中间件和检查点操作没有统一的“无事件即取消”规则,避免误杀合法研究任务。
|
||||
- 检查点回滚是实际数据修改,保持原有完成/取消语义,不把仍在执行的回滚丢到后台后开放下一轮。
|
||||
- 已提交给数据库的单条写入未必能被驱动撤销;取消和新轮次检查可阻止旧清理继续发起后续写入,不能保证撤回已经执行的 SQL。
|
||||
|
||||
再次出现时,记录对话 ID、运行 ID、最后一步和时间,并对照上游请求日志、运行状态及
|
||||
`RUN_COMPLETION_TIMEOUT run_id=... phase=terminal_checkpoint|terminal_status|metadata_cleanup`。
|
||||
技能锁超时会包含 `Timed out waiting for skill view ...`;沙箱命令超时会包含 `Command timed out`。
|
||||
不要仅凭页面最后显示“读取技能”就断定仍在读文件,它也可能是结束消息没有传到浏览器。
|
||||
|
||||
## 回归入口
|
||||
|
||||
- 后端 `packages/harness/tests/test_skill_view_waits.py`:锁竞争和并发校验超时、失败后恢复。
|
||||
- 后端 `packages/harness/tests/test_skill_view_cache.py`、`test_skill_view_tool_reuse.py`:既有启停与缓存安全性。
|
||||
- 后端 `packages/harness/tests/test_sandbox_command_liveness.py`:真实父子进程、输出、非交互输入及特殊文件/长行。
|
||||
- 后端 `packages/harness/tests/test_run_completion_timeout.py`:挂起的结束收尾、取消、新一轮隔离及正常长图执行。
|
||||
- 前端 `src/core/api/control-timeout.test.ts`、`src/core/threads/stream-terminal-recovery.test.ts`:控制请求超时及真实 SDK reader 恢复。
|
||||
- 前端既有 `stream-recovery`、`stream-display`、`stream-reasoning-replay`、`stream-cursor` 测试继续验证断流重连和消息显示。
|
||||
|
||||
## 本机验证结果
|
||||
|
||||
- 后端 `python -m pytest packages/harness/tests -q`:**263 passed**。旧同步日志断言补了显式测试配置,避免当前启用异步日志时与后台队列竞速;异步日志仍由独立测试覆盖,未改实际运行配置。
|
||||
- 前端八个相关测试文件打包后用 Node test runner 执行:**31 passed**;新增测试类型修正后其中三个新增用例再次通过。
|
||||
- 修改的后端代码和测试通过 Ruff;`git diff --check` 通过。
|
||||
- 完整前端类型检查未通过:**75 个 TypeScript 错误**,均在本次未修改的文件,涉及 `nextra` 缺失、旧 JSX 类型、既有测试类型等。本次修改文件及新增测试无诊断。详细输出保留在 `frontend-web/.runtime/hang-typecheck.log`(本机文件,不作为发版资产)。
|
||||
- 命令进程树测试在 Windows 使用真实子进程;Linux 进程组分支已审查,但本机未做 Linux 实跑,AIO 未连接实际容器。未用生产模型调用或真实业务技能执行验证。
|
||||
Reference in new issue
Block a user