# Azure AI Search Agent 基于 **Pydantic AI** 的 Azure AI Search 资源库管理 Agent,作为 **OpenClaw 的外部资源库**。 采用 **混合搜索**(关键词 BM25 + 向量语义),支持项目文档的上传、下载、修改、搜索和删除。 调用方可以是 OpenClaw 也可以是人类用户。 ## 搜索模式 | 模式 | 说明 | 适用场景 | |------|------|----------| | `hybrid`(默认) | 关键词 + 向量融合排序 | 通用场景,推荐 | | `keyword` | 纯 BM25 关键词匹配 | 精确搜索术语/ID | | `vector` | 纯语义向量搜索 | 自然语言提问,找语义相关文档 | - 上传文档时自动通过 LiteLLM 生成 embedding 向量(`taiji/text-embedding-3-small`,1536 维) - 更新 `title` 或 `content` 时自动重新生成向量 - 搜索时关键词和向量结果由 Azure AI Search 自动融合排序(RRF) ## 功能 | 工具 | 说明 | |------|------| | `create_index` | 创建混合搜索索引(含向量字段) | | `list_indexes` | 列出所有索引 | | `upload_documents` | 上传文档(自动生成向量,支持批量) | | `search_documents` | 混合/关键词/向量搜索,支持筛选、排序 | | `get_document` | 根据 ID 获取单个文档 | | `update_document` | 更新文档部分字段(自动重新生成向量) | | `delete_documents` | 删除文档 | | `smart_query` | AI 智能问答(混合搜索 + LLM 总结) | ## 文档 Schema | 字段 | 类型 | 说明 | |------|------|------| | `id` | String (Key) | 文档唯一 ID | | `title` | String (Searchable) | 标题 | | `content` | String (Searchable) | 正文内容 | | `content_vector` | Collection(Single) | 内容向量(1536 维,自动生成) | | `project` | String (Filterable) | 项目名称 | | `category` | String (Facetable) | 分类 | | `tags` | String (Filterable) | 标签,逗号分隔 | | `source` | String (Filterable) | 来源(openclaw / human) | | `author` | String (Filterable) | 作者 | | `created_at` | DateTimeOffset | 创建时间 | | `updated_at` | DateTimeOffset | 更新时间 | | `metadata` | String (Searchable) | 额外元数据 JSON | ## 如何运行(标准 Azure 函数) **重要**:`func start` 会使用当前 shell 的 Python 环境加载你的代码。必须先在**本项目目录**下创建并激活虚拟环境、安装依赖,再启动,否则会报 `ModuleNotFoundError: No module named 'fastapi'`。 在项目根目录 `azure_search_agent/` 下按顺序执行: ```bash # 1. 进入项目目录 cd /path/to/azure_search_agent # 2. 创建虚拟环境(若尚未创建) python3 -m venv .venv # 3. 激活虚拟环境(必做,否则 func 找不到依赖) source .venv/bin/activate # Linux/macOS # .venv\Scripts\activate # Windows # 4. 安装依赖 pip install -r requirements.txt # 5. 确认已有 local.settings.json,再启动 func start ``` 一行命令(在项目目录下,且已存在 .venv): ```bash source .venv/bin/activate && func start ``` 启动成功后终端会显示: ``` Functions: http_app: [GET,POST,DELETE,HEAD,PATCH,PUT,OPTIONS] http://localhost:7071/{*route} ``` 在浏览器或 curl 访问: - 根路径: - 健康检查: - 搜索 API:`curl -X POST http://localhost:7071/api/v1/search -H "Content-Type: application/json" -H "api-key: sk-xxx" -d '{"query":"test","top":5}'` 如需先安装 **Azure Functions Core Tools**(尚未安装时): ```bash # Windows (npm) npm i -g azure-functions-core-tools@4 # macOS brew tap azure/functions && brew install azure-functions-core-tools@4 # Linux (Ubuntu/Debian) curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-$(lsb_release -cs)-prod $(lsb_release -cs) main" > /etc/apt/sources.list.d/dotnetdev.list' sudo apt-get update && sudo apt-get install azure-functions-core-tools-4 ``` --- ## 快速开始 ### 1. 配置环境变量 ```bash export AZURE_SEARCH_ENDPOINT="https://your-search-service.search.windows.net" export AZURE_SEARCH_API_KEY="your-admin-key" export AZURE_SEARCH_INDEX_NAME="openclaw-resources" # LiteLLM(用于 embedding 和 smart_query) export OPENAI_BASE_URL="https://litellm.example.com/v1" export OPENAI_API_KEY="sk-your-key" export EMBEDDING_MODEL="taiji/text-embedding-3-small" ``` ### 2. 本地测试 ```bash pip install -r requirements.txt python run_api_server.py ``` ## 部署到 Azure Function App 本 Agent 支持直接部署到 **Azure 函数应用**(无需 AKS)。 ### 1. 安装 Azure Functions Core Tools ```bash # Windows (npm) npm i -g azure-functions-core-tools@4 # macOS (Homebrew) brew tap azure/functions && brew install azure-functions-core-tools@4 # Linux 见: https://learn.microsoft.com/azure/azure-functions/functions-run-local ``` ### 2. 本地配置 ```bash cp local.settings.json.example local.settings.json # 编辑 local.settings.json,填入 AZURE_SEARCH_*、OPENAI_* 等 ``` **注意**:`local.settings.json` 含密钥,不要提交到 Git(已在 .gitignore 中忽略)。 ### 3. 本地运行(模拟 Function App) ```bash pip install -r requirements.txt func start ``` 默认地址:`http://localhost:7071`。例如健康检查:`http://localhost:7071/health`。 ### 4. 部署到 Azure **方式 A:Azure CLI** ```bash # 登录 az login # 创建资源组与存储(若尚未创建) az group create --name rg-azure-search-agent --location eastasia az storage account create --name styouragent --resource-group rg-azure-search-agent --sku Standard_LRS # 创建 Function App(Linux + Python 3.11) az functionapp create \ --resource-group rg-azure-search-agent \ --consumption-plan-location eastasia \ --runtime python \ --runtime-version 3.11 \ --functions-version 4 \ --name func-azure-search-agent \ --storage-account styouragent \ --os-type Linux # 配置应用设置(与 local.settings.json 中的 Values 对应) az functionapp config appsettings set --name func-azure-search-agent --resource-group rg-azure-search-agent --settings \ AZURE_SEARCH_ENDPOINT="https://aiagnet.search.windows.net" \ AZURE_SEARCH_API_KEY="<你的密钥>" \ AZURE_SEARCH_INDEX_NAME="openclaw-resources" \ OPENAI_BASE_URL="https://litellm.graystone-fb459c5d.southeastasia.azurecontainerapps.io/v1" \ OPENAI_API_KEY="<你的密钥>" \ EMBEDDING_MODEL="taiji/text-embedding-3-small" # 部署代码(在项目根目录执行) func azure functionapp publish func-azure-search-agent ``` **方式 B:VS Code** 安装 [Azure Functions 扩展](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions),右键项目 → “Deploy to Function App” → 选择或新建 Function App。 **方式 C:使用发布配置文件 (agnetdoc.PublishSettings)** 若已从 Azure 门户下载了发布配置文件(如 `agnetdoc.PublishSettings`),可按以下任一方式发布到 Function App **agnetdoc**。 1. **命令行(先登录 Azure)** 在项目根目录执行(将 `agnetdoc` 换成你的 Function App 名称): ```bash az login func azure functionapp publish agnetdoc ``` 如需把本地的 `local.settings.json` 里的应用设置同步到 Azure(会覆盖远程同名设置): ```bash func azure functionapp publish agnetdoc --publish-local-settings ``` 2. **VS Code 使用发布配置文件** - 安装 Azure Functions 扩展。 - 在 Azure 视图中找到你的 Function App(如 agnetdoc),右键 → **Deploy to Function App using Publish Profile**。 - 选择已下载的 `agnetdoc.PublishSettings` 文件(例如在 `Downloads` 中)。 3. **用脚本 + 发布配置文件发布(无需 az login)** 项目内提供 `publish_with_profile.py`,读取 `.PublishSettings` 后通过 Kudu 做 zip 部署: ```bash # Windows(发布配置文件在下载目录) .venv\Scripts\python publish_with_profile.py "C:\Users\gjgon\Downloads\agnetdoc.PublishSettings" # Linux/macOS .venv/bin/python publish_with_profile.py /path/to/agnetdoc.PublishSettings ``` ### 5. 部署后访问 - 根路径:`https://.azurewebsites.net/` - 健康检查:`https://.azurewebsites.net/health` - REST API:`https://.azurewebsites.net/api/v1/search` 等 业务接口仍通过请求头 `api-key` 或 `Authorization: Bearer ` 鉴权,与本地/uvicorn 行为一致。 ### 6. 说明 - **host.json** 中 `routePrefix: ""` 必须保留,否则 FastAPI 路由会多一层前缀。 - **AzureWebJobsStorage 与本地 Unhealthy**:`local.settings.json` 里若为 `UseDevelopmentStorage=true`,需本机运行 [Azurite](https://learn.microsoft.com/azure/storage/common/storage-use-azurite) 才会通过存储检查;否则 func 会一直报 `azure.functions.webjobs.storage: Unhealthy`,但 HTTP 接口仍可用。若不想跑 Azurite,可把 `AzureWebJobsStorage` 改为真实的 Azure 存储连接串,Unhealthy 即会消失。 - 如需生产级鉴权,可在 Azure 门户将 HTTP 触发器改为 `FUNCTION` 级别,或在前端加 APIM/网关。 - 消费计划(Consumption)有冷启动与超时限制;长时间运行或高并发可考虑 **Premium** 或 **Dedicated** 计划。 ## API 接口 ### MCP 端点 - `POST /mcp` — MCP JSON-RPC - `GET /mcp/sse` — MCP SSE 连接 - `POST /mcp/sse` — MCP SSE 请求 ### REST API | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/indexes` | 创建索引 | | GET | `/api/v1/indexes` | 列出索引 | | POST | `/api/v1/upload` | 上传文档 | | POST | `/api/v1/search` | 搜索文档 | | GET | `/api/v1/documents/{id}` | 获取文档 | | PATCH | `/api/v1/documents/{id}` | 更新文档 | | DELETE | `/api/v1/documents/{id}` | 删除文档 | | POST | `/api/v1/smart-query` | AI 智能问答 | ### 使用示例 **上传文档:** ```bash curl -X POST http://localhost:8000/api/v1/upload \ -H "api-key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "documents": [ { "id": "doc-001", "title": "项目架构设计文档", "content": "本项目采用微服务架构...", "project": "my-project", "category": "设计文档", "source": "human", "author": "张三" } ] }' ``` **混合搜索(默认):** ```bash curl -X POST http://localhost:8000/api/v1/search \ -H "api-key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "系统用了什么技术栈", "top": 5}' ``` **纯关键词搜索:** ```bash curl -X POST http://localhost:8000/api/v1/search \ -H "api-key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"query": "微服务", "search_mode": "keyword"}' ``` **AI 智能问答:** ```bash curl -X POST http://localhost:8000/api/v1/smart-query \ -H "api-key: YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"question": "怎么开发新的 Agent?", "project": "openclaw"}' ``` ## 环境变量 | 变量 | 必需 | 说明 | |------|------|------| | AZURE_SEARCH_ENDPOINT | 是 | Azure AI Search 服务端点 URL | | AZURE_SEARCH_API_KEY | 是 | Azure AI Search 管理密钥 | | AZURE_SEARCH_INDEX_NAME | 否 | 默认索引名称,默认 `openclaw-resources` | | OPENAI_BASE_URL | 否 | LiteLLM Gateway URL(embedding + 智能问答) | | OPENAI_API_KEY | 否 | LiteLLM API Key | | EMBEDDING_MODEL | 否 | Embedding 模型名称,默认 `taiji/text-embedding-3-small` | | EMBEDDING_DIMENSIONS | 否 | 向量维度,默认 `1536` | | LITELLM_MODEL | 否 | LLM 模型名称,默认 `taiji/gpt-4o-mini` | | API_PORT | 否 | 端口,默认 8000 | ## 项目结构 ``` azure_search_agent/ ├── README.md ├── requirements.txt ├── run_api_server.py # 本地 uvicorn 启动(非 Function App) ├── function_app.py # Azure Function App 入口(ASGI 挂载 FastAPI) ├── host.json # Function 主机配置(routePrefix 为空) ├── local.settings.json.example ├── local.settings.json # 本地配置(勿提交,复制 example 后填写) └── src/ ├── __init__.py └── server/ ├── __init__.py ├── api_server.py # FastAPI + MCP HTTP + REST API └── mcp_server.py # MCP 工具定义(混合搜索 + embedding) ```