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 |
|
|
websearchmanager 对象实例。初始化时创建,不应直接访问。 |
其他属性均由
oWebSearchManager内部维护,外部无需关心。
4. 主要方法
SetProvider(tcProvider)
语法Procedure SetProvider(tcProvider) AS Logical
参数
|
|
|
|
|---|---|---|
tcProvider |
|
|
返回值
|
|
|
|---|---|
|
|
.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 |
|
|
返回值
Object – 始终返回一个 Collection 对象,结构如下:
|
|
|
|
|---|---|---|
lError |
|
.T. 表示错误,.F. 表示成功 |
.Count |
|
|
.Item(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。
|
|
|
|
|
|---|---|---|---|
ollama |
|
|
|
ollama_local |
|
|
|
exa |
|
|
|
serpapi |
|
|
|
serpapi_ai |
|
|
|
tavily |
|
|
|
ollama_fetch |
|
|
|
ollama_fetch_local |
|
|
|
anythingllm_fetch |
|
|
|
tavily_fetch |
|
|
|
tavily_crawl |
|
|
|
tavily_research |
|
|
|
¹
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. 注意事项
-
GetDebugObject 的使用:仅调试时使用。 -
异步研究(tavily_research):该适配器会轮询直到任务完成,可能阻塞调用线程数秒。请勿在 UI 线程中频繁调用。
10. 版本与作者
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
夜雨聆风