Cordis 桥接
Cordis 桥接层将 DeepSeek Harness(DSH)引擎嵌入 Libre WebUI 后端。DSH 作为插件树运行在 Libre WebUI 托管的 Cordis 运行时中,其能力以 Cordis 服务而非直接导入模块的形式提供。
桥接功能默认关闭。只有运维人员启用后,本页描述的功能才会运行,详见 Cordis 配置。
为什么使用桥接而非直接集成
在 Libre WebUI 服务中直接导入 DSH 软件包,代码虽然更短,却会形成编译时依赖。这样,无论更换模型适配器、替换智能体循环还是移除引擎,都需要修改并重新部署 Libre WebUI。
桥接层反转了这一依赖关系。Libre WebUI 只依赖一个抽象接口,由 Cordis 组合文档决定如何实现:
- 无需重新构建即可切换目标。 组合是 YAML 文件,更换引擎的提供商只需修改配置。
- 配置能力。 每项能力对应一个 Loader 行。运维人员维护的组合变更在宿主下次启动时生效。
- 完整移除。 引擎安装的所有服务、监听器和副作用都归根纤程所有。释放根纤程即可撤销全部内容,因此可在不重启 Libre WebUI 的情况下停止引擎。
分层
具体的 DSH 依赖都限制在 backend/src/cordis/dsh/ 内。路由和应用服务使用桥接接口。Work 驱动使用独立的内存组合,绝不会挂载宿主文件系统插件。
接口约定
接口定义位于 backend/src/cordis/contracts.ts,刻意保持精简:仅以不依赖引擎术语的形式描述 Libre WebUI API 所需的数据。
| 接口 | 用途 |
|---|---|
DshEngine.status() | 各引擎服务的生命周期状态(pending / ready / failed) |
DshEngine.modelConfiguration() | 当前组合的模型和提供商默认值 |
DshEngine.listSessions() | 按最新优先排序的会话摘要 |
DshEngine.getSession(id) | 获取会话及其投影消息 |
DshEngine.createSession(opts) | 预留会话 ID 和工作目录 |
DshEngine.updateSessionSettings(id, settings) | 空闲时持久化真实模型选择和原生文件系统权限模式 |
DshEngine.decideApproval(id, approvalId, decision) | 为所属会话处理一项待决的原生工具审批 |
DshEngine.deleteSession(id) | 结束会话并释放其智能体 |
DshEngine.listAgents() | 列出运行中的智能体,并标记根或子智能体 |
DshEngine.listTools() | 引擎注册的模型可用工具 |
DshEngine.sendMessage(id, txt) | 开始一轮并返回流句柄 |
DshEngine.cancel(id) | 取消会话正在执行的一轮 |
此接口作为 Cordis 服务 libreDshEngine 提供。使用方通过 ctx.get('libreDshEngine') 读取它,不直接导入桥接模块。
EngineStreamChunk 携带 text、reasoning、tool-call、tool-result、approval-request、approval-decision、error 和 done。实时帧按所属智能体和会话路由,不会再次发送对应的持久化助手消息。sendMessage 返回的句柄通过 subscribe 重放已发送的内容,因此即使首个令牌在 HTTP 处理器挂接监听器之前就已生成,也不会丢失。
时序:一轮聊天
一次请求之后,一轮对话就是单向的服务器到客户端序列,因此使用 NDJSON。将它保留在 POST 请求内,可避免第二次握手、票据及重连协议,并让整轮对话都属于同一个经过身份验证的请求。
DONE 与 PENDING
Cordis 在插件声明的服务可用时才激活插件,因此配置行会处于尚未运行的状态。必须区分以下两个概念,混淆它们是引擎静默不响应的常见原因。
Loader 条目状态。 Loader 跟踪每一行的 PENDING → LOADING → ACTIVE 或 FAILED 状态。缺少声明服务的行会一直等待,而不是报错,因此不完整的组合可能表现为引擎已启动,却不提供任何功能。
服务可用性。 宿主为每个预期服务报告以下状态:
| 状态 | 含义 | 原因 |
|---|---|---|
pending | 尚未在上下文上注册 | 提供该服务的行尚未激活或已禁用 |
ready | 已注册且可用 | 提供该服务的行已激活 |
failed | 已声明但不可用 | 通过 detail 字符串说明原因 |
host.status() 列出每个预期服务的可用性,并指出缺少哪些必需服务;GET /api/cordis/health 提供相同信息。若组合缺少必需服务,启动会抛出错误,而不是提供只返回空列表的引擎。
以下两条依赖链容易配置错误:
- 缺少
systemPrompt时,dsh-tools无法启动。 - 只有
agents、sessions、llm、tools、systemPrompt和sessionProjections全部存在,dsh-agent-loop才能启动。
缺少其中任一项时,组合可能拥有正常的会话存储,但引擎永远不会回复消息。
提供商配置
随附的 libre-webui-llm-adapter 行服务于 Libre WebUI 中配置的模型提供商。引擎页面的模型选择器为会话选择提供商模型,无需替换该行。
组合中插件行的变更在宿主下次启动时生效。请重启后端,或在管理员开关未锁定时关闭再启用 Cordis。持久化会话保留在配置的存储中,并通过当前组合恢复。
可信集成代码可以直接使用 Cordis Loader 生命周期 API。桥接层不暴露适配器替换端点,也不会在替换失败时自动恢复先前的适配器。
回滚
释放宿主的根纤程会移除引擎安装的全部内容。保证来自这一统一的归属关系:
- 服务由插件注册,会随其纤程一起撤销。
session/event订阅在桥接自身的构造函数内注册,归桥接行的纤程所有。- 桥接层跟踪智能体句柄,并在清理副作用中释放它们。
- 宿主释放拥有全部配置行的根上下文。
stopCordisHost() 是幂等的,并已接入后端关闭流程。引擎的定时器和文件句柄会显式释放,而不是等待进程退出。
会话标识与持久化
引擎页面在创建会话时预留一个不透明的会话 ID。启用持久化时,会话头立即写入存储,因此空会话也能在重启后保留。桥接层同时列出已存储和运行中的会话,通过 DSH 验证过的持久化 API 读取日志,并在后续对话中以同一 ID 恢复智能体。新用户消息使用 DSH 的带标识消息构造器。
删除会话时,会先取消并释放其智能体,再移除会话文件。本地 JSONL 删除适配器验证存储和会话路径,并拒绝符号链接。不支持删除的自定义持久化后端会返回错误,不会声称数据已移除。
取消会传递给原生智能体、模型请求及工具执行。客户端断开连接会取消当前轮次;已完成的消息仍然可读。流重放缓冲区有容量限制,能保留在读取方连接之前快速返回的响应。
宿主引擎是单副本 solo 功能。团队部署不能挂载其本地 JSONL 运行时。沙箱化 Work 则使用现有的 SQL 任务、运行、消息、审批和事件仓库。
HTTP 接口
| 方法 | 路径 | 用途 |
|---|---|---|
GET | /api/cordis/health | 桥接状态,无需身份验证 |
GET | /api/cordis/sessions | 列出会话 |
POST | /api/cordis/sessions | 创建会话 |
GET | /api/cordis/sessions/:id | 读取会话及消息 |
DELETE | /api/cordis/sessions/:id | 结束会话 |
POST | /api/cordis/sessions/:id/messages | 发送消息并流式返回 NDJSON |
POST | /api/cordis/sessions/:id/cancel | 取消正在执行的轮次 |
GET | /api/cordis/agents | 列出运行中的智能体 |
GET | /api/cordis/tools | 列出注册的工具 |
除 /health 外,每条路由都要求经过身份验证的管理员会话。桥接层无法处理请求时返回 503,其中 code 为 CORDIS_DISABLED、CORDIS_STARTING 或 CORDIS_UNAVAILABLE。

页面实现位于 frontend/src/pages/CordisPage.tsx,可从侧边栏访问 /cordis。它列出会话及注册工具、创建会话,并将当前轮次流式写入记录。桥接关闭或无法启动时,页面显示原因而非空列表,避免把“没有会话”与“没有引擎”混为一谈。
浏览器客户端位于 frontend/src/utils/api/cordisApi.ts,只调用上述接口,不导入后端类型或任何 @deepseek-ai/* 软件包,因此替换引擎无需修改前端。通过 sendMessage(sessionId, text, { onChunk }) 消费一轮响应;客户端自行解析换行分隔的 JSON,并处理被网络读取边界拆开的数据块。
引擎聊天控件
引擎页面渲染 Markdown、表格和带语法高亮的代码块,并提供回复和代码的复制控件。系统提示词与注入的运行时上下文收纳在默认折叠的会话上下文中,不会显示成用户编写的消息。公开的推理和工具活动分别折叠展示,重新加载后工具结果仍与正确操作配对。
请在输入区选择真实的提供商模型。选择器列出当前管理员可用的本地和插件模型及其提供商标识。Chat 中的人设和智能体选择不是模型 ID,也不会将其指令注入引擎会话。旧的失败人设模型会话头不会用作默认模型提示,已保存日志保持不变。
每个会话都有独立的只读或工作区写入设置,由 DSH 文件系统策略及桥接层的规范工作区边界强制执行。输入区显示工作区范围。设置以原生会话事件保存,重启后保留;轮次执行中拒绝修改。
原生提权请求会作为附属于该操作的允许一次 / 拒绝卡片显示。批准仅对这次请求有效,不改变长期权限模式。过期或已取消的请求无法获批,无界面的 Chat 调用会拒绝无法展示的询问。桥接层不提供无限制宿主访问。
额外的管理员端点包括:
| 方法 | 路径 | 用途 |
|---|---|---|
GET | /api/cordis/models | 可用的提供商模型及当前真实模型默认值 |
PATCH | /api/cordis/sessions/:id/settings | 设置会话的模型和/或权限模式 |
POST | /api/cordis/sessions/:id/approvals/:approvalId | 以 allowed-once 或 rejected 处理一项待决请求 |
在 Chat 中使用引擎
同时启用访问与策略 → CLI代理模型和 Cordis 引擎,管理员即可在 Chat 中选择 DeepSeek Harness。每个请求都会创建新的临时引擎会话,只包含该 Chat 请求提供的对话记录。常规 Chat 数据库仍是权威来源;不相关的对话、分支和重试不会共享隐藏的引擎历史。临时引擎日志在完成或取消后移除,也不会出现在引擎页面中。
标准提供商组合还会在智能体组列出 DeepSeek Harness · 模型(提供商)。保存的 ID 包装了引擎页面所用的同一限定提供商路由:dsh:lwui:ollama:<model> 或 dsh:lwui:plugin:<plugin>:<model>,各提供商组成部分均使用百分号编码。可选的本地原生 DSH 连接从该实例的实时目录添加 dsh:native:<provider>:<model> 选项,并复用其原生提供商配置与凭据。可从 libre-webui/dsh-native-provider 安装 Apache-2.0 许可的独立软件包,或从 Libre WebUI 发行版准备包。两者使用 @libre-webui/dsh-native-provider 这一名称,并将提供商密钥保留在原生 DSH 中。连接要求同一 Unix 主机和 OS 账户,通过私有 Unix 套接字通信;它无法隔离共用该账户的应用。它只提供模型推理,不提供原生智能体会话或原生工具执行。安装、配置档重启、升级和移除方法请参阅配置指南。缺少连接或所选模型时会失败,不会切换提供商。原生调用也会出现在提供商使用情况中,显示所选模型、报告的令牌数、延迟及结果状态。基础 dsh 配置档继续使用当前组合的默认模型。自定义适配器组合只暴露基础配置档,不会宣传不受支持的 Libre WebUI 提供商覆盖选项。
标题和思考摘要会将 DSH 选择解析为底层提供商,直接发送文本请求,不使用工具或创建智能体会话。基础配置档请求读取运行中引擎的默认值,包括桥接行覆盖项,不会根据当前提供商目录猜测。自定义适配器需要为这些功能明确配置 Ollama 或插件任务模型。所选提供商不可用时,会正常失败或显示本地标题预览,不会向其他提供商发出请求。
请求使用经过身份验证的管理员的提供商设置和凭据,不会暗中选用其他管理员的凭据。默认工作区仍是配置的 Cordis 工作区;Chat 不会改用服务器用户的主目录。
沙箱化 Work
启用 Cordis 后,Work 提供独立的引擎控件,可选 Libre WebUI 或 DeepSeek Harness。模型选择器保留普通模型名称和提供商标识。对 LWUI 支持的提供商,DSH 选择内部保存为 dsh:<model>;原生 DSH 选择则保存 providerType: dsh、确切的原生提供商 ID 和原始模型 ID。常规工具能力和访问检查仍然有效;使用原生凭据还要求活跃管理员身份。
每次运行都会创建隔离的内存 DSH 智能体循环。模型适配器接收当前 Work 记录、提供商元数据、图像及工具模式。工具函数只等待 Work 返回结果,无法读取宿主文件或启动宿主进程。
Work 继续负责验证参数、请求批准、在工作区运行时执行工具、在 SQL 中记录结果与提供商重放状态、强制预算和发布事件。被拒绝的工具返回正常的拒绝结果。取消会释放 DSH,并遵循 Work 既有的容器清理流程。工作进程恢复后,新的 DSH 驱动接收恢复的 Work 上下文,不会重复已完成工具的副作用。
Work 集成不需要宿主引擎组合或 JSONL 会话存储。它遵循 Work 既有的 Docker/Kubernetes 运行时和部署规则,包括团队模式的共享持久化要求。
安全边界
引擎页面和宿主侧 Chat 智能体仅供管理员使用。引擎会话及其系统提示词属于共享管理员控制台,不是按用户隔离的工作区。普通账户无法通过 API 读取、创建、修改或取消这些会话。
随附的宿主文件系统工具通过规范化目标路径(包括符号链接解析)将读写限制在配置的工作区内。会话覆盖的工作目录必须仍在该工作区中。原生 DSH 修改策略及引擎单次审批仍然适用。运维人员安装的组合插件属于可信服务器代码,可以授予额外能力。引擎审批独立于 Work 的审批与容器执行流程。
Work 的 DSH 驱动是独立的:它不挂载宿主文件系统、shell 或持久化插件,只能通过 Work 现有的授权和沙箱执行。远程模型提供商仍需主动启用,并使用所选账户配置的提供商路由。