ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Agno 媒体存储(Media Storage)实战指南:把图片与生成文件卸载到本地磁盘、S3 与 GCS

Agno 媒体存储(Media Storage)实战指南:把图片与生成文件卸载到本地磁盘、S3 与 GCS Agno 媒体存储Media Storage实战指南把图片与生成文件卸载到本地磁盘、S3 与 GCS【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno本篇技术指南以cookbook/06_storage目录下的测试日志TEST_LOG.md及其配套的 7 个媒体存储示例为核心系统讲解 Agno 中的媒体卸载media offload机制如何让 Agent、Team、Workflow 把图片、音频、视频与生成的文件写入外部存储而数据库中只保留一个轻量MediaReference指针。读完本文你将掌握LocalMediaStorage、S3MediaStorage、GCSMediaStorage三种后端的完整配置方式理解persist_remote_urls的行为差异、多轮会话中媒体引用的重新签名机制、delete_mediaTrue的删除语义以及如何验证卸载确实生效。为什么需要媒体存储避免 base64 撑爆数据库在默认形态下Agno 的 run 记录把媒体内容以内联 base64 形式存在数据库里。当对话涉及多张图片、长音频或生成的 CSV 文件时run 行会迅速膨胀到数 MB 甚至更大会话列表读取、备份与迁移都会变慢。cookbook/06_storage目录在普通数据库会话存储见 01_persistent_session_storage.py、02_session_summary.py、03_chat_history.py之外专门演示了媒体存储的完整链路正如 README.md 所述把媒体内容图片、音频、视频、文件卸载到外部存储数据库里只保留轻量引用。该目录还明确指出了直接存预签名 URL 的缺陷——预签名 URL 会过期因此引用的核心设计是绝不存字节、绝不存会过期的预签名 URL。核心概念MediaStorage 抽象接口与 MediaReference 引用媒体存储建立在两个核心类型之上它们的实现位于 base.py 与 reference.py。MediaStorage 抽象基类MediaStorage以及对应的AsyncMediaStorage定义了后端必须实现的五个抽象方法upload(media_id, content, *, mime_type, filename, metadata) - storage_key上传字节返回存储键download(storage_key) - bytes按存储键下载字节get_url(storage_key, *, expires_in) - Optional[str]生成访问链接expires_inNone时使用后端配置的默认过期时间返回None表示该后端无法签名 URL调用方应改为流式传输字节delete(storage_key) - bool删除对象幂等——返回True表示对象已不存在无论是否由本次调用删除False表示删除失败exists(storage_key) - bool检查对象是否存在。基类还提供类属性backend_name持久化在每条引用上的后端标识如s3、bucket写入的容器桶或本地基础路径、region和persist_remote_urls为True时以裸 URL 形式到达的媒体会被抓取存储而不是保留为链接。MediaReference 引用模型卸载之后数据库中持久化的不是内容而是 MediaReferencemedia_id与storage_key存储键同时充当反序列化的判别器session_id上传该对象的会话storage_backend后端标识s3、gcs、local等bucket、region容器信息url预签名或公开 URLmime_type、filename、size、content_hashSHA-256、media_typeimage、audio、video、file与metadata。三种后端与测试验证结果以下是测试日志TEST_LOG.md记录的验证结果概览示例脚本后端状态核心验证点05_media_storage_local.pyLocalMediaStoragePASS2 个文件卸载到./tmp/media_storageURL-only 媒体默认跳过persist_remote_urlsTrue时下载并存储06_media_storage_s3.pyS3MediaStoragePASS2 个内容寻址对象上传到agno/media/各 65129 字节与源哈希一致run 记录持有media_reference而非 base6407_media_storage_multiturn.pyS3MediaStoragePASS第二轮请求携带重新签名的 S3 URL——0 base64、0 次download()调用run 行仅 2897 字节08_media_storage_gcs.pyGCSMediaStoragePASS对象上传到agno/media/ADC 无法签名 URL引用中无 URL由 AgentOS 流式传输字节09_media_storage_delete.pyS3MediaStorageLIVEPASS无标志删除会话时对象保留delete_mediaTrue只清扫本会话自己的对象10_media_storage_workflow.pyS3MediaStorageLIVEPASSWorkflow 同时卸载run(images...)与步骤产出run 行仅持有 MediaReference对象 65129 字节11_media_storage_file_generation.pyS3MediaStorageLIVEPASS生成的 CSV 卸载后读回 93 字节且首行正确get_url返回真实预签名 S3 URL测试日志中 01、02、03 三个会话存储示例仍标记为 PENDING待补测试04 会话摘要限制示例则对应last_n_runs/conversation_limit参数。LocalMediaStorage本地文件系统卸载适合开发05_media_storage_local.py 演示开发环境最轻量的方案媒体写入文件系统数据库中只保留 MediaReference。其文档字符串明确建议开发用 LocalMediaStorage生产环境改用 S3MediaStorage 或 GCSMediaStorage。关键代码形态from agno.agent import Agent from agno.db.sqlite import SqliteDb from agno.media.storage import LocalMediaStorage from agno.models.openai import OpenAIResponses storage LocalMediaStorage(base_path./tmp/media_storage) agent Agent( modelOpenAIResponses(idgpt-5.5), media_storagestorage, dbSqliteDb(db_filetmp/data.db), )运行前先用httpx.get(IMAGE_URL, follow_redirectsTrue).content把图片下载成字节再通过Image(contentimage_bytes, formatjpeg, mime_typeimage/jpeg)传入——mime_type会决定存储对象的文件扩展名与 Content-Type。测试结果证实内容媒体被正确卸载到./tmp/media_storage共 2 个文件。默认行为URL-only 媒体被跳过三种后端的默认行为一致只传 URL 的媒体在卸载时会被跳过。这避免了对外部链接的重复抓取与存储符合只托管自己产生的内容的预期。persist_remote_urlsTrue自动抓取 URL 内容当希望连 URL-only 媒体也一并托管时在后端构造时加上该标志storage_with_persist LocalMediaStorage( base_path./tmp/media_storage, persist_remote_urlsTrue, )测试确认默认情况下 URL-only 图片不落盘而persist_remote_urlsTrue时会被自动下载并存储。S3MediaStorage对象存储卸载生产首选06_media_storage_s3.py 演示生产环境的核心方案。前置要求uv pip install agno[s3] # boto3 aioboto3环境变量AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_REGION以及必须设置为自己拥有的桶的环境变量MEDIA_S3_BUCKET。import os from agno.media.storage import S3MediaStorage bucket os.getenv(MEDIA_S3_BUCKET) if not bucket: raise ValueError(MEDIA_S3_BUCKET must be set to an S3 bucket you own) storage S3MediaStorage( bucketbucket, regionos.getenv(AWS_REGION), # 未设置时回退到 AWS_DEFAULT_REGION 或 ~/.aws/config prefixagno/media/, presigned_url_expiry3600, # 1 小时 ) agent Agent( modelOpenAIResponses(idgpt-5.5), media_storagestorage, dbSqliteDb(db_filetmp/data.db), )示例代码中的注释特别提醒了一个容易漏掉的失败模式如果桶不是你拥有的每次上传都会失败而卸载会静默回退到内联 base64——run 仍然成功失败很难被发现。因此代码在启动时就校验MEDIA_S3_BUCKET。测试结果OpenAIResponses(idgpt-5.5)真实 AWS S3三次视觉响应全部成功返回2 个内容寻址对象上传到agno/media/每个 65129 字节与源哈希一致URL-only 媒体默认被跳过持久化的 run 记录持有media_reference而非 base64。注意 S3 后端 never a pre-signed URL 的设计——持久化时不写入预签名 URL会过期需要读取时再现场签名。多轮会话媒体引用如何在第二轮被重新签名07_media_storage_multiturn.py 验证了最重要的场景之一第一轮上传图片第二轮不再重新附加图片模型仍能读到它。关键配置差异agent Agent( modelOpenAIResponses( idgpt-5.5, storeFalse # 保持历史在客户端侧见文件 docstring ), media_storagestorage, dbSqliteDb(db_filetmp/multiturn.db), session_idmultiturn-session, add_history_to_contextTrue, )storeFalse的作用OpenAIResponses 默认会通过previous_response_id把轮次串起来那样第二轮根本不会发送任何图片。设置为False后历史保持在客户端侧第二轮从数据库中重新水合引用。测试日志记录的精细数据非常具有参考价值两轮都正确回答同一张图片无卸载失败警告第一轮上传 1 个对象113255 字节存储键为会话级multiturn-session-media_id-hash.jpg向模型发送了 151008 个 base64 字符对出站请求埋点发现第二轮只携带 1 个input_image内容是一个新鲜的预签名 S3 URL——0 个 base64 字符、0 次download()调用模型直接从 S3 读取对象run 行始终保持在 2897 字节携带media_reference且无 base64。这就是核心机制存储在库里的引用在读取时重新签名字节永远不经过数据库往返。GCSMediaStorageGoogle Cloud Storage 卸载与 ADC 签名限制08_media_storage_gcs.py 演示 GCS 后端uv pip install agno[gcs] # google-cloud-storage认证方式gcloud auth application-default login或通过credentials_path传入服务账号 JSON并设置MEDIA_GCS_BUCKET与可选的GCP_PROJECT。from agno.media.storage import GCSMediaStorage storage GCSMediaStorage( bucketbucket, projectos.getenv(GCP_PROJECT), prefixagno/media/, presigned_url_expiry3600, )测试结果揭示了一个重要的平台差异URL 签名需要服务账号私钥因此在 application-default credentialsADC下无法签名。结果是引用中不存储 URLAgentOS 改为通过/media路由流式传输字节。这一点与 S3 形成对比——S3 后端能用真实凭据生成预签名 URL。删除语义delete_mediaTrue 与孤儿对象09_media_storage_delete.py 演示一个容易踩坑的生命周期问题默认情况下卸载的媒体比会话活得更久。因为行中的引用是哪个对象属于哪个会话的唯一记录——先删行就再也找不到对象了。正确顺序由标志保证传入delete_mediaTrue实现会先读键、再删行、最后清扫对象。该标志同时存在于 Agent、Team、Workflow 的同步与异步版本。测试采用 LIVE 模式两个会话把同一张图片卸载到同一个真实 S3 桶前缀agno/media_delete/两次运行后桶中有 2 个对象不带标志删除第一个会话keeps-media行删了2 个对象原样保留产生 1 个孤儿对象带delete_mediaTrue删除第二个会话sweeps-media只清扫了本会话自己的对象桶中剩 1 个示例打印出剩余键即那个有意的孤儿。agent.delete_session(session_idkeeps-media) # 行没了对象留下 agent.delete_session(session_idsweeps-media, delete_mediaTrue) # 先读键再删行后清扫Workflow 中的媒体卸载run 输入与步骤产出10_media_storage_workflow.py 验证 Workflow 场景下媒体来自两个位置且都被卸载到 S3、数据库中只留 MediaReference传给workflow.run(images...)的媒体某个步骤的 agent 产出的媒体。workflow Workflow( nameImage Description Workflow, dbSqliteDb(db_filetmp/workflow_media.db), media_storagestorage, store_mediaTrue, steps[ Step(namedescribe, agentdescriber), Step(namesummarize, agentsummarizer), ], )一个关键保障每个步骤运行前步骤输入会被重新水合rehydrate因此下游步骤拿到的是字节而不是空指针——即使上游已经卸载。测试结果run 行携带 MediaReferenceinline bytes: None桶中对象 65129 字节。示例脚本还展示了如何遍历run.images与run.step_results来检查每条媒体是否真的被卸载打印storage_key、bucket、size、inline bytes。生成文件的卸载与读回get_content_bytes / get_url11_media_storage_file_generation.py 展示与前面附加媒体不同的场景agent 自己生成的文件。FileGenerationTools会把文件字节放在 run 上媒体存储把它们当作附件一样处理字节进 S3数据库行只保留 MediaReference。生成的文件名与 mime 类型会写到引用上不下载就能知道对象是什么。agent Agent( modelOpenAIResponses(idgpt-5.5), dbSqliteDb(db_filetmp/generated_media.db), media_storagestorage, store_mediaTrue, # save_filesFalse 让字节保留在 run 上——这正是媒体存储要卸载的东西 tools[FileGenerationTools(allTrue, save_filesFalse)], )save_filesFalse的含义值得强调该工具也可以写本地目录但那只是运行 agent 的机器上的一份副本——让生成文件可随时随取的是媒体存储。读回的方式均有异步变体data file.get_content_bytes(storagestorage) # 拿字节 url file.get_url(storagestorage) # 拿新签名的链接None 表示后端无法签名测试结果run 行只保留引用inline bytes: None读回得到 93 字节且首行内容正确get_url返回真实的预签名 S3 URL。示例还演示了把读回的字节保存到本地tmp/目录。另外一个细节run() 返回的对象上媒体仍携带字节——卸载作用于副本所以调用方拿到的返回结果不会被掏空而数据库行里才是指针。升级注意启用媒体存储是单向门README.md 给出了一个必须全员周知的运维警告启用媒体存储是一扇单向门。没有 schema 变更——不加表、不加列、不迁移——但现有列中媒体的形态改变了卸载后的图片携带media_reference而没有content早于该功能发布的读取方会校验媒体必须有url、filepath或content三者之一而卸载后的图片三者皆无。它不是跳过这张图而是抛异常——一条卸载过的行就会让整个会话列表的get_sessions()失败包括升级前写入的干净会话新代码读旧行没有问题所以升级方向安全、回滚方向不安全。因此正确的发布顺序是先把新版本部署到全量环境再开启media_storage一旦行里开始携带引用退回旧版本后这些媒体将不可读直到回到新版本。测试要点与验收清单结合 TEST_LOG.md 的验证方法复现或自行测试时可按以下清单验收本地后端确认字节文件出现在base_path如./tmp/media_storage数据库行中media_reference非空且无内联 contentS3 后端确认对象数量、字节数与源哈希一致内容寻址URL-only 媒体默认 0 对象persist_remote_urlsTrue后出现对象多轮复用对出站请求埋点第二轮应携带预签名 URL 而非 base64且无download()调用GCS 后端确认引用中storage_backend gcs且 ADC 下url为空、走流式字节路径删除分别用不带标志与带delete_mediaTrue删除两个会话核对桶中剩余对象数Workflow遍历run.images与step_results的媒体确认均持有引用生成文件用get_content_bytes(storage...)读回字节并校验首行用get_url(storage...)验证签名链接。总结Agno 的媒体存储把数据库存指针、对象存储存字节这一模式做成了 Agent/Team/Workflow 的统一能力以MediaStorage抽象接口base.py对接本地磁盘、S3、GCS 等后端以 MediaReference 保持行的轻量读取时按需重新签名或流式传输配合persist_remote_urls控制 URL 媒体的托管策略、delete_mediaTrue管理对象生命周期。通过 06_storage 目录的示例与测试日志开发者可以按先本地、后 S3/GCS的路径平滑落地并牢记启用该功能是单向门这一发布纪律。【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表