公司动态

Agent 接收用户上传文件

📅 2026/8/30 18:45:41
Agent 接收用户上传文件
1. 问题与目标前面的工作已经实现了用户端文件上传但目前文件主要用于用户侧操作。下一步需要让 Agent 本身能够接收这些文件。对于通用 Agent 来说文件不应该只限制在图片。例如用户上传一个 Word 文档希望 Agent 阅读并分析其中的内容或者上传 Excel、PDF、Markdown 等文件再让 Agent 根据文件执行任务。LangGraph CLI 创建的工程默认已经支持文件上传但前端组件支持的类型主要是pjp, jfif, pjpeg, jpeg, jpg, png, gif, webp, pdf这些类型本质上都是图片格式PDF 在默认情况下也会作为图片处理。如果项目只要求 Agent 接收图片那么后端只需要将模型替换成支持视觉输入的 VLM 即可其他部分基本不需要调整。但本项目后续还需要处理 Word、Excel、PDF、Markdown 等文件因此这里先解决一个更基础的问题让 Agent 能够接收任意类型的用户文件并将文件保存到当前 Agent 工作目录。文件内容的具体解析、转换和处理属于后续工作。2. 实现方案整体流程比较简单用户上传文件 ↓ Agent Chat UI ↓ 文件转换为 Base64 ↓ AgentState.upload_files ↓ before_agent Middleware ↓ Base64 解码 ↓ user_uploads/ ↓ Agent 读取文件具体分为两部分修改 Agent Chat UI使上传组件支持任意文件并将文件信息放入upload_files。在后端before_agent中读取upload_files将 Base64 文件恢复成实际文件。3. 前端文件数据前端接收到用户上传的文件后不直接将二进制文件放进 JSON而是使用 Base64 字符串表示文件内容。例如一个上传文件的数据结构如下{ type: file, mimeType: image/jpeg, data: DQoNCiMjIEFJIEFnZW50..., metadata: { filename: a.md, size: 8238, lastModified: 1757389979338 } }其中type固定为filemimeType文件 MIME 类型data文件内容对应的 Base64 字符串metadata.filename原始文件名metadata.size文件大小metadata.lastModified文件最后修改时间多个文件则组成一个列表并通过 Agent State 传递from langchain.agents.middleware import AgentState class CustomState(AgentState): upload_files: list3.1 为什么使用 Base64Base64 的作用只是将二进制数据转换成字符串方便放进 JSON 等文本格式中进行传输。例如原始文件 ↓ 二进制数据 ↓ Base64 编码 字符串 ↓ JSON ↓ 后端 ↓ Base64 解码 原始文件需要注意Base64不是加密同时会使数据体积增加约三分之一因此这里只把它作为前后端之间的传输方式而不是文件存储方式。4. 后端实现4.1 Base64 文件处理在content/utils下新增base64_util.py负责将 Base64 数据恢复成文件。import base64 import os def save_base64_file_from_content_block(content_block, save_dir): 从前端文件内容块中提取 Base64 数据并保存为文件 return save_base64_file( base64_datacontent_block[data], filenamecontent_block[metadata][filename], save_dirsave_dir ) def save_base64_file( base64_data: str, filename: str, save_dir: str ): 将 Base64 数据保存为文件 file_path os.path.join(save_dir, filename) file_data base64.b64decode(base64_data) with open(file_path, wb) as f: f.write(file_data) return { success: True, file_path: str(file_path), filename: filename, }这里将具体的 Base64 解码逻辑独立出来Middleware 只负责处理 Agent 生命周期和状态不直接处理文件编码细节。4.2 配置上传目录在base/configs.py中增加USER_UPLOAD_PATH user_uploads后续如果需要调整目录只修改配置即可不需要再修改 Middleware。4.3 在 Middleware 中处理上传文件文件上传发生在 Agent 开始执行之前因此放在FileMiddleware的abefore_agent中处理。首先增加相关状态和依赖from base.configs import USER_UPLOAD_PATH from content.utils import base64_util as bu class CustomState(AgentState): upload_files: list start_work_time: float file_update_time: float然后修改abefore_agentasync def abefore_agent(self, state, runtime): file_update_time time.time() # 当前线程对应的工作目录 dir_path os.path.join( rt.get_root_thread_dir(), USER_UPLOAD_PATH ) # 创建用户上传目录 await asyncio.to_thread( os.makedirs, dir_path, exist_okTrue ) files state.get(upload_files) if files: content 用户上传了如下文件文件路径如下 for file in files: # Base64 解码并保存文件 result await asyncio.to_thread( bu.save_base64_file_from_content_block, file, dir_path ) # 返回给 Agent 相对路径 content f\n{rt.get_out_path(result[file_path])} return { messages: [ ToolMessage( contentcontent, tool_call_idget_uuid(), namefile_upload ) ], upload_files: None, start_work_time: time.time(), file_update_time: file_update_time } return { start_work_time: file_update_time, file_update_time: file_update_time }这里有两个需要注意的地方。第一文件操作没有直接阻塞异步流程。os.makedirs和文件写入属于同步 I/O因此使用await asyncio.to_thread(...)放到线程中执行避免直接阻塞 Agent 的异步执行流程。第二处理完成后清空upload_files。文件已经落盘后就没有必要继续将完整的 Base64 数据保存在 Agent State 中upload_files: None这样可以避免后续 Agent 状态继续携带较大的文件数据。此时 Agent 实际拿到的是类似下面的信息用户上传了如下文件文件路径如下 ./user_uploads/a.md ./user_uploads/test.pdf ./user_uploads/data.xlsx后续 Agent 可以根据路径继续调用文件处理工具。5. 测试前端选择多个不同类型的文件进行上传例如test.pdf test.docx data.xlsx demo.md image.png上传后检查当前线程对应的工作目录thread_work_dir/ └── user_uploads/ ├── test.pdf ├── test.docx ├── data.xlsx ├── demo.md └── image.png确认文件能够正常生成并且文件内容与原文件一致即可说明前后端文件传输链路已经打通。6. 最终目录结构本次只列出新增和修改的部分├─ base │ └─ configs.py │ # 添加 USER_UPLOAD_PATH │ ├─ content │ ├─ middles │ │ └─ file_manager_middle.py │ │ # 处理 Agent 接收用户文件 │ │ │ └─ utils │ ├─ runtime_util.py │ └─ base64_util.py │ # 新增负责 Base64 文件解码7. 小结这一阶段没有处理文件内容只解决了Agent 接收文件这个基础问题。目前链路为前端上传任意文件 ↓ Base64 ↓ AgentState.upload_files ↓ FileMiddleware.abefore_agent ↓ Base64 解码 ↓ 当前线程 / user_uploads/ ↓ Agent 获取文件路径至此Agent 已经可以接收任意类型的用户文件。下一步只需要在这个基础上继续增加文件解析能力例如Word / PDF / Markdown ↓ 文本提取 ↓ Markdown / 纯文本 ↓ Agent 分析而图片、Excel 等文件则可以根据实际类型交给对应的工具或 SubAgent 处理。