使用 mistral.rs 的 HTTP API 实现 Chat Completions 网页搜索(web_search_options 完整实战指南)
【免费下载链接】mistral.rsFast, flexible LLM inference项目地址: https://gitcode.com/GitHub_Trending/mi/mistral.rs
本篇指南讲解如何在 mistral.rs 中以 OpenAI 兼容的 HTTP API 为 Chat Completions 请求启用内建网页搜索能力:只需在服务端以--agent模式启动一个支持搜索工具的服务器,然后在客户端请求体中传入web_search_options={},模型即可自主联网检索、抓取网页内容并基于实时信息作答。读完本文,你将掌握启动命令、完整的 Python 调用代码、web_search_options的全部可配置字段,以及搜索工具在服务端底层的真实执行机制。
概述:一条web_search_options引发的联网推理
mistral.rs 对 OpenAI 的web_search_options请求字段提供了服务端原生支持。该功能不需要你在客户端自行实现"搜索→工具调用→回传结果"的多轮循环,而是由服务端内置的 Agent 工具循环自动完成:模型按需调用预置的网页搜索工具与网页内容提取工具,检索结果被重新注入上下文后继续生成,最终直接返回一段引用了实时信息的最终回答。
对应到当前仓库,该能力由三部分协同构成:
- mistralrs-server-core/src/chat_completion.rs:解析并归一化请求中的
web_search_options,与tools一起转换为服务端内部工具配置; - mistralrs-core/src/search/mod.rs:定义搜索/提取工具的提示词描述、参数 Schema 与真实执行逻辑;
- mistralrs-cli/src/args/mod.rs:提供
--agent、--enable-search、--search-embedding-model等命令行开关。
第一步:以 Agent 模式启动搜索服务器
原文档给出的启动命令是:
mistralrs serve --agent -p 1234 -m Qwen/Qwen3-4B参数拆解如下:
| 参数 | 作用 |
|---|---|
serve | 启动 OpenAI 兼容的 HTTP 服务器 |
--agent | 构建本地 Agent:同时启用网页搜索、Python 代码执行与 Shell 执行,并运行 Agent 工具循环(--agentic是其别名) |
-p 1234 | 服务监听端口,客户端将访问http://localhost:1234/v1/ |
-m Qwen/Qwen3-4B | 指定 HuggingFace 模型仓库标识,此处使用 Qwen3-4B |
需要说明的是:--agent是一个"全家桶"开关。从 mistralrs-cli/src/commands/serve.rs 的源码可以看到,传入--agent后服务端会自动把enable_search置为true。因此如果你只想启用网页搜索、不希望附带代码执行等能力,可以显式使用更精确的组合:
# 仅启用网页搜索(需搭配 embedding 重排序模型) mistralrs serve --enable-search -p 1234 -m Qwen/Qwen3-4B其中:
--enable-search:启用与 OpenAIweb_search_options兼容的搜索能力,会加载一个搜索 embedding 重排序模型(默认是 EmbeddingGemma);--search-embedding-model:指定要加载的内建搜索 embedding 模型,该参数要求同时传入--enable-search或--agent,否则 CLI 会直接报错(见 mistralrs-cli/src/commands/serve.rs)。
从服务端构建器的源码(mistralrs-server-core/src/mistralrs_for_server_builder.rs)看,enable_search负责加载搜索重排序模型,search_embedding_model用于覆盖默认的 EmbeddingGemma,而search_callback则允许以编程方式完全替换内置的搜索实现——这些都是高级自定义入口,默认情况下无需改动。
第二步:运行示例脚本
服务启动后,在仓库根目录执行:
python examples/server/web_search.py脚本的完整源码位于 examples/server/web_search.py。下面逐段解析其工作方式。
构造 OpenAI 兼容客户端
from openai import OpenAI client = OpenAI(api_key="foobar", base_url="http://localhost:1234/v1/")base_url指向 mistral.rs 服务器的/v1/端点;api_key只是占位符(mistral.rs 本地服务不做鉴权),可以填写任意字符串。
发起带网页搜索的请求
messages = [ { "role": "user", "content": "Can you show me some code using mistral.rs for running Llama 3.2 Vision?", } ] completion = client.chat.completions.create( model="default", messages=messages, tool_choice="auto", max_tokens=1024, web_search_options={}, )这里的关键参数是web_search_options={}:
- 传
{}表示启用网页搜索且全部选项走默认值; tool_choice="auto"允许模型自主决定是否调用搜索工具;max_tokens=1024限制单次生成的最大 token 数;- 服务端在请求归一化阶段会把
web_search_options与tools统一转换为内部工具配置(见 mistralrs-server-core/src/chat_completion.rs),随后交给 Agent 工具循环执行。
读取结果
# print(completion.usage) print(completion.choices[0].message.content) if completion.choices[0].message.tool_calls is not None: # Should never happen. tool_called = completion.choices[0].message.tool_calls[0].function print(tool_called)- 正常路径下,模型完成联网检索后会把最终答案写入
choices[0].message.content,直接打印即可; - 示例中
tool_calls的分支仅为防御性代码:由于web_search_options模式下搜索由服务端内部完成,客户端最终收到的应当是纯文本回答,因此理论上不应出现未消费的工具调用;如确实出现,说明发生了异常情况,可借此排查; - 被注释掉的
completion.usage行可取消注释,用于观察本次请求的 token 用量统计。
第三步:web_search_options的完整参数说明
虽然示例只传了{},但该字段支持丰富的配置项,其数据结构定义于 mistralrs-core/src/request.rs。下表整理自该结构体:
| 字段 | 类型 | 说明 |
|---|---|---|
search_context_size | SearchContextSize | 控制注入上下文的搜索结果规模 |
user_location | WebSearchUserLocation | 用户地理位置信息,影响搜索结果的本地化 |
filters | WebSearchFilters | 域名过滤:allowed_domains(仅允许)与blocked_domains(排除),值为域名字符串列表 |
external_web_access | bool | 是否允许外部网络访问 |
return_token_budget | WebSearchReturnTokenBudget | 搜索结果回传的 token 预算,枚举值为default/unlimited |
search_content_types | Vec<WebSearchContentType> | 搜索内容类型,枚举值为text/image |
image_settings | WebSearchImageSettings | 图片搜索设置:max_results(最大结果数)与caption(是否生成说明) |
search_description | String | 覆盖搜索工具默认的提示词描述 |
extract_description | String | 覆盖网页提取工具默认的提示词描述 |
地理位置示例
user_location采用type: "approximate"的序列化格式(见 mistralrs-core/src/request.rs),支持city、region、country、timezone四个可选维度。一个带本地化搜索的完整请求示例如下:
completion = client.chat.completions.create( model="default", messages=messages, tool_choice="auto", max_tokens=1024, web_search_options={ "user_location": { "type": "approximate", "approximate": { "city": "Shanghai", "country": "CN", "region": "CN-31", "timezone": "Asia/Shanghai", }, }, "return_token_budget": "default", "filters": { "allowed_domains": ["arxiv.org", "github.com"], }, }, )服务端在构造搜索工具时,会把上述地理位置拼接到搜索工具的描述文本中(如The user's location is: Shanghai, CN-31, CN, Asia/Shanghai.),从而引导模型给出本地化更强的查询词——该逻辑见 mistralrs-core/src/search/mod.rs。
底层原理:搜索工具与执行流程
启用web_search_options后,服务端会注册两个内建工具(工具名定义于 mistralrs-core/src/search/mod.rs):
| 工具名 | 输入参数 | 职责 |
|---|---|---|
mistralrs_search_the_web | query(字符串) | 根据查询词执行网页搜索,返回标题、摘要、URL 与正文内容 |
mistralrs_website_content_extractor | url(字符串) | 提取指定 URL 的网页正文内容 |
二者的工具描述、参数 JSON Schema 由get_search_tools动态生成(见 mistralrs-core/src/search/mod.rs),并且均声明为strict: true的 Function 工具。
搜索执行的真实路径
以mistralrs_search_the_web为例(mistralrs-core/src/search/mod.rs),一次搜索的完整链路是:
- URL 直取:若模型传入的"查询词"本身是
http://或https://开头的 URL,则直接抓取该页面并把正文作为唯一结果返回(因为 DuckDuckGo 对裸 URL 的搜索返回为空); - 搜索引擎查询:否则请求
https://html.duckduckgo.com/html/?q=<编码后的查询词>,使用形如mistralrs/<版本号> (<OS>; <ARCH>; <FAMILY>)的 User-Agent; - 解析结果页:用
scraper解析 DuckDuckGo HTML 结果,通过.result、.result__title、.result__snippet、.result__url四个 CSS 选择器提取标题、摘要与链接,过滤掉任一字段为空的结果,最多保留MAX_SEARCH_RESULTS = 10条; - 并发抓取正文:对每条结果并发发起页面抓取,并用
html2text把 HTML 转换为纯文本后填充content字段; - 返回结构化结果:最终把
SearchResult(title/description/url/content)列表回传给模型,模型据此继续生成带引用的回答。
mistralrs_website_content_extractor则更简单直接:抓取给定 URL,将 HTML 转为纯文本返回;抓取失败时返回ERROR: failed to extract content占位文本(见 mistralrs-core/src/search/mod.rs)。
两个工具共用的网络参数位于文件顶部的常量:连接超时 5 秒(SEARCH_CONNECT_TIMEOUT)、请求超时 10 秒(SEARCH_REQUEST_TIMEOUT)、最大结果数 10(MAX_SEARCH_RESULTS),见 mistralrs-core/src/search/mod.rs。
工具描述即"提示词"
值得注意的一个设计细节是:搜索/提取工具的英文描述本身就是精心编写的提示词。例如搜索工具描述中明确要求模型"如果用户需要最新信息就调用此工具""调用后必须基于输出完成回答""输入应是查询词而非 URL",并给出了期望的 JSON 输出结构(sources与output数组)。这些描述(SEARCH_DESCRIPTION、EXTRACT_DESCRIPTION)会被注入模型可见的工具定义中,直接影响模型的调用决策质量。
与普通 Tool Calling 的区别及 API 兼容性
如果你已经熟悉 examples/server/tool_calling.py 展示的"客户端侧工具调用"模式,可以这样对比理解:
- 普通 tool calling(客户端循环):客户端在
tools中自行声明函数 Schema,模型返回tool_calls后由客户端执行函数、再把结果以role: "tool"的消息回传,需要客户端维护多轮对话; - web_search_options(服务端 Agent 循环):搜索工具由服务端注册,检索与回传全部在服务端完成,客户端只需传
web_search_options={},最终直接拿到文本回答。
此外需要特别留意 API 差异:在 Chat Completions 接口中应使用web_search_options字段;而tools[].type="web_search"这种工具声明形式只被 Responses API 支持,如果在 Chat Completions 中使用会触发明确的报错提示:"tools[].type="web_search"is only supported by the Responses API; useweb_search_optionswith Chat Completions"(见 mistralrs-server-core/src/openai.rs)。这一点在 mistralrs-server-core/src/openai.rs 的单元测试中也有对应断言。
实践要点与注意事项
- 网络可达性:搜索功能依赖对
html.duckduckgo.com及目标网站的实时访问,服务器所在环境需要具备出网能力; - 默认值即可用:
web_search_options={}是最低成本的启用方式,全部选项取默认值;按需再叠加user_location、filters、return_token_budget等精细化配置; - 域名过滤:
filters.allowed_domains/blocked_domains用于限定搜索与提取的域名范围,适合对检索来源有合规要求的场景;注意域名列表存在数量上限校验(见 mistralrs-server-core/src/openai.rs); - 模型选择:
--agent/--enable-search会加载搜索 embedding 重排序模型,首次启动需要下载对应权重;主模型建议选择工具调用能力较强的型号(如 Qwen3 系列),以获得更稳定的搜索决策; - 结果上限:单次搜索最多返回 10 条结果,且每条正文内容会在后续处理中按 token 预算截断(
SearchResult::cap_content_len,见 mistralrs-core/src/search/mod.rs),因此大文档页面只会保留前缀内容。
延伸阅读
- 服务端 Agent 循环与多工具调度:examples/server/agentic_tool_rounds.py、examples/server/tool_dispatch.py
- 客户端侧手工工具调用范式:examples/server/tool_calling.py
- 搜索/提取工具的 Rust 实现:mistralrs-core/src/search/mod.rs
web_search_options数据结构定义:mistralrs-core/src/request.rs- CLI 参数(
--agent/--enable-search/--search-embedding-model):mistralrs-cli/src/args/mod.rs
【免费下载链接】mistral.rsFast, flexible LLM inference项目地址: https://gitcode.com/GitHub_Trending/mi/mistral.rs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考