附件:图片与文件 (Attachments)
给 Agent 发图片或文件分两步:先上传字节拿到一个稳定的 URL,再把 URL 作为消息的「部件 (part)」随消息发出。反过来,Agent 生成的图片会带着 URL 回到流里,它产出的文档则带着一个工作区路径,需要通过接口取回。 SDK 把这几步都封装好了;浏览器里的聊天组件连调用都不用写。
上传并发送
client.files.uploadPart 上传字节,直接返回一个可以发送的部件:图片返回 image_url 部件,其余返回 file 部件(按 MIME 类型和扩展名判断)。
import { readFile } from 'node:fs/promises';
const parts = [
await client.files.uploadPart(
{ data: await readFile('chart.png'), filename: 'chart.png' },
{ agentId }
),
await client.files.uploadPart(
{ data: await readFile('report.pdf'), filename: 'report.pdf' },
{ agentId }
),
];
const turn = await client.sendMessage('这两份材料里有什么值得注意的?', { parts });
- 消息文本放
message,附件放parts。 不要自己把附件链接拼进文本,服务端会生成规范的附件说明,自己拼会出现两份。 - 传
agentId(或chatGroupId):服务端在保存文件的同时,把它放进 Agent 的工作区,Agent 里需要真实文件路径的工具(脚本、数据处理、读文档)才读得到。只是让 Agent「看一眼」图片可以不传。
在浏览器里直接把 File / Blob 交给 uploadPart,File 自带文件名:
const part = await client.files.uploadPart(fileInput.files[0], { agentId });
上传选项
| 选项 | 说明 |
|---|---|
agentId | 把文件放进该 Agent 的工作区 |
chatGroupId | 把文件放进该对话的工作区 |
filename | 存储用的文件名。裸 Blob 必填;传入时覆盖 File 自带的名字 |
contentType | MIME 类型。默认取 Blob.type,其次按扩展名推断,最后是 application/octet-stream |
as | 仅 uploadPart:强制部件类型,'image' 或 'file' |
signal | 取消上传 |
例如让一张图片以文件形式发送,让 Agent 当附件处理而不是「看图」:
await client.files.uploadPart(blob, { filename: 'scan.png', as: 'file' });
只要 URL
client.files.upload 只上传,返回上传结果,适合保存 URL 复用到多条消息:
const uploaded = await client.files.upload({ data: bytes, filename: 'chart.png' }, { agentId });
// → { url, filename, size, mimeType, provider, nasPath?, agentPath? }
const part = client.files.toPart(uploaded); // 之后随时转成部件
已经有公开 URL
文件已经在别处且可以公开访问,就跳过上传,自己拼部件:
const parts = [
{ type: 'image_url', image_url: { url: 'https://example.com/chart.png' } },
{ type: 'file', file: { filename: 'report.pdf', file_url: 'https://example.com/report.pdf' } },
];
读取 Agent 生成的图片
Agent 生成的图片带着 URL 回到流里,用 extractImages 取出:
import { extractImages } from '@openhex-ai/agent-sdk';
for await (const record of client.runTurn('把这份数据画成柱状图', { parts })) {
for (const url of extractImages(record)) {
console.log('图片:', url);
}
}
读取 Agent 产出的文件
Agent 写完一份报告、一个页面或一份表格时,会把它作为一条单独的记录发出来。这条记录携带的是文件在对话工作区里的路径,而不是 URL——文件需要通过带鉴权的接口读取,裸链接访问不到。
import { extractFileAttachment } from '@openhex-ai/agent-sdk';
const convo = client.conversation();
for await (const record of convo.stream('把设计方案写成一份 markdown 文档')) {
const file = extractFileAttachment(record);
if (!file) continue;
console.log(file.name, file.workspacePath); // report.md /outputs/report.md
// 文本文件:直接读内容
const { content, encoding, mimeType } = await client.files.readConversationFile(
convo.id!,
file.workspacePath
);
const text = encoding === 'base64' ? Buffer.from(content, 'base64').toString('utf8') : content;
// 任意文件:拿到 Blob,适合下载或预览
const blob = await client.files.downloadConversationFile(convo.id!, file.workspacePath);
}
| 方法 | 返回 | 适合 |
|---|---|---|
readConversationFile(conversationId, path) | { content, encoding, mimeType },二进制文件为 base64 | 文本、小文件 |
downloadConversationFile(conversationId, path) | Blob | 大文件、二进制文件、给用户下载 |
路径不存在时返回 404 File not found。
在 React 组件里
内置聊天组件默认支持附件:选择按钮、粘贴(⌘/Ctrl+V 贴图)、拖拽都能用,组件替你上传,并在发送前显示预览。消息里的附件直接渲染在气泡里:图片显示缩略图,文件显示为可下载的卡片——包括 Agent 产出的文件,点击时组件会通过鉴权接口取回内容。
<ChatBox agentId="…" getToken={getToken} /> {/* 默认全开 */}
<ChatBox agentId="…" getToken={getToken} acceptFileTypes="image/*" /> {/* 只允许选图片 */}
<ChatBox agentId="…" getToken={getToken} allowAttachments={false} /> {/* 关闭附件 */}
acceptFileTypes 就是文件选择框的 accept 属性(如 ".pdf,.docx"),只限制选择按钮,不限制粘贴和拖拽。
用无头 Hook 时,send 的第二个参数接受原始 files(替你上传)或已经拼好的 parts,downloadAttachment 取回 Agent 产出的文件:
const { send, downloadAttachment } = useOpenhexChat({ agentId: '…', getToken });
await send('帮我看看这个', { files: [myFile] });
const blob = await downloadAttachment(message.attachments![0]);