Skill: jeck164/baidu-search-cn (version 1.0.2)

--- name: 百度搜索 version: 1.0.1 description: 通过百度 AI 搜索 API 进行网页搜索，获取实时信息和搜索结果。 homepage: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/2m0u2qdmd metadata: clawdbot: emoji: "🔍" requires: bins: - uv env: - BAIDU_API_KEY primaryEnv: BAIDU_API_KEY install: - id: brew kind: brew formula: uv bins: - uv label: Install uv via Homebrew --- # 🔍 百度搜索 *Search the web with Baidu AI* 通过百度 AI 搜索 API 进行网页搜索，获取中文互联网的实时信息。 ## Setup ```bash cd {baseDir} echo "BAIDU_API_KEY=your-api-key" > .env uv venv && uv pip install -e ".[dev]" uv run --env-file .env uvicorn baidu_search.main:app --host 127.0.0.1 --port 8001 ``` 需要在 `.env` 或环境变量中设置 `BAIDU_API_KEY`。 ## 获取 API Key 1. 访问 [百度智能云控制台](https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application) 2. 创建应用获取 API Key ## Quick Start 1. **检查服务:** `curl http://127.0.0.1:8001/ping` 2. **搜索网页:** ```bash curl -X POST http://127.0.0.1:8001/search \ -H "Content-Type: application/json" \ -d '{ "query": "北京有哪些旅游景区", "top_k": 10 }' ``` 3. **带时间过滤的搜索:** ```bash curl -X POST http://127.0.0.1:8001/search \ -H "Content-Type: application/json" \ -d '{ "query": "最新科技新闻", "top_k": 5, "recency_filter": "week" }' ``` 4. **限定网站搜索:** ```bash curl -X POST http://127.0.0.1:8001/search \ -H "Content-Type: application/json" \ -d '{ "query": "天气预报", "top_k": 5, "site_filter": ["www.weather.com.cn"] }' ``` ## API 参数 | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `query` | string | 必填 | 搜索关键词 | | `top_k` | int | 10 | 返回结果数量 (1-20) | | `recency_filter` | string | null | 时间过滤: `day`, `week`, `month`, `year` | | `site_filter` | list | null | 限定搜索的网站列表 | ## Response Format ```json { "results": [ { "title": "北京十大必去景点", "url": "https://example.com/beijing-attractions", "snippet": "北京作为中国的首都，拥有众多著名景点...", "site_name": "旅游网" } ], "total": 10 } ``` ## Conversation Flow 1. 用户提问需要搜索的内容 2. 判断是否需要时间过滤（如"最新"、"今天"等） 3. 调用搜索 API 获取结果 4. 整理并展示相关信息 5. 可根据需要深入查看某个结果 ## 使用场景 - 查询实时信息（新闻、天气、股票等） - 搜索中文互联网内容 - 获取特定网站的信息 - 时效性要求高的查询

path: pyproject.toml
content: [project]
name = "baidu-search"
version = "0.1.0"
description = "百度 AI 搜索 API 代理服务"
readme = "SKILL.md"
requires-python = ">=3.11"
dependencies = [
  "fastapi>=0.110.0",
  "httpx>=0.27.0",
  "uvicorn[standard]>=0.29.0",
]

[project.optional-dependencies]
dev = [
  "pytest>=8.0.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/baidu_search"]

[tool.pytest.ini_options]
addopts = "-q"
testpaths = ["tests"]


path: src/baidu_search/__init__.py
content: """百度搜索 API 代理服务"""


path: src/baidu_search/baidu_api.py
content: from __future__ import annotations

import logging
import os
from typing import Any

import httpx
from fastapi import HTTPException

from baidu_search.schemas import SearchRequest, SearchResponse, SearchResult

BAIDU_API_URL = "https://qianfan.baidubce.com/v2/ai_search/web_search"
logger = logging.getLogger("baidu_search.api")


def _get_api_key() -> str:
    """获取百度 API Key"""
    [REDACTED_SECRET]"BAIDU_API_KEY")
    if not api_key:
        raise HTTPException(
            status_code=500,
            detail="BAIDU_API_KEY is not set.",
        )
    return api_key


def _build_request_body(request: SearchRequest) -> dict[str, Any]:
    """构建百度 API 请求体"""
    body: dict[str, Any] = {
        "messages": [
            {
                "content": request.query,
                "role": "user"
            }
        ],
        "search_source": "baidu_search_v2",
        "resource_type_filter": [
            {
                "type": "web",
                "top_k": request.top_k
            }
        ]
    }

    # 时间过滤
    if request.recency_filter:
        valid_filters = ["day", "week", "month", "year"]
        if request.recency_filter in valid_filters:
            body["search_recency_filter"] = request.recency_filter

    # 网站过滤
    if request.site_filter:
        body["search_filter"] = {
            "match": {
                "site": request.site_filter
            }
        }

    return body


def _parse_response(data: dict[str, Any]) -> SearchResponse:
    """解析百度 API 响应"""
    results: list[SearchResult] = []
    
    # 从响应中提取搜索结果
    # 百度 AI 搜索返回格式可能包含 references 字段
    search_results = data.get("references", [])
    
    # 如果在 choices 中有结构化数据
    if not search_results and "choices" in data:
        for choice in data.get("choices", []):
            message = choice.get("message", {})
            # 尝试从 tool_calls 或其他字段提取
            if "search_results" in message:
                search_results = message["search_results"]
                break
    
    # 也可能直接在顶层
    if not search_results:
        search_results = data.get("web_search_results", [])
    
    for item in search_results:
        results.append(SearchResult(
            title=item.get("title"),
            url=item.get("url") or item.get("link"),
            snippet=item.get("snippet") or item.get("content") or item.get("abstract"),
            site_name=item.get("site_name") or item.get("source") or item.get("website")
        ))

    return SearchResponse(
        results=results,
        total=len(results)
    )


def search(request: SearchRequest) -> SearchResponse:
    """执行百度搜索"""
    [REDACTED_SECRET])
    
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    
    body = _build_request_body(request)
    
    logger.info("Searching Baidu: %s", request.query)
    
    try:
        with httpx.Client(timeout=30.0) as client:
            response = client.post(
                BAIDU_API_URL,
                headers=headers,
                json=body
            )
    except httpx.HTTPError as exc:
        logger.error("Baidu API request failed: %s", exc)
        raise HTTPException(
            status_code=502,
            detail="Baidu API unavailable."
        ) from exc

    if response.status_code != 200:
        logger.error(
            "Baidu API error: status=%d, body=%s",
            response.status_code,
            response.text
        )
        raise HTTPException(
            status_code=response.status_code,
            detail=f"Baidu API error: {response.text}"
        )

    data = response.json()
    return _parse_response(data)


path: src/baidu_search/main.py
content: import logging
import os

from fastapi import FastAPI, Request
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

from baidu_search.baidu_api import search
from baidu_search.schemas import SearchRequest, SearchResponse

app = FastAPI(
    title="百度搜索 API",
    description="百度 AI 搜索代理服务",
    version="0.1.0",
    servers=[{"url": os.getenv("OPENAPI_SERVER_URL", "http://127.0.0.1:8001")}],
)
logger = logging.getLogger("baidu_search.main")


@app.get("/ping")
def ping() -> dict[str, str]:
    """健康检查"""
    return {"message": "pong"}


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    logger.error(
        "Validation error on %s %s. body=%s errors=%s",
        request.method,
        request.url.path,
        exc.body,
        exc.errors(),
    )
    return JSONResponse(
        status_code=422,
        content=jsonable_encoder({"detail": exc.errors()}),
    )


@app.post("/search", response_model=SearchResponse)
def web_search(request: SearchRequest) -> SearchResponse:
    """
    搜索网页
    
    - **query**: 搜索关键词
    - **top_k**: 返回结果数量 (1-20, 默认 10)
    - **recency_filter**: 时间过滤 (day, week, month, year)
    - **site_filter**: 限定搜索的网站列表
    """
    return search(request)


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


path: src/baidu_search/schemas.py
content: from __future__ import annotations

from pydantic import BaseModel, Field


class SearchRequest(BaseModel):
    """搜索请求参数"""
    query: str = Field(min_length=1, description="搜索关键词")
    top_k: int = Field(default=10, ge=1, le=20, description="返回结果数量")
    recency_filter: str | None = Field(
        default=None,
        description="时间过滤: day, week, month, year"
    )
    site_filter: list[str] | None = Field(
        default=None,
        description="限定搜索的网站列表"
    )


class SearchResult(BaseModel):
    """单条搜索结果"""
    title: str | None = None
    url: str | None = None
    snippet: str | None = None
    site_name: str | None = None


class SearchResponse(BaseModel):
    """搜索响应"""
    results: list[SearchResult]
    total: int


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)