Skip to content

第 4 章 · 提示词与消息模型

本章目标:

  • 掌握三种提示词形态:text prompt、system prompt(instructions)与 message prompt
  • 理解 ModelMessage 的角色体系与 content parts 结构
  • 学会在消息中携带图像、PDF、音频等多模态内容
  • 了解 providerOptions 在函数级 / 消息级 / 消息部件级的三个作用层次

4.1 Prompt 是什么

Prompt(提示词)是你给大语言模型的指令,告诉它要做什么。就像向人问路——问题越清晰,得到的指引越好。

许多 LLM provider 的提示词接口很复杂,涉及不同的角色与消息类型。虽然强大,但难以理解和使用。为简化提示编写,AI SDK 支持 text、message、system 三种提示形式。

4.2 Text Prompt

Text prompt 就是字符串,适合简单的生成场景,例如对同一提示文本的不同变体反复生成。通过 generateTextstreamText 等 AI SDK 函数的 prompt 属性设置:

ts
import { generateText, createGateway } from 'ai';

const gateway = createGateway({
  apiKey: process.env.AI_GATEWAY_API_KEY ?? '',
});

const result = await generateText({
  model: gateway('openai/gpt-5'),
  prompt: 'Invent a new holiday and describe its traditions.',
});

可以用模板字面量为 prompt 注入动态数据:

ts
const result = await generateText({
  model,
  prompt:
    `I am planning a trip to ${destination} for ${lengthOfStay} days. ` +
    `Please suggest the best tourist activities for me to do.`,
});

4.3 System Prompt:instructions

System prompt 是给模型的初始指令集,用于引导和约束模型的行为与响应方式,通过 instructions 属性设置。它可与 promptmessages 属性同时使用。

⚠️ 注意:默认情况下 prompt/messages 中的 system 消息会被拒绝;要么用 instructions 属性传系统指令,要么在发送含系统消息的历史记录时显式设置 allowSystemInMessages: true

ts
const result = await generateText({
  model,
  instructions:
    `You help planning travel itineraries. ` +
    `Respond to the users' request with a list ` +
    `of the best stops to make in their destination.`,
  prompt:
    `I am planning a trip to ${destination} for ${lengthOfStay} days. ` +
    `Please suggest the best tourist activities for me to do.`,
});

⚠️ 开启 allowSystemInMessages 会引入 prompt injection 风险——用户可能通过注入 system 消息覆盖系统提示。大多数情况下,只应由受信任的服务端代码通过 instructions 设置系统指令。

4.4 Message Prompt

Message prompt 是 user、assistant、tool 消息组成的数组,适合聊天界面和更复杂的多模态提示,通过 messages 属性设置。

每条消息都有 rolecontent 属性。content 既可以是纯文本,也可以是与该消息类型相关的内容部件(parts)数组:

ts
const result = await generateText({
  model,
  messages: [
    { role: 'user', content: 'Hi!' },
    { role: 'assistant', content: 'Hello, how can I help?' },
    { role: 'user', content: 'Where can I buy the best Currywurst in Berlin?' },
  ],
});

⚠️ 并非所有模型都支持所有消息与内容类型。例如某些模型不支持多模态输入或 tool 消息,请查阅所用模型的 capability 说明。

4.5 Provider Options 的三个层次

可以通过 providerOptions 把 provider 特有的元数据透传给底层 API,共有三个粒度:

函数调用级——不需要细粒度控制时使用:

ts
const { text } = await generateText({
  model,
  providerOptions: {
    openai: {
      reasoningEffort: 'low',
    },
  },
  prompt: 'Invent a new holiday and describe its traditions.',
});

消息级——精确控制某条消息的行为,例如给 system 消息打上缓存断点:

ts
const result = await generateText({
  model,
  instructions: {
    role: 'system',
    content: 'Cached system message',
    providerOptions: {
      // Sets a cache control breakpoint on the system message
      anthropic: { cacheControl: { type: 'ephemeral' } },
    },
  },
  prompt: 'Invent a new holiday and describe its traditions.',
});

消息部件级——某些 provider 特有选项必须配置到具体的 part 上:

ts
import { ModelMessage } from 'ai';

const messages: ModelMessage[] = [
  {
    role: 'user',
    content: [
      {
        type: 'text',
        text: 'Describe the image in detail.',
        providerOptions: {
          openai: { imageDetail: 'low' },
        },
      },
      {
        type: 'file',
        mediaType: 'image',
        data: 'https://github.com/vercel/ai/blob/main/examples/ai-functions/data/comic-cat.png?raw=true',
        // Sets image detail configuration for image part:
        providerOptions: {
          openai: { imageDetail: 'low' },
        },
      },
    ],
  },
];

💡 AI SDK UI 的 hooks(如 useChat)返回的 UIMessage 数组不支持 provider options;应先用 convertToModelMessages 转成 ModelMessage 再附加。

4.6 User Messages:多模态内容部件

Text Parts

文本是最常见的内容类型。如果只需发文本,content 直接给字符串即可;也可以拆成多个 parts:

ts
const result = await generateText({
  model,
  messages: [
    {
      role: 'user',
      content: [
        {
          type: 'text',
          text: 'Where can I buy the best Currywurst in Berlin?',
        },
      ],
    },
  ],
});

Image Parts

用户消息可包含图像,支持四种来源:

  • base64 编码图像(字符串或 data URL);
  • 二进制数据(ArrayBuffer / Uint8Array / Buffer);
  • URL 字符串或 URL 对象。

二进制图像示例(Buffer):

ts
import fs from 'node:fs';

const result = await generateText({
  model,
  messages: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'Describe the image in detail.' },
        {
          type: 'file',
          mediaType: 'image',
          data: fs.readFileSync('./data/comic-cat.png'),
        },
      ],
    },
  ],
});

base64 与 URL 示例:

ts
// base64
{
  type: 'file',
  mediaType: 'image',
  data: fs.readFileSync('./data/comic-cat.png').toString('base64'),
}

// URL
{
  type: 'file',
  mediaType: 'image',
  data: 'https://github.com/vercel/ai/blob/main/examples/ai-functions/data/comic-cat.png?raw=true',
}

File Parts

用户消息还可以携带文件(同样支持 base64、二进制、URL 三种来源),需要指定 MIME 类型。目前仅部分 provider/model 支持文件部件(Google Generative AI、Google Vertex AI、OpenAI 音频/PDF、Anthropic 等)。

PDF 文件示例:

ts
import fs from 'node:fs';
import { generateText } from 'ai';

const result = await generateText({
  model,
  messages: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'What is the file about?' },
        {
          type: 'file',
          mediaType: 'application/pdf',
          data: fs.readFileSync('./data/example.pdf'),
          filename: 'example.pdf', // optional, not used by all providers
        },
      ],
    },
  ],
});

mp3 音频文件示例:

ts
import fs from 'node:fs';
import { generateText } from 'ai';

const result = await generateText({
  model,
  messages: [
    {
      role: 'user',
      content: [
        { type: 'text', text: 'What is the audio saying?' },
        {
          type: 'file',
          mediaType: 'audio/mpeg',
          data: fs.readFileSync('./data/galileo.mp3'),
        },
      ],
    },
  ],
});

自定义下载函数(实验性)

默认实现会并行自动下载模型不支持的 URL 文件。你可以传入 experimental_download 实现限流、重试、认证、缓存等自定义逻辑:

ts
const result = await generateText({
  model,
  experimental_download: async (
    requestedDownloads: Array<{
      url: URL;
      isUrlSupportedByModel: boolean;
    }>,
  ): PromiseLike<
    Array<{
      data: Uint8Array;
      mediaType: string | undefined;
    } | null>
  > => {
    // ... download the files and return an array with similar order
  },
  messages: [
    {
      role: 'user',
      content: [
        {
          type: 'file',
          data: new URL('https://api.company.com/private/document.pdf'),
          mediaType: 'application/pdf',
        },
      ],
    },
  ],
});

4.7 Assistant Messages

Assistant 消息的 role 为 assistant,通常是助手的历史回复,可包含文本、推理(reasoning)和工具调用部件。

纯文本回复:

ts
const result = await generateText({
  model,
  messages: [
    { role: 'user', content: 'How many calories are in this block of cheese?' },
    {
      role: 'assistant',
      content: [
        {
          type: 'tool-call',
          toolCallId: '12345',
          toolName: 'get-nutrition-data',
          input: { cheese: 'Roquefort' },
        },
      ],
    },
  ],
});

4.8 Tool Messages 与多模态工具结果

对于支持工具调用的模型,assistant 消息可含 tool-call 部件,tool 消息则承载工具输出。一条 assistant 消息可以并行调用多个工具,一条 tool 消息也可以包含多个结果:

ts
import fs from 'node:fs';

const result = await generateText({
  model,
  messages: [
    {
      role: 'user',
      content: [
        {
          type: 'text',
          text: 'How many calories are in this block of cheese?',
        },
        {
          type: 'file',
          mediaType: 'image',
          data: fs.readFileSync('./data/roquefort.jpg'),
        },
      ],
    },
    {
      role: 'assistant',
      content: [
        {
          type: 'tool-call',
          toolCallId: '12345',
          toolName: 'get-nutrition-data',
          input: { cheese: 'Roquefort' },
        },
        // there could be more tool calls here (parallel calling)
      ],
    },
    {
      role: 'tool',
      content: [
        {
          type: 'tool-result',
          toolCallId: '12345', // needs to match the tool call id
          toolName: 'get-nutrition-data',
          output: {
            type: 'json',
            value: {
              name: 'Cheese, roquefort',
              calories: 369,
              fat: 31,
              protein: 22,
            },
          },
        },
        // there could be more tool results here (parallel calling)
      ],
    },
  ],
});

工具结果本身也可以是多部件多模态的(如文本 + 图像),用 output: { type: 'content', value: [...] } 表达:

ts
{
  type: 'tool-result',
  toolCallId: '12345', // needs to match the tool call id
  toolName: 'get-nutrition-data',
  // for models that support multi-part tool results:
  output: {
    type: 'content',
    value: [
      {
        type: 'text',
        text: 'Here is the nutrition data for the cheese:',
      },
      {
        type: 'file-data',
        data: fs
          .readFileSync('./data/roquefort-nutrition-data.png')
          .toString('base64'),
        mediaType: 'image/png',
      },
    ],
  },
}

对于不支持多部件结果的模型,仍可用普通 JSON 输出部件兜底。

4.9 System Messages

System 消息在用户消息之前发给模型,引导助手行为。也可以改用 instructions 属性(见 4.3 节):

ts
const result = await generateText({
  model,
  messages: [
    { role: 'system', content: 'You help planning travel itineraries.' },
    {
      role: 'user',
      content:
        'I am planning a trip to Berlin for 3 days. Please suggest the best tourist activities for me to do.',
    },
  ],
});

本章小结

  • 三种提示形态:prompt(文本)、instructions(系统指令,推荐替代 system 消息)、messages(消息数组);
  • 默认拒绝 messages 中的 system 角色;allowSystemInMessages: true 有注入风险需谨慎;
  • ModelMessage 的 content 可由 text/image/file/tool-call/tool-result 等 parts 组合,一条消息可并行多个工具调用;
  • providerOptions 支持函数级、消息级、消息部件级三个粒度的 provider 特有配置;
  • UIMessage 不支持 provider options,需先经 convertToModelMessages 转换。

🛠️ 动手实践

  1. 用同一个旅行规划需求分别以 prompt + instructions 和 system message 两种方式实现,对比输出风格差异。
  2. 构造一条包含「文本 + 图片 URL」的用户消息发给视觉模型,再尝试把图片换成 base64 Buffer 形式,确认两种写法结果一致。
  3. 手工拼装一组完整的 user → assistant(tool-call) → tool(tool-result) 三条消息模拟工具历史,观察模型如何基于历史继续对话。