Skip to content

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

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

feat(chat): 数字员工产出文件可下载(捕获/存储/消息卡片/下载端点)#55
tianling536 wants to merge 3 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 通过。

说明

田领 added 3 commits July 31, 2026 07:28
规范层(app/general_skills/standard.py):
- name/description 规范校验(1-64 小写连字符/1-1024)
- YAML frontmatter 解析与 SKILL.md 组装;可选字段
  license/compatibility/metadata/allowed-tools 全支持
- name 恒等于 slug(规范:与目录名一致);存量技能读路径同样输出规范形态

保存与发布:
- /import 与 _create_imported_general_skill(zip/GitHub/clawhub)统一归一化:
  frontmatter 以表单/解析字段重组、正文保留;新建 slug 不合规 400,
  导入名不合规自动 _slugify 清洗;published 缺 description 400,草稿宽限
- publish 端点发布前强制规范校验
- 新增 GET /{slug}/export 导出标准 zip(根目录=slug,SKILL.md 规范化)

运行时:
- 物化到工作区的 SKILL.md 一律规范化
- allowed-tools 门控:未声明 Bash 时 runtime 仅 python(计划输入约束+
  计划后强制),声明 Bash(...) 或缺省不限制

前端:
- 新建模板换标准 SKILL.md skeleton;基本信息新增 license/compatibility/
  allowed-tools 三字段(与 frontmatter 同步)
- 文件编辑器支持任意路径(scripts/references/assets)
- 编辑器新增"导出技能包"按钮

测试:规范层 10 例、保存归一化/非法 slug/发布校验/导出 zip 往返/
物化标准化/门控等;存量测试按归一化新行为对齐;全量 1243 passed、
ruff 零告警、前端 build 与 i18n 新增文案齐全
- 文件列表从平铺改为目录树:按路径组织层级、文件夹可折叠(默认展开)、
  文件显示 basename 并缩进、悬停看完整路径;右键菜单(重命名/删除)不变
- 新建文件改下拉菜单:预置 scripts/run.py、references/guide.md、
  assets/data.json 三个规范目录模板(重名自动加序号),自定义路径项
  展开内联输入(回车创建/Esc 收起)
- 通用技能 runner 执行后捕获净新增产出(排除技能包输入与 runner 脚本,
  ≤12MB、≤20 个);仅当采用结果可用时持久化,临时目录执行后一律清理
  (修复此前 run_dir 从不清理的残留泄漏)
- AgentOutputFile 模型:元数据与归属落库,本体写用户数据目录 outputs/
  (不内联 base64);产出经 output_sink 回调贯穿 agent_loop 流式/工具
  两条调用路径与通用技能运行测试页
- assistant 消息落库时按会话+时序挂载 output_files,聊天气泡渲染
  可点击下载卡片;运行测试页结果区同挂下载
- GET /api/chat/outputs/{file_id}/download:仅本人或管理员,
  FileResponse 直出,outputs 根外路径拒绝
- 测试:净新增捕获/超限跳过/持久化与挂载幂等/路径逃逸拒绝/
  下载权限(owner/他人 403/admin/404)/runner 产出收集与临时目录清理
- 全量 1249 passed、ruff 零告警、前端 build 通过

Stacked on OpenBMB#54(基于 feat/general-skills-standard 开发)
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.

1 participant