第 6 章 · 生成设置与生命周期回调
本章目标:
- 掌握
maxOutputTokens、temperature、topP等语言模型调用选项的含义与取舍- 区分影响生成行为的 Call Options 与影响传输行为的 Request Options
- 学会用
timeout对象对总时长、单步、首块、工具执行分别设置超时- 掌握
onStart/onStepStart/onLanguageModelCallEnd/onToolExecutionEnd等生命周期回调的执行顺序与应用场景
6.1 常见设置总览
💡 本章示例中的
model变量按第 3 章的方式构造:createGateway()(Vercel AI Gateway)或createOpenAICompatible()(自定义 OpenAI 兼容 Provider)均可,设置项与 provider 无关。
大语言模型(LLM)通常提供一些设置来调整输出。除 model、prompt 以及 provider 特定设置外,所有 AI SDK 函数都支持以下常见设置:
const result = await generateText({
model,
maxOutputTokens: 512,
temperature: 0.3,
maxRetries: 5,
timeout: 10000,
prompt: 'Invent a new holiday and describe its traditions.',
});⚠️ 部分 provider 不支持所有通用设置。使用不支持的设置时会产生警告,可通过结果对象的
warnings属性查看。
这些设置分为两大类:
- Language Model Call Options:影响模型如何生成响应(token 上限、采样行为、惩罚、停止序列、seed、reasoning)
- Request Options:影响传输层——重试、取消、超时,不影响生成行为本身
6.2 Language Model Call Options
maxOutputTokens
生成的最大 token 数。
temperature
温度设置。值会透传给 provider,范围取决于 provider 和模型。对多数 provider 而言,0 意味着结果几乎确定,值越高随机性越强。建议只设置 temperature 或 topP 其中之一,不要同时设置。
💡 自 AI SDK 5.0 起,temperature 不再默认设为
0。
topP
Nucleus sampling(核采样)。对多数 provider 来说是 0 到 1 之间的数值,例如 0.1 表示只考虑概率质量前 10% 的 token。同样建议与 temperature 二选一。
topK
每个后续 token 只从概率最高的 K 个选项中采样,用于剔除「长尾」低概率响应。仅推荐高级场景,通常只需要 temperature。
presencePenalty 与 frequencyPenalty
presencePenalty:影响模型重复 prompt 中已有信息的倾向frequencyPenalty:影响模型重复使用相同词语或短语的倾向
对多数 provider,0 表示无惩罚,范围取决于具体 provider 和模型。
stopSequences
停止序列。设置后,模型生成其中任一序列时就会停止生成文本。provider 可能限制停止序列的数量。
seed
用于随机采样的种子(整数)。如果设置且模型支持,调用将产生确定性结果。
reasoning
控制模型在回答前进行多少推理:
| 取值 | 行为 |
|---|---|
'provider-default' | 使用 provider 默认推理行为(省略时的默认值) |
'none' | 关闭推理 |
'minimal' | 最少推理 |
'low' | 快速简洁的推理 |
'medium' | 平衡的推理 |
'high' | 彻底的推理 |
'xhigh' | 最大程度推理 |
如果你同时在 providerOptions 中设置了 reasoning 相关选项(如 openai.reasoningEffort 或 anthropic.thinking),provider 特定选项优先,顶层 reasoning 参数会被忽略。
6.3 Request Options
maxRetries
最大重试次数。设为 0 可禁用重试。默认值为 2。
abortSignal
可选的 abort signal,用于取消调用。可以从用户界面转发以取消调用,或用 AbortSignal.timeout 定义超时:
const result = await generateText({
model,
prompt: 'Invent a new holiday and describe its traditions.',
abortSignal: AbortSignal.timeout(5000), // 5 秒
});timeout
以毫秒为单位的可选超时。调用超过指定时长会被中止。这是便捷参数,内部会创建 abort signal;可与 abortSignal 同时使用——两者任一满足即中止。
可以传数字,也可以传功能更丰富的对象:
totalMs:整个调用(含所有步骤)的总超时stepMs:每一步(LLM 调用)的超时,适合多步生成中独立限制每步时间firstChunkMs:(仅流式)每步第一个内容输出的超时。文本增量、reasoning 增量、tool-input 增量、生成文件和 tool call 都算内容输出;响应元数据、流开始、空增量和传输活动不算chunkMs:(仅流式)开始输出后内容块之间的超时,用于检测「生成中途卡死」的流toolMs:所有工具执行的默认超时,超时会中止并以 tool-error 返回,让模型可以响应或重试tools:按工具名覆盖超时(如weatherMs、slowApiMs),优先级高于toolMs
// 数字格式:5 秒总超时
await generateText({
model,
prompt: 'Invent a new holiday and describe its traditions.',
timeout: 5000,
});
// 组合:总共 60 秒 + 每步 10 秒
await generateText({
model,
prompt: 'Invent a new holiday and describe its traditions.',
timeout: { totalMs: 60000, stepMs: 10000 },
});
// 流式:内容卡住 5 秒即中止
streamText({
model,
prompt: 'Invent a new holiday and describe its traditions.',
timeout: { chunkMs: 5000 },
});
// 工具执行超时与按工具覆盖
await generateText({
model,
tools: { weather: weatherTool, slowApi: slowApiTool },
timeout: {
toolMs: 5000, // 所有工具默认 5 秒
tools: {
weatherMs: 3000, // weather 工具 3 秒
slowApiMs: 10000, // slowApi 工具 10 秒
},
},
prompt: 'What is the weather in San Francisco?',
});headers
随请求发送的额外 HTTP 头,仅适用于基于 HTTP 的 provider。例如部分可观测性 provider 支持 Prompt-Id 这样的头:
import { generateText } from 'ai';
const result = await generateText({
model,
prompt: 'Invent a new holiday and describe its traditions.',
headers: {
'Prompt-Id': 'my-prompt-id',
},
});💡
headers设置只针对单个请求。也可以在 provider 配置中设置headers,那会随该 provider 的每个请求发送。
6.4 生命周期回调基础
事件回调让你能在 AI SDK 调用的重要节点运行自己的代码。可以直接附加到 generateText、streamText、embed、embedMany 和 rerank 调用上,用于观察发生了什么、记录用量、调试多步生成和监控工具执行。
典型用途:
- 记录请求使用了哪个模型、prompt 形态和设置
- 为分析或计费记录 token 用量、延迟、finish reason 和警告
- 理解多步工具调用如何从模型响应走到工具执行再到最终答案
- 通过
runtimeContext和toolsContext附加自己的请求/用户/租户标识
需要跨应用自动 OpenTelemetry 埋点时用 Telemetry(第 13 章);只想针对特定调用运行自定义代码时用事件回调。
import { generateText } from 'ai';
const result = await generateText({
model,
prompt: 'What is the weather in San Francisco?',
onStart({ callId, modelId }) {
console.log('Generation started', { callId, modelId });
},
onEnd({ callId, usage, finishReason }) {
console.log('Generation finished', {
callId,
finishReason,
totalTokens: usage.totalTokens,
});
},
});回调可以是同步或异步的。回调抛错时会被内部捕获,AI SDK 调用继续进行。由于回调属于生命周期的一部分,请保持它们轻量,或将耗时工作排入后台系统。
6.5 典型应用场景
请求日志
用 onStart 和 onEnd 分别记录一次调用的开始与结束。callId 在所有生命周期事件中都可用,可用于关联同一请求的日志:
import { generateText } from 'ai';
const result = await generateText({
model,
prompt: 'Write a short product description for a camping mug.',
onStart({ callId, provider, modelId }) {
logger.info('ai.request.started', {
callId,
provider,
modelId,
});
},
onEnd({ callId, finishReason, usage, warnings }) {
logger.info('ai.request.finished', {
callId,
finishReason,
usage,
warningCount: warnings?.length ?? 0,
});
},
});这个模式非常适合审计日志、内部看板和特定功能的用量追踪。
测量模型性能
onLanguageModelCallEnd 会在 provider 响应被规范化并解析完成后触发。对 streamText,事件还包含流式专属的计时数据,如首个输出耗时和输出块间隔:
import { streamText } from 'ai';
const result = streamText({
model,
prompt: 'Explain partial prerendering in two paragraphs.',
onLanguageModelCallEnd({ callId, modelId, usage, performance, providerMetadata }) {
metrics.histogram('ai.model.response_time_ms', performance.responseTimeMs, {
callId,
modelId,
});
metrics.gauge('ai.model.tokens_per_second', {
output: performance.outputTokensPerSecond,
total: performance.effectiveTotalTokensPerSecond,
tokens: usage.totalTokens,
});
logger.info('ai.model.provider_metadata', { callId, providerMetadata });
},
});
for await (const textPart of result.textStream) {
process.stdout.write(textPart);
}要单独衡量 provider 的工作量就用 model-call 事件;要包含 SDK 管理的工作(如本地工具执行)的计时就用 step 事件。
调试多步工具调用
使用工具时,一个用户请求可能涉及多次模型调用。每次模型调用是一个 step:模型可能在某步调用工具、拿到工具结果后在下一步产出最终回答:
import { generateText, isStepCount, tool } from 'ai';
import { z } from 'zod';
const result = await generateText({
model,
stopWhen: isStepCount(5),
prompt: 'What is the weather in San Francisco?',
tools: {
weather: tool({
description: 'Get the weather in a location',
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }) => getWeather(location),
}),
},
onStepStart({ stepNumber, messages, steps }) {
console.log(`Step ${stepNumber} started`, {
messageCount: messages.length,
previousSteps: steps.length,
});
},
onStepEnd({ stepNumber, finishReason, toolCalls, usage, performance }) {
console.log(`Step ${stepNumber} finished`, {
finishReason,
toolCalls: toolCalls.map(toolCall => toolCall.toolName),
totalTokens: usage.totalTokens,
stepTimeMs: performance.stepTimeMs,
});
},
});这能回答:模型是调用了工具还是直接回答?请求用了几步?哪一步消耗最多 token?时间花在模型响应还是本地工具执行?
监控工具执行
工具执行回调围绕工具的 execute 函数运行,用于记录工具用量、延迟、成功结果与错误:
import { generateText, tool } from 'ai';
import { z } from 'zod';
const result = await generateText({
model,
prompt: 'Find flights from SFO to JFK tomorrow morning.',
tools: {
searchFlights: tool({
description: 'Search available flights',
inputSchema: z.object({
origin: z.string(),
destination: z.string(),
}),
execute: async input => searchFlights(input),
}),
},
onToolExecutionStart({ callId, toolCall }) {
logger.info('ai.tool.started', {
callId,
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
input: toolCall.input,
});
},
onToolExecutionEnd({ callId, toolCall, toolExecutionMs, toolOutput }) {
logger.info('ai.tool.finished', {
callId,
toolCallId: toolCall.toolCallId,
toolName: toolCall.toolName,
durationMs: toolExecutionMs,
success: toolOutput.type === 'tool-result',
});
},
});toolOutput 是一个 discriminated union:当 toolOutput.type === 'tool-result' 时输出在 toolOutput.output;为 'tool-error' 时错误在 toolOutput.error。
6.6 生成生命周期顺序
generateText 和 streamText 的生命周期最丰富,因为它们可能涉及 prompt、模型调用、工具调用和多步骤。
典型的单步生成按此顺序执行回调:
onStartonStepStartonLanguageModelCallStartonLanguageModelCallEndonStepEndonEnd
带本地工具执行的多步生成通常如下:
onStartonStepStartonLanguageModelCallStartonLanguageModelCallEndonToolExecutionStartonToolExecutionEndonStepEnd- 重复 step 回调直到满足停止条件
onEnd
onStepStart / onStepEnd 描述完整的一步;onLanguageModelCallStart / onLanguageModelCallEnd 只描述这一步内部的模型调用。当一步包含本地工具执行时,这个区别很重要——step 时长可能大于模型响应时长。
⚠️ 旧的
onStepFinish已弃用,新代码请使用onStepEnd。
6.7 Runtime Context 与 Tool Context
生命周期回调会收到贯穿整个调用的 runtimeContext 和 toolsContext,这让回调可以在不改 prompt 或工具输入的情况下附加应用上下文:
import { generateText, tool } from 'ai';
import { z } from 'zod';
const result = await generateText({
model,
prompt: 'Check the order status.',
runtimeContext: {
requestId: 'req_123',
tenantId: 'tenant_abc',
},
tools: {
getOrderStatus: tool({
inputSchema: z.object({ orderId: z.string() }),
contextSchema: z.object({ region: z.string() }),
execute: async ({ orderId }, { context }) =>
getOrderStatus(orderId, context.region),
}),
},
toolsContext: {
getOrderStatus: {
region: 'us-east-1',
},
},
onStart({ callId, runtimeContext }) {
logger.info('ai.request.started', {
callId,
requestId: runtimeContext.requestId,
tenantId: runtimeContext.tenantId,
});
},
onToolExecutionStart({ toolCall, toolContext }) {
logger.info('ai.tool.started', {
toolName: toolCall.toolName,
region: toolContext.region,
});
},
});⚠️ 遥测集成可以在导出前过滤
runtimeContext和toolsContext,但生命周期回调收到的是完整的上下文对象——小心不要从回调里记录密钥或敏感用户数据。
嵌入与重排序也有简化版回调:embedMany / rerank 提供 onStart / onEnd,可用于统计检索管道中嵌入了多少值、重排序如何改变结果集等。
本章小结
- 设置分两类:Language Model Call Options(temperature/topP/maxOutputTokens/reasoning 等)影响生成;Request Options(maxRetries/abortSignal/timeout/headers)影响传输
temperature与topP建议二选一;AI SDK 5.0 起 temperature 不再默认 0reasoning统一了各家的推理力度控制,provider 特定的providerOptions优先于顶层reasoningtimeout支持数字与对象格式,可细粒度控制 totalMs/stepMs/firstChunkMs/chunkMs/toolMs 及按工具覆盖- 生命周期回调顺序:onStart → onStepStart → onLanguageModelCallStart → onLanguageModelCallEnd → (onToolExecutionStart → onToolExecutionEnd) → onStepEnd → onEnd
- 回调抛错被内部捕获不中断流程;
callId可关联同一请求的全部事件;注意不要在回调里记录敏感数据
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 关于 temperature 和 topP,官方建议是?
2. timeout 对象格式中,chunkMs 的作用是什么?
3. 带本地工具执行的多步生成中,正确的回调顺序是?
4. 生命周期回调内部抛出异常会怎样?
🛠️ 动手实践
- 写一个对比实验:同一 prompt 分别用
temperature: 0和temperature: 1各生成 3 次,观察差异;再用seed: 42验证确定性。 - 给一个带慢速工具(内部 sleep 3 秒)的多步生成配置
timeout: { totalMs: 30000, stepMs: 8000, toolMs: 2000 },观察 tool-error 如何产生并被模型处理。 - 用
onStart/onStepStart/onLanguageModelCallEnd/onToolExecutionEnd/onStepEnd/onEnd六个回调各打印一行日志并带上callId,跑一次两步的工具调用,验证第 6.6 节的顺序图。