Chrome Skills重磅上线!浏览器秒变"龙虾助理",开发者必看
从开发者视角深度解析Chrome Skills功能,提供实战代码和最佳实践
导读
2026年4月,谷歌在Chrome浏览器中正式上线Skills功能,将OpenClaw(龙虾)的强大自动化能力深度集成到浏览器生态。
对于开发者来说,这不仅仅是一个新的功能,更是浏览器插件开发的新范式。本文将从技术实现、API设计、性能优化和最佳实践等角度,为开发者提供完整的Chrome Skills开发指南。
一、Chrome Skills技术架构
1.1 整体架构设计
Chrome Skills采用了现代化的分层架构:
interface ChromeSkillsArchitecture {
// 用户交互层
ui: {
sidebarPanel: SidebarUI,
popup: PopupUI,
omnibox: OmniboxIntegration,
contextMenu: ContextMenuHandler
},
// 技能管理层
skillManagement: {
storage: SkillStorageManager,
versionControl: SkillVersionManager,
registry: SkillRegistry,
permissionManager: PermissionManager
},
// 执行引擎层
executionEngine: {
geminiClient: GeminiClient,
taskScheduler: TaskScheduler,
agentOrchestrator: AgentOrchestrator,
resultCache: ResultCacheManager
},
// 数据持久化层
persistence: {
localStore: chrome.storage.local,
syncStore: chrome.storage.sync,
cacheManager: CacheManager,
auditLogger: AuditLogger
}
}
// 初始化架构
const skills = new ChromeSkillsArchitecture();
await skills.initialize();
1.2 核心接口定义
// 技能接口定义
interface Skill {
id: string;
name: string;
description: string;
version: string;
// 执行配置
executionConfig: {
type: 'simple' | 'agent' | 'multi_step';
timeout: number;
maxTokens: number;
temperature: number;
},
// 提示词模板
promptTemplate: string;
variables: SkillVariable[];
// 权限要求
permissions: Permission[];
// API配置
apiProvider: 'gemini' | 'local' | 'custom';
}
// 执行接口
interface SkillExecutor {
execute(
skill: Skill,
context: BrowserContext
): Promise<ExecutionResult>;
}
// 浏览器上下文
interface BrowserContext {
activeTab: chrome.tabs.Tab;
allTabs: chrome.tabs.Tab[];
selection: string;
clipboard: string;
history: chrome.history.HistoryItem[];
bookmarks: chrome.bookmarks.BookmarkTreeNode[];
}
// 执行结果
interface ExecutionResult {
success: boolean;
content?: string;
error?: string;
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
executionTime: number;
}
二、Gemini API集成
2.1 API客户端封装
import { GoogleGenerativeAI } from '@google/generative-ai';
class GeminiAPIClient {
private apiKey: string;
private model: string;
private client: GoogleGenerativeAI;
constructor(apiKey: string, model: string = 'gemini-2.0-pro') {
this.apiKey = apiKey;
this.model = model;
this.client = new GoogleGenerativeAI({ apiKey: this.apiKey });
}
async generate(prompt: string, options?: GenerationOptions): Promise<GenerationResponse> {
const generationConfig = {
model: this.model,
generationConfig: {
temperature: options?.temperature || 0.7,
topP: options?.topP || 1,
topK: options?.topK || 40,
maxOutputTokens: options?.maxTokens || 2048,
},
};
try {
const startTime = performance.now();
const result = await this.client.generateContent({
contents: [{ role: 'user', parts: [{ text: prompt }] }],
generationConfig,
});
const endTime = performance.now();
const executionTime = endTime - startTime;
return {
success: true,
text: result.response.text(),
usage: {
promptTokens: result.response.promptTokenCount,
completionTokens: result.response.candidatesTokenCount,
totalTokens: result.response.totalTokenCount,
},
executionTime,
model: this.model
};
} catch (error) {
console.error('Gemini API error:', error);
return {
success: false,
error: error.message
};
}
}
// 使用示例
const geminiClient = new GeminiAPIClient('your-api-key');
const result = await geminiClient.generate('分析这个网页内容...', {
temperature: 0.8,
maxTokens: 1024
});
console.log('生成结果:', result);
2.2 流式响应处理
class StreamingGeminiClient extends GeminiAPIClient {
async *generateStream(prompt: string): AsyncGenerator<StreamChunk> {
const generationConfig = {
model: this.model,
generationConfig: {
temperature: 0.7,
topP: 1,
topK: 40,
maxOutputTokens: 4096,
},
};
try {
const result = await this.client.generateContentStream({
contents: [{ role: 'user', parts: [{ text: prompt }] }],
generationConfig,
});
const stream = result.stream;
const reader = stream.getReader();
let fullText = '';
let chunkCount = 0;
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
if (value) {
chunkCount++;
const chunk = value.parts[0]?.text;
if (chunk) {
fullText += chunk;
// 触发进度事件
this.onProgress?.({
chunk,
chunkNumber: chunkCount,
partialText: fullText
});
}
}
}
return {
success: true,
text: fullText,
chunks: chunkCount
};
} catch (error) {
console.error('Stream error:', error);
return {
success: false,
error: error.message
};
}
}
}
// 流式处理使用示例
const streamingClient = new StreamingGeminiClient('your-api-key');
// 设置进度回调
streamingClient.onProgress = (progress) => {
console.log(`收到第${progress.chunkNumber}个片段:`, progress.partialText.substring(-50));
// 可以实时更新UI
if (typeof updateUI === 'function') {
updateUI(progress.partialText);
}
};
// 执行流式生成
async function executeStreamingSkill(skillPrompt) {
const generator = streamingClient.generateStream(skillPrompt);
for await (const chunk of generator) {
console.log('处理流式响应:', chunk);
}
console.log('生成完成:', chunk.text);
}
三、实战代码示例
3.1 网页分析技能
// manifest.json
{
"manifest_version": 3,
"name": "网页分析助手",
"version": "1.0.0",
"permissions": [
"activeTab",
"scripting"
],
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"background": {
"service_worker": "background.js",
"type": "module"
}
}
// background.js - Service Worker
import { GeminiAPIClient } from './gemini-client.js';
class SkillManager {
constructor() {
this.geminiClient = new GeminiAPIClient(
chrome.storage.local.get('gemini_api_key') || 'default-key'
);
this.skills = {
'web_analysis': {
id: 'web_analysis',
name: '网页内容分析',
promptTemplate: `
你是一个专业的网页内容分析助手。请分析当前网页内容,按照以下要求生成分析:
1. 提取核心观点(不超过3个)
2. 总结关键信息(不超过5条)
3. 评估内容质量(1-5分)
4. 给出阅读建议(1-2条)
当前网页内容:
{page_content}
`,
variables: [
{
name: 'page_content',
type: 'text',
required: true
}
],
executionConfig: {
type: 'simple',
timeout: 30000,
maxTokens: 1024
}
}
};
}
async executeSkill(skillId: string, context: any) {
const skill = this.skills[skillId];
if (!skill) {
throw new Error(`Skill ${skillId} not found`);
}
// 执行技能
const result = await this.geminiClient.generate(
skill.promptTemplate,
{
temperature: skill.executionConfig.temperature,
maxTokens: skill.executionConfig.maxTokens
}
);
// 返回结果给content script
return {
skillId,
success: result.success,
content: result.text,
usage: result.usage
};
}
}
// content.js - 内容脚本
class ContentAnalyzer {
async analyzeCurrentPage() {
// 获取当前页面内容
const [tab] = await chrome.tabs.query({ active: true });
if (!tab[0]) {
throw new Error('No active tab');
}
try {
// 提取页面内容
const [result] = await chrome.scripting.executeScript({
target: { tabId: tab[0].id },
func: () => {
// 提取页面标题、正文等内容
const title = document.querySelector('h1')?.textContent || document.title;
const content = document.body?.innerText || '';
const metaDescription = document.querySelector('meta[name="description"]')?.content || '';
return {
title,
content,
metaDescription,
url: tab[0].url
};
}
});
if (!result || result[0].error) {
throw new Error('Failed to extract content');
}
const pageContent = result[0].result;
// 调用background进行技能执行
const response = await chrome.runtime.sendMessage({
action: 'executeSkill',
skillId: 'web_analysis',
context: pageContent
});
// 显示分析结果
this.displayAnalysisResult(response);
} catch (error) {
console.error('Analysis failed:', error);
this.displayError(error.message);
}
}
displayAnalysisResult(result) {
// 创建结果展示面板
const panel = document.createElement('div');
panel.className = 'skill-result-panel';
panel.innerHTML = `
<div class="skill-header">
<h2>📊 网页分析结果</h2>
</div>
<div class="skill-content">
${this.formatContent(result.content)}
</div>
<div class="skill-footer">
<button id="closePanel">关闭</button>
</div>
`;
document.body.appendChild(panel);
// 添加关闭按钮事件
document.getElementById('closePanel')?.addEventListener('click', () => {
panel.remove();
});
}
formatContent(content) {
// 格式化分析内容
return content
.replace(/\n\n/g, '<br><br>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/_([^_]+)_/g, '<em>$1</em>');
}
}
// 初始化分析器
const analyzer = new ContentAnalyzer();
// 监听页面加载完成事件
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'analyzePage') {
analyzer.analyzeCurrentPage();
sendResponse({ success: true });
}
});
3.2 多标签页数据聚合技能
// 跨标签页数据聚合
class CrossTabDataAggregator {
constructor() {
this.activeTasks = new Map();
this.collectedData = new Map();
}
async aggregateData(skillConfig: any) {
// 1. 获取所有标签页
const tabs = await chrome.tabs.query({});
// 2. 提取每个页面的数据
const tasks = tabs.map(tab => ({
tabId: tab.id,
url: tab.url,
status: 'pending'
}));
// 3. 并发执行数据提取
const results = await Promise.all(
tasks.map(task => this.extractPageData(task))
);
// 4. 汇总数据
return this.aggregateResults(results);
}
async extractPageData(task) {
const { tabId, url } = task;
try {
const [result] = await chrome.scripting.executeScript({
target: { tabId },
func: () => {
// 页面数据提取逻辑
const data = this.extractPageData();
return {
success: true,
data,
tabId
};
}
});
task.status = 'completed';
task.result = result;
task.data = result;
return result;
} catch (error) {
task.status = 'failed';
task.error = error.message;
return { success: false, error };
}
}
extractPageData() {
// 实现页面数据提取
// 根据具体页面类型实现不同的提取逻辑
const commonElements = {
products: document.querySelectorAll('.product-item'),
prices: document.querySelectorAll('.price-tag'),
titles: document.querySelectorAll('h1, h2, h3'),
descriptions: document.querySelectorAll('.description')
};
return {
products: Array.from(commonElements.products).map(el => ({
name: el.querySelector('.name')?.textContent,
price: el.querySelector('.price')?.textContent
})),
prices: Array.from(commonElements.prices).map(el => el.textContent),
titles: Array.from(commonElements.titles).map(el => el.textContent),
descriptions: Array.from(commonElements.descriptions).map(el => el.textContent)
};
}
aggregateResults(results) {
// 汇总所有标签页的数据
const aggregated = {
totalTabs: results.length,
successfulTabs: results.filter(r => r.success).length,
allProducts: results.flatMap(r => r.data?.products || []),
allPrices: results.flatMap(r => r.data?.prices || []),
allTitles: results.flatMap(r => r.data?.titles || []),
allDescriptions: results.flatMap(r => r.data?.descriptions || []),
timestamp: Date.now()
};
// 发送结果
chrome.runtime.sendMessage({
action: 'aggregationComplete',
data: aggregated
});
return aggregated;
}
}
// 使用示例
const aggregator = new CrossTabDataAggregator();
// 执行数据聚合
const aggregationConfig = {
targetTabs: 'all',
dataTypes: ['products', 'prices', 'titles']
};
const result = await aggregator.aggregateData(aggregationConfig);
console.log('聚合结果:', result);
四、性能优化策略
4.1 内存管理优化
class MemoryOptimizedSkillManager {
constructor() {
this.skills = new Map();
this.cache = new LRUCache(100); // 最多缓存100个结果
this.memoryPool = new Pool(10); // 对象池复用
}
async getSkill(skillId: string) {
// 1. 检查缓存
const cached = this.cache.get(skillId);
if (cached && !this.isExpired(cached)) {
return cached.data;
}
// 2. 从存储加载
const skill = await this.loadSkill(skillId);
if (!skill) {
throw new Error(`Skill ${skillId} not found`);
}
// 3. 缓存结果
this.cache.set(skillId, {
data: skill,
timestamp: Date.now()
});
return skill;
}
isExpired(cachedItem) {
const ttl = 5 * 60 * 1000; // 5分钟TTL
return Date.now() - cachedItem.timestamp > ttl;
}
}
// LRU缓存实现
class LRUCache {
constructor(maxSize) {
this.maxSize = maxSize;
this.cache = new Map();
this.accessOrder = [];
}
get(key) {
const item = this.cache.get(key);
if (item) {
// 更新访问顺序
this.accessOrder = this.accessOrder.filter(k => k !== key);
this.accessOrder.push(key);
return item.data;
}
return undefined; // 缓存未命中
}
set(key, data) {
// 检查容量
if (this.cache.size >= this.maxSize) {
this.evict();
}
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
evict() {
// LRU淘汰策略
const oldestKey = this.accessOrder.shift();
this.cache.delete(oldestKey);
}
}
4.2 异步任务队列优化
class AsyncTaskQueue {
constructor(maxConcurrent = 3) {
this.queue = [];
this.activeCount = 0;
this.maxConcurrent = maxConcurrent;
this.results = new Map();
}
async enqueue(task) {
return new Promise((resolve, reject) => {
this.queue.push({
task,
resolve,
reject,
timestamp: Date.now()
});
this.processQueue();
});
}
processQueue() {
while (this.activeCount < this.maxConcurrent && this.queue.length > 0) {
const { task, resolve, reject } = this.queue.shift();
this.activeCount++;
task()
.then(result => {
this.results.set(task.id, {
success: true,
result,
timestamp: Date.now()
});
resolve(result);
})
.catch(error => {
this.results.set(task.id, {
success: false,
error,
timestamp: Date.now()
});
reject(error);
})
.finally(() => {
this.activeCount--;
});
}
}
async getResult(taskId) {
return new Promise((resolve, reject) => {
const checkInterval = setInterval(() => {
const result = this.results.get(taskId);
if (result) {
clearInterval(checkInterval);
resolve(result);
} else if (this.queue.length === 0 && this.activeCount === 0) {
clearInterval(checkInterval);
reject(new Error('Task failed and queue is empty'));
}
}, 100);
});
}
}
// 使用示例
const taskQueue = new AsyncTaskQueue(5);
// 添加任务
const task1 = {
id: 'task1',
fn: async () => {
console.log('Executing task 1...');
await new Promise(resolve => setTimeout(resolve, 2000));
return 'Task 1 result';
}
};
const task2 = {
id: 'task2',
fn: async () => {
console.log('Executing task 2...');
await new Promise(resolve => setTimeout(resolve, 1500));
return 'Task 2 result';
}
};
// 并发执行任务
const results = await Promise.all([
taskQueue.enqueue(task1),
taskQueue.enqueue(task2)
]);
console.log('All tasks completed:', results);
五、安全与权限管理
5.1 最小权限原则
// 权限请求策略
class MinimalPermissionManager {
constructor() {
this.requiredPermissions = [];
this.optionalPermissions = [];
}
async requestPermissions(permissions) {
// 只请求必要的权限
const minimalPermissions = this.filterMinimalPermissions(permissions);
const granted = await chrome.permissions.request(minimalPermissions);
if (granted) {
console.log('Permissions granted:', minimalPermissions);
return true;
} else {
console.error('Permissions denied:', minimalPermissions);
return false;
}
}
filterMinimalPermissions(requested) {
// 移除不必要的权限
const dangerousPermissions = ['<all_urls>', 'history', 'bookmarks'];
return requested.filter(perm =>
!dangerousPermissions.includes(perm)
);
}
}
// 使用示例
const permissionManager = new MinimalPermissionManager();
// 只请求必要的权限
const necessaryPermissions = ['activeTab', 'scripting'];
const granted = await permissionManager.requestPermissions(necessaryPermissions);
六、开发工具与调试
6.1 技能调试工具
// 技能调试器UI
class SkillDebugger {
constructor() {
this.currentSkill = null;
this.variables = {};
this.executionHistory = [];
this.isRecording = false;
}
loadSkill(skillId) {
chrome.storage.local.get([`skill_${skillId}`], (result) => {
if (result[`skill_${skillId}`]) {
this.currentSkill = result[`skill_${skillId}`];
this.renderSkillEditor();
}
});
}
renderSkillEditor() {
if (!this.currentSkill) return;
const container = document.getElementById('skill-editor-container');
if (!container) return;
container.innerHTML = `
<div class="skill-editor">
<div class="skill-info">
<h3>${this.currentSkill.name}</h3>
<p>${this.currentSkill.description}</p>
</div>
<div class="variables-section">
<h4>变量定义</h4>
<div id="variables-editor"></div>
<button id="add-variable">+ 添加变量</button>
</div>
<div class="prompt-section">
<h4>提示词模板</h4>
<textarea id="prompt-editor" rows="10">${this.currentSkill.promptTemplate}</textarea>
</div>
<div class="actions">
<button id="save-skill">保存技能</button>
<button id="test-skill">测试技能</button>
<button id="record-macro">录制宏</button>
</div>
<div class="execution-history">
<h4>执行历史</h4>
<div id="history-list"></div>
</div>
</div>
`;
// 添加事件监听
this.attachEventListeners();
}
attachEventListeners() {
document.getElementById('save-skill')?.addEventListener('click', () => {
this.saveSkill();
});
document.getElementById('test-skill')?.addEventListener('click', () => {
this.testSkill();
});
document.getElementById('add-variable')?.addEventListener('click', () => {
this.addVariable();
});
document.getElementById('record-macro')?.addEventListener('click', () => {
this.recordMacro();
});
}
}
// 使用调试器
const debugger = new SkillDebugger();
debugger.loadSkill('web_analysis');
七、总结与最佳实践
7.1 开发最佳实践
-
模块化设计
- 将功能拆分为独立模块
- 降低耦合度
- 便于测试和维护
-
错误处理
- 完善的错误捕获机制
- 提供友好的错误提示
- 实现优雅的降级
-
性能优化
- 使用缓存减少API调用
- 实现异步处理
- 优化内存使用
-
安全考虑
- 最小权限原则
- 数据加密存储
- 用户隐私保护
-
用户体验
- 提供实时反馈
- 优化加载速度
- 设计直观的界面