返回

Node.js Gemini AI history 参数错误 (text in H) 修复指南

Ai

搞定 Gemini AI 在 Node.js 中的 history 参数报错:Cannot use 'in' operator to search for 'text' in H

刚上手在 Node.js 项目里接入 Google Gemini AI?要是你直接从文档拷贝示例代码,然后兴冲冲地跑起来,说不定会撞上一个有点懵的报错,尤其是在处理 history 参数的时候:

{"error": "Cannot use 'in' operator to search for 'text' in H"}

奇怪的是,把 history 设置成 null,哎,程序居然又能跑通了。

这是你可能遇到的代码片段(源自 Stack Overflow 提问者):

   // ... 其他配置 ...
   history: [
     {
       role: "user",
       // 注意这一行!可能就是罪魁祸首
       parts: : "Hello, I have 2 dogs in my house.",
     },
     {
       role: "model",
       parts: "Great to meet you. What would you like to know?",
     },
   ],
   generationConfig: {
     maxOutputTokens: 100,
   },
   // ... 其他配置 ...

控制台跟着输出一长串错误堆栈,开头几行长这样:

TypeError: Cannot use 'in' operator to search for 'text' in H
    at validateChatHistory (C:\...\node_modules\@google\generative-ai\dist\index.js:760:25)
    at new ChatSession (C:\...\node_modules\@google\generative-ai\dist\index.js:816:13)
    at GenerativeModel.startChat (C:\...\node_modules\@google\generative-ai\dist\index.js:1035:16)
    at C:\...\server\routes\chat.js:21:22
    // ... 后面省略 ...

别慌,这问题通常不是什么玄学 Bug,多半是 history 数据的格式没整对。咱们来捋一捋。

问题根源分析

错误信息 TypeError: Cannot use 'in' operator to search for 'text' in H 是个典型的 JavaScript 类型错误。in 操作符是用来检查一个对象(或者它的原型链)里是不是包含某个指定的属性。当你想检查的对象压根儿就不是个对象,或者结构不对时,比如是个字符串、数字,甚至是 undefined,JavaScript 老哥就会有点懵,然后甩给你这个 TypeError

看错误堆栈,问题出在 @google/generative-ai 这个 SDK 内部的 validateChatHistory 函数。顾名思义,这函数就是负责检查你传进去的 history 数组格式对不对的。它估计是想遍历 history 里的每个对象,再看看每个对象的 parts 字段里有没有 text 这个属性(或者类似的结构检查)。

再回头看一眼上面那段代码里的 history 部分:

{
  role: "user",
  parts: : "Hello, I have 2 dogs in my house.", // 多了一个冒号!
},

看到没?parts: : "..." 这里明显多了一个冒号。这直接导致了 JavaScript 语法错误。解析器很可能没法正确理解这个 parts 字段,导致传给 validateChatHistory 函数的数据结构跟它期望的不一样。也许 parts 被错误地解析成了别的什么东西(比如报错里的 'H' 可能就是一个被压缩混淆后代表错误数据的变量名),总之不再是那个包含 text 属性的预期对象了。当验证函数试图用 in 操作符去检查这个畸形数据时,TypeError 就来了。

所以,问题的核心八九不离十就是:你给 startChat 函数的 history 参数,它的数据结构或者语法存在错误,不符合 Gemini AI SDK 的要求。

解决方案

搞清楚了原因,解决起来就简单多了。下面列几种方法,你可以挨个试试:

方案一:修正 history 结构语法错误

这是最可能也是最直接的解决办法。仔细检查你的 history 数组,特别是每个对象的 parts 字段。

原理和作用:
确保你提供给 SDK 的 history 数据是完全符合 JavaScript 语法和 Gemini AI API 预期的 JSON 结构。一个多余的冒号、逗号,或者括号不匹配都可能导致解析失败。

操作步骤与代码示例:

找到代码里定义 history 的地方,把那个多余的冒号干掉。同时,根据 Gemini API 的标准(通常 parts 需要是一个包含对象的数组,即使只有一个文本片段),修正 parts 的结构。

错误示例:

history: [
  {
    role: "user",
    // 语法错误,多了一个冒号,且结构可能不完全对
    parts: : "Hello, I have 2 dogs in my house.",
  },
  {
    role: "model",
    // 这个 parts 如果直接是字符串,也可能不符合最新的 API 要求
    parts: "Great to meet you. What would you like to know?",
  },
],

正确示例:
标准的 Gemini API 通常要求 parts 是一个对象数组,每个对象代表一个内容部分(比如文本、图片等)。对于纯文本,通常结构是 [{ text: "..." }]

history: [
  {
    role: "user",
    parts: [{ text: "Hello, I have 2 dogs in my house." }], // 修正语法,并使用标准 parts 结构
  },
  {
    role: "model",
    parts: [{ text: "Great to meet you. What would you like to know?" }], // 同样修正结构
  },
],

说明:
上面的正确示例假设你传递的是纯文本内容。如果你要处理多模态内容(比如包含图片),parts 数组里会有不同类型的对象,例如 { inlineData: { mimeType: 'image/jpeg', data: '...' } }。重点是,parts 必须是一个数组 ,里面装着符合 API 定义的 Part 对象。

修改完代码后,保存,重启你的 Node.js 服务,再试试看。

方案二:确认 parts 字段的正确格式

即使没有语法错误,parts 字段本身的结构也可能不对。Gemini API 对此有明确要求。

原理和作用:
Gemini 模型支持多模态输入,parts 字段设计为一个数组,是为了容纳不同类型的内容片段(文本、图片等)。即使你只发送文本,也需要遵循这个数组套对象的格式。SDK 内部的验证逻辑会严格检查这个结构。

操作步骤与代码示例:

确保 history 数组中,每个对象的 parts 字段都是一个数组 (Array) ,并且数组里的每个元素都是一个对象 (Object) 。对于文本内容,这个对象通常包含一个 text 属性。

代码示例 (确认文本格式):

const history = [
  {
    role: "user",
    parts: [ // 必须是数组
      { text: "这是用户的第一句话。" } // 包含 text 属性的对象
    ]
  },
  {
    role: "model",
    parts: [ // 同样是数组
      { text: "这是模型的回应。" } // 包含 text 属性的对象
    ]
  }
];

// ... 然后在调用 startChat 时使用这个 history
const chat = model.startChat({ history: history });

进阶使用技巧 (处理多模态内容):
如果你需要在历史记录中包含图片,parts 数组的结构会长得不一样。你需要提供图片的 Base64 编码数据和 MIME 类型。

const historyWithImage = [
  {
    role: "user",
    parts: [
      { text: "看看这张图片里是什么?" },
      {
        inlineData: {
          mimeType: "image/jpeg", // 或 image/png 等
          data: "这里是图片的Base64编码字符串..." // 省略了很长的编码数据
        }
      }
    ]
  },
  {
    role: "model",
    parts: [
      { text: "图片里看起来像是一只猫在睡觉。" }
    ]
  }
];

查阅你使用的 @google/generative-ai SDK 版本的官方文档,是确认 Part 对象具体结构的最好方式。

方案三:检查 @google/generative-ai SDK 版本

有时候,你本地安装的 SDK 版本和你参考的文档或示例代码版本不一致,也可能导致 API 结构不兼容。

原理和作用:
库的开发者可能会在不同版本中调整 API 的数据结构。旧版本可能接受 parts: "text" 这样的简写,但新版本强制要求 parts: [{ text: "text" }]。或者反过来。如果你用的代码是基于一个版本写的,但运行环境里是另一个版本,就可能出错。

操作步骤与命令行指令:

  1. 查看当前安装的版本:
    打开你的项目终端,运行:

    npm list @google/generative-ai
    # 或者如果你用 yarn
    # yarn list @google/generative-ai
    

    记下你当前使用的版本号。

  2. 查阅对应版本的文档:
    去 Google AI for Developers 官网或者 GitHub 仓库,找到与你版本号匹配的文档。仔细阅读关于 startChat 函数和 history 参数格式要求的部分。

  3. 更新 SDK (如果需要):
    如果你发现当前版本太旧,或者想统一到最新稳定版,可以更新:

    npm update @google/generative-ai
    # 或者
    npm install @google/generative-ai@latest
    # 对于 yarn 用户
    # yarn upgrade @google/generative-ai
    # yarn add @google/generative-ai@latest
    

    更新后,最好再确认一下新版本的 API 要求是否有变化。

安全建议:
在生产环境中,建议锁定依赖版本(使用 package-lock.jsonyarn.lock),避免意外的自动更新导致 API 不兼容问题。升级主要版本(如从 1.x 到 2.x)前,务必仔细阅读发布说明(Release Notes)中的破坏性变更(Breaking Changes)。

方案四:逐步调试与日志记录

如果以上方法都没立刻解决问题,那就得上点调试手段了。

原理和作用:
通过打印日志或者使用调试器,可以精确地看到在调用 model.startChat() 之前,你构造的 history 变量到底长什么样。这有助于发现那些肉眼不容易察觉的结构问题或者 undefined 值。

操作步骤与代码示例:

  1. 打印日志:
    在你调用 model.startChat({...}) 的代码正上方 ,加上一行日志输出:

    const history = [
      // ... 你构造的 history 数组 ...
    ];
    
    // 打印 history 的 JSON 字符串表示,方便检查结构
    console.log("即将传入 startChat 的 history 数据:", JSON.stringify(history, null, 2));
    
    try {
      const chat = model.startChat({
        history: history,
        generationConfig: {
          maxOutputTokens: 100,
        },
      });
      // ... 后续代码 ...
    } catch (error) {
      console.error("调用 startChat 出错:", error);
      // 可以考虑在这里也打印 history,看看是不是过程中被意外修改了
      // console.error("出错时的 history 数据:", JSON.stringify(history, null, 2));
    }
    

    运行代码,然后仔细检查控制台输出的 history 数据结构,看它是不是完全符合预期格式。JSON.stringify 的第二和第三个参数 (null, 2) 是为了让输出的 JSON 更易读(带缩进)。

  2. 使用调试器:
    如果你熟悉 Node.js 的调试工具(比如 VS Code 的内置调试器,或者 Chrome DevTools for Node.js),可以在调用 model.startChat 的那行代码打个断点。当程序执行到断点时,检查 history 变量的值和结构。这是最强大的检查方式。

说明:
调试的关键在于确认传递给 SDK 函数的实际 数据是什么样的,而不是你以为 它是什么样的。有时候,数据可能在传递过程中被其他代码无意中修改了。

进阶技巧:管理 history 长度

请注意,随着对话轮次的增加,history 数组会越来越长。Gemini API 对输入 Token 总数是有限制的(包括历史记录和当前提示)。如果 history 太长,会导致请求失败(通常是报 Token 超限的错误)或者增加 API 调用成本。

管理策略:

  1. 截断: 保留最近的 N 轮对话。例如,只保留最近的 10 条消息(5 轮用户-模型对话)。
  2. 摘要: 对较早的对话历史进行总结,用一个简短的摘要替换多轮对话内容。这需要额外调用模型来生成摘要。
  3. 滑动窗口: 类似于截断,但更精细地控制保留的 Token 数量。

选择哪种策略取决于你的应用场景对上下文连贯性的要求以及成本预算。在实现这些策略时,也要确保处理后的 history 结构依然符合 API 要求。

希望上面这些分析和解决方案能帮你顺利解决在 Node.js 中使用 Gemini AI 时遇到的 history 参数问题。核心就是要细心检查数据结构和语法,确保它和官方文档或你正在使用的 SDK 版本要求一致。