跳到主要内容

外部搜索

注意

本教程为社区贡献内容,不属于 OPL 数据空间团队的官方支持范围。它主要演示如何按你的具体场景定制 OPL 数据空间。若想参与贡献,请查看贡献指南。

提示

关于 Web Search 相关环境变量的完整列表,包括并发、返回结果数量等,请参阅 环境配置文档

Troubleshooting

如果 Web Search 出现问题,请先查看 Web Search 故障排查指南,里面覆盖了代理配置、连接超时、空内容等常见问题。

External Web Search API

这个选项允许你把 OPL 数据空间接到自托管网页搜索 API 上,适合这些场景:

  • 接入 OPL 数据空间暂未原生支持的搜索引擎
  • 实现自己的搜索逻辑、过滤规则或结果处理
  • 使用私有或内网搜索索引

OPL 数据空间侧配置

  1. 打开 OPL 数据空间管理员面板
  2. 进入 设置Web Search
  3. 启用 Enable Web Search
  4. Web Search Engine 设为 external
  5. External Search URL 中填写你的自定义搜索 API URL,例如 http://localhost:8000/search
  6. External Search API Key 中填写访问该接口所需的密钥;如果端点不要求鉴权,可留空,但不建议用于公网接口
  7. 点击 Save

OPL 数据空间 Admin panel showing External Search config

API 规范

OPL 数据空间会按以下方式调用 External Search URL

  • Method: POST
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <YOUR_EXTERNAL_SEARCH_API_KEY>
  • Request Body(JSON):
{
  "query": "The user's search query string",
  "count": 5
}
  • query:用户搜索词

  • count:希望返回的最大结果数量;你的 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:与查询相关的摘要文本

若发生错误或查无结果,建议直接返回空数组 []

示例实现(Python / FastAPI)

下面是一个基于 FastAPI 和 duckduckgo-search 的简化示例:

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)