Day 14 / 共 20 天 · 第 3 周 工具/记忆/资源

资源管理 resource manager

智能体的"工作区":它读入的输入文件、产出的输出文件怎么存、怎么灌进向量库供检索、怎么生成摘要。读 resource_manager/

📍 你在整门课的位置
第1周 核心概念 第2周 Agent执行 D11-12 工具 D13 向量记忆 D14 资源管理 D15 数据模型 第4周 平台/异步
L01

什么是资源

🤔 智能体不就是"聊天+调工具"吗,为什么单独拿一天讲文件? 因为一旦智能体要"写报告""分析你上传的 PDF""生成代码",就绕不开文件。而文件一多就出问题:存哪(本地还是云)?不同智能体的文件会不会互相覆盖?100 页 PDF 塞不进上下文怎么查?这些都得有人统一管——就是资源管理器。
💡 一句话本质 资源管理器 = 智能体的"文件系统 + 图书管理员"。它做四件事:存(本地/S3 双写)、隔离(按 agent/execution 分目录)、可检索(灌进向量库做 RAG)、摘要(告诉智能体"手头有哪些资料")。入口是 FileManagerresource_manager/file_manager.py:11),工具(WriteFileTool 等)都把读写委托给它。

资源管理就像公司的"共享网盘":每个员工每个项目有自己的文件夹(隔离)、重要文件本地留一份又传网盘一份(双写)、网盘有全文搜索(灌向量库)、每个文件夹置顶一份目录清单(摘要)。今天全天沿用这个"公司网盘"世界观。

智能体干活会涉及文件——你上传给它的输入文件(PDF、CSV),它产出的输出文件(写的报告、生成的代码)。这些统称资源(Resource),由资源管理器管理。目录 resource_manager/

为什么需要资源管理? 智能体不只是"聊天",它会读写文件(CodingTool 写代码、你给它一个 PDF 让它分析)。这些文件需要:① 存储(本地或云);② 隔离(不同智能体/运行的文件别混);③ 可检索(把文件内容灌进向量库,让智能体能"查阅");④ 摘要(让智能体知道"我手头有哪些文件、大概讲什么")。资源管理器管这一切。
L02

FileManager 双写

FileManagerresource_manager/file_manager.py:11)是工具(如 CodingTool、WriteFileTool)读写文件的入口。核心模式:"先写本地磁盘,再同步到 S3":

# write_file (file_manager.py:48)
final_path = ResourceHelper.get_agent_write_resource_path(file_name, agent=..., agent_execution=...)
with open(final_path, mode="w") as file:
    file.write(content)
self.write_to_s3(file_name, final_path)   # 再同步 S3
# write_to_s3 (:35):先在 DB 登记 Resource 记录;storage_type==S3 时才真上传
读法:写文件 = 写本地磁盘 + (如果配了 S3)上传 S3 + 在 DB 登记一条 Resource 记录。Day 03 的 WriteFileTool 就是把活委托给这个 resource_manager.write_file
write_file 的"双写 + 登记":一次调用,三件事 WriteFileTool 写 report.txt FileManager .write_file() ① 写本地磁盘(隔离目录) ② 若配 S3 → 上传 S3 ③ DB 登记 Resource 记录 工具只说"写这个文件",落盘/上云/登记的细节全被 FileManager 封装——工具不用关心存储在哪。
一次 write_file:写本地 + (可选)上传 S3 + DB 登记,三件事一起做。
📝 输入→发生了什么 智能体调用 WriteFile(file_name="report.txt", content="季度总结…")
• 本地:写到 .../resources/agent_7/run_42/report.txt(隔离路径,见 L03)
• 云:若 storage_type==S3,同一份内容上传到 S3
• DB:插入一条 Resource(path=..., channel="OUTPUT", agent_id=7, ...)
下次要读,就用 read_filefile_manager.py:94)按同样的隔离路径取回。
🧳 第一人称:假如你是那份 report.txt 「我是一份刚被 WriteFileTool 生出来的 report.txt。FileManager 先把我安置在 agent_7/run_42/ 这个专属文件夹(别的智能体进不来);接着发现平台配了 S3,又把我复制了一份传上云(这样别的容器也能看到我);然后在公司档案里给我登记了户口——一条 Resource 记录,写明我住哪、我是"产出(OUTPUT)"。如果主人后来想让智能体"查阅"我,我还会被切块、做成索引卡灌进向量库(L05),并在目录清单里留个名(L06)。从出生到可被检索,我一路都有人管。」

👶 小白:既然最后要传到 S3 云上,为什么还先在本地磁盘写一份?直接传云不就省事了?

👨‍🏫 老师:两份各有用。本地那份"就近、快、不强依赖网络",写完立刻能读;S3 那份负责"跨容器共享"——回忆 Day 04,backend 和 celery 是不同容器,本地磁盘互不相通,只有云上这份大家都看得到。而且 write_to_s3 会先在 DB 登记一条 Resource 记录,再看 storage_type 决定要不要真上传——本地开发没配 S3 时,只写本地照样能跑。

L03

路径按 agent 隔离

get_agent_write_resource_path 算出 agent + execution 专属的写入路径。

📝 同名文件,因隔离而不打架 智能体 A(id=7,run=42)和智能体 B(id=9,run=88)都写了个 output.csv
• A → resources/agent_7/run_42/output.csv
• B → resources/agent_9/run_88/output.csv
同名,但路径带了各自的 agent_id/execution_id 前缀,彼此不覆盖。去掉隔离,B 的文件就会把 A 的冲掉。
为什么路径要按 agent/execution 隔离? 平台同时跑很多智能体、每个智能体跑很多次。如果所有文件都堆在一个目录,会互相覆盖污染——A 智能体写的 report.txt 被 B 智能体的同名文件覆盖。路径按 agent_id + execution_id 隔离(每个运行有自己的目录),保证互不干扰。多租户/并发场景,"按身份隔离资源路径"是必须的。这和 OpenHands 的 workspace 按会话隔离、AutoGPT 用 execution_id 隔离一个道理。
生活类比(网盘世界观):公司网盘从来不是所有人共用一个根目录,而是"部门/项目/个人"层层建文件夹——市场部的《方案.pptx》永远盖不掉设计部的同名文件。agent_id/execution_id 就是这套文件夹层级。
L04

Resource 登记表

Resource 模型(models/resource.py:6)登记每个文件:

class Resource(DBBaseModel):
    storage_type = Column(String)   # FILESERVER / S3
    path = Column(String)
    channel = Column(String)        # INPUT(用户给的)/ OUTPUT(智能体产的)
    agent_id / agent_execution_id
    summary = Column(String)
读法:每个文件在 DB 有一条记录:存在哪(storage_type/path)、是输入还是输出(channel)、属于哪个智能体/运行、内容摘要。channel 区分 INPUT/OUTPUT 很实用——摘要时只汇总输入资源(L06)。
L05

文件灌向量库(RAG 入库)

ResourceManager.save_document_to_vector_storeresource_manager/resource_manager.py:72)用 LlamaIndex 解析文件、灌进向量库:

for docs in documents:
    docs.metadata["agent_id"] = str(self.agent_id)     # 打标签
    docs.metadata["resource_id"] = resource_id
vector_store = LlamaVectorStoreFactory(...).get_vector_store()  # 默认 Redis
index = VectorStoreIndex.from_documents(documents, storage_context=...)
为什么要把文件灌进向量库? 你给智能体一个 100 页的 PDF,不可能整个塞进 LLM 上下文。把 PDF 切块、灌进向量库(Day 13 的 RAG)——智能体需要时按语义检索出相关的几段,而不是读全文。打上 agent_id 标签,检索时只召回"本智能体的文件"(隔离)。这就是"给智能体挂知识库"——它能"查阅"你给的资料,而非死记硬背。用 LlamaIndex(文档解析 + RAG 框架)做解析和切块。
L06

资源摘要

ResourceSummarizer.generate_agent_summaryresource_manager/resource_summary.py:50)把该智能体所有 INPUT 资源的名字拼起来,写进 AgentConfigurationresource_summary 键。LlamaDocumentSummary 用 LLM 生成单个文档的内容摘要。

摘要有什么用? 让智能体在提示里知道"我手头有哪些文件、大概讲什么"——而不必把整个文件塞进上下文。比如提示里加一句"你有这些资料:竞品分析.pdf、财报.csv",智能体就知道"需要竞品数据时去查那个 PDF"。摘要是"文件的目录/索引"——让智能体知道有什么可查,具体内容再用 RAG 检索(L05)。回忆 Day 08 提示里的 resource_summary 就来自这里。这也是为什么上传文件后会触发后台 summarize_resource 任务(Day 17)。
生活类比(网盘世界观):摘要就是网盘文件夹里置顶的那份《目录清单.md》——新人进项目组不用把几十个文件挨个打开,扫一眼清单就知道"有什么资料、要用时去翻哪份"。
一句话复述:摘要管"知道有什么",RAG 检索管"需要时取出来"——清单在手,用时才翻。
L07

存储可插拔

write_to_s3if resource.storage_type == StorageType.S3.value: S3Helper().upload_file(...)——存储后端(FILE/S3)由配置的 StorageType 枚举决定。

⚡ 错误驱动:生产环境如果只写本地磁盘,会出什么事故? 回忆 Day 04:生产部署里 backend 和 celery 是两个独立容器。设想只写本地:celery worker(容器 A)执行工具、把 report.txt 写进了 A 的磁盘;用户在 GUI 点下载,请求打到 backend(容器 B)——B 的磁盘上根本没有这个文件,404,"明明生成了却下载不到"。容器一销毁文件还会彻底丢。S3 是所有容器都能访问的"公共网盘",写到 S3 才算大家都能看到。这就是存储必须可切换成 S3 的根本原因。
为什么存储要可插拔? 本地开发用文件系统(简单);生产/多实例用 S3(多个 backend/celery 容器共享文件——本地磁盘各容器不通,S3 才行)。StorageType 枚举 + if 分支切换——改配置就换存储后端,业务代码不变。和 AutoGPT 的 local/S3、Day 10 的 LLM 工厂一个思路——面向接口、可插拔。LlamaVectorStoreFactoryllama_vector_store_factory.py)同理,给文件 RAG 选 Pinecone/Redis/Chroma/Qdrant。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 什么是资源?资源管理要解决哪四件事?
  • FileManager 的"双写"是什么?
  • 为什么文件路径要按 agent/execution 隔离?
  • 为什么要把文件灌进向量库?摘要有什么用?
  • 存储为什么可插拔(FILE/S3)?

✋ 动手

P=superagi/resource_manager
sed -n '35,64p' file_manager.py               # 双写
sed -n '72,109p' resource_manager.py          # 灌向量库
grep -n 'def generate_agent_summary' resource_summary.py
sed -n '6,33p' ../models/resource.py          # Resource 表
明天预告 · Day 15(第3周收官)数据模型——系统地看 35 个 DB 模型的关键几个:DBBaseModel、三层租户、AgentConfiguration、AgentExecution/Feed、Workflow、Tool/Toolkit、记忆相关模型。数据模型就是整个系统的骨架。
← Day 13 向量记忆 Day 15 · 数据模型 →