共计 1946 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
在实际开发中,直接调用 ChatGPT API 会遇到几个典型问题:

- 响应延迟高:普通 HTTP 请求需要等待完整响应,对话体验不流畅
- token 限制严格:长对话容易触发 4096 token 限制(gpt-3.5-turbo 模型)
- 状态维护复杂:多轮对话需要自行管理上下文关联
- 成本不可控:频繁调用可能产生意外费用
架构选型
方案对比
- Web App 方案
- 优点:开发简单,无需处理浏览器兼容性
-
缺点:无法深度集成浏览器功能,每次访问需重新加载
-
Browser Extension 方案
- 优点:常驻内存、支持后台运行、可访问浏览器 API
- 缺点:需处理 manifest 版本兼容问题
技术栈选择
最终采用的技术组合:
- Service Worker:处理后台网络请求
- IndexedDB:本地存储对话历史
- Streaming API:实现实时响应
- Protobuf:压缩存储数据
核心实现
带退避策略的 API 调用模块
/**
* 带指数退避的 API 调用
* @param prompt 用户输入
* @param maxRetries 最大重试次数(默认 3 次)*/
async function callWithRetry(prompt: string, maxRetries = 3) {
let delay = 1000;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {return await fetchAPI(prompt);
} catch (error) {if (attempt === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // 指数退避
}
}
}
流式响应处理
// 处理 Server-Sent Events (SSE)
function parseSSE(response: Response) {const reader = response.body?.getReader();
const decoder = new TextDecoder();
return new ReadableStream({async start(controller) {while (true) {const { done, value} = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
controller.enqueue(chunk.split('\n\n')
.filter(line => line.startsWith('data:'))
.map(line => JSON.parse(line.slice(6)))
);
}
controller.close();}
});
}
性能优化
对话历史压缩存储
// protobuf 定义
syntax = "proto3";
message Conversation {
repeated Message messages = 1;
message Message {
string role = 1;
string content = 2;
int64 timestamp = 3;
}
}
敏感词过滤
// 基础过滤规则
const SENSITIVE_PATTERNS = [/\b(密码 | 银行卡 | 身份证)\d{4,}/,
/\b(暴力 | 色情 | 毒品)\b/i
];
function sanitizeInput(text) {return SENSITIVE_PATTERNS.reduce((str, pattern) =>
str.replace(pattern, '[REDACTED]'),
text
);
}
避坑指南
Manifest V3 关键配置
{
"permissions": [
"storage",
"scripting",
"activeTab"
],
"host_permissions": ["https://api.openai.com/*"],
"content_security_policy": {"extension_pages": "script-src'self'; object-src'self'"}
}
Content Script 冲突解决
- 采用 Shadow DOM 隔离样式
- 使用唯一 ID 前缀(如
#chatgpt-ext-) - 避免使用!important 样式声明
代码规范
所有代码遵循:
- Airbnb 风格指南(2 空格缩进)
- TypeScript 严格模式
- 函数必须包含 JSDoc 注释
- 接口名称以
I前缀开头
延伸思考
如何实现插件的动态模块加载?可以考虑:
- WebAssembly 运行时加载算法模块
- 基于 import()的动态代码分割
- 使用 Web Worker 运行非关键路径代码
希望这篇实战指南能帮助你快速构建高效的 AI 助手插件。如果在实现过程中遇到具体问题,欢迎在评论区交流讨论。
正文完
发表至: 未分类
近两天内
