开发

借助Cloudflare Workers打造一个专属OpenAI API

目前已上线积薪,未来会支持本博客

积薪功能包括根据文章内容生成分类、标签和摘要,曾使用百度API,但准确度较差。建议将AI接口迁移到OpenAI上,通过Cloudflare或Vercel方便调用。演示如何搭建专属OpenAI API,配置路由、保障安全。测试时部署到Cloudflare。这样即可获得稳定的、适用于SEO展示的摘要和其他AI服务。

  1. #OpenAI
  2. #API
  3. #Cloudflare
  4. #Workers
  5. #AI

2003

A detailed miniature train model with vibrant yellow tracks and colorful railings is depicted on a white background, surrounded by scattered wooden sticks. A colorful cargo ship is positioned on the tracks, adding a dynamic element to the scene.A detailed miniature train model with vibrant yellow tracks and colorful railings is depicted on a white background, surrounded by scattered wooden sticks. A colorful cargo ship is positioned on the tracks, adding a dynamic element to the scene.

积薪有一个功能,即根据文章内容生成分类、标签和摘要,此前使用的是百度的API。这也成了反馈最为集中的功能,因为百度的AI实在是太拉垮了。

不仅分类经常出错,摘要也经常是驴唇不对马嘴,甚至就是文章内容的随机拼凑,连语序通顺都做不到。

A screenshot of the HCP website with Chinese text, including the HCP website address and a link to the HCP website.A screenshot of the HCP website with Chinese text, including the HCP website address and a link to the HCP website.
可以看出分类错得离谱
A white background with black Chinese text on a white background.A white background with black Chinese text on a white background.
摘要也是胡言乱语,连日期都摘上了

当初选百度纯粹是因为用起来方便,但这个准确度实在太差。GPT的准确度比较高,不如把AI接口迁移到OpenAI上。

但使用OpenAI可以说是穿墙领域最难的事了,你要同时对抗两个大国。如果只是单纯地调用API,很容易遭到封禁。

于是利用Cloudflare或Vercel这种具有边缘计算功能的平台来调用API,成了最方便稳妥的选择。

本文将示范如何通过Workers搭建一个你的专属OpenAI API。

前提

首先你需要一个OpenAI的账号,搞定支付相关事项。至于怎么搞定,你自己想办法。

然后你需要一个Cloudflare账号。

安装wrangler,在本地编写Workers代码:

typescript

npm install wrangler

然后新建一个Workers项目:

typescript

npm create cloudflare@latest

通用AI接口

workers.ts是主入口,所有请求都会经过这里。

不过先不用管它,我们从最末端的功能写起。

我希望这个接口能更通用,通过不同的路由指向不同的prompt。比如请求/summary并发送一段文本,就会返回摘要;请求/category会返回这段文本的分类。

这就需要有一个函数来负责这项工作,接收两个参数:prompt和content。

typescript

// 接收一个propmt和内容,请求OpenAI,返回结果 interface OpenAIResponse { choices: { "finish_reason": string; "index": number; "message": { "content": string; "role": string; } } []; created: number; id: string; model: string; object: string; usage: { "completion_tokens": number; "prompt_tokens": number; "total_tokens": number; } } async function openAI(prompt: string, content: string, key: string) { const message = [ { "role": "system", "content": prompt }, { "role": "user", "content": content } ]; const apiURL = 'https://api.openai.com/v1/chat/completions'; const model = content.length > 3000 ? 'gpt-3.5-turbo-16k-0613' : 'gpt-3.5-turbo-0613'; const response = await fetch(apiURL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${key}` }, body: JSON.stringify({ messages: message, model: model, max_tokens: 500, temperature: 0.5, stop: ['\n'] }) }); const data: OpenAIResponse = await response.json(); const result = data.choices[0].message.content; return result; } export default openAI;

这段应该不难理解。为了节省开销,我做了个简单的判断:文本长度超出一定限制时使用gpt-3.5-turbo-16k-0613模型,未超出默认使用gpt-3.5-turbo-0613。当然文本长度并不等于token,这只是个简单的判断,够用就行。

当你向该函数传入预设条件和待处理文本时,返回的就是一个纯文本。

设定prompt

通用函数写好,接下来就是处理不同接口对应的prompt了。

假设我希望在访问/seo时,返回这段文字适用于SEO展示的摘要。

typescript

// 导入刚才的通用函数 import openAI from "./open-ai"; interface Env { API_KEY: string; } const prompt = "我需要根据下面的这段文字, 针对搜索引擎优化, 写一段摘要, 要求写明文字的主要内容, 字数不得超过120个汉字"; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const content = await request.text(); return new Response(await openAI(prompt, content, env.API_KEY), { headers: { "Content-Type": "text/plain" } }); } }

这里的prompt很重要,由于我们希望这是一个接口,输出的内容保持稳定,因此你可以在prompt的设置上多花些心思。比如你可以使用更严谨的语言:

根据文本内容,输出1-3个与内容相关的标签。格式如下: “标签1”, “标签2”

这里面可以优化的空间太多了,在此不赘述。

配置路由

接下来需要在workers.ts里进行设置,以便在请求 /seo的时候能访问到相应代码。

typescript

import generateSEO from './seo'; interface Env { API_KEY: string; } // Export a default object containing event handlers export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const url = new URL(request.url); switch (url.pathname) { case '/seo': return generateSEO.fetch(request, env, ctx); } return new Response( `This is a private API and cannot be accessed without authorization`, { headers: { 'Content-Type': 'text/html' } } ); }, };

这段代码的作用是,读取环境变量中的token,根据请求的endpoint,分发到相应处理逻辑中。你可以照此增加更多prompt和逻辑。

OpenAI的token可以在Workers的Dashboard - 设置中添加。

安全性

为了防止接口被滥用,可以稍稍增加一点安全措施。你可以随便生成一段token,放在请求头里,对于token不正确的请求一律拒绝。

最终workers.ts的代码是这样的:

typescript

import generateSEO from './seo'; interface Env { API_KEY: string; TOKEN: string; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { // verify if the bearer token is valid const auth = request.headers.get('Authorization'); if (!auth || auth !== `Bearer ${env.TOKEN}`) { return new Response( `This is a private API and cannot be accessed without authorization`, { headers: { 'Content-Type': 'text/html' } } ); } const url = new URL(request.url); switch (url.pathname) { case '/seo': return generateSEO.fetch(request, env, ctx); } return new Response( `This is a private API and cannot be accessed without authorization`, { headers: { 'Content-Type': 'text/html' } } ); }, };

测试

为了避免封号,建议一律先部署到Cloudflare,然后在云端测试。

执行npx wrangler deploy即可上传代码。

然后在该Workers内点击“快速编辑”,就可以在窗口中进行调试了。

A computer screen displays a Microsoft Word document with a blue background and white text. The document contains a list of text fields, each with a different color, and a list of fields with a different color. The fields are organized in a grid-like manner, with the list of text fields at the top and the list of fields at the bottom. The Microsoft Word interface is visible, with a blue window and a yellow "OK" button. The window is located at the top of the image, and the "OK" button is located at the bottom. The overall layout suggests a user-friendly interface for managing and organizing text fields.A computer screen displays a Microsoft Word document with a blue background and white text. The document contains a list of text fields, each with a different color, and a list of fields with a different color. The fields are organized in a grid-like manner, with the list of text fields at the top and the list of fields at the bottom. The Microsoft Word interface is visible, with a blue window and a yellow "OK" button. The window is located at the top of the image, and the "OK" button is located at the bottom. The overall layout suggests a user-friendly interface for managing and organizing text fields.

这样你就有了一个稳定的专属API,只要向其不同的endpoint发送文本,就会根据预设的prompt返回相应的回复。

目前这个接口已经上线积薪,但我计划把本博客的后端彻底从头开发一遍,到时候也在这里提供相应的AI服务。这是个大工程,不知道什么时候能完成。

未登录用户的评论需要等待审核。您填写的邮箱将不会被公开。 登录