Gemini,本地且免费,使用 Chrome 与 Angular

发布: (2025年12月30日 GMT+8 12:00)
10 min read
原文: Dev.to

Source: Dev.to

可用的 API

  • Language API – 检测给定文本的语言。
  • Translation API – 将文本从一种语言翻译成另一种语言。
  • Prompt API – 接受自由形式的提示以生成结构化输出。
  • Summarization API – 将长文本压缩为简洁的摘要。

在本文中,我将展示如何利用这些 API 添加真正有用的功能,而无需为 token 付费。

我觉得特别有趣的一点是,Google 搜索现在经常会给出 AI 生成的摘要,甚至直接给出答案。有时你只想要要点,而不是整篇文章。这个想法正是我们将在这里复现的内容。

Google 搜索摘要功能

Google 搜索摘要功能

目标: 构建一段小逻辑,为博客文章生成 TL;DR(太长不看)要点列表,让读者能够瞬间了解文章内容。

入门

  1. 更新 Chrome – 确保您使用的是最新版本。旧版本可能不包含最新的本地模型。

  2. 启用所需的标志 并重新启动 Chrome:

    chrome://flags/#optimization-guide-on-device-model
    chrome://flags/#prompt-api-for-gemini-nano-multimodal-input
  3. 安装模型,在控制台(或能够访问 Chrome API 的脚本)中运行以下代码:

    const session = await LanguageModel.create({
      monitor(m) {
        m.addEventListener('downloadprogress', (e) => {
          console.log(`Downloaded ${e.loaded * 100}%`);
        });
      },
    });
    
    const summarizer = await Summarizer.create({
      monitor(m) {
        m.addEventListener('downloadprogress', (e) => {
          console.log(`Downloaded ${e.loaded * 100}%`);
        });
      },
    });

    当下载达到 100 % 时,即可使用这些 API。

实现

我们将使用 Angular 来实现。如果你还没有安装好环境:

# 安装 Angular CLI
npm install -g @angular/cli

# 创建一个新项目(将 <name> 替换为你想要的名称)
ng new <name>

UI 提示(用于 Antigravity IDE)

下面是我们输入到 Google 新 IDE Antigravity 中以生成 UI 组件的提示:

You are an expert Frontend Developer. Your task is to build a premium, visually stunning blog post page for an Angular application. The project is already initialized.

### Goal
Create a standalone component named `BlogPost` that serves as a static blog post page. The design should be modern, "dark mode" by default, and evoke a high‑tech, futuristic feel suitable for the topic of "Agentic AI".

### Structure & Content Constraints
The page must contain the following specific elements, stacked vertically:

1. **Header Section**
   - **Tags**: A row of small pill‑shaped tags: "Artificial Intelligence", "Future", "Tech".
   - **Title**: Large, impactful typography: "The Rise of Agentic AI: A New Era of Coding".
   - **Subtitle**: A lighter sub‑heading: "How autonomous agents are transforming the software development landscape".

2. **TL;DR Section**
   - Placed prominently below the header but before the main content.
   - Must clearly stand out (e.g., border, different background tint, or accent color).
   - **Heading**: "TL;DR".
   - **Content**: A bulleted list summarizing the article (e.g., AI moving from autocomplete to autonomy, changing developer roles to architects).

3. **Main Content**
   - Several paragraphs discussing "Agentic AI".
   - Explain how it differs from traditional coding assistants.
   - Discuss the shift from "writing code" to "guiding agents".
   - Use highly readable typography with good line height and contrast.

### Design & Aesthetics (Crucial)
- **Theme**: Dark mode. Background very dark (nearly black), text light grey/white.
- **Typography**: Clean sans‑serif font like *Inter*.
- **Color Palette**: Neon/electric accents.
  - *Primary Accent*: Electric teal or cyan (for tags/highlights).
  - *Secondary Accent*: Electric purple (for the TL;DR section or links).
- **Visual Style**:
  - Blog post container looks like a "card" floating in the center with subtle shadow and rounded corners.
  - Subtle gradients for the title if possible.
  - Fully responsive (mobile‑friendly).

### Technical Requirements
- Use Angular **Standalone Components**.
- Hardcode all text directly in the template or component class.
- **Do NOT** implement any actual AI calls or backend services; this is purely a UI implementation task.

生成的 UI Mockup

Antigravity IDE

此时,重点纯粹在 UI 上。尚未涉及任何 AI 调用。

实现 API

创建一个新的 Angular 服务来封装设备上的 API。下面是一个干净的起始模板:

import { Injectable } from '@angular/core';

// Types for the Chrome on‑device APIs (replace with actual typings if available)
declare const LanguageModel: any;
declare const Summarizer: any;

@Injectable({
  providedIn: 'root',
})
export class OnDeviceAiService {
  private languageModel: any;
  private summarizer: any;

  constructor() {
    this.initModels();
  }

  /** Load the on‑device models */
  private async initModels(): Promise {
    this.languageModel = await LanguageModel.create({
      monitor: (m: any) => {
        m.addEventListener('downloadprogress', (e: any) => {
          console.log(`Language model download: ${e.loaded * 100}%`);
        });
      },
    });

    this.summarizer = await Summarizer.create({
      monitor: (m: any) => {
        m.addEventListener('downloadprogress', (e: any) => {
          console.log(`Summarizer download: ${e.loaded * 100}%`);
        });
      },
    });
  }

  /** Detect language of a string */
  async detectLanguage(text: string): Promise {
    if (!this.languageModel) {
      await this.initModels();
    }
    return this.languageModel.detectLanguage(text);
  }

  /** Summarize a block of text */
  async summarize(text: string): Promise {
    if (!this.summarizer) {
      await this.initModels();
    }
    return this.summarizer.summarize(text);
  }
}

现在可以在任何组件(例如 BlogPost 组件)中注入 OnDeviceAiService,并调用 summarize() 来生成 TL;DR 项目符号列表。

接下来的步骤

  1. 将服务接入 BlogPost 组件,并在 TL;DR 区域显示生成的项目符号。
  2. 添加错误处理,以应对模型加载失败的情况。
  3. 尝试使用 Prompt API,获取更结构化的输出(例如 JSON 格式的摘要)。

就这样!你已经拥有一个零成本、在设备上运行的 AI 流程,能够在不消耗 token 预算的情况下为你的 Web 应用增添智能功能。祝编码愉快!

使用 Chrome 的 Summarizer 与 Prompt API 实现 AI 驱动的 TL;DR 生成

以下是一份完整、整理后的指南,教你如何创建一个服务,从文章中提取关键点并在 Angular 组件中以 TL;DR 列表的形式呈现。代码使用 Chrome 的 Summarizer API(稳定版)和 Prompt API(实验版)——均在本地运行,无需消耗 token 或产生费用。

1. 创建 Summarizer 会话

Summarizer 用于从文本中提取最重要的要点。

private async createSummarizerSession() {
  return await Summarizer.create({
    type: 'key-points',               // 决定摘要的风格
    length: 'short',                 // 简短摘要
    expectedInputLanguages: ['en'],  // 输入语言
    outputLanguage: 'en',            // 输出语言
    expectedContextLanguages: ['en'],
    sharedContext: 'About AI and Agentic AI'
  });
}

注意: type 字段决定了摘要的生成方式。

Summarizer's parameters

完整选项列表请参见官方文档。

2. 创建 Prompt(语言模型)会话

我们将使用 Prompt API 将原始要点转换为干净的 HTML。

private async createLanguageModelSession() {
  return await LanguageModel.create({
    initialPrompts: [
      {
        role: 'system',
        content: 'Convert these bullets into HTML. Return only the HTML.'
      }
    ],
  });
}

3. 合并两个会话

下面的函数将所有步骤串联起来:

async generateTlDr(content: string): Promise {
  // 1️⃣ 对文章进行摘要
  const summarizer = await this.createSummarizerSession();
  const summary = await summarizer.summarize(content, {
    context: 'This article is intended for a tech‑savvy audience.',
  });
  summarizer.destroy();

  // 2️⃣ 将摘要转换为结构化输出
  const lm = await this.createLanguageModelSession();
  const result = await lm.prompt(summary, { responseConstraint: schema });
  lm.destroy();

  // 3️⃣ 解析 JSON 响应
  const parsed = JSON.parse(result);
  return parsed?.items ?? [];
}

schema(后文会展示)强制 Prompt API 返回一个包含 "bullet_list" 类型和 items 字符串数组的 JSON 对象。

4. 完整服务代码

import { Injectable } from '@angular/core';

declare const Summarizer: any;
declare const LanguageModel: any;

const schema = {
  type: 'object',
  required: ['type', 'items'],
  properties: {
    type: {
      type: 'string',
      enum: ['bullet_list'],
      description: 'Identifies the content as a bullet list'
    },
    items: {
      type: 'array',
      minItems: 1,
      items: {
        type: 'string',
        minLength: 1
      },
      description: 'Each entry is one bullet item, without bullet symbols'
    }
  },
  additionalProperties: false
};

@Injectable({
  providedIn: 'root'
})
export class AiService {
  async generateTlDr(content: string): Promise {
    const summarizer = await this.createSummarizerSession();
    const summary = await summarizer.summarize(content, {
      context: 'This article is intended for a tech‑savvy audience.',
    });
    summarizer.destroy();

    const lm = await this.createLanguageModelSession();
    const result = await lm.prompt(summary, { responseConstraint: schema });
    lm.destroy();

    const parsed = JSON.parse(result);
    return parsed?.items ?? [];
  }

  private async createSummarizerSession() {
    return await Summarizer.create({
      type: 'key-points',
      length: 'short',
      expectedInputLanguages: ['en'],
      outputLanguage: 'en',
      expectedContextLanguages: ['en'],
      sharedContext: 'About AI and Agentic AI'
    });
  }

  private async createLanguageModelSession() {
    return await LanguageModel.create({
      initialPrompts: 

Source:

At this point the heavy lifting is done.

5. 将服务接入组件

组件 TypeScript

import { Component, inject, OnInit, signal } from '@angular/core';
import { AiService } from './ai.service';

@Component({
  selector: 'app-post',
  templateUrl: './post.component.html',
  styleUrls: ['./post.component.scss']
})
export class PostComponent implements OnInit {
  private aiService = inject(AiService);
  public readonly postContent = content;               // ([]);

  async ngOnInit() {
    const tldr = await this.aiService.generateTlDr(this.postContent);
    this.tltrContent.set(tldr);
  }
}

组件模板 (post.component.html)

<h3>TL;DR</h3>

@if (tltrContent().length > 0) {
  <ul>
    @for (item of tltrContent(); track $index) {
      <li>{{ item }}</li>
    }
  </ul>
} @else {
  Loading...
}

TL;DR 列表将在本地 AI 完成处理后渲染。

6. 结果预览

以下是完全在设备上生成的真实示例(无外部令牌,无费用)。

Blog post final example

7. 支持与稳定性

API稳定性在本示例中的作用
Summarizer API稳定生成要点摘要
Prompt API实验性将项目符号格式化为 HTML(或 JSON)
Back to Blog

相关文章

阅读更多 »