hiwork-knowledge
v0.3.4
Published
HiWork 知识库插件:接腾讯 WeKnora,向 Agent 提供检索/阅读/问答工具,并在桌面端提供知识库浏览与设置。
Readme
hiwork-knowledge
HiWork 的知识库插件:把腾讯 WeKnora 接进桌面端。
两个半边,职责分离:
| 半边 | 做什么 |
| --- | --- |
| Host(src/*.ts) | 凭据、出网、Agent 工具、loopback RPC。是唯一事实来源 |
| Web(src/client/**) | 中央页浏览(知识库/文档/检索)+ 设置页配置与自检。不发 HTTP、拿不到凭据 |
- 包名 / bundle id:
hiwork-knowledge - client 插件名:
hiwork-knowledge-client - 中央页 feature:
id = knowledge,order = 30 - RPC 频道:
/hiwork-knowledge(端点snapshot/config-save/config-test/docs/search) - Agent 工具(M0 全部只读):
knowledge_list_bases、knowledge_search、knowledge_read_document、knowledge_ask - 后端(内网):
https://hiwork-knowledge.hivery.cn(部署见该服务器~/docker-projects/weknora/DEPLOY-HIWORK.md)
目录
src/
index.ts Host 入口:开存储域、挂工具、注册 RPC 与提示词片段
service.ts KnowledgeService:凭据解析 / 出网客户端 / 检索健康自检
weknora.ts WeKnora REST 客户端(超时、SSE、错误归一)
tools.ts 4 个 Agent 工具
rpc.ts / protocol.ts /hiwork-knowledge 的 Host 实现与线上契约
types.ts 领域类型、默认值、存储域定义(hiwork_knowledge)
prompt.ts 系统提示词片段与工具指引
client/
index.ts Web 入口:注册中央 feature + 设置页分区
runtime.ts 客户端状态源(Host 快照 + 动作;含传输失败重试一次)
KnowledgeView.tsx 中央页:知识库列表 / 文档列表 / 检索片段
SettingsSection.tsx 设置页:凭据表单 + 「测试连接」(自检)
contracts.ts 宿主 client 能力的结构契约(type-only)
locales.ts / styles.* 词典与样式
scripts/
host-build.mjs Host 产物自包含打包(esbuild)
client-build.mjs Client 产物闭包工厂打包(esbuild)
live-smoke.{ts,mjs} 联调冒烟(打真实后端)
tests/ 客户端协议、服务、工具、RPC、入口、视图、打包契约两个入口(同时存在,不再二选一)
| 入口 | 注册条件 | 用途 |
| --- | --- | --- |
| 中央页 feature knowledge | 存在 hiwork-core 的 hiworkFeatureCenter | 浏览:知识库 / 文档 / 检索 |
| 设置页分区 settings.section#knowledge | 始终注册 | 配置:凭据 / 默认范围 / 问答 Agent / 自检 |
与
hiwork-automation的策略不同:那边设置页是中央页的降级替身,所以 core 出现后要撤下; 这边设置页承载的是配置(浏览面里没有第二处可改),撤掉用户就没法配凭据,因此恒在。
没有 hiwork-core 时插件仍可用:只能配置 + 通过 Agent 工具检索,没有中央页入口。
聊天会话里的集成(M1)
工具卡片与「带进聊天」是可选增强:宿主缺哪个能力就少哪块 UI,工具本身照常可用。
| 座位 / 能力 | 注册条件 | 作用 |
| --- | --- | --- |
| tool.call.toolview(按线上工具名 keyed) | 始终注册 | 4 个 knowledge_* 工具在聊天里渲染成知识库卡片(文档名 + 段号 + 片段 + 「读全文 / 追问」),不再落回通用工具行 |
| sessions(ctx.get('sessions')) | 宿主提供时 | 「追问」把追问句写进该会话的输入框;页面「带进聊天」写进当前会话 |
| 文档正文(document RPC 端点) | 始终注册 | 中央页点文档行 → 右栏读全文(分页),消掉「只能搜出片段」 |
三条硬约束(都踩过,见 src/client/composer.ts 与 src/tool-names.ts 的头注):
- 工具名走
src/tool-names.ts单一事实源。keyed 座位按线上工具名分派,拼错不抛错、 只是卡片永不渲染——两边各写一份必然漂移。 - 放进输入框只能走
actx.get('conversation').input.for(actx).insertText(...), 不能conversation.send():属性访问会撞 cordis 注入门禁 (cannot get property "conversation" without inject),而且send()会立刻发一轮、用户无法反悔。 读快照到插入之间不得有 await(draftRev是 CAS 票据)。 - 动作失败必须看得见:插入会以「没有打开的会话 / 输入框正忙」被拒,
insertDraftText因此返回{ok, reason}而不是void,卡片与页面把原因显示在行内。
凭据与安全边界
- 有效凭据来源优先级:本机设置 > 环境变量(
HIWORK_KNOWLEDGE_API_KEY/WEKNORA_API_KEY)> 空 (网关下发在 M1 接入,届时插到最前面,见设计文档 §D1)。 - API Key 只存在于 Host 半边:设置页只能看到
hasApiKey布尔与来源,输入框是password。 - 落盘位置:DSH 存储域
hiwork_knowledge的settings表(域名单下划线,见types.ts)。 - 出网错误信息里回显 WeKnora 的原始理由,但绝不回显 Key(
tests/weknora.spec.ts有断言)。 - 一期只发只读能力:Key 用
role=viewer+capabilities=[retrieve, chat, read_agents],不下发ingest。
检索健康自检(别删这块)
后端的检索会静默给错结果,HTTP 层看不出来。selfCheck() 因此分两层:
| 断言 | 判据 | 为什么要它 |
| --- | --- | --- |
| connection | GET /knowledge-bases 成功 | 认证/网络是否通 |
| retrieval / probe-miss / rerank-missing | 用「第一篇文档的标题」做探针检索:0 命中 → probe-miss;最高分 < 0.05 → rerank-missing | 未绑 rerank 时后端退化成 RRF 排序,任意查询都返回同一个分块(实测分数恒为 0.0164;绑好后同一批查询是 0.44–0.62) |
| ask / ask-unconfigured | 是否配了自建问答 Agent | 内置 Agent 的 model_id 为空,/agent-chat 会直接报错 |
「测试连接」按钮会把这几条逐条渲染出来。
命令
| 命令 | 作用 |
| --- | --- |
| pnpm typecheck | 严格类型检查 |
| pnpm build | 构建 lib/index.js(Host)与 lib/client.js(Web) |
| pnpm test | Vitest(DOM 用例首行 // @vitest-environment jsdom) |
| pnpm verify | typecheck → build → test(提交前跑这个) |
| pnpm smoke:live | 打真实后端的联调冒烟(见下) |
联调冒烟(真实后端)
HIWORK_KNOWLEDGE_API_KEY=sk-... \
HIWORK_KNOWLEDGE_AGENT_ID=<自建问答 Agent ID> \
HIWORK_KNOWLEDGE_CHAT_MODEL_ID=<chat 模型 ID> \
pnpm smoke:live它会跑:自检 → 列库 → 列文档 → 四个工具(经真实工具定义执行)。任一环节失败以非零码退出。
可选变量:HIWORK_KNOWLEDGE_BASE_URL(默认指向内网部署域名)。
本地试用(不打包桌面端)
pnpm build && pnpm pack # 产出 hiwork-knowledge-0.2.0.tgz在 DSH profile(如 ~/.dsh/profiles/web)的 package.json 里加依赖
"hiwork-knowledge": "file:<绝对路径>/hiwork-knowledge-0.2.0.tgz",并在
dsh.profile.bundles 追加 "hiwork-knowledge",然后 pnpm install --force
并重启 dsh web(Host 半边变化必须重启,只改 client 时刷新页面即可)。
界面样式(改版式前先读)
样式表是 src/client/styles.css(纯 CSS,运行时由 styles.ts 注入一个 <style>),
词表与 hiwork-automation / hiwork-capabilities 对齐(view / header / heading /
toolbar / button / tag / count / card / panel / status / notice / muted),三个插件视觉上
属于同一家族。新增类名必须同时在样式表里写规则,否则浏览器会按默认样式渲染——<h2>
变成 1.5em 粗体(比 16px 的页头还大)、<ul> 带出圆点和 40px 左缩进,整页立刻像没写完。
tests/styles-coverage.spec.ts 拿源码和样式表交叉比对,漏写规则会先红。
四条用实测换来的约束(都写在 styles.css 文件头,别凭直觉改回去):
- 主题里没有
state-error-tertiary(亮暗两套都没有),语义淡底一律用color-mix(in srgb, <primary> 10%, transparent); bg-layer-1/2/3在亮色下全是#fff,拿它当卡片/徽标底等于没有底——白底上要看得见的 中性填充用bg-module-platform(亮#f5f6f7)或interactive-bg-hover;- 别把容器的底色 token 再用在它内部的元素上:左栏导轨铺
bg-module-platform, 计数徽标一开始也用它,结果徽标在导轨上直接隐形(截图上只剩一个没有容器的裸数字)。 徽标因此改成描边式,白色右栏和浅灰导轨上都成立; - 颜色只给异常态:一份正常的知识库里绝大多数文档都是
completed,逐行染绿会把 真正要一眼看到的failed淹掉,所以正常态是「静默徽标 + 状态点」,只有解析中/失败/排队上色。
版式还有两条硬规矩:中央区接管时页面根不滚(滚动收进两栏各自的
.hiwork-knowledge-scroll),以及列表头是标签不是标题(12px/600 次级色,比 13px/600
主色的内容标题更小更淡)。
client bundle 纯度
src/client/** 对 @deepseek-ai/* 只允许 import type(打包时被擦除),运行时只
import react / react/jsx-runtime。tests/packaging.spec.ts 会在构建后扫描
lib/client.js 的 require 说明符,越界即失败;Host 产物则要求零模块说明符
(依赖全部内联,桌面端补种时 profile 的 node_modules/@deepseek-ai/* 会被清空)。
里程碑
| 阶段 | 内容 | 状态 |
| --- | --- | --- |
| M0 | Host:凭据 + 出网客户端 + 4 个只读工具 + 自检;Web:中央页列表 + 设置页 | ✅ 已交付 |
| M1 | 中央页检索问答(SSE 实时渲染)、右侧栏检索面板、引用卡片、signed_token 身份隔离 | 待开工 |
| M2 | 上传/删除(人工确认门)、重解析、标签、移动、工作区↔知识库、@知识库 | — |
| M3 | Wiki 浏览与图谱、FAQ、数据源、office 在线预览联动 | — |
设计与决策记录:docs/superpowers/specs/2026-09-18-weknora-integration-design.md、
docs/superpowers/plans/2026-09-18-weknora-m0-plan.md。
