科技

AI蓝皮书:Gemini反代教程,免费API调用Gemini 3 pro模型(兼容酒馆/Chatbox)

AI本地布置&远端调用系列教程

[展开/折叠]

Google AI/Gemini终于出3.0版本了!但是3.0版本当前仅允许在官方页面上免费试用,任何第三方API调用都要付费。虽然网页版给的很大方(100次/天),但是确实有很多应用第三方调用体验会更好。为了解决这个问题,BLOG主今天教大家如何通过反代(劫持网页端)的方式,让第三方软件也能免费调用GOOGLE最新的Gemini 3 pro模型!

PS:本教程整合自类脑多位大大的成果,原始教程参考的是这篇,已经集成了“𝓟𝓻𝓲𝓼𝓸𝓷𝓮𝓻”大佬提供的最新版本,安全性和稳定性都得到过验证。

SillyTavern(酒馆/AI猫娘)系列

[展开/折叠]

一、搭建Huggingface服务

我们首先需要使用Huggingface搭建一个反代服务器,架设一个从Google AI到终端服务(比如酒馆/chatbox)的桥梁。

1、打开官网,点击右上角Sign Up按钮,按照要求填写邮箱和密码(如果打不开网站,你应该需要梯子)。注册完成后,记住去邮箱点击验证链接,否则后面会无法继续操作。

2、接着打开BLOG主建立的这个仓库地址,点击上方右侧的三个点,在展开菜单中,选择“Duplicate this Space”,将会复制BLOG主这个仓库的内容到你自己的账号上。

3、在弹出的对话框中,Visibility中选择Public,确保服务器能被公网检索。然后下面的MY_SECRET_KEY是连接这个代理服务器的密码(展示密码为13579,第三方接入,这个密码是必须的),这里建议自己设置的复杂一些,以免出现安全风险。最后点击Duplicate Space,完成全部设置。

4、等待约1分钟,随后根据下图,确保这个新建的服务属于自己,确保服务已经正确运行。

5、然后再次点击上面的三个点,选择“Embed this Space”,随后在打开的窗口中,点击“Direct URL”右面的“Copy”按钮,这样就会在剪贴板中获得该服务的API接口地址(建议找个地方保存)。

6、保持这个网页开启,不能关闭!!不能关闭!!不能关闭!!

二、浏览器部分

1、类脑的大佬对服务部分做了更新,建议下载最新版本的“dark-browser-vps.js”(最新服务端dark-server-vps.js已经整合)。由于不同版本的内容需要对应,因此建议直接点击这里下载BLOG主整理好的文件。

扩展内容:更新服务器

[展开/折叠]
  • 如果未来代码有更新,并且希望手动更新到最新版,则需要到仓库下载“dark-server-vps.js”和“package.json”,上传并覆盖原本在“Files”下的同名文件。如有必要还需要更新Dockerfile中的代码(但一般不需要,BLOG主已经写好)。

2、接下来我们需要修改“dark-browser-vps.js”的一些配置,右键这个文件,选择笔记本打开,然后“CTRL+F”搜索“constructor(endpoint =”。最后在后面添加刚刚复制好的API接口地址。注意需要把原始地址中的“https”改成“wss”。

扩展内容:下载困难的朋友,可直接复制内容(记住修改)

[展开/折叠]
  • const WS_ENDPOINT = “wss://wuchen.zeabur.app”;

    const log = (…msgs) => {
    const time = new Date().toLocaleTimeString(“zh-CN”, { hour12: false });
    const ms = `.${new Date().getMilliseconds().toString().padStart(3, “0”)}`;
    const timestamp = `[${time}${ms}]`;
    console.log(`[ProxyClient]`, timestamp, …msgs);

    const div = document.createElement(“div”);
    div.textContent = `${timestamp} ${msgs.join(” “)}`;
    document.body.appendChild(div);
    };

    class Connection extends EventTarget {
    #ws = null;
    #reconnectTimer = null;
    #attempts = 0;

    constructor(endpoint = “wss://hyy890627-studiosep.hf.space”) {
    super();
    this.endpoint = endpoint;
    this.connected = false;
    }

    async connect() {
    if (this.connected) return;
    log(“连接中:”, this.endpoint);
    try {
    this.#ws = new WebSocket(this.endpoint);
    this.#bindEvents();
    } catch (err) {
    log(“WS初始化失败:”, err.message);
    this.#reconnect();
    }
    }

    send(data) {
    if (!this.connected) return log(“发送失败: 未连接”);
    this.#ws.send(JSON.stringify(data));
    }

    #bindEvents() {
    this.#ws.addEventListener(“open”, () => {
    this.connected = true;
    this.#attempts = 0;
    this.#clearReconnectTimer();
    log(“✅ 连接成功”);
    this.dispatchEvent(new Event(“connected”));
    });

    this.#ws.addEventListener(“close”, () => {
    if (this.connected) {
    this.connected = false;
    log(“❌ 连接断开”);
    this.dispatchEvent(new Event(“disconnected”));
    }
    this.#reconnect();
    });

    this.#ws.addEventListener(“error”, (err) => log(“WS错误:”, err));
    this.#ws.addEventListener(“message”, (e) => this.dispatchEvent(new MessageEvent(“message”, { data: e.data })));
    }

    #reconnect() {
    if (this.#reconnectTimer) return;
    this.#attempts++;
    const delay = 5000;
    log(`${delay / 1000}秒后尝试重连 (第 ${this.#attempts} 次)…`);
    this.#reconnectTimer = setTimeout(() => {
    this.#reconnectTimer = null;
    this.connect();
    }, delay);
    }

    #clearReconnectTimer() {
    clearTimeout(this.#reconnectTimer);
    this.#reconnectTimer = null;
    }
    }

    class Processor {
    #ops = new Map();
    #domain = “generativelanguage.googleapis.com”;

    async exec(spec, id) {
    const ctrl = new AbortController();
    this.#ops.set(id, ctrl);
    try {
    return await this.#retry(spec, ctrl);
    } finally {
    this.#ops.delete(id);
    }
    }

    cancelAll() {
    this.#ops.forEach((ctrl) => ctrl.abort(“Connection closed”));
    this.#ops.clear();
    }

    async #retry(spec, ctrl) {
    for (let i = 1; i <= 3; i++) {
    try {
    log(`执行请求 (${i}/3):`, spec.method, spec.path);
    const url = this.#buildUrl(spec);
    const config = this.#buildConfig(spec, ctrl.signal);
    const res = await fetch(url, config);
    if (!res.ok) throw new Error(`API错误: ${res.status} ${res.statusText} ${await res.text()}`);
    return res;
    } catch (err) {
    if (err.name === “AbortError”) throw err;
    log(`❌ 尝试 #${i} 失败: ${err.message.slice(0, 200)}`);
    if (i < 3) await new Promise((r) => setTimeout(r, 2000));
    else throw err;
    }
    }
    }

    #buildUrl(spec) {
    let path = spec.path.startsWith(“/”) ? spec.path.slice(1) : spec.path;
    const params = new URLSearchParams(spec.query_params);
    if (spec.streaming_mode === “fake”) {
    path = path.replace(“:streamGenerateContent”, “:generateContent”);
    params.delete(“alt”);
    }
    const query = params.toString();
    return `https://${this.#domain}/${path}${query ? `?${query}` : “”}`;
    }

    #buildConfig(spec, signal) {
    const config = { method: spec.method, headers: this.#cleanHeaders(spec.headers), signal };
    if ([“POST”, “PUT”, “PATCH”].includes(spec.method) && spec.body) config.body = spec.body;
    return config;
    }

    #cleanHeaders = (headers) => {
    const clean = { …headers };
    [“host”, “connection”, “content-length”, “origin”, “referer”, “user-agent”, “sec-fetch-mode”, “sec-fetch-site”, “sec-fetch-dest”].forEach(h => delete clean[h]);
    return clean;
    }
    }

    class Proxy {
    #conn;
    #proc = new Processor();

    constructor(endpoint) {
    this.#conn = new Connection(endpoint);
    this.#setup();
    }

    async init() {
    log(“系统初始化…”);
    await this.#conn.connect();
    log(“系统就绪”);
    }

    #setup() {
    this.#conn.addEventListener(“message”, (e) => this.#onMessage(e.data));
    this.#conn.addEventListener(“disconnected”, () => this.#proc.cancelAll());
    }

    async #onMessage(data) {
    try {
    const spec = JSON.parse(data);
    log(`收到请求: ${spec.method} ${spec.path} (${spec.streaming_mode || “fake”})`);
    await this.#process(spec);
    } catch (err) {
    log(“处理错误:”, err.message);
    this.#sendError(err, JSON.parse(data)?.request_id);
    }
    }

    async #process(spec) {
    const { request_id: id, streaming_mode: mode = “fake”, path } = spec;
    const isStream = path.includes(“:streamGenerateContent”);
    let finishReason = “UNKNOWN”;

    try {
    const res = await this.#proc.exec(spec, id);
    this.#sendHeaders(res, id);

    if (!res.body) {
    this.#logComplete({ mode, isStream, fullBody: “”, finishReason });
    return this.#sendEnd(id);
    }

    const stream = res.body.pipeThrough(new TextDecoderStream());
    let fullBody = “”;

    for await (const chunk of stream) {
    if (mode === “real”) {
    if (isStream) {
    finishReason = this.#extractFinish(chunk, finishReason);
    }
    this.#sendChunk(chunk, id);
    } else {
    fullBody += chunk;
    }
    }

    log(“流读取完成”);
    this.#logComplete({ mode, isStream, fullBody, finishReason });

    if (mode === “fake”) this.#sendChunk(fullBody, id);
    this.#sendEnd(id);
    } catch (err) {
    log(`❌ 错误: ${err.message}`);
    this.#sendError(err, id);
    }
    }

    #extractFinish(chunk, currentReason) {
    try {
    const lines = chunk.split(‘\n’);
    for (const line of lines) {
    if (line.startsWith(‘data: ‘)) {
    const payload = JSON.parse(line.slice(5));
    const reason = payload.candidates?.[0]?.finishReason;
    if (reason) return reason;
    }
    }
    } catch {}
    return currentReason;
    }

    #logComplete({ mode, isStream, fullBody, finishReason }) {
    if (mode === “real”) {
    if (!isStream) return log(“✅ 响应成功”);
    return log(finishReason === “STOP” ? “✅ 响应成功” : `🤔 响应异常: ${finishReason}`);
    }

    let msg = “✅ 响应成功”;
    if (isStream) {
    try {
    const parsed = JSON.parse(fullBody);
    const reason = parsed.candidates?.[0]?.finishReason;
    msg = reason === “STOP” ? “✅ 响应成功” : `🤔 响应异常: ${reason || “未知”}`;
    } catch {
    msg = “⚠️ 响应非JSON”;
    }
    }
    log(msg);
    }

    #sendHeaders(res, id) {
    const headers = {};
    res.headers.forEach((v, k) => (headers[k] = v));
    this.#conn.send({ request_id: id, event_type: “response_headers”, status: res.status, headers });
    }

    #sendChunk(chunk, id) {
    if (chunk) this.#conn.send({ request_id: id, event_type: “chunk”, data: chunk });
    }

    #sendEnd(id) {
    this.#conn.send({ request_id: id, event_type: “stream_close” });
    log(“任务完成”);
    }

    #sendError(err, id) {
    if (!id) return;
    this.#conn.send({
    request_id: id,
    event_type: “error”,
    status: 504,
    message: `浏览器错误: ${err.name === “AbortError” ? “请求被中止” : err.message}`,
    });
    log(“错误已发送”);
    }
    }

    async function main() {
    document.body.innerHTML = “”;
    try {
    const proxy = new Proxy();
    await proxy.init();
    } catch (err) {
    log(“启动失败:”, err.message);
    }
    }

    main();

3、打开这个链接,登录自己的GOOGLE账号(需要梯子、不建议用大号)。在打开的页面中,点击“Code”,然后点击“index.tsx”,最后将上一步修改好的“dark-browser-vps.js”(或者直接复制修改好的上述代码)中所有内容,复制到右面的框中。

4、记住保存一下这个配置,点击右上角的保存图标,重新命名之后就可以保存(比如BLOG主保存名字是“酒馆”)。下一次使用的时候,我们只需要在GOOGLE AI主页,点击“Your apps”,然后选择刚刚保存的“酒馆”就可以再次载入,而无需再执行一次第三步。

5、我们回到“Preview”页面,正常来说等待几秒,看到绿色的“连接成功”标志,即代表GOOGLE AI已经正常工作。

6、我们再次观察浏览器,当前你的浏览器必须同时开启“Huggingface”和“Google AI”两个页面(只要使用服务就必须开启)。各位可以对照下图,再次确认是否左右两个网站都标注为绿色的“代理就绪”和绿色的“连接成功”,查看左右两边是否都是自己项目的名字。如果与下图一致,那么恭喜你,反代已经完成。

三、服务调用(范例)

1、以chatbox为例

添加“OpneAI API 兼容”页面,在下面填上Huggingface服务中生成的API链接以及刚刚自己设置的连接密码(如下图所示),然后点击右下角“获取”,即可得到当下支持的模型,这里选择最新的“gemini-3-pro-preview”。

回到主页面,选择好对应的模型,即可安心的使用。(反代当前有100次/天的使用限制,如果超过该数量,建议重复上面全部操作,做多一个API/KEY供使用。另外也可以根据BLOG主以前的NEW-API教程做API轮询)

2、以酒馆(SillyTavern)为例

打开酒馆API设置页面,按照下图进行设置。

如果OpenAI兼容有问题,可以选择使用Google AI Studio进行反代设置,跟着下图设置就行,理论上直接设置反代会比使用openai格式好一些。

完成API设置后,就可以愉快的免费使用Gemini 3.0逗猫娘了!对了,记住把预设中的“流式传输”打开才能正常输出哦。

一条评论

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注