PrivateGPT Workbench 演示 UI 设计:基于 OpenAPI 契约的单文件静态应用
本文围绕 PrivateGPT 仓库中的 Workbench PRD 展开,讲解这个位于 ui/ 目录的轻量演示界面如何把 PrivateGPT 的 Claude 兼容 API(聊天、文档摄取、Text-to-SQL、Web 搜索、MCP、Skills、自定义工具等)变成一个可直观操作的本地应用:包括以 Fern 生成的 OpenAPI 文件为唯一契约来源的请求构建规则、localStorage 持久化状态模型、会话级 API Debugger、以及浏览器端 JavaScript 自定义工具的闭环执行机制。读完本文,你可以理解 Workbench 每个界面背后的 API 调用方式与源码实现对应关系,也能照着 PRD 的分层设计思路为本地推理服务搭建自己的演示前端。
一、定位与边界:Workbench 是演示器,不是主产品
PrivateGPT 的价值主张不是本地推理本身,而是构建在任意 OpenAI 兼容本地推理后端之上的高层应用层:Chat/messages API、文件摄取、带引用的检索、Text-to-SQL、沙箱 Python 表格分析、Web 搜索与抓取、MCP、Skills、自定义工具以及 Embeddings 等底层原语。Workbench(暂定名 PrivateGPT Workbench)存在的目的,是让这些 API 能力变得"可感知":
- 对非技术用户,它应呈现为:一个免费的本地 AI 助手,可查询文档、知识库、网站、CSV 和数据库,无需依赖云端 API key;
- 对开发者,它应呈现为:一个本地 Claude 兼容 API 层,可以在其上构建应用。
PRD 明确给 Workbench 划定了边界——它必须停留在 ui/index.html 这个轻级演示器的形态里,不得演变成重量级前端应用或维护负担。这一边界在仓库中得到了严格贯彻:ui/ 目录下唯一的运行时实现文件就是 index.html(当前约 7974 行,HTML/CSS/JS 全部内联),其余全是文档与视觉参考资产。ui/README.md 中的工作规则也规定:除非明确要求重构,否则保持实现收敛在 index.html 中;行为、视觉、持久化或 API 接线变化时,文档与实现必须同步更新。
目标与非目标
Primary Goals(PRD 原文四点):
- 让非技术用户体验 PrivateGPT 作为本地 AI 助手;
- 让用户配置助手可用的本地上下文:文档、数据库、Web 搜索、MCP、Skills、自定义工具;
- 让开发者通过轻量级会话级 API Debugger 观察 UI 与 API 的交互过程;
- 保持实现简单:理想形态是一个含 vanilla JS/CSS 的静态 HTML 文件加浏览器
localStorage。
Non-Goals(明确不做的事):无用户账号、无服务端 UI 数据库、无项目/文件夹/组织/工作区层级、无复杂设计系统、无云同步、除非绝对必要否则不引入重型前端框架、不试图替代浏览器 DevTools、Debugger 不做持久化存储。
二、两个 Source of Truth:OpenAPI 契约与视觉风格指南
2.1 API 契约:Fern 生成的 openapi.json
Workbench 的 API 契约由仓库根目录下的 fern/openapi/openapi.json 定义(从 ui/ 目录看即 ../fern/openapi/openapi.json)。PRD 强调:该 OpenAPI 文件是端点、请求/响应体形状、Schema 名称、工具/上下文结构、消息块格式、Artifact 格式、MCP 字段的唯一权威来源;PRD 中的示例仅是示意,凡与契约不符处以契约为准。
实现者在做请求构建器之前,应先解析或人工检查该文件,对齐这些关键 Schema:ChatBody、MessageInput、ToolSpecBody、ContextFilter、FileArtifact、SqlDatabaseArtifact、McpServerConfig 以及工具响应块 Schema。这些 Schema 在当前契约中均存在,可核对其字段:
ChatBody顶层字段包括model、messages、system、tools、thinking、tool_context、mcp_servers、container、stream、max_tokens,以及temperature、top_p、presence_penalty等采样参数;ToolSpecBody字段为name、type、description、inputSchema、context、deferLoading、instructions;ContextFilter字段为collection、artifacts、metadata_filter;SqlDatabaseArtifact字段为type、connection_string、schemas、ssl、enable_tables、enable_views、enable_functions、enable_procedures、description,与 PRD 中 Databases 小节的 JSON 示例完全一致;McpServerConfig字段为name、url、authorization_token、tool_configuration。
PRD 列出的重要端点(当前契约中均可在 openapi.json 的 38 个 /v1/* 路径中找到):
POST /v1/messages
POST /v1/messages/count_tokens
POST /v1/messages/validate
GET /v1/models
POST /v1/artifacts/ingest
GET /v1/artifacts/list?collection=<collection>
POST /v1/artifacts/delete
POST /v1/artifacts/content
POST /v1/artifacts/chunked-content
POST /v1/primitives/search
POST /v1/tools/semantic-search
POST /v1/tools/tabular-data-analysis
POST /v1/tools/database-query
POST /v1/tools/web-fetch
POST /v1/tools/web-search
PRD 的设计判断是:POST /v1/messages 是中心端点。产品体验的大部分应通过聊天流走,Context 负责提供输入,Debugger 负责解释底层的 API 交互。
2.2 视觉与 UX:STYLE_GUIDE 与参考图
PRD 要求与视觉方向文件 ui/docs/STYLE_GUIDE.md 配套使用,它是布局、玻璃质感表面(glass surfaces)、背景处理、侧边栏行为、聊天输入区处理、Context 行与 Debugger 视觉密度的权威来源。风格指南引用了仓库本地的参考图:
- ui/references/primary-chat-layout.png
- ui/references/search-overlay.png
- ui/references/chat-tools-composer.png
- ui/references/context-knowledge-base.png
ui/docs/SOURCE_OF_TRUTH.md 进一步固化了文档分工:产品行为归 docs/PRD.md,视觉规则归 docs/STYLE_GUIDE.md,运行时代码归 index.html,参考图归 references/,且不得维护一份 UI 本地的 OpenAPI 快照副本。
三、架构:单文件静态应用 + localStorage
3.1 目录与存储结构
PRD 推荐的最初实现形态即当前仓库形态:
ui/
index.html # 单文件静态应用(HTML + CSS + JS)
浏览器存储分工:
localStorage:持久化应用状态(连接设置、上下文配置、会话、外观覆盖);- 内存态:API Debugger 事件(页面刷新即清空,绝不落盘)。
3.2 默认 API 地址:PRD 与当前实现的差异
PRD 给出的默认 PrivateGPT API base URL 是 http://127.0.0.1:8001,并要求允许用户在 Web 应用内覆盖、无需编辑任何配置文件。
从源码看,ui/index.html 中的实际默认值略有演化:
const DEFAULT_BASE_URL = window.location.origin === "null"
? "http://127.0.0.1:8080"
: window.location.origin;
即:当页面通过 Web 服务访问时默认指向服务自身的 origin(这样与 PrivateGPT 同源部署时零配置),以本地文件方式打开(origin 为 null)时回落到 http://127.0.0.1:8080。这印证了 PRD"本地场景优先"的 CORS 立场:应用应首先在"本地静态文件 + 本地 PrivateGPT API"场景下工作;部署到其他位置时用户仍可在 UI 内改 URL 与 token,跨域所需的服务器策略由 PrivateGPT 部署侧处理。
连接设置包含两项:PrivateGPT API base URL 与可选的 HTTP Basic 认证(用户名/密码两个字段)。配置了认证时,请求头携带 Authorization: Basic <base64(username:password)>——实现位于 ui/index.html 的 createBasicAuthHeader()。
3.3 两个互不相干的连接概念
PRD 特别澄清了容易混淆的两层连接:
- PrivateGPT API URL 与认证——Workbench 直接调用的对象,必须在 Workbench UI 中可配置,v1 仅支持可选的 HTTP Basic 认证(用户名+密码字段);
- LLM Gateway URL 与认证——指向 PrivateGPT 底层的推理提供者(如本地 Ollama,常见默认
http://127.0.0.1:11434)。它配置在 PrivateGPT 自己的配置文件(仓库根目录的 settings.yaml 等)中,Workbench v1 不得在 UI 中暴露 LLM Gateway 配置。所有 Workbench 的 API 调用只打向配置的 PrivateGPT API base URL,浏览器绝不直连 LLM Gateway。
针对开发与自动化测试,PRD 约定本地环境可能提供 PGPT_BASE_URL 与 PGPT_TOKEN 两个环境变量,实现可在本地测试脚本或 dev-server 启动时读取它们,但绝不得存储、打印、提交或硬编码实际值。
四、持久化状态模型(localStorage)
PRD 定义了 Workbench 的完整状态形状,index.html 的 state 对象即按此实现:
{
privateGptBaseUrl: string,
privateGptUsername: string,
privateGptPassword: string,
systemPrompt: string,
useCitations: boolean,
selectedModel: string | null,
uiAppearance: {
brief: string,
brandName: string,
welcomeTitle: string,
welcomeSubtitle: string,
customInstructions: string,
palette: { accent, secondary, surface, background },
features: {
databases: boolean, web: boolean, mcp: boolean,
skills: boolean, customTools: boolean, apiDebugger: boolean,
github: boolean, productionNotice: boolean
}
},
onboarding: {
completed: boolean,
step: 1 | 2,
appearanceSkipped: boolean,
lastCheck: {
ok: boolean, testedAt: string, summary: string,
steps: Array<{ label: string, detail: string, ok: boolean | null }>
} | null
},
context: {
documents: { defaultCollection: string },
databases: DatabaseConfig[],
mcpServers: McpServerConfig[],
skills: SkillConfig[],
customTools: CustomToolConfig[]
},
chats: ChatSession[],
activeChatId: string | null
}
会话对象:
type ChatSession = {
id: string;
title: string;
createdAt: string;
updatedAt: string;
messages: ChatMessage[];
settings: {
enabledDocuments: boolean;
enabledDatabases: string[];
enabledWeb: boolean;
enabledMcpServers: string[];
enabledSkills: string[];
enabledCustomTools: string[];
model: string | null;
};
};
(当前实现在此骨架上扩展了 enabledTabular、enabledCodeExecution、reasoningEffort 等字段,见 ui/docs/SOURCE_OF_TRUTH.md 的实现备注。)
刷新后必须恢复的行为清单:onboarding 成功完成后保持关闭;Context 恢复;聊天列表恢复;聊天消息恢复;每会话工具开关恢复;外观覆盖与可选功能区可见性恢复。而 Debugger 始终为空。
五、信息架构:侧边栏、屏幕与 Hash 导航
5.1 布局骨架
应用有一个常驻左侧边栏与主内容区:
Sidebar
Context
New Chat
Chats
Contract review
CSV analysis
Database demo
Custom tool test
API Debugger
Settings
GitHub
Not for Production
Main
首次启动或重跑 onboarding:引导式 onboarding 覆盖层(Step 1: URL + collection + 实时检查;Step 2: 可选外观定制)
Context 选中:Context 配置屏
Settings 选中:API 连接设置与助手行为
API Debugger 选中:会话级请求/响应 trace
Chat 选中:聊天界面
侧边栏行为细则:
- Context——打开全局 Context 屏;
- New Chat——创建新会话,默认标题
New chat,从全局默认复制上下文设置并打开; - 聊天列表——按
updatedAt DESC排序持久化会话,当前会话高亮,支持内联重命名/删除,长标题省略号截断,且侧边栏与列表绝不出现横向滚动条; - API Debugger——打开会话级 trace,位于 Settings 之上的底部分组;
- Settings——位于 API Debugger 之下;
- GitHub——链接到 PrivateGPT 仓库,GitHub 可达时显示实时 star 数;
- Not for Production——打开说明性模态框,位于 GitHub 组件之下。
明确没有"项目"或任何分组概念。
5.2 URL Hash 导航
应用采用 hash 导航,使刷新后能恢复当前视图与 Context 标签页:
#context/{tab} Context 屏 + 具体标签(documents/databases/web/mcp/skills/customTools)
#settings Settings 屏
#apiDebugger API Debugger 屏
#chat/{chatId} 指定会话
实现位于 ui/index.html:syncHash() 在每次 render() 结束和 Context 标签切换后调用;restoreFromHash() 在启动首次渲染前运行一次,并在 hashchange 时运行以支持浏览器前进/后退。
六、Settings 屏:连接与全局助手行为
Settings 屏拥有"不属于助手上下文"的 Workbench 级配置:
- PrivateGPT API base URL;
- 可选 HTTP Basic 认证(用户名、密码);
- 可选 system prompt;
- 可选的 workspace instructions,置于 system prompt 之前;
- Use citations 开关,默认开启;
- Collection——当前活动文档集合名,用于文档摄取、列表、删除、搜索与聊天请求。PRD 特别强调它属于 Settings 而非 Context > Documents,因为它是全局指针,不是某个上下文源配置;
- 品牌文案、欢迎文案、色板与可选可见区的外观覆盖;
- 重跑 onboarding、测试 API 连接、保存设置、清空本地浏览器数据。
几个关键约定:
- system prompt 走顶层
system字段,而不是system角色的消息。用户留空时就不发 system 文本;但为携带citations.enabled这类请求级选项,system对象仍可能以无text的形式发出。 - 清空本地数据只清除本 Workbench 的浏览器状态(会话、设置、token、上下文、偏好),绝不暗示删除 PrivateGPT 侧数据(已摄取文档、后端配置等)。
从源码看,ui/index.html 的 buildChatBody() 体现了"system 对象按需组装"的规则:只有存在 system 文本、启用引用、扩展、内建工具或 thinking 时才生成 body.system,其中 system.citations.enabled 由"文档启用 && useCitations 非 false"决定,system.text 仅在合并后的 system 文本非空时写入。
七、Onboarding:连接验证 + LLM 生成外观
首次启动时,Workbench 在正常使用前弹出 onboarding 覆盖层。只要 state.onboarding.completed !== true 就会显示(状态由 state.onboarding 控制,见 SOURCE_OF_TRUTH.md 的实现备注)。
Step 1(连接与验证):
- 收集 PrivateGPT base URL、可选 HTTP Basic 认证、活动 collection ID,写入与 Settings 相同的持久化状态;
- 对以下端点跑实时检查:
GET /v1/modelsGET /v1/artifacts/list?collection=<collection>GET /v1/skills?collection=<collection>
- 展示简单的通过/失败清单,检查全部通过前不放行。
Step 2(外观定制,必须可跳过):
- 接收一段自然语言主题 brief,通过
POST /v1/messages调用已配置的 LLM 生成初始外观方案; - 必须在用户完成 onboarding 前展示生成结果,并允许随后手工定制品牌名、欢迎文案、workspace instructions、颜色与可选可见区;
- GitHub/Zylon 引用必须保持可见,不能通过生成或手工外观设置移除;
- 写入持久化外观变量(覆盖运行时 UI 状态与 CSS 自定义属性),之后仍可从 Settings 或重跑 onboarding 编辑。
实现侧对应 applyAppearance()(ui/index.html):外观覆盖是运行时变量,state.uiAppearance 同时驱动文案、功能可见性与 CSS 自定义属性;主题 brief 经聊天 API 生成后解析为 JSON,回写进用户可手工编辑的同一组外观表单字段。
"Not for Production" 披露
侧边栏常驻 Not for Production 入口(位于 GitHub 组件下方),点击打开可关闭的玻璃风格模态框,标题为 "This demonstrator is not intended for Production use",解释该 UI 适合试用 API 能力、调试请求、探索本地 AI 工作流,但不应作为生产应用发布,并覆盖四点风险:
- 浏览器存储不是安全的密钥存储——bearer token、聊天、上下文配置与设置都保存在
localStorage; - 没有应用级访问控制——任何能访问该页面的人都可使用所配置的 API 端点、token、工具、文档与模型访问;
- 调试数据被刻意可见——API Debugger 可显示 prompt、文档摘录、头、元数据、请求与响应;
- 自定义工具运行浏览器 JavaScript——只应使用可信代码,UI 不应在没有评审过部署模型的情况下对外暴露。
八、Context 屏:定义助手能用什么
Context 屏分六个区:Documents / Databases / Web / MCP / Skills / Custom Tools,允许使用紧凑的 tab 或手风琴布局。
8.1 Documents:本地知识库管理
能力清单:
- 设置 collection 名(实际字段在 Settings,全局生效);
- 上传本地文件;
- 经
POST /v1/artifacts/ingest摄取; - 经
GET /v1/artifacts/list?collection=<collection>列出; - 经
POST /v1/artifacts/delete删除; - 可选诊断搜索框走
POST /v1/tools/semantic-search; - 可选内容预览走
POST /v1/artifacts/content。
Collection 字段必须可配置的原因:有的 PrivateGPT 部署按需创建集合,有的部署经过网关、把每个 bearer token 限制在若干允许的集合内——若部署强制 allowed collections,摄取/列表/搜索必须使用允许的 collection id,否则 API 可能拒绝对象。Workbench v1 只有一个活动 collection,不暴露聊天级集合选择器。
上传行为:文件转 base64;artifact id 由文件名+时间戳或 UUID 派生;metadata 携带 file_name。PRD 给出的摄取请求示例:
{
"artifact": "contract-2026-05-14",
"collection": "default",
"input": {
"type": "file",
"value": "<base64>"
},
"metadata": {
"file_name": "contract.pdf"
}
}
8.2 文档参与聊天的请求形态
当某会话启用了 Documents,/v1/messages 请求应启用语义搜索工具并把它限定到配置的文档集合:
{
"model": "default",
"messages": [
{
"role": "user",
"content": "Find the property address in the documents. Answer just with the address, no extra text."
}
],
"tools": [
{ "name": "semantic_search", "type": "semantic_search_v1" }
],
"tool_context": [
{
"type": "ingested_artifact",
"context_filter": {
"collection": "<configured-collection>",
"artifacts": []
}
}
]
}
artifacts 为空数组表示搜索该集合内所有文档。buildChatBody() 的对应实现(ui/index.html)正是这条规则的直接落地。
8.3 Databases:SQL 数据库工件
仅本地存储的字段集:id、name、connection_string、description、可选的逗号分隔 schemas、ssl、enable_tables、enable_views、enable_functions、enable_procedures。在聊天中被选中时转换为 tool_context 工件:
{
"type": "sql_database",
"connection_string": "...",
"schemas": null,
"ssl": false,
"enable_tables": true,
"enable_views": true,
"enable_functions": true,
"enable_procedures": true,
"description": "Local sales database"
}
这与 OpenAPI 中的 SqlDatabaseArtifact 字段一一对应;实现侧在 ui/index.html 中,为每个被选中的数据库工件同时追加 database_query(database_query_v1)工具规格。
8.4 Web:说明性质,凭据留在后端
Context > Web 区不收集 web provider 名、API key 或额外 web 配置——这些属于 PrivateGPT 后端配置(见仓库根 settings.yaml)。OpenAPI 当前暴露 POST /v1/tools/web-search 与 POST /v1/tools/web-fetch;Workbench 在此区展示静态说明文字,由聊天级 Tools 菜单决定 web_search 与 web_extract 是否进入该次聊天请求。PRD 还特别区分了两处命名:直连诊断端点叫 /v1/tools/web-fetch,而 /v1/messages 中的聊天工具规格写作 { "name": "web_extract", "type": "web_extract_v1" }。
8.5 MCP:连接器配置
字段:id、name、server_config_json、可选 allowed_tools 列表。OpenAPI 的 ChatBody 支持 mcp_servers 字段;为避免过度设计未知的 MCP 变体,UI 初期允许原始 JSON 编辑(名称输入框 + JSON 文本域 + "Validate JSON" 按钮)。被选中的 MCP 配置以解析后的对象写入请求的 mcp_servers 数组(实现见 ui/index.html)。
8.6 Skills:绑定到活动集合的技能
字段:id、display_title、collection、latest_version、source、loading、readonly。操作端点(作用域限定在 Workbench 的单一活动集合):
GET /v1/skills?collection=<collection>列出技能;POST /v1/skills(multipart)创建;POST /v1/skills/{skill_id}/versions(multipart)创建新版本;DELETE /v1/skills/{skill_id}?collection=<collection>删除非只读技能。
构建聊天请求时,选中的技能表示为 tool_context 工件:
{
"type": "skill",
"skill_filter": {
"collection": "<configured-collection>",
"skill_or_version_ids": ["<selected-skill-id>"]
}
}
8.7 Custom Tools:Claude 风格工具 + 浏览器 JS 处理器
字段:id、name、description、input_schema_json、javascript_handler、test_input_json、last_test_result。工具定义形状(JSON Schema 描述输入):
{
"name": "currency_converter",
"description": "Convert USD to EUR using a locally configured exchange rate.",
"inputSchema": {
"type": "object",
"properties": {
"amount": { "type": "number" }
},
"required": ["amount"]
}
}
处理器形状:
async function handle(input, context) {
const rate = Number(context.localStorage.getItem("usd_eur_rate") || "0.92");
return {
type: "text",
text: `${input.amount} USD is approximately ${input.amount * rate} EUR.`
};
}
处理器执行上下文:
{
fetch: window.fetch.bind(window),
localStorage: window.localStorage,
privateGptBaseUrl: string,
currentChatId: string,
currentCollection: string,
log: (message: string, data?: unknown) => void
}
v1 直接浏览器执行、不需要额外沙箱——因为代码是用户在本地浏览器里显式编写的(这也正是"Not for Production"披露中列出的风险之一)。自定义工具测试流程:解析测试输入 JSON → 执行处理器 → 展示结果或错误 → 若当前处于聊天上下文则追加 Debugger 事件,否则仅展示本地结果。executeCustomTool()(ui/index.html)按上述上下文对象注入依赖并记录 custom_tool:execute_start 调试事件。
九、Chat 屏:从消息输入到 ChatBody 组装
9.1 输入区控制
Composer 工具栏(textarea 下方)内的控制项:
- 模型选择器——自定义玻璃下拉,数据来自
GET /v1/models,显示当前模型名与动画 chevron,选中后更新state.selectedModel(实现为renderModelSelect(),ui/index.html,使用#modelSelectBtn+#modelDropdown两个元素,而非原生<select>); - 模型选择器旁的刷新模型按钮;
- 推理强度(reasoning effort)内置于模型下拉,而非独立 Thinking 按钮:下拉左侧是可搜索的模型列表,右侧是按能力感知的 effort 轨道(None / Low / Medium / High / Max / XHigh);
- 选中 effort 按会话存储,作为
thinking: { enabled: Boolean(effort), type: effort }发送;不支持的选项依据所选模型响应里的capabilities.effort元数据禁用; - Tools 按钮——打开按类别切换的菜单:Documents / Web / Databases / MCP / Skills / Custom Tools。
对 Databases、MCP、Skills、Custom Tools:把已配置项渲染为可选 chips/下拉,选择结果按会话存储。文本输入按 Enter 发送、Shift+Enter 换行。
9.2 消息渲染规则
- 用户与助手消息分开渲染;文本块按 Markdown 渲染;
tool_use与 tool result 块渲染为折叠的 details 元素(块配对与渲染逻辑见blocksToHtml(),ui/index.html);- 内联
<citation ...></citation>标签绝不允许以文本形式渲染:必须替换为仅带引用序号的小圆形可点击标记;若原始引用标签使用 0 基index属性,显示index + 1;多个内联引用可显示同一编号; - 消息底部不渲染独立的引用列表;
- 点击引用标记打开可关闭的玻璃风格弹层,展示引用元数据与源文档摘录。摘录的提取规则:对 semantic-search 的
tool_resultpayload 解析 JSON 文本,用内联引用 id 匹配nodes[].id,取命中节点的content作为摘录,且匹配器要容忍括号差异(如4C40vs[4C40]); - 错误清晰展示;聊天中不显示原始响应块——原始请求/响应细节归 Debugger;
- 等待 PrivateGPT 时显示带"呼吸"动画的 PrivateGPT 圆形头像占位。
9.3 请求行为与基本请求体
请求行为约定:
- 使用
POST /v1/messages;由聊天消息+所选上下文/工具构建ChatBody; - 模型 id 取
/v1/models响应中的选中项,未加载时回落default; - 存在用户配置的 Settings system prompt 时优先使用;无 system prompt 且无技能指令时省略 system 文本;
- Documents 启用且 Settings > Use citations 开启时发送
system.citations.enabled: true——即使没有 prompt 文本,也可能需要顶层system对象; - 系统指令始终走顶层
system字段,保证在整个请求一致生效。
PRD 给出的基本请求体:
{
"model": "default",
"messages": [
{
"role": "user",
"content": "Summarize my documents and cite sources."
}
],
"system": {
"text": "You are a support agent. Reply with only a short ticket title.",
"use_default_prompt": false,
"citations": { "enabled": true }
},
"tools": [],
"tool_context": [],
"mcp_servers": [],
"stream": false,
"max_tokens": 4096
}
工具/上下文组装规则:
- Documents 启用 → 追加
semantic_search工具 + 作用域到全局文档集合的ingested_artifacttool context(artifacts: []表示全集合); - 选中 Databases → 选中的 SQL 数据库工件进
tool_context; - 选中 MCP → 选中的 MCP server 配置进
mcp_servers; - 选中 Custom Tools → 选中的自定义工具定义进
tools; - 选中 Skills → 按当前后端约定附加技能 prompt/配置(实现中即
skills_v1工具 +skilltool_context 工件)。
从源码看,当前实现(ui/index.html)在 PRD 骨架之上已扩展到更多内建能力:表格分析(tabular_analysis_v1)、代码执行(code_execution_v1,且启用时把 chat.id 作为 ChatBody.container 以复用同一沙箱会话)、Web(web_search_v1 + web_fetch_v1),并以 stream: true 发起流式请求——PRD 中"流式/异步端点可推迟"的取舍在实现中已被部分推进。
9.4 自定义工具闭环(Custom Tool Loop)
当助手响应包含某个自定义浏览器工具的 tool_use 块时:
- 按名称匹配自定义工具;
- 用
toolUse.input执行 JavaScript 处理器; - 把工具结果追加到同一条可见的助手消息气泡(不另起消息);
- 追加
tool_result后再发一次POST /v1/messages(沿用 API 预期的内容块形状); - 最终助手答案流式渲染回同一气泡;
- 循环中的所有 API 调用都出现在 API Debugger。
整个执行周期——初始响应、工具结果、后续回答——在聊天中呈现为单一合并的助手气泡;承载 tool 角色的隐藏消息只存在于 API 历史中,从不渲染到聊天 UI。处理器失败时:错误内联显示在气泡中,请求/响应错误记入 Debugger,并视情况发送 is_error: true 的 tool result。
十、API Debugger:会话级、实时、临时
设计约束三条:会话级、实时、临时——Debugger 事件绝不持久化,页面重载后为空。界面上方有一个小的非侵入 callout,说明它是当前会话的实时 trace、刷新即清空。
目的:教会开发者聊天交互如何映射为 API 调用;展示请求/响应载荷与错误;在有用时展示脱敏后的请求头。布局为"时间线列表 | 事件详情面板"。事件模型:
- 每次 API 调用对应一个时间线条目,可先以 pending 出现,随后就地更新为响应或错误;不得为同一调用显示分立的 request 与 response 条目;
- 每个事件包含:timestamp、method、URL、脱敏请求头、status、duration、请求 JSON、响应 JSON 或错误。
密钥红线:Debugger 中永不显示机密,Authorization、token 类头、API key 与 cookies 必须脱敏——实现为 ui/index.html 的 redactHeaders(),在事件落缓冲前对请求头做掩码。Debugger 应覆盖当前页面生命周期内来自 Chat、Context、Settings 的全部 API 事件。
10.1 API Client 封装
PRD 要求实现一个极小的客户端封装:
async function apiFetch(path, options, debugMeta)
职责:拼接 privateGptBaseUrl 前缀;设置 JSON 头;按配置的用户名/密码构建 Authorization: Basic <base64(username:password)>;测量耗时;解析 JSON/文本响应;把请求/响应/错误记入会话级 Debugger 缓冲;抛出有用的错误。
从源码看(ui/index.html),实际 apiFetch 还实现了 PRD 之外的健壮性细节:GET 请求附加 Cache-Control: no-cache 与 Pragma;对 5xx 与网络错误做最多 2 次重试,退避间隔 600ms × 尝试次数,重试事件以 Retry n/2 标注在同一条调试条目上就地更新;4xx 则直接抛出并携带解析后的错误体(detail / error.detail.explanation / error.message 逐级提取,见 ui/index.html)。此外实现中还提供了流式版 apiStreamFetch()(ui/index.html),对应 PRD 中标记为"可推迟"的流式端点。
PRD 列出的客户端使用端点:
GET /v1/models
POST /v1/messages
POST /v1/messages/count_tokens (可选)
POST /v1/messages/validate (可选)
POST /v1/artifacts/ingest
GET /v1/artifacts/list?collection=<collection>
POST /v1/artifacts/delete
POST /v1/artifacts/content (可选)
POST /v1/tools/semantic-search
POST /v1/tools/tabular-data-analysis (可选直连诊断)
POST /v1/tools/database-query (可选直连诊断)
POST /v1/tools/web-search (可选直连诊断)
POST /v1/tools/web-fetch (可选直连诊断)
流式/异步端点可推迟:/v1/messages/async、/v1/messages/async/{message_id}/stream(两者均已存在于当前 openapi.json 契约中,实现可在需要时直接启用)。
十一、UX 原则、MVP 验收标准与构建顺序
UX 原则:像实用的本地助手而非营销页;聊天是主表面;Context 解释助手能访问什么;Debugger 解释底层发生了什么;控制密度高但可读;避免大型装饰卡片与落地页 hero 区;朴素的工具型 UI;优先原生控件与简单 CSS;所有错误都可见且可操作。
MVP 验收标准(PRD 23 条,节选关键项):
- 用户可配置 API base URL;2. 可从 UI 配置可选 API key/bearer token;3. 可从
GET /v1/models加载模型并按会话选择;4. 可创建/重命名/删除/切换本地会话;5-6. 会话与每会话工具开关跨刷新持久化;7. 可向/v1/messages发送基础消息;8-9. 可上传摄取并列出文档;10. 可启用文档上下文;11. 文档启用的聊天请求确实追加semantic_search工具与限定到配置集合的ingested_artifacttool context;12. 可本地配置数据库工件;13. 可本地配置 MCP/Skills/自定义工具,且能看明白 web provider 凭据在 PrivateGPT 后端而非 Workbench;14. 可定义含 name/description/JSON schema/JS handler 的自定义工具;15-16. 聊天可把选中的自定义工具传给 API,并在助手发出匹配 tool call 时执行浏览器 JS 处理器;17-18. Debugger 实时展示当前浏览器会话的 API 事件且敏感头脱敏,刷新后消失;19. 不引入任何后端存储;20. 应用以静态文件运行;21. 实现以仓库内的相对 OpenAPI 文件为 API 契约,不硬编码与 schema 矛盾的 payload 假设;22. 侧边栏含 GitHub 仓库组件与 Not for Production 披露;23. Settings 含"清空本地数据"动作。
建议构建顺序:1) 静态布局(侧边栏、Context、Chat、Debugger);2) localStorage 状态模型;3) API base URL 与模型加载;4) 聊天会话;5) 基础 /v1/messages 聊天;6) Debugger 事件记录器;7) 文档摄取/列表/删除;8) 聊天工具/上下文开关;9) Database/Web/MCP/Skills 配置表单;10) 自定义工具定义 UI;11) 浏览器 JS 处理器执行循环;12) 打磨错误、空态与刷新行为。
十二、如何验证与继续深入
- 检查
ui/文档与实现的对应关系:ui/docs/PRD.md(产品行为)、ui/docs/SOURCE_OF_TRUTH.md(权威路径与"读代码看不出的"关键实现备注)、ui/docs/STYLE_GUIDE.md(视觉规则)、ui/README.md(目录地图与工作规则); - 运行时实现只有一个文件:ui/index.html,其中的
apiFetch(#L6330)、buildChatBody(#L6973)、executeCustomTool(#L7091)、redactHeaders(#L7936)、syncHash/restoreFromHash(#L4551)是理解各章节行为的最短路径; - 全部请求/响应形状以 fern/openapi/openapi.json 为准,它同时驱动 fern/docs 中的 API 指南与 scripts/extract_openapi.py 的契约抽取流程;
- 按 ui/README.md 的说明,改动
ui/index.html后可用其提供的 node 一行脚本验证内联脚本可解析; - 后端侧的配置入口是仓库根目录的 settings.yaml 与 settings-test.yaml,其中 LLM Gateway(推理提供者)相关设置属于后端职责,与 Workbench UI 中可配置的连接项互不重叠。
适用前提与限制:Workbench 是明确标注"Not for Production"的本地演示器——localStorage 不保存机密级数据、无访问控制、Debugger 刻意可见、自定义工具直接运行浏览器 JS;它只适用于本机试用 API、调试请求与探索本地 AI 工作流。本文描述的默认地址、端点集合与字段形状以当前仓库状态为准;契约随 fern/openapi/openapi.json 演化,实现细节(如流式请求、代码执行容器字段、推理强度轨道)也已在 PRD 初版示例之后持续扩展,请以源码现状为最终依据。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

