
版本: 2.0 最后更新: 2026年4月11日 适用范围: RAG.DLL(多线程 COM 组件)
RAG.MTServer 是一个 OLE Public 类(继承自 Session),被编译为 多线程 COM DLL(RAG.DLL)。它为各种 COM 客户端(VFP、C#、VB.NET、X#、Python、Delphi 等)提供了与 大语言模型后端服务(如 AnythingLLM)交互的统一接口。
该类封装了以下核心功能:
ProgID:RAG.MTServer
线程模型: 多线程单元(MTA) – 支持多线程并发调用(但需注意内部对象的线程安全性,见“注意事项”)。
regsvr32 工具注册 DLL。导航到 DLL 所在目录,执行:regsvr32 RAG.dll
语法:
Object.Set(tcServerName As String, tcApiKey As String, tcExePath As String, tcServerURL As String, tlLog As Logical) As Logical
参数:
参数 | 类型 | 必选 | 说明 |
|---|---|---|---|
tcServerName | String | 是 | 后端名称,目前仅支持 "AnythingLLM"(不区分大小写),预留 "RAGFLOW" |
tcApiKey | String | 是 | API 密钥 |
tcExePath | String | 是 | 后端可执行文件的完整路径(如 C:\AnythingLLM\AnythingLLM.exe) |
tcServerURL | String | 是 | API 基础 URL(如 http://localhost:3001) |
tlLog | Logical | 否 | 是否启用日志记录(默认 .T.) |
返回值:.T. 表示初始化调用成功(实际配置错误会在后续方法调用时体现)。
说明: 必须在使用任何其他方法之前调用 Set。该方法根据 tcServerName 创建对应的后端管理器,并保存配置信息。
示例:
loMT.Set("AnythingLLM", "sk-xxxx", "C:\AnythingLLM\AnythingLLM.exe", "http://localhost:3001", .T.)
语法:
Object.IsRunning() As Logical
返回值:.T. 服务正在运行且可访问;.F. 未运行或连接失败。
说明: 发送 GET /api/ping 请求检测服务状态。
语法:
Object.Start(tnTimeoutSeconds As Number) As Logical
参数:
tnTimeoutSeconds – 可选,等待服务就绪的超时秒数(默认 30)。返回值:.T. 启动成功;.F. 失败(可通过 GetStatus 获取错误原因)。
说明:
.T.。WaitForReady 循环检测服务响应,直至超时。语法:
Object.ListAllWorkSpaces() As Object
返回值:
字符串 – 如果只有一个工作区,返回该工作区的 slug(如 "default")。
COM 对象(JSON) – 如果有多个工作区,返回 JSON 对象,结构为:
{
"workspaces": [
{ "name": "Workspace1", "slug": "slug1", ... },
{ "name": "Workspace2", "slug": "slug2", ... }
]
}
.NULL. – 请求失败(网络错误、无工作区等)。
说明: 用于获取可用的工作区标识符(slug)。后续的 Warmup、VectorSearch 等方法需要传入 slug。
注意: 若没有任何工作区,API 可能返回空数组,此时访问 workspaces[0] 会出错。调用前请确保至少创建一个工作区。
语法:
Object.Warmup(tcWorkspaceSlug As String, tcApiKey As String) As Logical
参数:
tcWorkspaceSlug – 可选,工作区 slug(默认使用 Set 配置的)。tcApiKey – 可选,API 密钥(默认使用 Set 配置的)。返回值:.T. 预热成功;.F. 失败。
说明: 发送一个虚拟查询("warmup",topN=1),将知识库的向量数据加载到内存,从而加快后续真实的向量检索和对话的响应速度。
语法:
Object.VectorSearch(tcQuery As String, tnTopN As Integer, tcWorkspaceSlug As String, tcApiKey As String) As Object
参数:
tcQuery – 必需,查询文本。tnTopN – 可选,返回最相似文档片段的数量(默认 5)。tcWorkspaceSlug – 可选,工作区 slug。tcApiKey – 可选,API 密钥。返回值: JSON 对象(包含检索结果),失败返回 .NULL.。
返回 JSON 结构示例:
{
"results": [
{ "text": "文档片段内容...", "score": 0.85, "metadata": {...} },
...
]
}
说明: 不调用 LLM 生成回答,仅从知识库中检索与查询最相似的文档片段。适用于需要获取原始参考资料而不需要对话的场景。
语法:
Object.CreateSession(tcName As String, tcThreadSlug As String) As Object
参数:
tcName – 可选,会话显示名称。tcThreadSlug – 可选,线程标识(用于区分同一用户的多个对话分支)。返回值:ChatSession 对象(COM 对象,具有 cSessionId、nMessageCount 等属性)。
说明:
YYYYMMDD + SYS(2015),例如 20260407_7FR1DI1WR)。SaveCurrentSession 持久化到磁盘。SendMessage / SendMessageText 将针对该会话。语法:
Object.SwitchSession(tcSessionId As String) As Logical
参数:
tcSessionId – 必需,要切换到的会话 ID。返回值:.T. 切换成功;.F. 会话不存在或加载失败。
说明:
LoadSession)。语法:
Object.GetCurrentSession() As Object
返回值: 当前 ChatSession 对象,若没有当前会话则返回 .NULL.。
说明: 可用于读取当前会话的属性(如 cSessionId、Name、nMessageCount 等)。
语法:
Object.SaveCurrentSession() As Logical
返回值:.T. 保存成功;.F. 失败。
说明:
<会话ID>.json。AnythingLLM_Session 文件夹(若不存在会自动创建)。语法:
Object.LoadSession(tcSessionId As String) As Object
参数:
tcSessionId – 必需,会话 ID。返回值: 加载的 ChatSession 对象,失败返回 .NULL.。
说明:
语法:
Object.DeleteSession(tcSessionId As String) As Logical
参数:
tcSessionId – 必需,要删除的会话 ID。返回值:.T. 删除成功;.F. 失败。
说明:
.NULL.。语法:
Object.ListSessions() As Object
返回值: VFP Collection 对象(COM 可枚举),包含所有已保存会话的 完整 ChatSession 对象(包括全部消息)。
说明:
AnythingLLM_Session 目录下的所有 .json 文件,加载每个会话的完整内容。语法:
Object.ClearCurrentSession()
说明: 清空当前会话中的所有消息(保留会话 ID、名称、创建时间等元数据)。常用于开始新的对话主题但希望保持同一会话标识。
语法:
Object.SendMessage(tcMessage As String, tcMode As String, tcSessionID As String) As Object
参数:
tcMessage – 必需,用户输入的消息。tcMode – 可选,对话模式("chat" – 普通对话,"query" – 仅检索,默认 "chat")。tcSessionID – 可选,传递给 API 的会话 ID(用于 AnythingLLM 内部上下文关联)。若不提供,则使用本地当前会话的 ID。返回值: JSON 对象,失败返回 .NULL.。
返回 JSON 结构示例:
{
"textResponse": "助手的回答内容...",
"metrics": {
"total_tokens": 150,
"prompt_tokens": 50,
"completion_tokens": 100
},
"sources": [ ... ]
}
说明: 核心对话方法。内部执行以下步骤:
"默认会话")。ChatMessage 对象)添加到本地会话历史。POST /api/v1/workspace/{slug}/chat。注意:
tcSessionID 参数(可使用当前会话的 cSessionId),AnythingLLM 会基于该 ID 维护上下文。"query" 仅执行检索而不生成回答,返回格式略有不同。语法:
Object.SendMessageText(tcMessage As String, tcMode As String) As String
参数:
tcMessage – 必需,用户消息。tcMode – 可选,模式(默认 "chat")。返回值: 字符串,仅包含助手的文本回复。若失败返回空字符串。
说明: 简化版 SendMessage,内部调用 SendMessage 并提取 textResponse 字段。适用于只需要回复文本的场景。
语法:
Object.GetStatus() As Object
返回值: JSON 对象,包含以下字段:
{
"isRunning": true,
"serverUrl": "http://localhost:3001",
"workspaceConfigured": true,
"apiKeyConfigured": true,
"lastError": "",
"lastErrorCode": 0,
"hasCurrentSession": true,
"currentSessionId": "20260411123456789",
"currentSessionMessages": 5
}
说明: 用于监控和调试,可快速了解服务状态和当前会话信息。
语法:
Object.GetDebugObject() As Object
返回值: 一个新的 MTServer 对象(在 VFP IDE 环境中创建)。
说明:
典型用法(在客户端代码中):
* VFP 客户端调试
loMT = CREATEOBJECT("RAG.MTServer")
IF _DEBUG
loMT = loMT.GetDebugObject()
ENDIF
错误代码 | 说明 |
|---|---|
0 | 无错误 |
-1 | 连接失败(服务未启动或网络问题) |
-2 | 启动服务失败(如找不到可执行文件) |
-3 | 启动超时 |
-4 | 配置缺失(工作区 Slug 或 API Key 未设置) |
-5 | 服务未运行 |
-6 | 向量检索异常 |
-7 | 发送消息异常 |
-9 | JSON 解析错误 |
-10 | 会话不存在 |
-11 | 保存会话失败 |
-12 | 加载会话失败 |
-13 | 删除会话失败 |
400 | HTTP 400 – 请求格式错误 |
401 | HTTP 401 – API Key 无效 |
403 | HTTP 403 – 无权限 |
404 | HTTP 404 – 资源不存在(如工作区) |
429 | HTTP 429 – 请求频率过高 |
500 | HTTP 500 – 服务器内部错误 |
503 | HTTP 503 – 服务不可用 |
其他 HTTP 状态码直接返回该状态码作为错误代码。
LOCAL loMT, loStatus, lcSlug, loResponse
* 创建 COM 对象
loMT = CREATEOBJECT("RAG.MTServer")
* 初始化
IF NOT loMT.Set("AnythingLLM", "your-api-key", "C:\AnythingLLM\AnythingLLM.exe", "http://localhost:3001", .T.)
? "Set 失败"
RETURN
ENDIF
* 启动服务
IF NOT loMT.IsRunning()
IF NOT loMT.Start(30)
loStatus = loMT.GetStatus()
? "启动失败:" + loStatus.lastError
RETURN
ENDIF
ENDIF
* 获取工作区 slug
lcSlug = loMT.ListAllWorkSpaces()
DO CASE
CASE VARTYPE(lcSlug) = "C"
* 只有一个工作区
CASE VARTYPE(lcSlug) = "O" AND NOT ISNULL(lcSlug)
lcSlug = lcSlug.workspaces.Item(0).slug
OTHERWISE
? "获取工作区失败"
RETURN
ENDCASE
* 创建会话
loMT.CreateSession("测试会话", "")
* 发送消息
loResponse = loMT.SendMessage("VFP 如何调用 COM 对象?", "chat", "")
IF NOT ISNULL(loResponse)
? "助手回答:" + loResponse.textResponse
? "Token 使用量:" + TRANSFORM(loResponse.metrics.total_tokens)
ELSE
? "发送失败:" + loMT.GetStatus().lastError
ENDIF
* 保存会话
loMT.SaveCurrentSession()
LOCAL loMT, lcSessionId, loResp1, loResp2
loMT = CREATEOBJECT("RAG.MTServer")
loMT.Set("AnythingLLM", "your-key", "C:\AnythingLLM\AnythingLLM.exe", "http://localhost:3001", .T.)
loMT.Start(30)
* 创建会话并获取其 ID
lcSessionId = loMT.CreateSession("多轮对话", "").cSessionId
* 第一轮
loResp1 = loMT.SendMessage("我叫张三", "chat", lcSessionId)
? loResp1.textResponse
* 第二轮
loResp2 = loMT.SendMessage("我叫什么名字?", "chat", lcSessionId)
? loResp2.textResponse && 应输出 "张三"
* 保存会话
loMT.SwitchSession(lcSessionId)
loMT.SaveCurrentSession()
LOCAL loMT, loResult, lnI
loMT = CREATEOBJECT("RAG.MTServer")
loMT.Set("AnythingLLM", "your-key", "C:\AnythingLLM\AnythingLLM.exe", "http://localhost:3001", .T.)
loMT.Start(30)
loResult = loMT.VectorSearch("VFP COM 调试", 5, "", "")
IF NOT ISNULL(loResult)
FOR lnI = 0 TO loResult.results.Length - 1
? "结果 " + TRANSFORM(lnI + 1)
? "片段:" + loResult.results.Item(lnI).text
? "得分:" + TRANSFORM(loResult.results.Item(lnI).score)
ENDFOR
ENDIF
LOCAL loMT, loSession1, loSession2
loMT = CREATEOBJECT("RAG.MTServer")
loMT.Set("AnythingLLM", "your-key", "C:\AnythingLLM\AnythingLLM.exe", "http://localhost:3001", .T.)
loMT.Start(30)
loSession1 = loMT.CreateSession("会话A", "")
loSession2 = loMT.CreateSession("会话B", "")
* 切换到会话A 并发送消息
loMT.SwitchSession(loSession1.cSessionId)
loMT.SendMessageText("这是会话A的消息")
* 切换到会话B 并发送消息
loMT.SwitchSession(loSession2.cSessionId)
loMT.SendMessageText("这是会话B的消息")
* 删除会话A
loMT.DeleteSession(loSession1.cSessionId)
* 清空当前会话(会话B)的所有消息
loMT.ClearCurrentSession()
* 列出剩余会话
LOCAL loSessions = loMT.ListSessions()
FOR EACH loSess IN loSessions
? loSess.Name, loSess.nMessageCount
ENDFOR
MTServer 实例,避免共享同一个对象。不要在多个线程中同时调用同一实例的方法。Start 可能导致重复启动进程。应在应用层确保服务单例启动(例如使用互斥体)。AnythingLLM_Session\*.json)可能被多个线程同时读写。建议将会话 ID 与线程绑定,避免跨线程共享会话。.NULL.System.Runtime.InteropServices.Marshal.ReleaseComObject 或将对象设为 nullAnythingLLM.log。ListAllWorkSpaces 前,请确保 AnythingLLM 中至少存在一个工作区。否则 API 返回空数组,访问 Item(0) 会引发错误。Warmup 将向量加载到内存,可显著提高后续响应速度。GetDebugObject() 获取一个在 VFP IDE 中运行的对象,然后在 VFP 中设置断点(需要同时打开 VFP 项目)。Q1:启动服务时一直超时怎么办?
A:检查 cexepath 是否正确,且 AnythingLLM 是否正常启动。可以尝试手动运行 exe 观察输出。另外,首次启动可能因加载模型较慢,适当增加 tnTimeoutSeconds 值(如 60 秒)。
Q2:发送消息返回 .NULL.,如何获取详细错误?
A:调用 GetStatus() 方法,查看 lastError 和 lastErrorCode 字段。
Q3:如何实现多轮对话?
A:在每次调用 SendMessage 时,传递相同的 tcSessionID 参数(例如使用当前会话的 cSessionId)。AnythingLLM 会基于该 ID 自动维护对话历史。
Q4:为什么 ListSessions 很慢?
A:该方法会加载每个会话的完整消息内容。若会话数量多或消息量大,建议仅用于管理界面,或自行实现摘要列表方法。
Q5:能否在 64 位程序中使用? A:不能直接使用。VFP COM DLL 为 32 位,64 位程序可通过以下方式调用: - 编译为 32 位目标平台。 - 使用进程外 COM(配置 DLLSurrogate)。 - 使用中间件(如 HTTP 服务)桥接。
日期 | 版本 | 变更说明 |
|---|---|---|
2026.04.01 | 1.0 | 初始版本 |
2026.04.11 | 2.0 | 添加会话管理和对话能力 |
如有问题,请联系开发者: 作者: xinjie 文档日期: 2026年4月11日