
ChatDev 2.0 附件与工件 API 完整指南从文件上传、事件订阅到生命周期管理【免费下载链接】ChatDevChatDev 2.0: Dev All through LLM-powered Multi-Agent Collaboration项目地址: https://gitcode.com/GitHub_Trending/ch/ChatDev本指南面向 ChatDev 2.0 的高级用户与二次开发者系统讲解附件Attachment与工件Artifact两套 API如何在 Session 生命周期内上传、列举、引用文件如何通过 REST 长轮询或 WebSocket 实时订阅节点产出文件以及文件如何从上传、注册、分发一路走到打包下载。读完本文你将掌握uploads、artifact-events、artifacts、download四条核心接口的完整用法与底层实现原理并能在自己的客户端中复刻 Web UI 的附件交互逻辑。文档主体见 docs/user_guide/zh/attachments.md英文版见 docs/user_guide/en/attachments.md。一、先厘清概念附件Attachment与工件Artifact的区别官方文档明确给出了两者定义附件AttachmentSession 生命周期内可上传、可下载、可被节点注册引用的文件实体。它强调的是文件被纳入会话管理这一状态以attachment_id唯一标识。工件Artifact对附件事件的抽象用于实时监听文件产生、变化的过程。它强调的是事件流以event_id与单调递增的sequence序号标识。理解这条主线即可读懂全部接口上传/列举走uploads路由订阅监听走artifact-events取回文件内容走artifacts下载接口整包带走走download打包接口。值得一提的前提是一般业务场景下前端会自动处理这些调用本组 API 主要面向需要深度集成的客户端与自动化脚本。二、上传与列举/api/uploads/{session_id}2.1 上传单个文件POST /api/uploads/{session_id}HeadersContent-Type: multipart/form-dataForm 字段file单个文件对应 FastAPI 的UploadFile响应示例文档简化版{ attachment_id: att_bxabcd, name: spec.md, mime: text/markdown, size: 12345 }结合路由源码 server/routes/uploads.py 可以看到实际返回的 JSON 键为attachment_id、name、mime_type、sizemime_type取上传时携带的Content-Type缺失时用mimetypes推断见 server/services/attachment_service.py。因此对接时建议以源码返回的mime_type为准。对应的 curl 调用方式curl -X POST http://localhost:8000/api/uploads/session_id \ -F filespec.md底层处理链路AttachmentService.save_upload_file将上传流按 1 MB 分块写入临时目录mac_upload_*调用AttachmentStore.register_file()把临时文件复制进会话的附件目录依据 MIME 推断块类型记录extra元数据source: user_upload、origin: web_upload、session_id清理临时目录。AttachmentStore.register_file()utils/attachments.py会以uuid4().hex生成attachment_id计算文件sha256把文件复制到attachments_root/attachment_id/原始文件名并可选将记录持久化到 manifest。2.2 存储位置与 manifestGET /api/uploads/{session_id}返回该 Session 当前所有附件的元数据ID、文件名、mime、大小、来源。对应源码 server/routes/uploads.py 调用list_attachment_manifests()最终由AttachmentStore.export_manifest()utils/attachments.py只导出持久化的记录集合。文件实际落盘位置WareHouse/session_session_id/code_workspace/attachments/ ├── attachments_manifest.json # 附件元数据索引 └── attachment_id/原始文件名 # 文件本体路径拼接逻辑见 AttachmentService._session_attachments_pathWareHouse/session_session_id/code_workspace/attachments。manifest 记录source、workspace_path、storage等字段AttachmentStore在构造时即加载既有 manifest_load_manifest因此进程重启后附件记录不丢失。2.3 在执行请求中引用附件附件上传后需要喂给工作流执行有两种途径RESTPOST /api/workflow/execute请求体携带attachments: [att_xxx]WebSocket向 Session 发送human_input消息数据负载中携带attachments: [att_xxx]。无论哪种方式都必须同时提供task_prompt即便只想上传文件——这一点由 server/routes/execute_sync.py 的校验逻辑if (not task_prompt or not task_prompt.strip()) and not attachments与 server/services/message_handler.py 的 human_input 处理共同保证。REST 请求体模型定义见 server/models.pyclass WorkflowRequest(BaseModel): yaml_file: str task_prompt: str session_id: Optional[str] None attachments: Optional[List[str]] None log_level: Literal[INFO, DEBUG] INFO/api/workflow/execute路由server/routes/execute.py会把attachments透传给start_workflow最终存进 Session 的task_attachments见 server/services/session_store.py。WebSocket 的 human_input 处理链路在 server/services/prompt_channel.pyWebPromptChannel从响应负载中抽取text与attachments再调用AttachmentService.build_attachment_blocks()把attachment_id列表解析为MessageBlock列表——若目标 Store 与源 Store 根目录不同还会通过ingest_record()把文件复制进当前执行工作区utils/attachments.py确保 Python 节点运行时能看到附件。2.4 批量任务中的附件批量执行场景同样支持附件POST /api/uploads/batch之类的批量接口通过 server/routes/batch.py 解析 CSV/表格文件server/services/batch_parser.py 会从单元格提取attachment_paths并由batch_run_service构建包含附件路径的任务输入。说明附件能力在 REST、WebSocket、批量三条执行路径上是打通的。三、工件事件订阅/api/sessions/{session_id}/artifact-events3.1 接口与查询参数GET /api/sessions/{session_id}/artifact-events路由源码见 server/routes/artifacts.py查询参数与约束如下表参数类型默认值约束说明afterint无 0游标只拉取sequence after的事件wait_secondsfloat25.00.0 ~ 60.0无新事件时的阻塞等待秒数长轮询include_mimestr无逗号分隔MIME 前缀/精确过滤如image/include_extstr无逗号分隔扩展名过滤如png,jpg自动去点、小写化max_sizeint无 0只返回小于等于该字节数的事件limitint251 ~ 100单次返回事件条数上限响应结构{ events: [], next_cursor: 12, has_more: false, timed_out: true }events本次返回的事件数组next_cursor下次请求应传入的after值实现增量拉取has_morequeue.last_sequence next_cursor时为true提示仍有未拉取的事件timed_out等待超时且无匹配事件时为true客户端应携带next_cursor继续轮询。3.2 事件结构源码级字段文档中的事件示例为简化版。对照 ArtifactEvent.to_dict()真实事件包含以下完整字段{ event_id: hex-uuid, sequence: 12, node_id: python_runner, attachment_id: att_456, file_name: result.json, relative_path: code_workspace/result.json, workspace_path: /abs/path/to/result.json, mime_type: application/json, size: 2048, sha256: sha256 hex digest, data_uri: null, created_at: 1732699900.123, change_type: created, extra: {} }sequence是ArtifactEventQueue.append_many()分配的单调递增序号server/services/artifact_events.py是after游标机制的基础。3.3 底层线程安全的阻塞事件队列ArtifactEventQueueserver/services/artifact_events.py是一个有界默认最多 2000 条的线程安全队列append_many()追加事件并分配sequence超出容量时从队头淘汰同步推进_min_sequencesnapshot()按after游标、MIME/扩展名/大小过滤条件快照事件并返回next_cursorwait_for_events()在Condition上阻塞等待直到出现匹配事件或到达wait_seconds截止时间返回(events, next_cursor, timed_out)。路由通过asyncio.to_thread把阻塞调用交给线程池避免阻塞事件循环。该队列挂在 Session 对象上server/services/session_store.py所以事件是按 Session 隔离的。3.4 WebSocket 镜像推送同一事件会被镜像为 WebSocket 消息推送给前端类型为artifact_created。实现见 server/services/artifact_dispatcher.py{ type: artifact_created, data: { session_id: session_xxx, events: [ { event_id: ..., sequence: 12, node_id: python_runner, ... : ... } ] } }ArtifactDispatcher.emit()同时做两件事把事件追加进队列供 REST 长轮询读取并通过websocket_manager.send_message_sync()推送给已连接的 WebSocket 客户端。这意味着客户端既可以直接订阅 WS也可以用 RESTafter游标兜底补拉两条通道最终消费的是同一批ArtifactEvent。四、下载工件与整包打包4.1 下载单个工件GET /api/sessions/{session_id}/artifacts/{artifact_id}查询参数参数默认值说明modemetameta只返回元数据stream返回文件内容downloadfalsetrue时响应头附带Content-Disposition: attachment否则为inline实现见 server/routes/artifacts.pymodestream以StreamingResponse流式返回文件字节Content-Disposition由download参数决定modemeta返回artifact_id、name、mime_type、size、sha256、local_path、extra等元数据。小文件内联data_uri当附件文件大小不超过 20 MBMAX_FILE_SIZE 20 * 1024 * 1024见 server/routes/artifacts.py时meta模式会自动用encode_file_to_data_uri()生成data: mime;base64,...形式的内联数据utils/attachments.py避免前端为小文件再发一次下载请求。若服务器启用了内联策略该能力可直接复用。4.2 打包下载整个 SessionGET /api/sessions/{session_id}/download将WareHouse/session_session_id/整个目录包含工作区、附件、运行产物打包为 zip 一次性下载实现见 server/routes/sessions.py校验session_id格式仅允许字母、数字、下划线、连字符正则^[a-zA-Z0-9_-]$非法输入记录安全日志并返回 400检查WareHouse/session_session_id目录存在性不存在返回 404用shutil.make_archive在临时目录生成 zip通过FileResponse返回并以BackgroundTask(cleanup_zip)在响应结束后自动删除临时 zip。五、文件生命周期全景把以上接口串联起来一个附件从诞生到归档的完整生命周期如下① 用户上传 ──► POST /api/uploads/{session_id} │ AttachmentService.save_upload_file() ▼ ② 落盘 ──► WareHouse/session_id/code_workspace/attachments/ │ AttachmentStore.register_file() attachments_manifest.json ▼ ③ 执行引用 ──► POST /api/workflow/execute 或 WS human_input 携带 attachments[] │ build_attachment_blocks() → MessageBlock ▼ ④ 节点产出 ──► WorkspaceArtifactHook 扫描节点工作区新文件 │ AttachmentStore.register_file(copy_fileFalse) ▼ ⑤ 事件分发 ──► ArtifactDispatcher.emit() │ ArtifactEventQueueREST 长轮询可读 ▼ └──► WebSocket 消息 typeartifact_created ⑥ 消费下载 ──► GET /api/.../artifacts/{id}单文件 GET /api/.../download整包 zip几个关键实现细节值得展开节点产出的自动注册WorkspaceArtifactHookworkflow/hooks/workspace_artifact.py在节点执行前后对工作区做快照对比 sha256 签名发现新增/变更文件后调用_register_artifact()注册为附件——注意这里copy_fileFalse即直接引用工作区原文件不复制副本同时记录node_id与relative_path。这正是文档中Python 节点或工具可调用AttachmentStore.register_file()把 workspace 文件注册为附件的落地实现。去重能力AttachmentStore维护_hash_indexsha256 → attachment_idregister_file()开启deduplicateTrue时相同内容的文件只保留一份记录utils/attachments.py。MIME → 块类型推断MessageBlockType.from_mime_type()entity/messages.py把image/*映射为image、audio/*为audio、video/*为video其余一律为file。前端据此决定以图片/音频/视频/文件哪种形式渲染附件块。保留与清理策略默认保留所有附件便于运行结束后下载。若需自动清理设置环境变量export MAC_AUTO_CLEAN_ATTACHMENTS1该开关在 server/services/attachment_service.py 读取1/true/yes均视为开启cleanup_session()只在Session 完成后删除attachments/目录server/services/attachment_service.py。注意它不会清理工作区其他产物也不会删除 manifest 中引用但已移除的内容/download打包下载同样不删除原文件归档/清空需要额外策略cron 或专门的清理任务。六、大小限制与安全建议官方文档与源码共同给出以下运维要点大小限制后端未硬编码上传上限分块读取即可处理大文件可在反向代理层配置client_max_body_sizeNginx、max_request_body_size或在自定义分支的AttachmentService.save_upload_file中添加校验。唯一与大小相关的常量是工件下载时data_uri内联的 20 MB 阈值与utils/attachments.py中DEFAULT_INLINE_LIMIT 512 * 1024。文件类型基于 MIME 推断MessageBlockTypeimage/audio/video/file客户端可通过include_mime精确过滤避免拉取无关大文件。病毒/敏感信息上传前由客户端自查必要时在保存后触发外部扫描服务。仓库本身不内置扫描器。权限控制Attachment 相关 API 仅依赖 Session ID 鉴权生产部署应在代理层或 JWT 内部校验调用者身份防止越权下载他人 Session 的文件session_id的格式校验^[a-zA-Z0-9_-]$在 server/routes/sessions.py 中有安全日志记录。七、常见问题排查表问题排查步骤上传 413 / Payload Too Large调整反向代理或 FastAPI 请求体上限client_max_body_size、max_request_body_size确认磁盘配额充足下载链接 404确认session_id拼写仅允许字母/数字/_-检查 Session 是否已被清理或MAC_AUTO_CLEAN_ATTACHMENTS是否已触发删除工件事件缺失确认 WebSocket 是否连接或在 REST 事件接口中使用after游标重拉事件队列有界超量后旧事件会被淘汰附件未在 Python 节点可见检查code_workspace/attachments/是否被清理或_context[python_workspace_root]是否正确指向工作区根目录has_more持续为 true说明next_cursor未正确回传应把上次响应的next_cursor作为下次请求的after八、客户端实现建议结合源码验证官方文档给出三类客户端的落地建议Web UI使用artifact-events长轮询wait_seconds建议 10~30 秒或 WebSocket 的artifact_created消息实时刷新附件列表在节点成功后提供下载全部按钮命中download打包接口。CLI / 自动化脚本运行结束后调用/api/sessions/{session_id}/download拉取整包 zip若只需部分文件先用artifact-events的include_ext如json或include_mime如image/精准过滤事件再逐个调用单文件下载接口。测试环境可用脚本模拟上传 → 执行 → 拉事件 → 下载全流程重点验证反向代理与 CORS 配置uploads接口依赖multipart/form-dataartifact-events依赖长轮询两者对代理超时与跨域头都比较敏感。一个可复用的 CLI 参考流程# 1. 上传文件记录返回的 attachment_id curl -F filespec.md http://localhost:8000/api/uploads/session_id # 2. 触发执行并携带附件必须带 task_prompt curl -X POST http://localhost:8000/api/workflow/execute \ -H Content-Type: application/json \ -d {yaml_file:yaml_instance/demo_code.yaml, task_prompt:根据附件完成分析, session_id:session_id, attachments:[att_xxx]} # 3. 长轮询工件事件携带游标增量拉取 curl http://localhost:8000/api/sessions/session_id/artifact-events?after0wait_seconds30include_extjson # 4. 按需下载单文件或整包 curl -o result.json http://localhost:8000/api/sessions/session_id/artifacts/artifact_id?modestreamdownloadtrue curl -o session.zip http://localhost:8000/api/sessions/session_id/download总结ChatDev 2.0 的附件/工件体系是一套存储 事件 下载三层解耦的完整实现AttachmentStore负责文件落盘与 manifest 持久化ArtifactEventQueueArtifactDispatcher负责事件缓冲与 REST/WS 双通道分发WorkspaceArtifactHook则让节点工作区里产生的每一个文件都能自动进入事件流。理解这一设计后无论你是接入 Web UI 的前端开发者还是编写自动化流水线的运维工程师都能把文件交付能力平滑地集成进自己的工作流。更完整的接口契约与执行流程可继续阅读 docs/user_guide/zh/web_ui_guide.md 与 docs/user_guide/zh/execution_logic.md。【免费下载链接】ChatDevChatDev 2.0: Dev All through LLM-powered Multi-Agent Collaboration项目地址: https://gitcode.com/GitHub_Trending/ch/ChatDev创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考