编辑
2025-02-15
Linux
0
请注意,本文编写于 77 天前,最后修改于 77 天前,其中某些信息可能已经过时。

目录

通过Docker安装open-webui
使用API访问DeepSeek R1

通过Docker安装open-webui

用Docker安装只需要一行命令

bash
docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui ghcr.io/open-webui/open-webui:main

其中的3000可以改成任何不冲突的端口

由于是部署在服务器上的,服务器自然没有浏览器,因此需要配置nginx启用反向代理,然后用浏览器访问

nginx
server { listen 443 ssl; server_name <域名>; # SSL 证书配置(请确认证书包含子域名) ssl_certificate <SSL证书地址>; ssl_certificate_key <SSL私钥地址>; # SSL 安全配置 ssl_session_timeout 1d; ssl_session_cache shared:MozSSL:10m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; ssl_prefer_server_ciphers off; # 反向代理设置 location / { proxy_pass http://127.0.0.1:<端口号,按照上文设置的话是3000>; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket支持 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; # 超时设置(可选) proxy_connect_timeout 6000s; proxy_send_timeout 6000s; proxy_read_timeout 6000s; } }

随后访问https://<上面设置的域名>即可访问面板,初次使用需要注册,第一个账号自动成为管理员账户

需要注意的是,初次登录会存在“OI”结束后白屏的情况,这是因为默认的OpenAI API与LLama API处于开启状态。这时不能气馁,多次刷新页面,有小概率能成功进入。进入后来到管理员面版

image.png

来到设置-外部链接,将两个API的按钮都关掉即可正常登录(这里打开是因为后面部署了模型)

image.png

使用API访问DeepSeek R1

首先需要拿到一个API和密钥,此处不妨使用硅基流动的服务:跳转硅基流动账户注册

使用我的邀请码FngeZEAX注册后可以获得14元的免费额度。注册完成后来到API密钥页面,点击新建密钥

image.png

复制密钥,然后回到open-webui的管理员页面,转到函数,然后新建函数

image.png

编辑函数文件(来自open-webui社区

python
""" title: DeepSeek R1 author: zgccrui description: 在OpwenWebUI中显示DeepSeek R1模型的思维链 - 仅支持0.5.6及以上版本 version: 1.2.6 licence: MIT """ import json import httpx import re from typing import AsyncGenerator, Callable, Awaitable from pydantic import BaseModel, Field import asyncio class Pipe: class Valves(BaseModel): DEEPSEEK_API_BASE_URL: str = Field( default="https://api.siliconflow.cn/v1", description="DeepSeek API的基础请求地址", ) DEEPSEEK_API_KEY: str = Field( default="<刚刚注册的API密钥>", description="用于身份验证的DeepSeek API密钥,可从控制台获取", ) DEEPSEEK_API_MODEL: str = Field( default="deepseek-ai/DeepSeek-R1", description="API请求的模型名称,默认为 deepseek-reasoner ", ) def __init__(self): self.valves = self.Valves() self.data_prefix = "data: " self.thinking = -1 # -1:未开始 0:思考中 1:已回答 self.emitter = None def pipes(self): return [ { "id": self.valves.DEEPSEEK_API_MODEL, "name": self.valves.DEEPSEEK_API_MODEL, } ] async def pipe( self, body: dict, __event_emitter__: Callable[[dict], Awaitable[None]] = None ) -> AsyncGenerator[str, None]: """主处理管道(已移除缓冲)""" self.thinking = -1 self.emitter = __event_emitter__ # 验证配置 if not self.valves.DEEPSEEK_API_KEY: yield json.dumps({"error": "未配置API密钥"}, ensure_ascii=False) return # 准备请求参数 headers = { "Authorization": f"Bearer {self.valves.DEEPSEEK_API_KEY}", "Content-Type": "application/json", } try: # 模型ID提取 model_id = body["model"].split(".", 1)[-1] payload = {**body, "model": model_id} # 处理消息以防止连续的相同角色 messages = payload["messages"] i = 0 while i < len(messages) - 1: if messages[i]["role"] == messages[i + 1]["role"]: # 插入具有替代角色的占位符消息 alternate_role = ( "assistant" if messages[i]["role"] == "user" else "user" ) messages.insert( i + 1, {"role": alternate_role, "content": "[Unfinished thinking]"}, ) i += 1 # yield json.dumps(payload, ensure_ascii=False) # 发起API请求 async with httpx.AsyncClient(http2=True) as client: async with client.stream( "POST", f"{self.valves.DEEPSEEK_API_BASE_URL}/chat/completions", json=payload, headers=headers, timeout=300, ) as response: # 错误处理 if response.status_code != 200: error = await response.aread() yield self._format_error(response.status_code, error) return # 流式处理响应 async for line in response.aiter_lines(): if not line.startswith(self.data_prefix): continue # 截取 JSON 字符串 json_str = line[len(self.data_prefix) :] try: data = json.loads(json_str) except json.JSONDecodeError as e: # 格式化错误信息,这里传入错误类型和详细原因(包括出错内容和异常信息) error_detail = f"解析失败 - 内容:{json_str},原因:{e}" yield self._format_error("JSONDecodeError", error_detail) return choice = data.get("choices", [{}])[0] # 结束条件判断 if choice.get("finish_reason"): return # 状态机处理 state_output = await self._update_thinking_state( choice.get("delta", {}) ) if state_output: yield state_output # 直接发送状态标记 if state_output == "<think>": yield "\n" # 内容处理并立即发送 content = self._process_content(choice["delta"]) if content: if content.startswith("<think>"): match = re.match(r"^<think>", content) if match: content = re.sub(r"^<think>", "", content) yield "<think>" await asyncio.sleep(0.1) yield "\n" elif content.startswith("</think>"): match = re.match(r"^</think>", content) if match: content = re.sub(r"^</think>", "", content) yield "</think>" await asyncio.sleep(0.1) yield "\n" yield content except Exception as e: yield self._format_exception(e) async def _update_thinking_state(self, delta: dict) -> str: """更新思考状态机(简化版)""" state_output = "" # 状态转换:未开始 -> 思考中 if self.thinking == -1 and delta.get("reasoning_content"): self.thinking = 0 state_output = "<think>" # 状态转换:思考中 -> 已回答 elif ( self.thinking == 0 and not delta.get("reasoning_content") and delta.get("content") ): self.thinking = 1 state_output = "\n</think>\n\n" return state_output def _process_content(self, delta: dict) -> str: """直接返回处理后的内容""" return delta.get("reasoning_content", "") or delta.get("content", "") def _format_error(self, status_code: int, error: bytes) -> str: """错误格式化保持不变""" try: err_msg = json.loads(error).get("message", error.decode(errors="ignore"))[ :200 ] except: err_msg = error.decode(errors="ignore")[:200] return json.dumps( {"error": f"HTTP {status_code}: {err_msg}"}, ensure_ascii=False ) def _format_exception(self, e: Exception) -> str: """异常格式化保持不变""" err_type = type(e).__name__ return json.dumps({"error": f"{err_type}: {str(e)}"}, ensure_ascii=False)

需要将<刚刚注册的API密钥>替换为刚刚注册的API密钥,然后点击保存,并启用函数

image.png

随后DeepSeek-R1就自动出现在模型页面中了,退出管理员面板即可与其正常对话

本文作者:GBwater

本文链接:https://gbwater.icu/post/90

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!