接入 Open WebUI:用 OpenViking Tool Server 把记忆、知识与技能变成原生工具
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
导读
OpenViking 是一个面向 AI Agent 的自进化上下文数据库,统一了 Agent 记忆(Memory)、知识库(RAG)与技能(Skills)。本指南讲解仓库中 examples/openwebui-plugin 提供的独立 FastAPI 工具服务器:它将 OpenViking 的一组核心 HTTP 端点封装成OpenAPI 工具,使 Open WebUI 无需粘贴 Python 脚本、无需在管理后台手动上传,即可自动发现并调用ov_search、ov_add_memory等 7 个原生工具。读完本文,你将掌握该插件的安装运行、环境变量语义、每个工具的请求/响应结构、底层转发与多租户鉴权原理,以及如何按四步流程扩展出你自己的工具。
背景:Open WebUI 的两种工具集成机制
Open WebUI 支持两种工具接入方式:
- Python "Functions":把一段 Python 函数粘贴到 Open WebUI 管理后台中,由 Open WebUI 运行时加载执行;
- 外部 OpenAPI 工具服务器:提供一个 Web 服务,Open WebUI 从
/openapi.json自动发现工具清单,并把每个操作(operation)暴露给 LLM 作为可调用工具。
本插件实现的是第二种机制。它本质上是一个薄转发层(thin translation layer):每个工具路由把请求转发给对应的 OpenViking HTTP 端点、附加多租户标识头,再把响应原样返回。插件内不包含任何业务逻辑——语义检索、内容写入、资源摄入等能力全部由 OpenViking 服务端完成(见 openviking/server/routers 下的search.py、content.py、filesystem.py、resources.py、sessions.py等路由实现)。
这一设计的好处是:同一套工具服务器可以被任何支持 OpenAPI 的客户端复用,不仅限于 Open WebUI。
插件架构与代码结构
插件位于examples/openwebui-plugin/,代码量极小,共 4 个模块 + 1 组测试:
| 文件 | 职责 |
|---|---|
| openviking_openwebui/config.py | 环境变量解析,生成只读Settings数据类 |
| openviking_openwebui/client.py | 基于httpx.AsyncClient的薄 HTTP 客户端,负责附加租户头与错误透传 |
| openviking_openwebui/tools.py | Pydantic 请求模型 + 7 个 FastAPI 路由处理器 |
| openviking_openwebui/server.py | create_app()组装 FastAPI 应用、生命周期管理与/health探针 |
| openviking_openwebui/main.py | python -m openviking_openwebui的入口,调用 uvicorn 启动 |
| tests/test_tools.py | 用respx模拟 OpenViking HTTP 层的 9 个测试用例 |
从源码结构可以看出,插件启动链路是:__main__.py读取环境变量 →server.create_app()在 lifespan 中创建共享OVClient挂到app.state→tools.router中的每个 handler 通过 FastAPI 依赖注入拿到同一个客户端 → 转发请求。依赖见 pyproject.toml:fastapi>=0.110、uvicorn[standard]>=0.27、httpx>=0.27、pydantic>=2.5,要求 Python >= 3.9。
快速开始:运行工具服务器
cd examples/openwebui-plugin pip install -e . OV_API_KEY=your-key python -m openviking_openwebui- 默认监听
0.0.0.0:8765; - 启动后终端应出现
INFO: Uvicorn running on http://0.0.0.0:8765; - 也可以使用
pyproject.toml注册的控制台脚本openviking-openwebui直接启动。
验证 OpenAPI 规格是否正常对外提供服务:
curl http://localhost:8765/openapi.json | jq '.paths | keys'输出应包含 7 个工具路径(/tools/ov_search、/tools/ov_recall_memories、/tools/ov_add_memory、/tools/ov_list_memories、/tools/ov_read_resource、/tools/ov_add_resource、/tools/ov_session_status)。FastAPI 会根据每个路由的operation_id(在tools.py中显式设置)自动生成 OpenAPI 文档,这正是 Open WebUI 能识别工具名的关键。另外 server.py 还暴露了GET /health探针,返回{"status": "ok", "endpoint": <OV_ENDPOINT>},可用于容器编排与负载均衡的健康检查。
接入 Open WebUI
- 进入 Open WebUI →Settings→Tools→Add Tool Server;
- 粘贴工具服务器可达的地址,例如
http://localhost:8765; - Open WebUI 会自动抓取
/openapi.json,列出全部 7 个工具,并在每次对话轮次中把它们作为可调用工具呈现给 LLM。
整个过程无需复制粘贴 Python 文件、无需管理后台上传。tests/test_tools.py中的test_openapi_lists_seven_tools用例专门断言了/openapi.json中至少包含这 7 个operationId,确保自动发现机制始终有效。
配置:全部通过环境变量
插件没有配置文件,所有配置均来自环境变量,这是有意的设计——让部署单元保持"一个二进制 + 一组环境变量"。config.py中的load_settings()在进程启动时一次性读取,因此修改环境变量后需要重启服务。
| 变量 | 默认值 | 说明 |
|---|---|---|
OV_ENDPOINT | http://localhost:1933 | OpenViking 服务端基础 URL(末尾/会被自动去除) |
OV_API_KEY | 空 | Bearer Token,以Authorization: Bearer …头发送;为空时不附加该头 |
OV_ACCOUNT | default | 租户(Tenant),以X-OpenViking-Account头发送 |
OV_USER | default | 用户,以X-OpenViking-User头发送 |
OV_AGENT | default | Actor peer ID,以X-OpenViking-Actor-Peer头发送 |
OV_BIND | 0.0.0.0:8765 | 工具服务器绑定的host:port |
OV_TIMEOUT | 30 | 调用 OpenViking 的 HTTP 超时(秒) |
几个值得注意的实现细节(见 config.py):
OV_BIND的解析是容错的:bind_host/bind_port属性用partition(":")拆分,缺省端口回退到8765,解析失败也回退8765;memories_uri属性固定返回viking://~/memories/——这是 OpenViking 中"个人记忆"的约定 URI 前缀,ov_recall_memories、ov_add_memory、ov_list_memories三个工具都依赖它;- 所有环境变量都经过
strip()处理,空字符串视为未设置并回退默认值。
OVClient(见 client.py)在每次请求时统一构造请求头:仅当OV_API_KEY非空才附加Authorization头;X-OpenViking-Account、X-OpenViking-User、X-OpenViking-Actor-Peer三个租户头则总是附加。这些头正是 OpenViking 服务端鉴权体系识别的字段——在 openviking/server/auth/init.py 中可以看到服务端通过 FastAPIHeader依赖读取同名请求头;trusted鉴权模式(见 openviking/server/auth/plugins/trusted.py)会直接信任并归一化这些头,据此确定请求所属的 account/user。
工具参考:7 个工具的参数与端点映射
下表是工具与 OpenViking 端点的完整映射:
| 工具 | OpenViking 端点 | 用途 |
|---|---|---|
ov_search | POST /api/v1/search/find | 跨记忆、资源、技能做语义检索 |
ov_recall_memories | POST /api/v1/search/find(限定viking://~/memories/) | 针对当前查询召回个人记忆 |
ov_add_memory | POST /api/v1/content/write写入viking://~/memories/<name> | 持久化一条新记忆 |
ov_list_memories | GET /api/v1/fs/ls?uri=viking://~/memories/ | 浏览记忆目录 |
ov_read_resource | GET /api/v1/content/read | 读取任意viking://URI 的全文 |
ov_add_resource | POST /api/v1/resources | 摄入远程 URL 或服务端可达的路径文件 |
ov_session_status | GET /api/v1/sessions/{id} | 查看会话的消息数、归档状态等元数据 |
所有路由定义都位于 tools.py,下面逐一给出源码确认的请求模型字段与约束。
ov_search—— 顶层语义检索
{"query": "…", "limit": 10, "target_uri": null, "score_threshold": null}query(必填):自然语言查询;limit:默认10,约束1 ≤ limit ≤ 100;target_uri:可选的viking://前缀,用于把检索限定到某个子树;score_threshold:可选的相似度阈值,约束0.0 ≤ score_threshold ≤ 1.0。
响应为SearchResponse:hits是由uri、score、snippet?组成的结构化命中列表,外加raw保留 OpenViking 的原始响应。命中结果的展平逻辑在_hits_from_find中实现:它会遍历 OpenViking/search/find响应result对象里的memories、resources、skills、results四个桶,从每个条目提取uri(或target_uri)、score、以及snippet/abstract/preview三选一的摘要文本。这也是本工具能"跨记忆、资源、技能一起搜"的底层原因。
ov_recall_memories—— 只搜个人记忆
{"query": "…", "limit": 6}与ov_search调用同一个POST /api/v1/search/find,但target_uri被强制设为viking://~/memories/,因此只检索个人记忆。适合在聊天中回答"关于我,你记得什么?"这类问题。limit默认6,约束1 ≤ limit ≤ 50——比ov_search更小,符合记忆召回的"精简优先"语义。
ov_add_memory—— 持久化新记忆
{"name": "profile.md", "content": "…", "mode": "replace", "wait": false}name(必填):viking://~/memories/下的文件名,例如profile.md(代码中会strip()并去掉开头的/,空名返回 400);content(必填):记忆正文,纯文本或 Markdown;mode:枚举replace | append | create,默认replace;wait:布尔值,默认false,置true时阻塞直到语义索引完成。
工具会把name拼成完整viking://~/memories/<name>URI,再POST /api/v1/content/write。响应为AddMemoryResponse,包含最终uri与raw原始响应。测试用例test_ov_add_memory_writes_under_memories验证了请求体确实携带完整 URI 与内容。
ov_list_memories—— 浏览记忆目录
{"recursive": false, "limit": 200}对应GET /api/v1/fs/ls,查询参数为uri=viking://~/memories/、recursive(布尔值转小写字符串)、node_limit。limit默认200,约束1 ≤ limit ≤ 1000,透传为服务端的node_limit。测试用例test_ov_list_memories_calls_fs_ls断言了 URL 中这三个参数的编码结果。
ov_read_resource—— 读取任意 viking:// 资源
{"uri": "viking://~/memories/a.md", "offset": 0, "limit": -1}uri(必填):完整的viking://URI;offset:默认0;limit:默认-1(表示读取全部)。
对应GET /api/v1/content/read,三个参数原样透传为查询参数。这是 LLM 拿到命中 URI 后"展开全文"的标准动作。
ov_add_resource—— 摄入远程资源
{"path": "https://example.com/doc.md", "to": null, "parent": null, "reason": "", "instruction": "", "wait": false}path(必填):OpenViking 服务端可达的远程 URL 或本地路径;to/parent:可选的目标/父目录;reason/instruction:可选的摄入原因与附加指令;wait:是否阻塞等待摄入完成。
对应POST /api/v1/resources,是纯 HTTP 转发——路径/URL 的合法性校验完全由 OpenViking 服务端完成。测试用例test_ov_add_resource_posts_resources验证了请求体包含path与wait字段。
ov_session_status—— 查询会话元数据
{"session_id": "sess-42"}对应GET /api/v1/sessions/{session_id},返回该会话的消息计数、归档状态、待处理 token 等信息。测试用例test_ov_session_status_gets_session断言了请求 URL 路径为/api/v1/sessions/sess-42。
错误透传
当 OpenViking 返回非 2xx 时,OVClient.request会抛出携带status与响应体的OVError(见 client.py),路由层的_forward辅助函数(见 tools.py)会把它转成HTTPException,原样保留上游的状态码与错误详情返回给调用方。测试用例test_error_pass_through验证了 404 错误体被完整透传。
测试:用 respx 模拟上游
cd examples/openwebui-plugin pip install -e ".[test]" pytest tests -x -q测试套件(tests/test_tools.py)使用respx拦截并模拟 OpenViking 的 HTTP 层,断言每个工具调用了正确的方法/路径/请求体,并且逐字转发租户头。其中_assert_headers帮助函数集中校验四个请求头:
authorization: Bearer key-xyzx-openviking-account: acctx-openviking-user: alicex-openviking-actor-peer: webui
测试通过create_app(SETTINGS)注入自定义Settings与httpx.ASGITransport,无需真实启动网络服务(见 server.py 的注释:测试可注入自定义 Settings/OVClient)。pyproject.toml中开启了pytest-asyncio的asyncio_mode = "auto",因此异步测试函数无需显式标记。
局限性与边界
- 不支持流式输出:Open WebUI 工具是请求/响应模型,实时转录流式传输不在本插件范围内;
- 不支持文件上传:
ov_add_resource只接受远程 URL 或 OpenViking 服务端自身可达的路径。若要上传二进制数据,应直接调用 OpenViking 服务端的temp_upload端点(POST /api/v1/resources/temp_upload,可携带?token=临时上传凭证,见 openviking/server/upload_token_store.py 与 openviking/server/mcp_endpoint.py); - 无删除/移动类写操作:插件按"只读为主"设计,需要破坏性操作的用户请使用 OV CLI;
- 单进程单租户:租户身份来自环境变量,如果需要多租户,请为每个
(account, user)组合各跑一个工具服务器进程; - 不捆绑 Open WebUI:这只是工具服务器,Open WebUI 实例需自行准备。
扩展路线:添加新工具的四步流程
README 给出的扩展流程与源码完全对应:
- 在 openviking_openwebui/tools.py 中添加 Pydantic 请求模型;
- 添加路由 handler,并用
@router.post("/tools/<name>", operation_id="<name>")装饰——operation_id必须与工具名一致,因为 Open WebUI 依赖它识别工具; - 通过
OVClient(client.get/client.post)转发到对应 OpenViking 端点; - 在 tests/test_tools.py 中仿照现有用例,用
respxmock 上游并断言转发正确性。
社区可能期望的候选工具包括:ov_session_create、ov_session_commit、ov_grep、ov_glob、ov_overview、ov_abstract等。由于插件本身是"纯转发"架构,新增工具的边际成本很低——只需定义模型、路由与测试三处改动。
安全注意事项
- 切勿把
OV_API_KEY提交进版本库,一律通过环境变量注入; - 工具服务器自身没有任何鉴权——请绑定到 localhost 或内网,或在前端用代理强制鉴权;
- 租户身份属于服务端信任模型:任何持有
OV_API_KEY并伪造X-OpenViking-Account/User头的调用方,都能读取该租户的数据。这与 OpenViking 的标准信任模型一致——在服务端trusted鉴权模式下,这些头被直接信任(见 openviking/server/auth/plugins/trusted.py)。因此务必通过访问控制保护好OV_ENDPOINT指向的 OpenViking 服务。
小结
OpenViking Open WebUI 插件用不到两百行 Python 代码,把一个 Agent 记忆/知识/技能后端无缝接入 Open WebUI 的工具生态:7 个精心挑选的工具覆盖"检索—召回—写入—浏览—读取—摄入—会话诊断"的完整闭环,全部通过 OpenAPI 自动发现,零业务逻辑重复;同时用环境变量保持了部署单元的极简。对于希望让 LLM 在对话中真正"记住用户、检索知识、沉淀技能"的开发者,这是一条开箱即用的接入路径,也为后续按四步流程扩展更多 OpenViking 能力留下了清晰范式。
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考