跳到主要内容

外部搜索

注意

本教程为社区贡献内容,不属于 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)