@google/genai:sendMessage 会覆盖 chats.create 配置吗?
结论
根据目前公开的 @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,再评估模型是否遵守指令。
备注:内容仅供参考。
