backend/docs/FILE_UPLOAD.md
DeerFlow 后端提供了完整的文件上传功能,支持多文件上传,并自动将 Office 文档和 PDF 转换为 Markdown 格式。
POST /api/threads/{thread_id}/uploads
请求体: multipart/form-data
files: 一个或多个文件响应:
{
"success": true,
"files": [
{
"filename": "document.pdf",
"size": 1234567,
"path": ".deer-flow/threads/{thread_id}/user-data/uploads/document.pdf",
"virtual_path": "/mnt/user-data/uploads/document.pdf",
"artifact_url": "/api/threads/{thread_id}/artifacts/mnt/user-data/uploads/document.pdf",
"markdown_file": "document.md",
"markdown_path": ".deer-flow/threads/{thread_id}/user-data/uploads/document.md",
"markdown_virtual_path": "/mnt/user-data/uploads/document.md",
"markdown_artifact_url": "/api/threads/{thread_id}/artifacts/mnt/user-data/uploads/document.md"
}
],
"message": "Successfully uploaded 1 file(s)"
}
路径说明:
path: 实际文件系统路径(相对于 backend/ 目录)virtual_path: Agent 在沙箱中使用的虚拟路径artifact_url: 前端通过 HTTP 访问文件的 URLGET /api/threads/{thread_id}/uploads/list
响应:
{
"files": [
{
"filename": "document.pdf",
"size": 1234567,
"path": ".deer-flow/threads/{thread_id}/user-data/uploads/document.pdf",
"virtual_path": "/mnt/user-data/uploads/document.pdf",
"artifact_url": "/api/threads/{thread_id}/artifacts/mnt/user-data/uploads/document.pdf",
"extension": ".pdf",
"modified": 1705997600.0
}
],
"count": 1
}
DELETE /api/threads/{thread_id}/uploads/{filename}
响应:
{
"success": true,
"message": "Deleted document.pdf"
}
以下格式会自动转换为 Markdown:
.pdf).ppt, .pptx).xls, .xlsx).doc, .docx)转换后的 Markdown 文件会保存在同一目录下,文件名为原文件名 + .md 扩展名。
Agent 在每次请求时会自动收到已上传文件的列表,格式如下:
<uploaded_files>
The following files have been uploaded and are available for use:
- document.pdf (1.2 MB)
Path: /mnt/user-data/uploads/document.pdf
- document.md (45.3 KB)
Path: /mnt/user-data/uploads/document.md
You can read these files using the `read_file` tool with the paths shown above.
</uploaded_files>
Agent 在沙箱中运行,使用虚拟路径访问文件。Agent 可以直接使用 read_file 工具读取上传的文件:
# 读取原始 PDF(如果支持)
read_file(path="/mnt/user-data/uploads/document.pdf")
# 读取转换后的 Markdown(推荐)
read_file(path="/mnt/user-data/uploads/document.md")
路径映射关系:
/mnt/user-data/uploads/document.pdf(虚拟路径)backend/.deer-flow/threads/{thread_id}/user-data/uploads/document.pdf/api/threads/{thread_id}/artifacts/mnt/user-data/uploads/document.pdf(HTTP URL)上传流程采用“线程目录优先”策略:
backend/.deer-flow/threads/{thread_id}/user-data/uploads/ 作为权威存储sandbox_id=local)直接使用线程目录内容/mnt/user-data/uploads/*,确保运行时可见# 1. 上传单个文件
curl -X POST http://localhost:2026/api/threads/test-thread/uploads \
-F "files=@/path/to/document.pdf"
# 2. 上传多个文件
curl -X POST http://localhost:2026/api/threads/test-thread/uploads \
-F "files=@/path/to/document.pdf" \
-F "files=@/path/to/presentation.pptx" \
-F "files=@/path/to/spreadsheet.xlsx"
# 3. 列出已上传文件
curl http://localhost:2026/api/threads/test-thread/uploads/list
# 4. 删除文件
curl -X DELETE http://localhost:2026/api/threads/test-thread/uploads/document.pdf
import requests
thread_id = "test-thread"
base_url = "http://localhost:2026"
# 上传文件
files = [
("files", open("document.pdf", "rb")),
("files", open("presentation.pptx", "rb")),
]
response = requests.post(
f"{base_url}/api/threads/{thread_id}/uploads",
files=files
)
print(response.json())
# 列出文件
response = requests.get(f"{base_url}/api/threads/{thread_id}/uploads/list")
print(response.json())
# 删除文件
response = requests.delete(
f"{base_url}/api/threads/{thread_id}/uploads/document.pdf"
)
print(response.json())
backend/.deer-flow/threads/
└── {thread_id}/
└── user-data/
└── uploads/
├── document.pdf # 原始文件
├── document.md # 转换后的 Markdown
├── presentation.pptx
├── presentation.md
└── ...
client_max_body_size)Upload Router (app/gateway/routers/uploads.py)
Uploads Middleware (packages/harness/deerflow/agents/middlewares/uploads_middleware.py)
Nginx 配置 (nginx.conf)
markitdown>=0.0.1a2 - 文档转换python-multipart>=0.0.20 - 文件上传处理make gatewayuv run python -c "import markitdown"backend/.deer-flow/threads/{thread_id}/user-data/uploads/// 上传文件示例
async function uploadFiles(threadId: string, files: File[]) {
const formData = new FormData();
files.forEach(file => {
formData.append('files', file);
});
const response = await fetch(
`/api/threads/${threadId}/uploads`,
{
method: 'POST',
body: formData,
}
);
return response.json();
}
// 列出文件
async function listFiles(threadId: string) {
const response = await fetch(
`/api/threads/${threadId}/uploads/list`
);
return response.json();
}