如果一个 Agent 工具只返回下面的结果,你的下一步会怎么写?
{"success": true, "url": "https://files.example.com/result.png"}
下载,再继续?也许够做演示,但它没告诉你:这张图是哪一版、适合放在哪里、隔天还拿不拿得到。example.com 只是示意地址,不是真实资源。
这次想讨论的不是“让 Agent 更聪明”,而是一个容易被 UI 流畅度掩盖的接口问题:产物身份不应该与访问地址绑定。
入口在合并,文件仍要交接
Adobe 9 月 2 日发布的 Adobe for Slack,让用户在对话中调用创作与文档工具,并利用相关上下文继续编辑。官方也保留了进入专业应用做更细控制的路径。公告
这对减少窗口切换有意义。但作为下游系统开发者,我们还得处理另一个问题:A 工具产出的文件,B 工具究竟消费了哪一份?下面是通用设计,不是对 Adobe 内部实现的判断。
设想一个内容流水线:生成源海报 → 导出横幅与竖版 → 放入文档 → 预填平台。链接、文件版本和平台资源 ID 如果都叫 url,最后一层出问题时,很难知道该重试哪一步。

持久化引用,临时解析地址
我会先区分两个类型:
type ArtifactRef = {
artifactId: string;
revision: string; // 不可变内容版本
mediaType: string;
sha256: string; // 对实际产物字节计算
bytes: number;
};
type AccessGrant = {
url: string;
expiresAt?: string;
resolvedArtifactId: string;
resolvedRevision: string;
};
ArtifactRef 可以进任务状态;AccessGrant 由有权限的执行器临用时取得。不要让模型编签名、拼下载地址,或把可访问地址持久化成唯一索引。
这不是强迫所有系统用某家对象存储。S3 的两个规则只是很好的例子:开启版本控制后,同一 key 可以有不同 version ID;预签名链接的有效性还受底层凭证生命周期影响。版本机制、链接时效
因此,cover.png 可以只是定位名,r2 才是交付选择。后续解析应该坚持 r2:找不到就报错,不能偷偷回落到最新版。
临时签名链接可能携带访问能力,记录日志时也应脱敏。让执行器取得必要权限,不等于把所有链接贴进模型上下文。
把消费写成一次有结果的接收
下面是 TypeScript 接口示意,刻意把外部依赖留在适配器中。downloadAndInspect 应由受控代码完成实际下载、计算摘要、检测格式;绝不能只是复述工具给出的元数据。
type Observation = { sha256: string; bytes: number; mediaType: string };
type Receipt = { artifactId: string; revision: string; checkedAt: string };
async function receive(
ref: ArtifactRef,
resolve: (ref: ArtifactRef) => Promise<AccessGrant>,
downloadAndInspect: (url: string) => Promise<Observation>
): Promise<Receipt> {
const grant = await resolve(ref);
if (grant.resolvedArtifactId !== ref.artifactId ||
grant.resolvedRevision !== ref.revision) {
throw new Error("WRONG_VERSION");
}
const got = await downloadAndInspect(grant.url);
if (got.sha256 !== ref.sha256 || got.bytes !== ref.bytes) {
throw new Error("BYTES_MISMATCH");
}
if (got.mediaType !== ref.mediaType) {
throw new Error("FORMAT_MISMATCH");
}
return {
artifactId: ref.artifactId,
revision: ref.revision,
checkedAt: new Date().toISOString()
};
}
这段函数没有自动重试,也没有把失败转换成“换个最新版试试”。它的返回值只表示该接收检查通过,不代表图片没有错字、PDF 事实正确,更不代表有权公开。
上线还需要可信清单、身份鉴别、下载目标限制、大小上限和文件安全检查。不能允许不可信模型任意指定内部网络 URL,再让服务端照单下载。
一份源稿,几份导出,不能共用一个身份
横图不是竖图缩个尺寸那么简单。裁切会改变主体位置,字幕可能掉到边界之外;导出 PDF 时字体和分页也会变化。
所以源稿和派生件分别存,并记录派生输入:
{
"artifactId": "poster-portrait",
"revision": "r5",
"sourceRefs": [{"artifactId": "master-artwork", "revision": "r4"}],
"transform": {"name": "portrait-layout", "revision": "layout-2"},
"constraints": {"width": 1080, "height": 1440}
}
这里的数值是示例规格,不是某平台的统一要求。实际发布前要核对目标平台当日要求。

派生关系有两个用处。第一,竖版裁坏时只重做这一支。第二,源稿换版后能找出受影响产物,而不是把所有文件都改名叫“新版”。历史已交付包应保留自己的选版,不随当前源稿漂移。
不要急着追求通用 DAG 引擎。先把每份导出“来自哪一版、用了哪个转换配置”记清楚,简单列表也能解决很多接力问题。
重试的是故障层,不是整场对话
下面这些情况看起来都是“文件没拿到”,处理却完全不同:
- 链接过期:核验权限后重新解析同一个产物版本。
- 指定版不存在:报告缺件,不生成替代品冒充原件。
- hash 不匹配:隔离结果,排查覆盖、传输或定位错误。
- 规格不匹配:重新导出对应派生件。
- 平台上传超时:先查询或回读已有上传结果,未确认前不要重复提交。
同时设有限重试和总超时,别让“重新签一个地址”变成无限循环。日志记录 artifactId、revision、阶段、错误码与耗时,不必记完整敏感 URL。
平台压缩图片后,字节通常可能变化。原文件传输校验与平台页面验收应分开:前者认内容摘要,后者认资源标识以及真实页面上的可用内容。不能要求重新编码后的 CDN 文件仍保留原 hash。
最小测试集比漂亮的成功路径更重要
至少覆盖这些输入:同名不同版、链接到期、缺一件、多一件旧稿、下载半截、MIME 与实际内容不一致、正确文件被裁坏、上传成功但响应丢失。
其中“正确文件被裁坏”必须落到内容验收:原图字节一致,依然可能不是正确的视觉交付。程序检查不是替代视觉检查的借口。
你可以先落地一个很小的闭环:生产端输出清单,消费端比对版本与字节,平台端保存资源 ID 和页面回读结果。不要只统计生成工具成功率,也统计缺件率、错版率和失败后的重做范围。
AI 员工要接手的是工作,不只是链接
这类文件接力也是我们做 Tipkay 时关注的问题。面向小微企业、一人公司和小团队的岗位助手,可以各自带着经验、Skill、MCP 和流程,把内容继续推进到配图、排版、文件与发布准备。
本文的类型和接收函数是通用实现建议,不声称 Tipkay 已采用这套内部结构。产品定位之外,工程判断仍应独立成立:工具输出的文件必须能被下一步明确接收。
一个人的生意,也能有一支专业团队。对开发者来说,可以先把这句话落实成一个接口约束:success 后面,不能只跟着一个来历不明、明天可能失效的 URL。