使用集成调用 OpenAI 模型
连接 OpenAI 集成后在 Func 里用 ctx.ai.openai 调模型:chat 做对话与翻译,json 让模型稳定输出结构化数据,tools 做单次工具调用,image 生成图片并存进项目空间。API Key 只保存在服务端。
连接 OpenAI 集成之后,Func 里用 ctx.ai.openai 就能调模型:对话、翻译、抽取结构化数据、调用一次工具、生成图片。API Key 只保存在服务端,代码里不会出现。
连接 OpenAI
打开编辑器的后端 → 集成,点 OpenAI,按下表填写。保存时平台会真的打一次上游,密钥不对当场报错,而不是等到有人用到这个功能才发现。
| 字段 | 填什么 | 必填 |
|---|---|---|
| API Key | sk- 开头的密钥,在 OpenAI 后台创建 | 是 |
| 默认模型 | 调用没指定模型时用它,比如 gpt-4o-mini | 建议 |
| 接口地址 | 留空就是 https://api.openai.com/v1。填别家兼容地址即可换供应商 | 否 |
| 渠道标签 | 同一个项目可以接多份配置,用标签区分,见多渠道 | 否 |
chat:一次对话
ctx.ai.openai.chat(params) 发一次调用并返回完整结果。
export function translate(input, ctx) {
const r = ctx.ai.openai.chat({
messages: [
{ role: 'system', content: '把用户输入翻译成英文,只输出译文' },
{ role: 'user', content: input.text },
],
model: 'gpt-4o-mini', // 不传就用集成里配的默认模型
temperature: 0.2,
maxTokens: 512,
})
return { text: r.content, tokens: r.totalTokens }
}
| 参数 | 说明 |
|---|---|
messages | 必填。{ role, content } 数组,role 取 system / user / assistant / developer / tool |
model | 留空取集成里配的默认模型 |
temperature | 不传就不发这个字段 |
maxTokens | 不传就不发这个字段 |
json | true 时平台剥代码块并解析,结果放进 data,见要 JSON |
tools / toolChoice | 工具定义,见单次 tool call |
extra | 逃生舱,原样并进请求体,见逃生舱 |
返回:
| 字段 | 说明 |
|---|---|
content | 模型回复的文本。只要求调用工具时它是空的,这不是错误 |
model | 实际用到的模型 |
finishReason | stop / length / tool_calls 等 |
promptTokens / completionTokens / totalTokens | 用量,方便自己记账 |
data | 只有 json: true 时才有 |
toolCalls | 只有模型要求调用工具时才有 |
要 JSON 就传 json: true
让模型输出 JSON 是这个能力最常见的用法:翻译、抽取、分类全是。json: true 时平台会剥掉模型爱包的代码块再解析,结果放在 data 里。
export function extract(input, ctx) {
const r = ctx.ai.openai.chat({
messages: [{ role: 'user', content: `从这段话里抽出姓名和城市,返回 JSON:${input.text}` }],
json: true,
})
return { name: r.data.name, city: r.data.city }
}
单次 tool call
传 tools,模型要调工具时 toolCalls 里就有东西。典型用法是一轮:把用户的自然语言变成一次结构化的函数调用,站点自己执行,然后直接回话。
export function assistant(input, ctx) {
const r = ctx.ai.openai.chat({
messages: [{ role: 'user', content: input.text }],
tools: [{
type: 'function',
function: {
name: 'reschedule',
description: '把一个会议改期',
parameters: {
type: 'object',
properties: { meetingId: { type: 'string' }, date: { type: 'string' } },
required: ['meetingId', 'date'],
},
},
}],
toolChoice: 'auto',
})
if (r.toolCalls?.length) {
const c = r.toolCalls[0]
// c.arguments 已经是对象
ctx.db.update('meetings', c.arguments.meetingId, { date: c.arguments.date })
return { done: true }
}
return { reply: r.content }
}
toolCalls[i] | 说明 |
|---|---|
id | 这次调用的 id。要跑第二轮的话得把它带回去 |
name | 模型要调的函数名 |
arguments | 已经解析好的对象。模型吐出坏 JSON 时它是 undefined |
argumentsRaw | 参数原文,永远都在 |
想跑第二轮(执行完工具再让模型总结)也可以,把工具结果拼回 messages 再调一次即可:
const r2 = ctx.ai.openai.chat({
messages: [
{ role: 'user', content: input.text },
{ role: 'assistant', toolCalls: r.toolCalls }, // 回放模型上一轮的要求
{ role: 'tool', toolCallId: r.toolCalls[0].id, content: JSON.stringify(result) },
],
})
平台不替你跑这个循环:几轮停、工具能不能读数据库、超时怎么算,只有站点自己知道。
生成图片
ctx.ai.openai.image(params)。强烈建议带 upload: true,图片会直接存进项目自己的空间并返回永久地址。
export const config = { timeoutMs: 120000 } // 生图慢,几十秒是常态
export function cover(input, ctx) {
const r = ctx.ai.openai.image({
prompt: input.prompt,
size: '1024x1024',
quality: 'high',
upload: true,
})
return { url: r.images[0].fileUrl }
}
| 参数 | 说明 |
|---|---|
prompt | 必填。只传一个字符串也行:image('一只雪地里的红狐狸,水彩') |
model | 默认 gpt-image-1。不会用集成里配的对话模型,那是两回事 |
size | 如 1024x1024、1536x1024 |
quality | low / medium / high;dall-e-3 是 standard / hd |
style | dall-e-3 专有:vivid / natural |
background | gpt-image-1 专有:transparent / opaque / auto |
n | 张数,最多 4 |
upload | true 时存进项目空间并返回 fileUrl;不传则返回 imageBase64 |
filename | 只在 upload 时有用。默认按内容哈希命名 |
返回 { model, images: [...] },每张图是 { fileUrl | imageBase64, mimeType, bytes, revisedPrompt? }。
逃生舱:extra 与 call
想传平台没暴露的字段,用 extra。它原样并进请求体,但改不坏 messages:
ctx.ai.openai.chat({
messages,
extra: { top_p: 0.9, seed: 42, response_format: { type: 'json_object' } },
})
想调 /chat/completions 之外的接口,用 call(path, body)。请求体原样发出,上游返回的 JSON 原样交回,密钥同样不进沙箱:
const r = ctx.ai.openai.call('/embeddings', {
model: 'text-embedding-3-small',
input: input.text,
})
const vector = r.data[0].embedding
多渠道:via(tag)
一个项目可以接多份 OpenAI 配置,比如分类用便宜模型、写作用强模型,或者一份官方一份自建网关。给每份配置写上渠道标签,代码里用 via() 选:
ctx.ai.openai.via('cheap').chat({ messages })
ctx.ai.openai.via('strong').chat({ messages })
不写 via() 时用标签为 default 的那份。
报错怎么看
| 报错 | 该改哪一项 |
|---|---|
ai integration has no api key | 集成没连,或密钥被清空了 |
model is required | 调用没传 model,集成里也没配默认模型 |
message at index N has empty content | 有一条消息的 content 是空的 |
message at index N has role tool but no toolCallId | 跑第二轮时忘了带 toolCallId |
ai response is not valid json | 用了 json: true 但模型没给 JSON。报错里带原文片段,先看是提示词没说清楚还是模型跑偏 |
the image provider returned a temporary url | 网关没按要求返回图片字节。改用 call() 自己处理,或换一个支持 b64_json 的网关 |
invalid n | 一次最多 4 张 |
上游自己的报错(额度用完、模型不存在、速率限制)会原样带上文案转成 400 或 502。不会把上游的 401 直接抛给前端,那会让页面误以为是没登录。
不做的事
| 不做 | 为什么 |
|---|---|
| 多轮 agent 循环 | 停机条件、轮数预算、工具权限只有站点自己知道。单次 tool call 有,循环自己写 |
| 流式输出 | Func 的返回值本来就是一次性的。要打字机效果用 ctx.sse 自己推 |
| 缓存与去重 | 「什么算同一次请求」只有站点知道。要缓存自己建一张表 |
| 并入平台积分 | 这是你自己的 API Key、你自己的账单,平台不插手计费 |
