使用 Cloudflare Workers AI 部署 DeepSeek-R1

使用 Cloudflare Workers AI 部署 DeepSeek-R1

DeepSeek-R1 是 DeepSeek(深度求索)开源的大语言模型,在各种基准测试中的表现都优于 OpenAI-o1-mini。而 Cloudflare Workers AI 是运行在 Cloudflare 网络的 serverless GPU AI 服务,允许用户快速低成本部署许多开源模型。

Cloudflare Workers AI 提供了基于 Qwen2.5 从 DeepSeek-R1 中提炼出来的模型:DeepSeek-R1-Distill-Qwen-32B,详细信息见:developers.cloudflare.com/workers-ai/models/deepseek-r1-distill-qwen-32b

使用教程

  1. 登陆 Cloudflare 后台,进入 Workers

Cloudflare Workers AI

  1. 选择 AI LLM App 模版

选择 AI LLM App 模版

  1. 命名 Worker 后点击部署,随后点击编辑代码

命名 Worker 后点击部署,随后点击编辑代码

  1. 复制粘贴以下代码,代码兼容了 OpenAI /v1/chat/completions 接口格式,可以在适配 ChatGPT 的第三方软件或SDK上直接使用
export default {
    async fetch(request, env) {
        if (request.method === 'POST' && request.url.includes('v1/chat/completions')) {

            // todo 验证 key

            try {
                const requestData = await request.json();

                const { model, messages } = requestData;

                let isSteam = false;
                if (requestData?.stream) {
                    isSteam = true;
                }

                // get the current time in epoch seconds
                const created = Math.floor(Date.now() / 1000);
                const uuid = crypto.randomUUID();

                const stream = await env.AI.run('@cf/deepseek-ai/deepseek-r1-distill-qwen-32b', {
                    messages: messages,
                    stream: isSteam,
                });

                if (isSteam) {
                    // 创建一个转换流 (TransformStream) 以便实现流式返回
                    const { readable, writable } = new TransformStream();
                    const writer = writable.getWriter();
                    const reader = stream.getReader();

                    // 处理流读取数据并逐帧向客户端发送
                    async function processStream() {
                        const decoder = new TextDecoder();
                        const encoder = new TextEncoder();

                        try {
                            while (true) {
                                const { done, value } = await reader.read();
                                if (done) {
                                    break;
                                }

                                // 确保 value 被转换为字符串类型
                                const stringValue = decoder.decode(value);

                                // 处理每行数据
                                const lines = stringValue.split('\n').filter(line => !!line);
                                for (const line of lines) {
                                    // 跳过非 JSON 数据(例如 [DONE] )
                                    if (line.endsWith('data: [DONE]')) {
                                        continue;
                                    }

                                    try {
                                        const json = JSON.parse(line.replace(/^data: /, ''));

                                        const formattedValue =
                                            `data: ${JSON.stringify({
                                                id: uuid,
                                                created,
                                                object: 'chat.completion.chunk',
                                                model,
                                                choices: [
                                                    {
                                                        delta: { content: json.response },
                                                        index: 0,
                                                        finish_reason: null,
                                                    },
                                                ],
                                            })}\n\n`;
                                        const encodedValue = encoder.encode(formattedValue);
                                        await writer.write(encodedValue);
                                    } catch (e) {
                                        console.error('Failed to parse JSON: ', e);
                                    }
                                }
                            }

                            // 发送完成标识
                            await writer.write(encoder.encode('data: [DONE]\n\n'));
                            writer.close();

                        } catch (err) {
                            console.error('Stream processing error: ', err);
                            // 确保 writer 在错误情况下关闭
                            writer.close();
                        }
                    }


                    // 调用处理流函数
                    processStream().catch(err => {
                        console.error('Stream processing error: ', err);
                    });

                    return new Response(readable, {
                        headers: {
                            'content-type': 'text/event-stream',
                            'Cache-Control': 'no-cache',
                            'Connection': 'keep-alive',
                        },
                    });
                } else {
                    return Response.json({
                        id: uuid,
                        model,
                        created,
                        object: 'chat.completion',
                        choices: [
                            {
                                index: 0,
                                message: {
                                    role: 'assistant',
                                    content: stream.response,
                                },
                                finish_reason: 'stop',
                            },
                        ],
                        usage: {
                            prompt_tokens: 0,
                            completion_tokens: 0,
                            total_tokens: 0,
                        },
                    });
                }

            } catch (error) {
                return new Response(JSON.stringify({ error: 'Invalid request' }), {
                    status: 400,
                    headers: { "Content-Type": "application/json" },
                });
            }
        }

        return new Response('Not Found', { status: 404 });
    }
};
  1. 粘贴完后点击部署,复制 *.workers.dev 域名到第三方客户端就可以使用了,完整路径: https://*.workers.dev/v1/chat/completions 注意:workers.dev 域名在国内被 DNS 污染了,没梯子的话需要绑定一下自己的域名

部署

以 OpenCat 为例

  1. 将域名填写到配置中的 OpenAI 自定义API域名,注意如果直接使用 workers.dev 域名,需要关闭 OpenCat 设置中的使用加密DNS

OpenCat配置

  1. 选择任意 OpenAI 的模型即可使用

OpenCat配置

Cloudflare 成本

@cf/deepseek-ai/deepseek-r1-distill-qwen-32b