附加和下载文件
文件交付包含两个结果:任务能在云端沙箱中读取输入材料,应用能下载最终产物。Files API 保存文件对象,会话资源负责把输入文件挂载到沙箱;沙箱中的任意文件不会自动成为可下载的产物。
以 CSV 分析任务为例,约定输入路径 /data.csv 与报告文件 report.md,再按任务阶段操作:
- 上传 CSV,保存源文件的
file_id。 - 创建会话并挂载文件,指定沙箱内的读取路径。
- 向会话发送分析要求,让智能体读取数据并把报告写入
/mnt/session/outputs/report.md。 - 本轮执行结束后,列出并下载会话文件,确认预期产物已经收集。
任务中途收到新的数据,可补充会话文件。报告下载并验收后,再清理文件对象。会话创建和消息发送见会话。
同一份数据上传、挂载和生成输出时对应不同文件对象。保留策略需要区分这些对象,不能仅按沙箱路径判断:
| 文件对象 | 如何产生 | scope_id |
沙箱中删除路径 | 归档会话 | 删除会话 |
|---|---|---|---|---|---|
| 上传源文件 | POST /v1/files |
无 | 不适用 | 不受影响 | 不受影响 |
| 会话挂载副本 | 将源文件加入 resources |
会话 ID | 不影响文件对象 | 保留 | 删除 |
| 会话输出文件 | 智能体写入 /mnt/session/outputs/ 后被收集 |
会话 ID | 不影响文件对象 | 保留 | 删除 |
准备任务输入
先确认输入格式能由所选运行时、模型或工具处理,再上传材料并确定读取路径。需要分析多份数据时,把每个文件及其用途写入任务要求,避免智能体自行猜测。
判断材料能否处理
Files API 将文件按原始字节保存,不限制扩展名。智能体是否能理解或处理某种格式取决于所选运行时、模型和已安装工具。常见输入包括:
- 源代码(
.py、.js、.ts、.go、.rs等) - 数据文件(
.csv、.json、.xml、.yaml) - 文档(
.txt、.md) - 归档文件(
.zip、.tar.gz) - 图片、PDF 和其他二进制文件
上传源材料
完成接入准备后,上传本地 data.csv,保存返回的 file_id,供会话引用。此时文件仍只是源材料,尚未进入任何会话。以下 SDK 示例沿用SDK 客户端初始化中的 client。
cURL
file=$(curl --request POST "$AGENTAPI_BASE_URL/v1/files" \
--header "x-api-key: $AGENTAPI_API_KEY" \
--form "file=@data.csv")
file_id=$(jq -er '.id' <<<"${file}")
printf 'File ID: %s\n' "${file_id}"
Python
file = client.beta.files.upload(file=Path("data.csv"))
print(f"File ID: {file.id}")
TypeScript
const file = await client.beta.files.upload({
file: await toFile(readFile("data.csv"), "data.csv", { type: "text/csv" }),
});
console.log(`File ID: ${file.id}`);
C#
await using var stream = File.OpenRead(csvPath);
var file = await client.Beta.Files.Upload(new() { File = stream });
Console.WriteLine($"File ID: {file.ID}");
Go
csvFile, err := os.Open("data.csv")
if err != nil {
panic(err)
}
defer csvFile.Close()
file, err := client.Beta.Files.Upload(ctx, anthropic.BetaFileUploadParams{
File: csvFile,
})
if err != nil {
panic(err)
}
fmt.Printf("File ID: %s\n", file.ID)
Java
var file = client.beta().files().upload(
FileUploadParams.builder().file(dataCsv).build()
);
IO.println("File ID: " + file.id());
PHP
$file = $client->beta->files->upload(
file: FileParam::fromResource(fopen($csvPath, 'r'), filename: 'data.csv', contentType: 'text/csv'),
);
echo "File ID: {$file->id}\n";
Ruby
file = client.beta.files.upload(file: Pathname(csv_path))
puts "File ID: #{file.id}"
约定会话读取路径
创建智能体和云端环境后,用会话的 resources 引用源材料。以下示例把 CSV 挂载到 /data.csv;后续任务要求应使用同一路径,而不是本地上传路径。
说明
可省略
mount_path,此时读取路径为/mnt/session/uploads/<file_id>。显式指定路径便于在任务要求中定位材料。
cURL
session=$(
jq -n \
--arg agent_id "${agent_id}" \
--arg environment_id "${environment_id}" \
--arg file_id "${file_id}" \
'{
agent: $agent_id,
environment_id: $environment_id,
resources: [
{
type: "file",
file_id: $file_id,
mount_path: "/data.csv"
}
]
}' | curl --fail-with-body -sS -H "x-api-key: $AGENTAPI_API_KEY" "$AGENTAPI_BASE_URL/v1/sessions" -H "content-type: application/json" --data @-
)
session_id=$(jq -er '.id' <<<"${session}")
Python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
resources=[
{
"type": "file",
"file_id": file.id,
"mount_path": "/data.csv",
},
],
)
TypeScript
const session = await client.beta.sessions.create({
agent: agent.id,
environment_id: environment.id,
resources: [
{
type: "file",
file_id: file.id,
mount_path: "/data.csv",
},
],
});
C#
var session = await client.Beta.Sessions.Create(new()
{
Agent = agent.ID,
EnvironmentID = environment.ID,
Resources =
[
new BetaManagedAgentsFileResourceParams
{
Type = "file",
FileID = file.ID,
MountPath = "/data.csv",
},
],
});
Go
session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
Agent: anthropic.BetaSessionNewParamsAgentUnion{
OfString: anthropic.String(agent.ID),
},
EnvironmentID: environment.ID,
Resources: []anthropic.BetaSessionNewParamsResourceUnion{{
OfFile: &anthropic.BetaManagedAgentsFileResourceParams{
Type: anthropic.BetaManagedAgentsFileResourceParamsTypeFile,
FileID: file.ID,
MountPath: anthropic.String("/data.csv"),
},
}},
})
if err != nil {
panic(err)
}
Java
var session = client.beta().sessions().create(
SessionCreateParams.builder()
.agent(agent.id())
.environmentId(environment.id())
.addResource(
BetaManagedAgentsFileResourceParams.builder()
.type(BetaManagedAgentsFileResourceParams.Type.FILE)
.fileId(file.id())
.mountPath("/data.csv")
.build()
)
.build()
);
PHP
$session = $client->beta->sessions->create(
agent: $agent->id,
environmentID: $environment->id,
resources: [
BetaManagedAgentsFileResourceParams::with(
type: 'file',
fileID: $file->id,
mountPath: '/data.csv',
),
],
);
Ruby
session = client.beta.sessions.create(
agent: agent.id,
environment_id: environment.id,
resources: [
{
type: "file",
file_id: file.id,
mount_path: "/data.csv"
}
]
)
使用上述 mount_path,智能体将在 /data.csv 读取该文件。mount_path 是沙箱内的绝对路径,不会自动拼接 uploads 目录;省略时才使用 /mnt/session/uploads/<file_id>。请参阅文件路径。
AgentAPI 会为会话中的只读副本创建新的 file_id。删除原始上传文件不会影响已经挂载到会话中的副本。
区分输入路径与输出路径
说明
挂载到沙箱中的文件是只读副本。智能体可以读取它们,但无法修改原始上传的文件。若要使用修改后的版本,智能体会将其写入沙箱内的新路径。
mount_path是沙箱内的绝对路径:/data.csv会挂载到/data.csv- 如果省略
mount_path,文件将被放置在/mnt/session/uploads/<file_id> - 父目录会自动创建
- 路径应为绝对路径(以
/开头) - 智能体写入
/mnt/session/outputs/的文件可通过 Files API 获取,且限定于该会话范围内;请参阅列出和下载会话文件
为一项任务准备多份材料
例如同时分析原始数据和参考数据时,为每份材料添加一个 resources 条目,并分别指定读取路径:
cURL
"resources": [
{ "type": "file", "file_id": "file_abc123", "mount_path": "/data.csv" },
{ "type": "file", "file_id": "file_def456", "mount_path": "/config.json" },
{ "type": "file", "file_id": "file_ghi789", "mount_path": "/src/main.py" }
]
Python
resources = [
{"type": "file", "file_id": "file_abc123", "mount_path": "/data.csv"},
{"type": "file", "file_id": "file_def456", "mount_path": "/config.json"},
{"type": "file", "file_id": "file_ghi789", "mount_path": "/src/main.py"},
]
TypeScript
resources: [
{ type: "file", file_id: "file_abc123", mount_path: "/data.csv" },
{ type: "file", file_id: "file_def456", mount_path: "/config.json" },
{ type: "file", file_id: "file_ghi789", mount_path: "/src/main.py" }
]
C#
using Anthropic.Models.Beta.Sessions;
var resources = new[]
{
new BetaManagedAgentsFileResourceParams { Type = BetaManagedAgentsFileResourceParamsType.File, FileID = "file_abc123", MountPath = "/data.csv" },
new BetaManagedAgentsFileResourceParams { Type = BetaManagedAgentsFileResourceParamsType.File, FileID = "file_def456", MountPath = "/config.json" },
new BetaManagedAgentsFileResourceParams { Type = BetaManagedAgentsFileResourceParamsType.File, FileID = "file_ghi789", MountPath = "/src/main.py" },
};
Go
resources := []anthropic.BetaSessionNewParamsResourceUnion{
{OfFile: &anthropic.BetaManagedAgentsFileResourceParams{Type: "file", FileID: "file_abc123", MountPath: anthropic.String("/data.csv")}},
{OfFile: &anthropic.BetaManagedAgentsFileResourceParams{Type: "file", FileID: "file_def456", MountPath: anthropic.String("/config.json")}},
{OfFile: &anthropic.BetaManagedAgentsFileResourceParams{Type: "file", FileID: "file_ghi789", MountPath: anthropic.String("/src/main.py")}},
}
Java
import com.anthropic.models.beta.sessions.*;
import java.util.List;
var resources = List.of(
BetaManagedAgentsFileResourceParams.builder()
.type(BetaManagedAgentsFileResourceParams.Type.FILE).fileId("file_abc123").mountPath("/data.csv").build(),
BetaManagedAgentsFileResourceParams.builder()
.type(BetaManagedAgentsFileResourceParams.Type.FILE).fileId("file_def456").mountPath("/config.json").build(),
BetaManagedAgentsFileResourceParams.builder()
.type(BetaManagedAgentsFileResourceParams.Type.FILE).fileId("file_ghi789").mountPath("/src/main.py").build()
);
PHP
$resources = [
['type' => 'file', 'fileID' => 'file_abc123', 'mountPath' => '/data.csv'],
['type' => 'file', 'fileID' => 'file_def456', 'mountPath' => '/config.json'],
['type' => 'file', 'fileID' => 'file_ghi789', 'mountPath' => '/src/main.py'],
];
Ruby
resources = [
{type: "file", file_id: "file_abc123", mount_path: "/data.csv"},
{type: "file", file_id: "file_def456", mount_path: "/config.json"},
{type: "file", file_id: "file_ghi789", mount_path: "/src/main.py"}
]
一个会话最多可以包含 500 个资源,文件、GitHub 仓库和记忆存储资源共同计数。
执行中补充或撤下材料
任务开始后新增材料,无需为了附加文件重新创建会话。先上传文件,再用会话资源 API 添加到现有会话,并在后续消息中说明新增路径和用途。资源响应中的 id 用于管理会话关联,与上传返回的 file_id 分别保存。
cURL
resource=$(
jq -n --arg file_id "${file_id}" '{type: "file", file_id: $file_id}' \
| curl --fail-with-body -sS -H "x-api-key: $AGENTAPI_API_KEY" \
"$AGENTAPI_BASE_URL/v1/sessions/${session_id}/resources" -H "content-type: application/json" --data @-
)
resource_id=$(jq -er '.id' <<<"${resource}")
printf '%s\n' "${resource_id}" # "sr_01ABC..."
Python
resource = client.beta.sessions.resources.add(
session.id,
type="file",
file_id=file.id,
)
print(resource.id) # "sr_01ABC..."
TypeScript
const resource = await client.beta.sessions.resources.add(session.id, {
type: "file",
file_id: file.id,
});
if (resource.type !== "file") {
throw new Error(`Unexpected resource type: ${resource.type}`);
}
console.log(resource.id); // "sr_01ABC..."
C#
var resource = await client.Beta.Sessions.Resources.Add(session.ID, new()
{
Type = "file",
FileID = file.ID,
});
Console.WriteLine(resource.ID); // "sr_01ABC..."
Go
resource, err := client.Beta.Sessions.Resources.Add(ctx, session.ID, anthropic.BetaSessionResourceAddParams{
BetaManagedAgentsFileResourceParams: anthropic.BetaManagedAgentsFileResourceParams{
Type: anthropic.BetaManagedAgentsFileResourceParamsTypeFile,
FileID: file.ID,
},
})
if err != nil {
panic(err)
}
fmt.Println(resource.ID) // "sr_01ABC..."
Java
var resource = client.beta().sessions().resources().add(
session.id(),
ResourceAddParams.builder()
.betaManagedAgentsFileResourceParams(
BetaManagedAgentsFileResourceParams.builder()
.type(BetaManagedAgentsFileResourceParams.Type.FILE)
.fileId(file.id())
.build()
)
.build()
);
IO.println(resource.id()); // "sr_01ABC..."
PHP
$resource = $client->beta->sessions->resources->add(
$session->id,
type: 'file',
fileID: $file->id,
);
echo "{$resource->id}\n"; // "sr_01ABC..."
Ruby
resource = client.beta.sessions.resources.add(
session.id,
type: "file",
file_id: file.id
)
puts resource.id # "sr_01ABC..."
材料不再作为会话输入时,先用 resources.list 查找对应资源,再以资源 ID 调用 resources.delete。文件对象的删除和保留规则见清理说明。
cURL
curl --fail-with-body -sS -H "x-api-key: $AGENTAPI_API_KEY" \
"$AGENTAPI_BASE_URL/v1/sessions/${session_id}/resources" \
| jq -r '.data[] | "\(.id) \(.type)"'
curl --fail-with-body -sS -H "x-api-key: $AGENTAPI_API_KEY" -X DELETE \
"$AGENTAPI_BASE_URL/v1/sessions/${session_id}/resources/${resource_id}" >/dev/null
Python
listed = client.beta.sessions.resources.list(session.id)
for entry in listed.data:
print(entry.id, entry.type)
client.beta.sessions.resources.delete(resource.id, session_id=session.id)
TypeScript
const listed = await client.beta.sessions.resources.list(session.id);
for (const entry of listed.data) {
if (entry.type !== "memory_store") {
console.log(entry.id, entry.type);
}
}
await client.beta.sessions.resources.delete(resource.id, {
session_id: session.id,
});
C#
var listed = await client.Beta.Sessions.Resources.List(session.ID);
await foreach (var entry in listed.Paginate())
{
var type = entry.Match<string>(repo => repo.Type, fileRes => fileRes.Type, memoryStore => memoryStore.Type);
Console.WriteLine($"{entry.ID} {type}");
}
await client.Beta.Sessions.Resources.Delete(resource.ID, new() { SessionID = session.ID });
Go
listed, err := client.Beta.Sessions.Resources.List(ctx, session.ID, anthropic.BetaSessionResourceListParams{})
if err != nil {
panic(err)
}
for _, entry := range listed.Data {
fmt.Println(entry.ID, entry.Type)
}
if _, err := client.Beta.Sessions.Resources.Delete(ctx, resource.ID, anthropic.BetaSessionResourceDeleteParams{
SessionID: session.ID,
}); err != nil {
panic(err)
}
Java
var listed = client.beta().sessions().resources().list(session.id());
for (var entry : listed.data()) {
if (entry.isFile()) {
var fileResource = entry.asFile();
IO.println(fileResource.id() + " " + fileResource.type());
} else if (entry.isGitHubRepository()) {
var repoResource = entry.asGitHubRepository();
IO.println(repoResource.id() + " " + repoResource.type());
}
}
client.beta().sessions().resources().delete(
resource.id(),
ResourceDeleteParams.builder().sessionId(session.id()).build()
);
PHP
$listed = $client->beta->sessions->resources->list($session->id);
foreach ($listed->data as $entry) {
echo "{$entry->id} {$entry->type}\n";
}
$client->beta->sessions->resources->delete($resource->id, sessionID: $session->id);
Ruby
listed = client.beta.sessions.resources.list(session.id)
listed.data.each { puts "#{it.id} #{it.type}" }
client.beta.sessions.resources.delete(resource.id, session_id: session.id)
验收与下载任务产物
任务要求应同时约定交付文件,例如分析报告、处理后的数据或补丁。智能体的最终回答与文件交付是两个检查项:收到完成消息后,还应查询会话文件,确认预期产物存在并下载检查。
Portal 文件列表支持按文件名或 ID 搜索。搜索去除首尾空白,按字面量包含关系匹配,并在页码分页前计算总数;可以同时使用 scope_id 限定 Session。对应接口为 GET /portal/v1/files?page_index=1&q=关键词,q 必须与 page_index 一起使用,公开的 GET /v1/files 不接受该参数。
智能体生成的交付物
云端环境默认提供文件交付指引:供用户下载的最终文件写入
/mnt/session/outputs/,临时文件和项目文件保留在各自的工作路径。普通会话与 Outcome 均适用,无需客户端额外拼接目录提示。自托管 worker 不使用这条云端指引,其文件不会通过云端输出目录自动收集。AgentAPI 在执行完成后收集输出目录中的文件。文件出现在会话列表中后才能下载;若尚未出现,稍后重新查询。持续缺失时,结合事件检查执行结果,并确认产物确实写入交付目录且未超过输出限额。
cURL
# 列出与会话关联的文件
curl --request GET "$AGENTAPI_BASE_URL/v1/files?scope_id=$SESSION_ID" \
--header "x-api-key: $AGENTAPI_API_KEY"
# 下载文件
curl --request GET "$AGENTAPI_BASE_URL/v1/files/$FILE_ID/content" \
--header "x-api-key: $AGENTAPI_API_KEY" \
--output output.txt
Python
# 列出与会话关联的文件
files = client.beta.files.list(scope_id="ses_abc123")
for file in files:
print(file.id, file.filename)
# 下载文件
content = client.beta.files.download(files.data[0].id)
content.write_to_file("output.txt")
TypeScript
import { writeFile } from "node:fs/promises";
// 列出与会话关联的文件
const files = await client.beta.files.list({
scope_id: "ses_abc123"
});
for (const file of files.data) {
console.log(file.id, file.filename);
}
// 下载文件
const content = await client.beta.files.download(files.data[0].id);
await writeFile("output.txt", new Uint8Array(await content.arrayBuffer()));
C#
// 列出与会话关联的文件
var files = await client.Beta.Files.List(new()
{
ScopeID = "ses_abc123",
});
// 下载文件
using var content = await client.Beta.Files.Download(files.Items[0].ID);
await using var output = File.Create("output.txt");
await (await content.ReadAsStream()).CopyToAsync(output);
Go
// 列出与会话关联的文件
files, err := client.Beta.Files.List(ctx, anthropic.BetaFileListParams{
ScopeID: anthropic.String("ses_abc123"),
})
if err != nil {
panic(err)
}
// 下载文件
resp, err := client.Beta.Files.Download(ctx, files.Data[0].ID)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer out.Close()
if _, err := io.Copy(out, resp.Body); err != nil {
panic(err)
}
Java
// 列出与会话关联的文件
var files = client.beta().files().list(FileListParams.builder()
.scopeId("ses_abc123")
.build());
// 下载文件
try (HttpResponse response = client.beta().files().download(files.data().get(0).id())) {
try (InputStream body = response.body()) {
Files.copy(body, Path.of("output.txt"), StandardCopyOption.REPLACE_EXISTING);
}
}
PHP
// 列出与会话关联的文件
$files = $client->beta->files->list(
scopeID: 'ses_abc123',
);
foreach ($files->getItems() as $file) {
echo "{$file->id} {$file->filename}\n";
}
// 下载文件
$content = $client->beta->files->download($files->getItems()[0]->id);
file_put_contents('output.txt', $content);
Ruby
# 列出与会话关联的文件
files = client.beta.files.list(
scope_id: "ses_abc123"
)
# 下载文件
content = client.beta.files.download(files.data[0].id)
File.binwrite("output.txt", content.read)
控制交付大小与清理文件
交付前控制输入和输出大小,下载时处理分页:
- 单个文件的上传上限默认为 100 MiB;会话沙箱挂载的输入文件不能超过 100 MiB。自部署服务可调整上传上限,但不会改变沙箱挂载上限。
- 执行结束后收集到 Files API 的输出文件,单文件和本轮输出目录中的文件总计均不能超过 100 MiB。任一限制被超过时,本轮输出文件收集失败,并非只跳过超限文件;请缩小产物或移出不需要下载的文件后重试。
- 文件列表采用分页响应,包含
has_more、first_id、last_id和next_page。查询下一页时,将next_page原样作为page参数传入;也兼容after_id/before_id。详见 API 通用约定。
报告或数据下载验收后,按本页开头的对象类型决定保留范围。归档会话会保留会话文件;需要删除时分别处理源材料和会话对象:
- 删除单个对象:调用
DELETE /v1/files/{file_id};会话作用域的对象还会移除对应资源关联。 - 清理某个会话的全部文件:删除会话。目前没有按会话批量删除文件对象的专用接口。
- 独立上传文件需单独删除;删除源文件不会清理已挂载的副本。