乐于分享
好东西不私藏

VFP AI 插件开发花絮17:WebSearch.dll 帮助文档

VFP AI 插件开发花絮17:WebSearch.dll 帮助文档

1. 概述

WebSearch.MTServer 是一个 OLE 公共类,继承自 Visual FoxPro 的 Session 类。它封装了 WebSearchManager 对象,对外提供统一的 Web 搜索接口,支持多个搜索引擎提供者(Ollama、Exa、SerpAPI、Tavily 等),并能够处理 HTTP 错误、标准化返回结果。

主要用途:它为各种 COM 客户端(VFP、C#、VB.NET、X#、Python、Delphi 等)实现了一句话搜索并获取结构化结果。

ProgID:WebSearch.MTServer

线程模型: 多线程单元(MTA) – 支持多线程并发调用(但需注意内部对象的线程安全性)

类层次
Session ← MTServer (OlePublic)

依赖文件

  • config.json – 提供者配置文件(需位于当前目录或指定路径,见配置章节)

2. 注册

  • 打开命令行:以管理员身份打开命令提示符(cmd)。

  • 运行注册命令:使用 ‎regsvr32 工具注册 DLL。导航到 DLL 所在目录,执行:

regsvr32 WebSearch.dll

3. 属性

名称
类型
可见性
说明
oWebSearchManager
Object
Protected
内部使用的 ‎websearchmanager 对象实例。初始化时创建,不应直接访问。

其他属性均由 oWebSearchManager 内部维护,外部无需关心。


4. 主要方法

SetProvider(tcProvider)

语法
Procedure SetProvider(tcProvider) AS Logical

参数

参数
类型
说明
tcProvider
Character
提供者名称(不区分大小写),见下表“支持的提供者”

返回值

类型
说明
Logical
.T. 表示设置成功,‎.F. 表示提供者未在 ‎config.json 中定义

说明

  • 切换搜索后端。实际支持的提供者列表由 ‎config.json 中的 ‎webSearch 数组决定。
  • 调用后,后续 ‎WebSearch 将使用新提供者。

示例

loServer = CREATEOBJECT("WebSearch.MTServer")
IF loServer.SetProvider("exa")
    ? "已切换到 Exa 搜索"
ENDIF

WebSearch(tcQuery)

语法
Procedure WebSearch(tcQuery) AS Object

参数

参数
类型
说明
tcQuery
Character
搜索关键词、问题或 URL(具体取决于适配器类型)

返回值
Object – 始终返回一个 Collection 对象,结构如下:

集合属性/方法
类型
说明
lError
Logical
本次请求是否出错。‎.T. 表示错误,‎.F. 表示成功
.Count
Number
结果条目数(错误时通常为 1)
.Item(i)
Object (Empty)
第 i 个搜索结果,包含以下属性:
– ‎Title (Character) – 标题
– ‎Link (Character) – 链接
– ‎Snippet (Character) – 摘要或内容片段

错误情况
当 lError = .T. 时,集合中仅有一个 Empty 对象,其属性含义变为:

  • Title – HTTP 状态码(如 ‎"401"
  • Link – 错误类型说明(如 ‎"API 密钥无效或缺失"
  • Snippet – 服务器返回的原始错误信息

备注

  • 不同适配器返回的结果数量不同(例如 ‎AnythingLLMAdapter 只返回 1 条,‎ExaAdapter 最多返回 ‎nMaxResults 条)。
  • 对于某些适配器(如 ‎TavilyCrawlAdapter),‎Title 可能为空字符串。
  • 搜索结果的排序由后端 API 决定。

示例

loServer = CREATEOBJECT("WebSearch.MTServer")
loServer.SetProvider("serpapi")
loResults = loServer.WebSearch("Visual FoxPro COM")

IF NOT loResults.lError
    FOR i = 1 TO loResults.Count
        ? "标题:", loResults.Item(i).Title
        ? "链接:", loResults.Item(i).Link
        ? "摘要:", loResults.Item(i).Snippet
    ENDFOR
ELSE
    ? "错误:", loResults.Item(1).Snippet
ENDIF

GetDebugObject()

语法
Procedure GetDebugObject() AS Object

返回值
Object – 返回一个运行在独立 Visual FoxPro 应用程序实例中的 新 MTServer 对象

说明

  • 用于调试的场景。
  • 每个调用都需要启动一个新的 VFP 运行时。
  • 返回的对象可以像普通 ‎MTServer 一样调用 ‎SetProvider、‎WebSearch 等方法。

示例

loDebug = loServer.GetDebugObject()
? "调试对象类型:", VARTYPE(loDebug)   && "O"
loDebug.SetProvider("tavily")
loResults = loDebug.WebSearch("天气预报")

注意:频繁调用此方法会产生多个 VFP 进程,谨慎使用。


5. 支持的提供者 (Providers)

WebSearchManager 内置了以下适配器,通过 SetProvider 传入对应的名称即可使用。
实际可用列表取决于 config.json 中是否配置了对应的 name

提供者名称
说明
是否需要 API Key
默认最大结果数
ollama
Ollama 云搜索(需注册)
5
ollama_local
Ollama 本地模式(需本地安装 Ollama)
5
exa
Exa API(每月 1000 次免费)
5⁠¹
serpapi
SerpAPI 标准搜索(每月 250 次免费)
由 API 决定⁠²
serpapi_ai
SerpAPI AI 模式(返回 AI 生成摘要)
1
tavily
Tavily 搜索(返回网页结果)
5
ollama_fetch
Ollama 抓取单个 URL
1
ollama_fetch_local
同上(本地模式)
1
anythingllm_fetch
AnythingLLM 抓取 URL(需本地安装 AnythingLLM)
1
tavily_fetch
Tavily 批量抓取 URL(传入逗号分隔的多个 URL)
按输入数量
tavily_crawl
Tavily 爬取指定 URL 的所有子页面
不定
tavily_research
Tavily 深度研究(异步,轮询获取报告)
1(研究报告)

¹ nMaxResults 受 searchadapterbase.nmaxresults 属性控制,默认为 5。
² SerpAPI 的结果数量由 API 自身决定,通常为 10 条左右。
³ 对于“抓取类”适配器,查询参数应传入 URL 而非关键词。


5. 配置文件 config.json

WebSearchManager 初始化时会读取 config.json 文件,结构如下:

{
  "webSearch": [
    {
      "name": "ollama",
      "desc": "Ollama云搜索",
      "url": "https://api.ollama.ai/v1/search",
      "apiKey": "your-ollama-key"
    },
    {
      "name": "exa",
      "desc": "Exa搜索",
      "url": "https://api.exa.ai/search",
      "apiKey": "exa-api-key"
    }
  ]
}

要求

  • 文件必须位于 VFP 默认目录或通过 ‎SET DEFAULT 指定的路径。
  • name 字段必须与 ‎SetProvider 的参数小写形式匹配。
  • url 和 ‎apiKey 会被自动应用到对应的适配器。
  • 如果提供者不需要 API Key(如 ‎ollama_local),可留空字符串。

6. 结果结构详解

WebSearch 返回的 Collection 对象在成功和错误时结构不同:

6.1 成功时(lError = .F.

  • 集合中包含 N 个 ‎Empty 对象,每个对象有三个属性:‎Title, ‎Link, ‎Snippet
  • 示例:
loResults = loServer.WebSearch("VFP")
? loResults.lError      && .F.
? loResults.Count       && 5
? loResults.Item(1).Title
? loResults.Item(1).Link
? loResults.Item(1).Snippet

6.2 错误时(lError = .T.

  • 集合中只包含 1 个 ‎Empty 对象,属性含义变化:
? loResults.lError      && .T.
? loResults.Item(1).Title    && 例如 "401"
? loResults.Item(1).Link     && 例如 "API 密钥缺失或无效"
? loResults.Item(1).Snippet  && 详细的服务器错误信息

7. 完整使用示例(VFP)

LOCAL loServer, loResults, i
loServer = CREATEOBJECT("WebSearch.MTServer")

* 切换到 Exa 搜索(确保 config.json 中有对应项)
IF NOT loServer.SetProvider("exa")
    MESSAGEBOX("提供者不可用")
    RETURN
ENDIF

* 执行搜索
loResults = loServer.WebSearch("人工智能 最新进展")

IF loResults.lError
    MESSAGEBOX("搜索出错: " + loResults.Item(1).Snippet)
ELSE
    FOR i = 1 TO loResults.Count
        ? "【" + TRANSFORM(i) + "】"
        ? "标题:" + loResults.Item(i).Title
        ? "链接:" + loResults.Item(i).Link
        ? "摘要:" + LEFT(loResults.Item(i).Snippet, 200) + "..."
        ?
    ENDFOR
ENDIF

用 URL 抓取内容(例如 ollama_fetch

loServer.SetProvider("ollama_fetch")
loResults = loServer.WebSearch("https://example.com/article")
IF NOT loResults.lError
    ? "抓取到的内容片段:" + loResults.Item(1).Snippet
ENDIF

8. 错误处理建议

  • 始终检查返回集合的 ‎lError 属性,不要假设 ‎Title/Link/Snippet 一定存在有效内容。
  • 网络错误(如 DNS 失败、连接超时)会被 ‎WinHttp 捕获,‎Status 可能为 ‎0,错误信息会放入 ‎Snippet
  • 如果 ‎SetProvider 返回 ‎.F.,请检查 ‎config.json 中是否定义了该提供者名称,且大小写匹配(内部会转为小写比较)。
  • 建议使用 ‎TRY...CATCH 包裹 ‎CREATEOBJECT 和 ‎WebSearch 调用,以防类库缺失等问题。

9. 注意事项

  1. GetDebugObject 的使用:仅调试时使用。
  2. 异步研究(tavily_research):该适配器会轮询直到任务完成,可能阻塞调用线程数秒。请勿在 UI 线程中频繁调用。

10. 版本与作者

项目
内容
版本
2026.04.28
作者
xinjie
最后更新
2026-04-28