跳到主要内容

外部

警告

本教程为社区贡献,不受 Open WebUI 团队支持。它仅作为如何根据你的特定用例定制 Open WebUI 的示例。想贡献?请查看贡献教程。

外部网页搜索 API

此选项允许你将 Open WebUI 连接到你自己的自托管网页搜索 API 端点。这在你希望以下情况时非常有用:

  • 集成 Open WebUI 原生不支持的搜索引擎。
  • 实现自定义搜索逻辑、过滤或结果处理。
  • 使用私有或内部搜索索引。

Open WebUI 设置

  1. 导航到 Open WebUI 的 Admin Panel(管理面板)。
  2. 转到 Settings(设置)标签页,然后选择 Web Search(网页搜索)。
  3. Enable Web Search(启用网页搜索)切换到开启位置。
  4. 从下拉菜单中将 Web Search Engine(网页搜索引擎)设置为 external(外部)。
  5. External Search URL(外部搜索 URL)中填写自定义搜索 API 端点的完整 URL(例如,http://localhost:8000/searchhttps://my-search-api.example.com/api/search)。
  6. External Search API Key(外部搜索 API 密钥)中填写与自定义搜索端点进行身份验证所需的秘密 API 密钥。如果你的端点不需要身份验证,则留空(不推荐用于公共端点)。
  7. 点击 Save(保存)。

Open WebUI Admin panel showing External Search config

API 规范

Open WebUI 将按如下方式与你的 External Search URL(外部搜索 URL)进行交互:

  • 方法: POST

  • 请求头

    • Content-Type: application/json
    • Authorization: Bearer <YOUR_EXTERNAL_SEARCH_API_KEY>
  • 请求体 (JSON)

    {
    "query": "The user's search query string",
    "count": 5 // The maximum number of search results requested
    }
    • query (字符串):用户输入的搜索词。
    • count (整数):Open WebUI 期望的最大建议结果数量。你的 API 必要时可以返回更少的结果。
  • 预期响应体 (JSON): 你的 API 端点必须返回一个搜索结果对象的 JSON 数组。每个对象应具有以下结构:

    [
    {
    "link": "URL of the search result",
    "title": "Title of the search result page",
    "snippet": "A brief description or snippet from the search result page"
    },
    {
    "link": "...",
    "title": "...",
    "snippet": "..."
    }
    // ... potentially more results up to the requested count
    ]
    • link (字符串):搜索结果的直接 URL。
    • title (字符串):网页标题。
    • snippet (字符串):与查询相关的网页内容描述性文本摘要。

    如果发生错误或未找到结果,你的端点理想情况下应返回一个空的 JSON 数组 []

示例实现 (Python/FastAPI)

这是一个使用 Python 和 FastAPI 以及 duckduckgo-search 库实现的自托管搜索 API 的简单示例。

import uvicorn
from fastapi import FastAPI, Header, Body, HTTPException
from pydantic import BaseModel
from duckduckgo_search import DDGS

EXPECTED_BEARER_TOKEN = "your_secret_token_here"

app = FastAPI()


class SearchRequest(BaseModel):
query: str
count: int


class SearchResult(BaseModel):
link: str
title: str | None
snippet: str | None


@app.post("/search")
async def external_search(
search_request: SearchRequest = Body(...),
authorization: str | None = Header(None),
):
expected_auth_header = f"Bearer {EXPECTED_BEARER_TOKEN}"
if authorization != expected_auth_header:
raise HTTPException(status_code=401, detail="Unauthorized")

query, count = search_request.query, search_request.count

results = []
try:
with DDGS() as ddgs:
search_results = ddgs.text(
query, safesearch="moderate", max_results=count, backend="lite"
)

results = [
SearchResult(
link=result["href"],
title=result.get("title"),
snippet=result.get("body"),
)
for result in search_results
]

except Exception as e:
print(f"Error during DuckDuckGo search: {e}")

return results


if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8888)