PAYATHON 2026

@google/genai:sendMessage 会覆盖 chats.create 配置吗?

AI 小顾

结论

根据目前公开的 @google/genai 文档,向 sendMessage({ config }) 传入配置时,该配置会在当前请求中替代整个会话默认配置。SDK 不会自动逐字段合并,也不会修改 chat 对象中保存的默认配置。SendMessageParameters 文档

因此,只传入 config: { abortSignal } 时,本次请求不会继承会话配置中的 systemInstruction。如需同时使用两者,可以在应用侧显式组合配置,并将组合逻辑封装成函数,避免在每处调用时重复展开。

原问题没有提供 SDK 版本。以下说明依据公开文档和对应源码中的配置选择逻辑;排查具体项目时,请对照已安装的版本。

为什么只传 abortSignal 会丢失 systemInstruction

SDK 源码通过下面的表达式选择配置:

config: params.config ?? this.config

请求未提供 config 时,SDK 使用会话默认配置;提供 config 对象时,SDK 直接使用该对象,不进行对象展开或深度合并。Chat 源码

在问题中的调用里:

await chat.sendMessage({
  message: inputText.value,
  config: {
    abortSignal: abortController.signal,
  },
});

本次请求的配置中只有 abortSignal,不会从 chats.create 的配置中继承 systemInstruction。即使传入空的 config: {},SDK 也会使用这个空对象。

聊天历史不会因此清空,保存的会话默认配置也不会被删除。后续调用省略 config 时,仍会使用创建 chat 时保存的默认配置。

将默认配置和取消信号统一封装

先提取 baseConfig,让 chats.create 和发送函数共用。保留原示例中的 ai、inputText 和 result,用以下代码替换原来的 chat 定义、abortController 声明及两个请求函数:

const baseConfig = {
  systemInstruction:
    "You are an expert about tennis. Don't reply to other kinds of requests, but apologize and state that you're a tennis assistant only",
};

const chat = ai.chats.create({
  model: "gemini-3-flash-preview",
  config: baseConfig,
});

const isSending = ref(false);
let abortController = null;

function sendWithSignal(message, signal) {
  return chat.sendMessage({
    message,
    config: {
      ...baseConfig,
      abortSignal: signal,
    },
  });
}

async function sendRequest() {
  if (isSending.value) return;

  const controller = new AbortController();
  abortController = controller;
  isSending.value = true;

  try {
    const response = await sendWithSignal(
      inputText.value,
      controller.signal,
    );

    if (!controller.signal.aborted) {
      result.value = response.text ?? "";
    }
  } catch (e) {
    if (controller.signal.aborted || e?.name === "AbortError") {
      console.log("Request was aborted");
    } else {
      console.error(e);
    }
  } finally {
    abortController = null;
    isSending.value = false;
  }
}

function abortRequest() {
  abortController?.abort();
}

模板中的按钮可以同步反映请求状态:

<button :disabled="isSending" @click="sendRequest">Send</button>
<button :disabled="!isSending" @click="abortRequest">Abort</button>

这个封装在每次请求中同时传入 systemInstruction 和新的 abortSignal。isSending 限制界面同一时间只有一个未完成的请求,避免连续点击覆盖共享的 abortController。

调用 abort() 后,控制器引用会保留到 finally 清理,界面也会在旧请求收尾完成前保持忙碌状态。

合并配置和取消请求时的注意事项

对象展开只做浅合并。本例中 systemInstruction 和 abortSignal 都是顶层字段,使用 { ...baseConfig, abortSignal } 即可。如果以后需要覆盖 httpOptions 等嵌套对象的部分字段,就要明确组合相应的嵌套对象。展开顺序决定优先级:后写入的同名字段会覆盖前面的值。

AbortController 的 signal 一旦被取消,就不能重置后用于正常请求。需要独立取消的请求适合各自创建控制器;多个操作如果共享同一个尚未取消的 signal,取消该 signal 时它们会一起被取消。AbortSignal 说明

SDK 文档还指出,abortSignal 的取消发生在客户端,不会取消服务端已经执行的请求,适用的用量仍可能计费。GenerateContentConfig.abortSignal 文档

保留 systemInstruction 可以修复这次请求漏传指令的问题,但不能仅凭一次模型回复判断配置是否正确传递。验证时,应先检查 SDK 实际使用的配置或请求中的 systemInstruction,再评估模型是否遵守指令。

备注:内容仅供参考。