Chrome 137 中的 Gemini Nano:給 AI 工程師的筆記
原文由 Shawn Wang 于 發布,訂閱此部落格
終於,Gemini Nano 幾乎要向所有 Chrome 使用者開放了(我原本被誤導以為是在 Chrome 138——但我自己查證後發現,從 Chrome 137 起,在部分情境下已經開始不需開啟 flag 就釋出了)。我是被這篇 HN 貼文提醒的。我預期到今年年底會全面預設啟用。
我不太喜歡 Google 寫文件的方式,所以這篇部落格文章基本上就是我用符合自己腦袋的方式,把他們的文件重寫一遍。
他們提供了幾個針對常見使用情境的 API,不過說真的,身為工程師,你真正在乎的主要就是Prompt API,這是最靈活、開放性最高的一個。
設定
跟當初過度承諾的 window.ai(還催生了一堆像 xander 和 chromeai 這樣的仿製 shim,現在都沒人在維護了)不同,現在實際釋出的實作就沒那麼「乾淨」了。總之,以下是目前的設定方式。
- 確認你已經安裝 Chrome 137 以上版本
- 前往 chrome://flags/#prompt-api-for-gemini-nano 並把它開啟(很可惜,你得重新啟動 Chrome)
- 然後第一次呼叫
LangaugeModel.create()來下載模型——在家用 Wi-Fi 下大概要花幾分鐘。Gemini 說「下載大小約在 1.5 GB 到 2.4 GB 之間。」所以姑且可以說這是個 4-6B 參數、以 4-8 位元量化後的模型。
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 token 的上下文長度(只要在沒有任何 initialPrompts 的情況下去要 inputQuota 就能看到):
session.inputQuota
// 6144跟 Gemini Nano 團隊不同,我剛好是個覺得 function calling/JSON 輸出非常重要的人,所以來看看在 Gemini Nano 上怎麼把它跑起來,prompt 範例是從 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}陷阱
它在指令遵循(instruction following)方面表現不太好,所以 required 欄位其實不太會被遵守:
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 }另一點是,session 預設是有狀態(stateful)的,如果你忘了這件事會有點麻煩。所以一個無狀態(stateless)的版本看起來會像這樣:
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);就是因為有這些坑,你大概會想自己手刻一些小型的 wrapper 函式庫,或是參考 https://github.com/kstonekuan/simple-chromium-ai
最後給非 JS 專業人士的一個小技巧,是如何在瀏覽器環境中(也就是不用 npm install 或建置步驟)用 ESM 語法來引入這些 wrapper 函式庫(可能會需要 <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);隨機一篇部落格
留言
登入後參與討論