Gemini Nano in Chrome 137: notes for AI Engineers

Shawn Wang

Chrome 137のGemini Nano:AIエンジニア向けメモ

原文は Shawn Wang により に公開されました。 このブログを購読する

ついに、Gemini NanoがすべてのChromeユーザーにほぼ届こうとしている(当初はChrome 138で入ると誤った情報を得ていたが、自分で事実確認したところ、Chrome 137以降では限定的な状況でフラグなしで出荷され始めている)。このHNの投稿で思い出させてもらった。年内には完全にフラグなしで出荷されると予想している。

Googleのドキュメントの書き方はあまり好きではないので、このブログ記事は要するに、自分の頭に合うように彼らのドキュメントを書き直したものだ。

よく使われるパターン向けにいくつかのAPIが用意されているが、正直、エンジニアとして本当に気になるのはPrompt APIだろう。最も柔軟で汎用的なやつだ。

画像

セットアップ

当初の大風呂敷だったwindow.aixanderchromeaiといったコピーキャットのシムを生んだが、いずれも現在はメンテされていない)とは異なり、現在リリースされている実装ははるかに「クリーン」ではない。まあ、とにかく今のセットアップ方法はこうだ。

  1. Chrome 137以降を使っていることを確認する
  2. chrome://flags/#prompt-api-for-gemini-nano にアクセスしてオンにする(残念ながらChromeの再起動が必要になる)
  3. その後、初めて LangaugeModel.create() を呼び出してモデルをダウンロードする — 自宅のWi-Fiなら数分かかる。Geminiによれば「おおよそのダウンロードサイズは1.5 GB〜2.4 GB」とのことなので、4〜8ビット量子化で4〜6B程度のモデルということだろう。
const session = await LanguageModel.create({
monitor(m) {
  m.addEventListener("downloadprogress", (e) => {
    console.log(`Downloaded ${e.loaded * 100}%`);
  });
},
// // uncomment if want multimodal input https://developer.chrome.com/docs/ai/prompt-api#multimodal_capabilities 
// expectedInputs: [
//  { type: "audio" },
//  { type: "image" }
//  ]
})

基本的な重要ポイント

ロードされたモデルのコンテキストは6kトークンだ(initialPromptsなしでinputQuotaを聞けばわかる):

session.inputQuota
// 6144

さて、Gemini Nanoチームとは違って、僕はfunction calling / JSON出力がすごく重要だと考えているタイプなので、Gemini Nanoでそれをどうやるか見ていこう。プロンプト例はHamelとJasonから拝借したものだ:

const JSONschema = `<schema>
{
    "description": "Correctly extracted \`UserDetail\` with all the required parameters with correct types",
    "name": "UserDetail",
    "parameters": {
        "properties": {
            "age": {
                "title": "Age",
                "type": "integer"
            },
            "name": {
                "title": "Name",
                "type": "string"
            }
        },
        "required": [
            "age",
            "name"
        ],
        "type": "object"
    }
}
</schema>`
const JSONsession = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a helpful LLM that only responds in valid JSON fitting a schema: ' + JSONschema },
    { role: 'user', content: "Extract Jason is 35 years old" },
    { role: 'assistant', content: '{age: 35, name: Jason}'},
  ]
});

const result1 = await JSONsession.prompt("Extract sarah is 22 years old");
console.log(result1);
// {age: 22, name: Sarah}

落とし穴

指示への追従はあまり得意ではないので、必須フィールドもあまり尊重されない:

const result1 = await JSONsession.prompt("its been a year since vibhu's birthday, he was 28 last year, guess how old he is now");
console.log(result1);
// { "age": 29 }

もう一つは、セッションがデフォルトでステートフルなことだ。忘れるとちょっと厄介になる。なので、ステートレス版はこんな感じになる:

const baseSession = await LanguageModel.create({
  initialPrompts: // blah blah, as above
})

// you can also implement this as a class if you want to force users to use`new` keyword to make super clear it is stateless
const statelessSession = {  
    async prompt(str) {
        const clonedSession = await session.clone()
        return clonedSession.prompt(str)
    }
}

// these are all stateless calls now! yay repeatability and predictability!
const result1 = await statelessSession.prompt("Extract sarah is 22 years old");
console.log(result1);
const result2 = await statelessSession.prompt("Extract tanisha is 30 years old");
console.log(result2);

こういった落とし穴があるからこそ、小さなラッパーライブラリを自作するか、https://github.com/kstonekuan/simple-chromium-aiのようなものを参照したくなるはずだ。

JSに詳しくない人向けの最後のヒントは、ブラウザのコンテキストで(つまりnpm installやビルドステップなしで)ESM構文を使ってそういったラッパーライブラリをインポートする方法だ(<script type="module">が必要になる場合もある — localhostか、CSPが緩いサイトで実行すること):

// alternatively use https://cdn.jsdelivr.net/npm/[email protected]/dist/simple-chromium-ai.mjs
const ChromiumAI = await import('https://unpkg.com/[email protected]/dist/simple-chromium-ai.mjs');

const ai = await ChromiumAI.initialize("You are a friendly assistant");
const response = await ChromiumAI.prompt(ai, "Tell me a joke");
console.log(response);
const ChromiumAI = await import('https://unpkg.com/[email protected]/dist/simple-chromium-ai.mjs');

const ai = await ChromiumAI.initialize("You are a friendly assistant");
const response = await ChromiumAI.prompt(ai, "Tell me a joke");
console.log(response);
// Why don't scientists trust atoms?  Because they make up everything! 

// and of course... the structured output implementation now works:
const schema = {
  type: "object",
  properties: {
    sentiment: {
      type: "string",
      enum: ["positive", "negative", "neutral"]
    },
    confidence: {
      type: "number",
      minimum: 0,
      maximum: 1
    },
    keywords: {
      type: "array",
      items: { type: "string" },
      maxItems: 5
    }
  },
  required: ["sentiment", "confidence", "keywords"]
};

// Create session with response constraint
const response = await ChromiumAI.prompt(
  ai, 
  "Analyze the sentiment of this text: 'I love this new feature!'",
  undefined, // no timeout
  { responseConstraint: schema }
);

// Response will be valid JSON matching the schema
const result = JSON.parse(response);
console.log(result);

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント