
DeerFlow 文件上传路径系统path、virtual_path 与 artifact_url 三类路径全解析【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flowDeerFlow 的文件上传系统会为每个上传文件同时返回三种路径宿主机实际路径path、沙箱虚拟路径virtual_path和 HTTP 访问地址artifact_url。三者分别服务于后端直接访问、Agent 沙箱内操作和前端下载/预览三种场景。读完本文你将完整掌握三类路径的生成规则与适用边界能独立完成“前端上传 → Agent 读取 → 前端预览/下载”的全链路集成并理解 DeerFlow 在路径隔离与目录遍历防护上的源码级安全设计。一、三种路径类型总览DeerFlow 的文件上传系统返回三种不同的路径每种路径用于不同的场景场景使用的路径类型示例服务器后端代码直接访问path.deer-flow/threads/abc123/user-data/uploads/file.pdfAgent 工具调用virtual_path/mnt/user-data/uploads/file.pdf前端下载/预览artifact_url/api/threads/abc123/artifacts/mnt/user-data/uploads/file.pdf备份脚本path.deer-flow/threads/abc123/user-data/uploads/file.pdf日志记录path.deer-flow/threads/abc123/user-data/uploads/file.pdf1. 实际文件系统路径path.deer-flow/threads/{thread_id}/user-data/uploads/document.pdf用途文件在服务器文件系统中的实际位置相对于backend/目录用于直接文件系统访问、备份、调试等。示例# Python 代码中直接访问 from pathlib import Path file_path Path(backend/.deer-flow/threads/abc123/user-data/uploads/document.pdf) content file_path.read_bytes()需要注意path仅对运行在宿主机上的后端代码有意义。在部署为 Docker 沙箱或多用户隔离场景下线程目录可能进一步嵌套用户前缀具体由路径解析器统一计算下文有源码说明。2. 虚拟路径virtual_path/mnt/user-data/uploads/document.pdf用途Agent 在沙箱环境中使用的路径沙箱系统会自动映射到实际路径Agent 的所有文件操作工具都使用这个路径。示例——Agent 在对话中使用# Agent 使用 read_file 工具 read_file(path/mnt/user-data/uploads/document.pdf) # Agent 使用 bash 工具 bash(commandcat /mnt/user-data/uploads/document.pdf)虚拟路径前缀/mnt/user-data在源码中是一个明确定义的常量VIRTUAL_PATH_PREFIX 定义 中声明为VIRTUAL_PATH_PREFIX /mnt/user-data即“Agent 在沙箱内看到的虚拟路径前缀”。3. HTTP 访问 URLartifact_url/api/threads/{thread_id}/artifacts/mnt/user-data/uploads/document.pdf用途前端通过 HTTP 访问文件用于下载、预览文件可以直接在浏览器中打开。示例// 前端 TypeScript/JavaScript 代码 const threadId abc123; const filename document.pdf; // 下载文件 const downloadUrl /api/threads/${threadId}/artifacts/mnt/user-data/uploads/${filename}?downloadtrue; window.open(downloadUrl); // 在新窗口预览 const viewUrl /api/threads/${threadId}/artifacts/mnt/user-data/uploads/${filename}; window.open(viewUrl, _blank); // 使用 fetch API 获取 const response await fetch(viewUrl); const blob await response.blob();从实现上看artifact_url并非随意拼接upload_artifact_url 函数 会把文件名做百分号编码quote(filename, safe)因此含空格、#、?等字符的文件名在 URL 中也是安全的而 upload_virtual_path 函数 则统一生成/mnt/user-data/uploads/{filename}形式。上传与列表接口的每个文件条目都由 enrich_file_listing 函数 补齐virtual_path与artifact_url两个字段。二、路径在源码中的生成与解析链路1. 线程数据目录结构三个子目录线程数据目录由 ThreadDataMiddleware 管理它为每个线程创建如下结构{base_dir}/threads/{thread_id}/user-data/workspace {base_dir}/threads/{thread_id}/user-data/uploads {base_dir}/threads/{thread_id}/user-data/outputs三个目录分别对应工作区、用户上传文件、Agent 生成产物与三类虚拟路径一一映射宿主{base_dir}/threads/{thread_id}/user-data/uploads/↔ 沙箱/mnt/user-data/uploads/宿主{base_dir}/threads/{thread_id}/user-data/workspace/↔ 沙箱/mnt/user-data/workspace/宿主{base_dir}/threads/{thread_id}/user-data/outputs/↔ 沙箱/mnt/user-data/outputs/。对应的宿主路径计算方法见 Paths.sandbox_uploads_dir其中sandbox_work_dir、sandbox_uploads_dir、sandbox_outputs_dir三个方法都接受user_id参数用于多用户部署下的按用户路径隔离。中间件默认采用惰性初始化lazy_initTrue只计算路径、不立即建目录目录按需创建。2. 虚拟路径到实际路径的解析沙箱内虚拟路径与宿主机路径之间的双向转换由 Paths.resolve_virtual_path 承担。从源码结构看它的解析规则有两点值得注意要求精确的段边界匹配路径去掉前导/后必须等于mnt/user-data或以mnt/user-data/开头这样可以拒绝形如mnt/user-dataX/...的前缀混淆攻击解析失败或检测到路径穿越traversal时抛出ValueError上层将其转为 HTTP 错误。Gateway 侧的入口是 resolve_thread_virtual_path它把虚拟路径解析为线程user-data下的真实文件系统路径并约定检测到 traversal 时返回 403其他非法路径返回 400——这正是文档中“API 会验证路径防止目录遍历攻击”的具体实现。3. 上传接口的响应模型上传路由定义在 uploads.py其响应模型UploadedFileInfo与文档中示例字段一一对应class UploadedFileInfo(BaseModel): Uploaded file metadata exposed by upload and list APIs. filename: str size: int path: str virtual_path: str artifact_url: str extension: str | None None modified: float | None None original_filename: str | None None markdown_file: str | None None markdown_path: str | None None markdown_virtual_path: str | None None markdown_artifact_url: str | None None其中markdown_*字段均为可选仅当文档类文件成功转换为 Markdown 时才填充。上传路由还定义了应用级上传限额的默认值见 uploads.py 常量区UPLOAD_CHUNK_SIZE 8192 DEFAULT_MAX_FILES 10 DEFAULT_MAX_FILE_SIZE 50 * 1024 * 1024 # 单文件 50 MB DEFAULT_MAX_TOTAL_SIZE 100 * 1024 * 1024 # 单次总量 100 MB这些默认值可被uploads配置段覆盖非法配置会记录警告并回退到默认值。在写入宿主机之后上传流程还会把文件同步进沙箱uploads.py 中的 _sync_upload_to_sandbox 直接以virtual_path为键调用sandbox.update_file(...)保证 Agent 在下一轮对话中通过虚拟路径即可读取到刚上传的文件对挂载式沙箱uses_thread_data_mounts则依赖目录挂载无需逐文件拷贝。三、完整使用流程前端上传并让 Agent 处理场景前端上传文件并让 Agent 处理。// 1. 前端上传文件 async function uploadAndProcess(threadId: string, file: File) { // 上传文件 const formData new FormData(); formData.append(files, file); const uploadResponse await fetch( /api/threads/${threadId}/uploads, { method: POST, body: formData } ); const uploadData await uploadResponse.json(); const fileInfo uploadData.files[0]; console.log(文件信息, fileInfo); // { // filename: report.pdf, // path: .deer-flow/threads/abc123/user-data/uploads/report.pdf, // virtual_path: /mnt/user-data/uploads/report.pdf, // artifact_url: /api/threads/abc123/artifacts/mnt/user-data/uploads/report.pdf, // markdown_file: report.md, // markdown_path: .deer-flow/threads/abc123/user-data/uploads/report.md, // markdown_virtual_path: /mnt/user-data/uploads/report.md, // markdown_artifact_url: /api/threads/abc123/artifacts/mnt/user-data/uploads/report.md // } // 2. 发送消息给 Agent await sendMessage(threadId, 请分析刚上传的 PDF 文件); // Agent 会自动看到文件列表包含 // - report.pdf (虚拟路径: /mnt/user-data/uploads/report.pdf) // - report.md (虚拟路径: /mnt/user-data/uploads/report.md) // 3. 前端可以直接访问转换后的 Markdown const mdResponse await fetch(fileInfo.markdown_artifact_url); const markdownContent await mdResponse.text(); console.log(Markdown 内容, markdownContent); // 4. 或者下载原始 PDF const downloadLink document.createElement(a); downloadLink.href fileInfo.artifact_url ?downloadtrue; downloadLink.download fileInfo.filename; downloadLink.click(); }这个流程背后的运行时行为可以从源码得到印证上传中间件会在系统提示中告诉 Agent 上传目录的位置与用法。例如 uploads_middleware.py 会向 Agent 提示使用grep(patternkeyword, path/mnt/user-data/uploads/)搜索关键词、glob(pattern**/*, path/mnt/user-data/uploads/)列出全部上传文件list_uploaded_files_tool.py 列出的每个文件路径也都是/mnt/user-data/uploads/{filename}虚拟路径。也就是说Agent 自始至终只接触virtual_path与文档“Agent 只能看到和使用 virtual_path”的描述一致。四、代码示例集合1. Python——后端处理文档给出的原始示例通过THREAD_DATA_BASE_DIR定位上传目录。从当前代码结构看线程数据路径的计算已收敛到统一的Paths类等价的后端直接访问写法如下示例中的THREAD_DATA_BASE_DIR在现行源码中已由Paths解析器取代可直接参考 sandbox_uploads_dirfrom pathlib import Path from deerflow.agents.middlewares.thread_data_middleware import THREAD_DATA_BASE_DIR def process_uploaded_file(thread_id: str, filename: str): # 使用实际路径 base_dir Path.cwd() / THREAD_DATA_BASE_DIR / thread_id / user-data / uploads file_path base_dir / filename # 直接读取 with open(file_path, rb) as f: content f.read() return content更贴合当前实现的等价调用是from deerflow.config.paths import get_paths from deerflow.uploads.manager import get_uploads_dir # 无副作用地获取线程上传目录内部会校验 thread_id 与用户上下文 uploads_dir get_uploads_dir(thread_id) content (uploads_dir / filename).read_bytes()其中 get_uploads_dir 内部先调用validate_thread_id再委托Paths.sandbox_uploads_dir是后端直接访问线程上传目录的推荐入口。2. JavaScript——前端访问// 列出已上传的文件 async function listUploadedFiles(threadId) { const response await fetch(/api/threads/${threadId}/uploads/list); const data await response.json(); // 为每个文件创建下载链接 data.files.forEach(file { console.log(文件: ${file.filename}); console.log(下载: ${file.artifact_url}?downloadtrue); console.log(预览: ${file.artifact_url}); // 如果是文档还有 Markdown 版本 if (file.markdown_artifact_url) { console.log(Markdown: ${file.markdown_artifact_url}); } }); return data.files; } // 删除文件 async function deleteFile(threadId, filename) { const response await fetch( /api/threads/${threadId}/uploads/${filename}, { method: DELETE } ); return response.json(); }3. React 组件示例import React, { useState, useEffect } from react; interface UploadedFile { filename: string; size: number; path: string; virtual_path: string; artifact_url: string; extension: string; modified: number; markdown_artifact_url?: string; } function FileUploadList({ threadId }: { threadId: string }) { const [files, setFiles] useStateUploadedFile[]([]); useEffect(() { fetchFiles(); }, [threadId]); async function fetchFiles() { const response await fetch(/api/threads/${threadId}/uploads/list); const data await response.json(); setFiles(data.files); } async function handleUpload(event: React.ChangeEventHTMLInputElement) { const fileList event.target.files; if (!fileList) return; const formData new FormData(); Array.from(fileList).forEach(file { formData.append(files, file); }); await fetch(/api/threads/${threadId}/uploads, { method: POST, body: formData }); fetchFiles(); // 刷新列表 } async function handleDelete(filename: string) { await fetch(/api/threads/${threadId}/uploads/${filename}, { method: DELETE }); fetchFiles(); // 刷新列表 } return ( div input typefile multiple onChange{handleUpload} / ul {files.map(file ( li key{file.filename} span{file.filename}/span a href{file.artifact_url} target_blank预览/a a href{${file.artifact_url}?downloadtrue}下载/a {file.markdown_artifact_url ( a href{file.markdown_artifact_url} target_blankMarkdown/a )} button onClick{() handleDelete(file.filename)}删除/button /li ))} /ul /div ); }五、路径安全性设计源码级展开文档“注意事项”一节指出实际路径包含线程 ID 以确保隔离、API 会验证路径防止目录遍历、前端不应直接使用path。以下是对应的源码证据。1. 文件名规范化与遍历防护uploads/manager.py 定义了两类安全异常PathTraversalError路径逃逸出允许的基目录与UnsafeUploadPathError上传目标不是安全的常规文件路径。文件名入口是 normalize_filename只保留 basename剥离任何目录成分拒绝空名与./..显式拒绝含反斜杠的文件名防止 Windows 风格路径注入限制文件名为 255 字节UTF-8。而 validate_path_traversal 则通过path.resolve().relative_to(base.resolve())校验目标必须位于基目录之内否则抛出PathTraversalError。配合虚拟路径解析时的 403/400 语义见第二节三层校验覆盖了上传写入、读取解析与 Agent 沙箱三个环节。2. artifact 路由的响应安全artifacts.py 中定义了活跃内容类型集合ACTIVE_CONTENT_MIME_TYPES { text/html, application/xhtmlxml, image/svgxml, }从 _read_artifact_payload 的逻辑 看请求带?downloadtrue或文件属于上述 MIME 类型时一律走流式FileResponsedownload即强制下载/不内联渲染这正是文档中“使用?downloadtrue参数强制下载”的底层机制纯文本类则内联返回以便预览。此外该路由还要求每次请求先通过沙箱租约try_acquire_sandbox_for_request/require_permission保证访问与线程权限绑定。3. 隔离与映射小结路径安全性实际路径path包含线程 ID确保隔离API 会验证路径防止目录遍历攻击前端不应直接使用path而应使用artifact_url。Agent 使用Agent 只能看到和使用virtual_path沙箱系统自动映射到实际路径Agent 不需要知道实际的文件系统结构。前端集成始终使用artifact_url访问文件不要尝试直接访问文件系统路径使用?downloadtrue参数强制下载。Markdown 转换转换成功时会返回额外的markdown_*字段对应 上传路由中的字段填充逻辑建议优先使用 Markdown 版本更易处理原始文件始终保留。六、小结DeerFlow 的三类路径本质上是对同一份线程用户数据threads/{thread_id}/user-data/的三种视图宿主机视角的path、沙箱视角的virtual_path、HTTP 视角的artifact_url。集成时的选型规则很清晰后端脚本与备份用pathAgent 提示词与工具调用只写virtual_path前端一律拼artifact_url需要下载时加?downloadtrue。相关实现集中在 config/paths.py、uploads/manager.py、gateway/routers/uploads.py、gateway/routers/artifacts.py 与 gateway/path_utils.py可对照本文继续深入阅读相关测试如 test_paths_user_isolation.py、test_artifacts_router.py、test_local_sandbox_virtual_path_contract.py 也验证了用户隔离、artifact 访问与虚拟路径契约行为。【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考