Skip to content

feat(chat): 数字员工产出文件可下载(捕获/存储/消息卡片/下载端点) - #55

Open
tianling536 wants to merge 2 commits into
OpenBMB:mainfrom
tianling536:feat/agent-output-files
Open

feat(chat): 数字员工产出文件可下载(捕获/存储/消息卡片/下载端点)#55
tianling536 wants to merge 2 commits into
OpenBMB:mainfrom
tianling536:feat/agent-output-files

Conversation

@tianling536

Copy link
Copy Markdown
Contributor

概述

数字员工产出的文件(如通用技能生成的 PPT)从"死在临时目录"变成可下载的一等产出:runner 捕获 → 磁盘+DB 持久化 → assistant 消息挂载 → 聊天内点击下载。

参考 Clawith 的工作区模型后按我们的产品形态取舍:产出回聊天(对话即交付),不做独立 Workspace 面板。

实现

产出捕获(runner)

  • _execute_plan 执行后扫描工作区,捕获净新增文件(排除物化进去的技能包输入与 runner 脚本;单文件 ≤12MB、单次 ≤20 个);
  • 仅当该次尝试被采用且结果可用时才经 output_sink 持久化;失败/被废弃尝试的产出随临时目录清理;
  • 顺带修复既有泄漏:run_dir 此前从不清理,现在执行后一律 rmtree

存储(AgentOutputFile + output_files.py)

  • 元数据与归属(tenant/session/agent/user/message)落表,本体写用户数据目录 outputs/(不内联 base64——PPT 数 MB 会撑爆消息表);
  • 产出经 output_sink 回调贯穿三条运行路径:agent_loop 流式聊天、SOP 内 general_skill.* 工具调用、通用技能运行测试页;
  • assistant 消息落库时按会话+时序挂载到本轮(output_files 进消息 metadata),幂等。

下载

  • GET /api/chat/outputs/{file_id}/download:仅产出归属人或管理员,FileResponse 直出;outputs 根外路径拒绝(防逃逸);
  • 前端聊天气泡渲染下载卡片(文件名/大小/类型,点击 fetch→blob 保存,复用头像同款认证模式,不把 JWT 放 URL);运行测试页结果区同样挂下载。

测试

  • 净新增捕获(输入排除/runner 排除/空文件跳过/超限跳过);
  • 持久化与消息挂载(时间窗、幂等、路径逃逸拒绝);
  • 下载权限(owner 200 / 他人 403 / admin 200 / 不存在 404);
  • runner 端到端:_execute_plan 产出收集 + 临时目录确认删除;
  • 存量 mock 签名适配新 output_sink 参数;全量 1249 passed、ruff 零告警、前端 build 通过。

说明

@hm1229
hm1229 requested review from hm1229 and removed request for hm1229 August 1, 2026 09:23
@hm1229 hm1229 self-assigned this Aug 1, 2026
@hm1229

hm1229 commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

您好,我们目前已将新版能力进行了更新,回复卡片相关功能修改后我们继续评审

@tianling536
tianling536 force-pushed the feat/agent-output-files branch from 004b405 to 575c626 Compare August 6, 2026 14:04
@tianling536

Copy link
Copy Markdown
Contributor Author

已按新版能力重写本 PR(commit 575c626,基于最新 main 6f72f96)。

变化说明

新版 harness v2 的 artifact 管线(workspace_file artifact + HarnessArtifactDownloads 卡片 + 带 sha256 校验/路径安全的下载端点)已经覆盖了产出下载的主链路,因此原实现整体撤下:

  • 废弃:AgentOutputFile 表、outputs/ 目录、/api/chat/outputs/{id}/download 端点、metadata.output_files 挂载、前端 outputFiles/卡片——不再有双轨;
  • 保留并移植本 PR 的核心增量:产物自动补登兜底。现有管线要求生成代码在结果 JSON 里显式声明 artifacts,模型不声明就没有产物卡片——这正是本 PR 最初要解决的"产出丢失"问题。

重构后的实现(3 处小改 + 测试)

  1. runner._execute_plan:在 structured 回写工作区相对 artifact_dir(仅 TaskFrame 工作区内运行时;技能页试运行不落工作区则不带);
  2. harness_capability_invoker._invoke_general_skill:当模型未声明且运行成功时,自动扫描 artifact_dir,把净产出文件登记为 workspace_file artifact(≤20 个、跳过空文件、拒绝越出工作区的路径,每个文件仍走 open_harness_artifact 校验 + sha256),并记 general_skill_artifacts_auto_declared 事件;显式声明存在时不触发,不会重复;失败运行不补登(半成品不进下载区);
  3. prompt 修正:repair prompt 里的 OUTPUT_DIR 与实际环境变量 ARTIFACT_DIR 不一致(main 预存在);两个 prompt 补充了自动补登语义(显式声明仍是首选,自动补登会丢失 display_name/description)。

验证

  • 新增 5 例:runner 回写字段(有/无 workspace_root)、兜底补登(sha256/大小/来源断言)、显式声明不重复、失败运行不补登、逃逸路径拒绝;
  • 全量 1303 passed(main 新基线)、ruff 零告警。

前端无需改动——artifact 进 metadata.harness_artifacts 后由现有 HarnessArtifactDownloads 自动渲染。技能页试运行(无 TaskFrame 工作区)的产出仍不可下载,属于该路径的既有语义,如需要可后续单开。

田领 added 2 commits August 7, 2026 22:17
按 harness v2 重构本 PR:新版管线(workspace_file artifact +
HarnessArtifactDownloads 卡片 + 安全下载端点)已覆盖产出下载主链路,
原 AgentOutputFile 表/outputs 目录/独立下载端点/消息 metadata 挂载全部
撤下,只保留本 PR 的核心增量——自动补登兜底:

- runner:_execute_plan 在 structured 回写工作区相对 artifact_dir
  (仅 workspace_root 存在时;试运行不落工作区则不带)
- invoker:模型未在结果 JSON 声明 artifacts 且运行成功时,自动扫描
  artifact_dir 把净产出文件登记为 workspace_file artifact(≤20 个、
  跳过空文件、拒绝越出 TaskFrame 工作区的路径;每个文件仍经
  open_harness_artifact 校验+sha256),并记
  general_skill_artifacts_auto_declared 事件
- prompt 修正:repair prompt 的 OUTPUT_DIR 与实际环境变量 ARTIFACT_DIR
  不一致(main 预存在);两个 prompt 补充自动补登语义说明

测试:runner 回写字段/有 workspace 与否;兜底补登(sha256/大小/来源断言)、
显式声明不重复、失败运行不补登、逃逸路径拒绝;全量 1303 passed、ruff 零告警
main 的 4bdab76(legacy 清理)丢了 staticmethod,实例调用即 TypeError,
新会话首轮必炸;实例调用验证+相关测试 75 passed
@tianling536
tianling536 force-pushed the feat/agent-output-files branch from 553dc3e to 3f3d905 Compare August 7, 2026 14:20
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