# dsh-scale-assessment-ui **Repository Path**: openminds/dsh-scale-assessment-ui ## Basic Information - **Project Name**: dsh-scale-assessment-ui - **Description**: DSH 心理量表测评插件(双半区:Host + Client UI)—— 在 dsh-scale-assessment 基础上增加 Client 半区,为 DSH Agent 提供状态化的心理量表测评能力,同时在对话界面注入可视化量表测评 UI:选择量表、逐题作答、自动计分、风险预警与 Markdown 报告生成。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-19 - **Last Updated**: 2026-11-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: dsh, SCALES ## README # dsh-scale-assessment-ui DSH 心理量表测评插件(双半区:Host + Client UI)—— 在 `dsh-scale-assessment` 基础上增加 Client 半区,为 DSH Agent 提供状态化的心理量表测评能力,同时在对话界面注入可视化量表测评 UI:选择量表、逐题作答、自动计分、风险预警与 Markdown 报告生成。 ## 特性 ### Host 侧 - **无需传递会话 ID**:会话键取自 `exec.agent.session.id`,一个 Agent 对话自动对应一份测评状态。 - **自动计分**:按 `score_rules` 映射选项分值,支持反向题(`min + max - raw`)、维度分聚合,以及 SDS/SAS 标准分 `int(1.25 × 总分)`。 - **BSRAT 专用规则**:第 1 题子项(1a–1f)取最大值而非求和;全部为 0 时终止评估(总分强制为 0)并同步替换 `max_score` 与「自杀意念与行为」维度分。 - **风险与安全提示**:命中高风险条目(PHQ-9 第 9 题选 D、SDS 第 19 题选 D、SCL-90 第 15 题选 E、BSRAT 1a–1f 或第 4 题选 B)或 BSRAT 总分 ≥10 时,自动附加提示。 - **标准化报告**:输出包含基本信息、分数统计、结果解释、重要提示、答题明细、量表说明与临床建议的 Markdown 报告。 - **会话治理**:TTL 淘汰(默认 1 小时)+ 容量上限(默认 1000),每分钟扫描一次。 ### Client 侧 - **对话界面注入**:通过 `conversation.view` slot 在对话界面注入量表测评标签页,用户无需调用工具即可完成测评。 - **量表卡片列表**:展示 17 份量表的缩写、中英文名、类别、题量与预计用时,支持分页浏览。 - **逐题答题表单**:单题展示、选项点击、进度条、上下题导航、键盘方向键翻页。 - **结果可视化**:分数总览(原始分 / 标准分 / 维度分)、结果解释、安全提示、答题明细表、临床建议。 - **Markdown 报告弹窗**:内置轻量 Markdown 渲染器,支持标题、加粗、表格、列表与段落。 - **纯前端计分**:Client 侧直接复用 Host 侧的 `scoring.ts` / `report.ts` 逻辑,无需额外网络请求。 ## 与 dsh-scale-assessment 的关系 本项目 fork 自 [`dsh-scale-assessment`](https://gitee.com/openminds/dsh-scale-assessment),在保留全部 Host 侧能力的基础上新增了 Client 半区: | 维度 | dsh-scale-assessment | dsh-scale-assessment-ui | |---|---|---| | 架构 | Host-only | Host + Client(双半区) | | 插件入口 | `src/index.ts` | `src/index.ts` + `src/client/index.tsx` | | 导出 | `.` | `.` + `./client` | | UI 框架 | — | React 18/19 | | tsconfig | 单一 `tsconfig.json` | `tsconfig.json` + `tsconfig.client.json` | | 测试 | 86 用例(3 文件) | 97 用例(4 文件,新增 `client.spec.ts`) | | Peer Dependencies | cordis / dsh-tools / schemastery | 同左 + react / react-dom | | Client 注入 | — | `dsh-client-ui-slots` / `dsh-client-runtime` | ## 环境要求 - Node.js >= 20 - pnpm - React 18 或 19(peerDependency) ## 安装与构建 ```bash pnpm install pnpm build # tsdown 同时构建 Host + Client,产物在 lib/ pnpm test # vitest run pnpm typecheck # tsc --noEmit(Host 侧) pnpm typecheck:client # tsc --noEmit -p tsconfig.client.json(Client 侧) ``` 构建产物分两个目录: | 产物 | 路径 | 格式 | 说明 | |---|---|---|---| | Host 入口 | `lib/index.js` + `lib/index.d.ts` | ESM | 插件主入口(`apply` / `name` / `inject`) | | Client 入口 | `lib/client/index.js` | CJS | Client UI 入口(React 组件,浏览器平台,含 sourcemap) | Host 侧为标准 ESM 模块;Client 侧为 CJS 格式、`platform: browser`,构建时通过 banner/footer 包裹在 `window.__ModuleLoader__.load(...)` 中,以适配 DSH Client 运行时的模块加载机制,并将 `react` / `react-dom` / `react/jsx-runtime` 声明为 external。 也可单独构建某一侧: ```bash pnpm build:host # 仅构建 Host(ESM) pnpm build:client # 仅构建 Client(CJS + browser) ``` ## 插件契约 ### Host 侧 ```ts export const name = 'dsh-scale-assessment-ui'; export const inject = ['tools']; export function apply(ctx: Context, config: PluginConfig = {}): void; ``` `apply` 会依次完成:创建日志器 → 加载并校验全部量表 → 创建会话存储 → 通过 `ctx.effect` 注册 TTL 清理定时器 → 注册全部工具。 ### Client 侧 ```ts export const name = 'dsh-scale-assessment-ui-client'; export const inject = ['slots', 'locale']; export function apply(ctx: any): void; ``` Client 入口通过 `ctx.slots.inject('conversation.view', ...)` 注入到对话视图,注册一个 `id: 'scale-assessment'`、`order: 20`、`label: '量表测评'` 的标签页,渲染 `App` 组件。 ### dsh 配置 `package.json` 中的 `dsh` 字段声明了 Client 注入信息: ```json { "dsh": { "bundle": { "patch": "./cordis.patch.yml" }, "client": { "platform": "web", "inject": [ "@deepseek-ai/dsh-client-ui-slots", "@deepseek-ai/dsh-client-runtime" ] } } } ``` ## 项目地址 - **Gitee**: https://gitee.com/openminds/dsh-scale-assessment-ui ## 使用 DSH CLI 部署插件 ### 1. 安装 DSH CLI ```bash npm install -g @deepseek-ai/dsh ``` ### 2. 添加插件到 DSH Profile ```bash # 添加插件到 web profile dsh plugin --profile web add https://gitee.com/openminds/dsh-scale-assessment-ui # 或者从本地目录添加 dsh plugin --profile web add /path/to/dsh-scale-assessment-ui ``` ### 3. 验证插件配置 ```bash # 查看当前 profile 配置 # Windows PowerShell: dsh --profile web --dump-config | Select-String "dsh-scale-assessment-ui" # 或者使用 findstr (Windows CMD): dsh --profile web --dump-config | findstr "dsh-scale-assessment-ui" # 或者使用 grep (Git Bash / WSL): dsh --profile web --dump-config | grep dsh-scale-assessment-ui # 预期输出应包含: # # == dsh-scale-assessment-ui # - id: dsh-scale-assessment-ui # name: dsh-scale-assessment-ui ``` ### 4. 启动 DSH 服务 ```bash # 启动 Web 界面 dsh web --port 3081 # 或者启动 CLI 模式 dsh cli ``` ### 5. 验证插件功能 **Client UI 方式**:在 DSH Web 界面的对话窗口中,点击「量表测评」标签页,选择量表后逐题作答,完成后查看结果与报告。 **Host 工具方式**:在 DSH Web 界面或 CLI 中测试以下工具: ```bash # 列出所有可用量表 list_scales # 开始一个 SDS 量表测评 start_assessment --abbreviation SDS # 提交答案(示例) submit_answer --item_id 1 --answer B # 计算结果 calculate_results # 生成报告 generate_report ``` ### 6. 配置插件 插件支持通过 profile 配置覆盖默认设置: ```yaml # 在 ~/.dsh/profiles/web/config.yml 中添加: plugins: dsh-scale-assessment-ui: config: sessionTtlMs: 7200000 # 2小时 maxSessions: 500 logLevel: debug ``` ### 常见问题排查 **权限问题**: - 如果遇到文件权限错误,尝试使用管理员权限运行命令 - 确保 Node.js 版本 >= 20 **网络问题**: - 如果从 Gitee 下载失败,可以先将项目克隆到本地再添加 - 检查网络连接和代理设置 **配置问题**: - 使用 `--dump-config` 检查配置是否正确加载 - 查看 DSH 日志文件(通常在 ~/.dsh/logs/)获取详细错误信息 ### 卸载插件 ```bash dsh plugin --profile web remove dsh-scale-assessment-ui ``` `package.json` 中已声明 `dsh.bundle.patch` 指向 `cordis.patch.yml`: ```yaml - insert: - id: dsh-scale-assessment-ui name: 'dsh-scale-assessment-ui' # config: # sessionTtlMs: 3600000 # maxSessions: 1000 # logLevel: info ``` ## 配置项 | 配置 | 类型 | 默认值 | 说明 | |---|---|---|---| | `sessionTtlMs` | `number` | `3600000` | 会话空闲淘汰时间(毫秒) | | `maxSessions` | `number` | `1000` | 最大会话数,超限时淘汰最久未访问的会话 | | `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | `'info'` | 日志级别 | ## 工具清单 共 24 个工具:测评流程 5 个、量表查询 2 个、量表专用 17 个。 ### 测评流程 | 工具 | 参数 | 说明 | |---|---|---| | `start_assessment` | `abbreviation` | 开始测评,返回题目总数与首题(题号、题干、选项) | | `submit_answer` | `item_id`、`answer` | 提交单题答案,返回进度与下一题;答完最后一题时直接返回完整结果 | | `cancel_assessment` | — | 取消当前对话的测评并清空答案,之后可重新 `start_assessment` | | `calculate_results` | — | 计算当前测评结果,未答完则报错 | | `generate_report` | — | 生成 Markdown 测评报告,未答完则报错 | ### 量表查询 | 工具 | 参数 | 说明 | |---|---|---| | `list_scales` | — | 列出可用量表(缩写、名称、简介、题量、预计用时) | | `get_scale_guide` | `abbreviation` | 获取量表背景、目的、适用人群、计分方式、结果解释与注意事项 | ### 量表专用工具 按量表定制的速评工具,直接从传入的分数给出分级与解释;`scales/*.json` 中的每一份量表都有对应工具。 | 工具 | 对应量表 | 参数 | |---|---|---| | `sds_depression_scale` | SDS 抑郁自评量表 | `score` | | `sas_anxiety_scale` | SAS 焦虑自评量表 | `score` | | `scl_90_symptom_checklist` | SCL-90 症状自评量表 | `total_score`、`positive_items` | | `phq_9_depression_screening` | PHQ-9 抑郁筛查 | `score` | | `gad_7_anxiety_screening` | GAD-7 焦虑筛查 | `score` | | `bsrat_behavior_risk_assessment` | BSRAT 简明自杀风险评估工具 | `score` | | `aaq_ii_acceptance_action` | AAQ-II 接纳与行动问卷 | `score` | | `cabs_autism_screening` | CABS 克氏孤独症行为量表 | `total_score` | | `cbf_pi_b_personality_assessment` | CBF_PI_B 中国大五人格问卷(简式版) | `neuroticism`、`conscientiousness`、`agreeableness`、`openness`、`extraversion` | | `cmqi_marital_quality` | CMQI 中国人婚姻质量问卷 | `total_score` | | `epds_postnatal_depression` | EPDS 爱丁堡产后抑郁量表 | `score` | | `fne_r_negativity_evaluation` | FNE_R 惧怕否定评价量表(修订) | `score` | | `gds_geriatric_depression` | GDS 老年抑郁量表 | `score` | | `pcl_ptsd_checklist` | PCL 创伤后应激障碍检查表 | `total_score` | | `tai_test_anxiety_inventory` | TAI 考试焦虑调查表 | `total_score` | | `tas_test_anxiety_scale` | TAS 考试焦虑量表 | `score` | | `znpq_negative_perfectionism` | ZNPQ 消极完美主义问卷 | `total_score` | ## 典型流程 ### Client UI 流程 ``` 量表列表(ScaleList)→ 点击量表卡片 ↓ 答题表单(AssessmentForm)→ 逐题选择选项 → 进度条更新 ↓ (全部作答完成) 点击「查看结果」→ calculateAll() ↓ 结果面板(ResultPanel)→ 分数总览 / 结果解释 / 安全提示 / 答题明细 / 临床建议 ↓ (可选) 点击「查看完整报告」→ ReportView 弹窗(Markdown 渲染) ``` ### Host 工具流程 ``` start_assessment(SDS) → submit_answer("1", "B") → … → submit_answer("20", "A") ↓ (最后一题返回完整 results) calculate_results() → 校验是否答完并返回结构化结果 generate_report() → 返回 Markdown 报告 ``` ## 量表数据 量表定义存放在 `scales/*.json`,字段结构如下: ```jsonc { "chinese_name": "抑郁自评量表", "english_name": "Self-Rating Depression Scale", "abbreviation": "SDS", "category": "精神病学临床量表", "outline": "…量表背景…", "contents": { "instruction": "…指导语…", "items": { "1": "题干…" }, // 题号 → 题干 "reverse_items": ["2", "5"], // 反向计分题 "scales": ["维度名"], // 维度 "scales_items": { "维度名": ["1", "2"] }, // 维度 → 题号 "factors": [], // 因子(预留) "factors_scales": {}, "rating": { "1": { "A": "没有或很少时间" } }, // 题号 → 选项 "score_rules": { "1": [1, 2, 3, 4] } // 题号 → 各选项分值 }, "implementation": "…", "measurements": {}, "interpretation": "…结果解释…", "applications": "…适用范围…" } ``` 当前随包提供的 17 份量表数据: | 缩写 | 中文名 | 类别 | |---|---|---| | AAQ_II | 接纳与行动问卷 | 行为方式量表 | | BSRAT | 简明自杀风险评估工具 | 精神病学临床量表 | | CABS | 克氏孤独症行为量表 | 精神病学临床量表 | | CBF_PI_B | 中国大五人格问卷(简式版) | 人格评估量表 | | CMQI | 中国人婚姻质量问卷 | 生活质量与满意度量表 | | EPDS | 爱丁堡产后抑郁量表 | 精神病学临床量表 | | FNE_R | 惧怕否定评价量表(修订) | 精神病学临床量表 | | GAD_7 | 广泛性焦虑障碍量表 | 精神病学临床量表 | | GDS | 老年抑郁量表 | 精神病学临床量表 | | PCL | 创伤后应激障碍检查表 | 应激及相关行为量表 | | PHQ_9 | 患者健康问卷抑郁症状群量表 | 精神病学临床量表 | | SAS | 焦虑自评量表 | 精神病学临床量表 | | SCL_90 | 90 项症状清单 | 一般心理健康与行为问题量表 | | SDS | 抑郁自评量表 | 精神病学临床量表 | | TAI | 考试焦虑调查表 | 学习与教育量表 | | TAS | 考试焦虑量表 | 学习与教育量表 | | ZNPQ | 消极完美主义问卷 | 人生价值、态度量表 | > Host 侧会话式测评由 `ScaleRepository` 动态扫描 `scales/*.json` 驱动;Client 侧通过 `scales-loader.ts` 静态 import 加载同一份数据。实际可用的量表取决于该目录中的 JSON 文件。上述 17 份均已加载,并各自注册了对应的速评工具。 ## 计分要点 - 单题:按 `rating` 的键序定位答案下标,取 `score_rules` 对应值;命中 `reverse_items` 时按 `min + max - raw` 翻转。 - 聚合:`item_scores` → 维度分(`scales_items` 求和)→ 因子分(`factors` 非空时才构建);未作答题目跳过。 - 标准分:仅 SDS / SAS,`Math.trunc(1.25 × 总分)`;其他量表返回 `null`。 - BSRAT:见上文「特性」。 ## 项目结构 ``` src/ ├── index.ts # Host 插件入口:apply / name / inject ├── config.ts # 配置默认值 ├── logger.ts # 日志器 ├── errors.ts # 业务错误工厂(中文提示,与黄金样例逐字对齐) ├── session.ts # SessionStore(Map + TTL + 容量淘汰) ├── scale-data.ts # 量表加载、ASCII 排序、数据校验(re-export natural-sort) ├── natural-sort.ts # 自然排序(naturalSortKey / naturalCompare,与 Python 端等价) ├── service.ts # ScaleRepository ├── scoring.ts # 计分引擎(含 BSRAT 规则隔离) ├── report.ts # Markdown 报告生成 ├── types.ts # 共享类型 ├── tools/ # 工具注册(scales / session / assessment / results / prompts) └── client/ # Client UI 半区 ├── index.tsx # Client 插件入口(slots 注入) ├── App.tsx # 主应用组件(三视图:量表列表 / 答题 / 结果) ├── data/ │ └── scales-loader.ts # Client 侧量表加载(静态 import + 元数据查询) ├── hooks/ │ ├── useAssessment.ts # 测评状态管理 Hook(量表选择 / 答题 / 计分 / 报告) │ └── usePagination.ts # 通用分页 Hook ├── components/ │ ├── ScaleList.tsx # 量表卡片列表(分页 / 题量 / 预计用时) │ ├── AssessmentForm.tsx # 逐题答题表单(进度条 / 选项 / 导航) │ ├── ResultPanel.tsx # 结果面板(分数 / 解释 / 提示 / 明细 / 建议) │ ├── ReportView.tsx # Markdown 报告弹窗(轻量渲染器) │ └── Pagination.tsx # 分页控件(键盘方向键支持) └── styles/ └── styles.ts # 内联 CSS 注入 scales/ # 17 份量表 JSON tests/ # vitest 用例 + tests/fixtures 黄金样例 ``` ## Client UI 架构 ### 三视图状态机 Client 侧的 `App` 组件通过 `useAssessment` Hook 管理三个视图的切换: ``` scale-list ──选择量表──→ assessment ──完成答题──→ result ↑ │ │ └─────返回列表──────────────┘──────────重新测评──────┘ ``` ### 数据流 ``` scales-loader.ts Host 侧共享逻辑 │ ┌──────────────────────────────────────┐ ├→ getAllScales() │ scoring.ts / report.ts / │ ├→ getScale(abbr) │ natural-sort.ts / scale-data.ts / │ └→ getEstimatedTime() │ tools/_shared.ts │ └──────────────────────────────────────┘ │ │ ↓ ↓ useAssessment() calculateResults() │ getStandardScore() │ generateReport() │ computeRiskFlag() ↓ ScaleList / AssessmentForm / ResultPanel / ReportView ``` Client 侧直接复用 Host 侧的 `scoring.ts`、`report.ts`、`natural-sort.ts`、`scale-data.ts` 和 `tools/_shared.ts` 中的计分、报告生成与风险检测逻辑,确保两端结果完全一致。 ### 组件说明 | 组件 | 功能 | |---|---| | `ScaleList` | 量表卡片网格列表,每页 6 个,显示缩写、中英文名、类别、题量与预计用时 | | `AssessmentForm` | 逐题答题表单,支持进度条、选项点击、上下题导航、键盘方向键翻页 | | `ResultPanel` | 结果面板,分段展示分数总览、结果解释、安全提示、答题明细表与临床建议 | | `ReportView` | Markdown 报告弹窗,内置轻量渲染器(标题 / 加粗 / 表格 / 列表 / 段落) | | `Pagination` | 通用分页控件,支持数字跳转按钮与键盘方向键导航 | ### Hooks 说明 | Hook | 功能 | |---|---| | `useAssessment` | 测评状态管理:量表列表加载、量表选择、答题提交、取消测评、计分计算、报告生成、视图切换 | | `usePagination` | 通用分页逻辑:当前页 / 总页数、上下页跳转、数字跳转、列表切片 | ## 双半区编译体系 | 配置文件 | 用途 | target | JSX | declaration | include | |---|---|---|---|---|---| | `tsconfig.json` | Host 侧 | ES2022 | — | `true` | `src/**/*.ts`(排除 `src/client`) | | `tsconfig.client.json` | Client 侧 | ES2020 | `react-jsx` | `false` | `src/client/**/*.tsx` + `*.ts` | 构建工具 `tsdown` 使用数组配置分别处理 Host 和 Client 两个入口。Host 侧输出到 `lib/`(ESM + dts),Client 侧输出到 `lib/client/`(CJS + sourcemap,无 dts)。`tsdown.config.ts` 配置: ```ts const CLIENT_ID = 'dsh-scale-assessment-ui'; export default defineConfig([ // Host 侧:ESM + 类型声明 { entry: ['src/index.ts'], format: ['esm'], outDir: 'lib', dts: true, clean: true, target: 'node20', outExtensions: () => ({ js: '.js', dts: '.d.ts' }), }, // Client 侧:CJS + 浏览器平台 + 模块加载器包裹 { entry: ['src/client/index.tsx'], format: ['cjs'], outDir: 'lib/client', platform: 'browser', dts: false, clean: false, sourcemap: true, external: ['react', 'react-dom', 'react/jsx-runtime'], banner: { js: `window.__ModuleLoader__.load({ id: ${JSON.stringify(CLIENT_ID)}, factory: (require) => {\nvar module = { exports: {} };\nvar exports = module.exports;`, }, footer: { js: `return module.exports;\n}\n});`, }, outExtensions: () => ({ js: '.js' }), }, ]); ``` Client 侧构建的关键差异: - **CJS 格式**:适配浏览器端模块加载机制,而非 ESM。 - **`platform: 'browser'`**:排除 Node.js 内置模块,面向浏览器环境。 - **`external`**:`react` / `react-dom` / `react/jsx-runtime` 不打包,由宿主环境提供。 - **banner / footer**:将输出包裹在 `window.__ModuleLoader__.load({ id, factory })` 中,由 DSH Client 运行时按需加载。 - **`sourcemap: true`**:生成 source map,便于浏览器调试。 - **无 `dts`**:Client 侧不生成类型声明文件。 ## 开发与测试 ```bash pnpm typecheck && pnpm typecheck:client && pnpm test && pnpm build ``` `tests/fixtures/` 中保存了由原 Python 服务真实产出、与 TS 实现逐字比对的黄金样例,评分测试会逐一校验 `results` 与 `partial_results`。 测试共 97 个用例(4 个文件): | 文件 | 用例数 | 覆盖内容 | |---|---|---| | `tests/scoring.spec.ts` | 18 | 26 份黄金样例的分数逐项比对 | | `tests/session.spec.ts` | 9 | 会话生命周期、TTL 淘汰与容量上限 | | `tests/all-scales.spec.ts` | 59 | 17 份量表的端到端流程(开始 → 逐题作答 → 结果 → 报告)、17 个速评工具、查询工具与错误路径 | | `tests/client.spec.ts` | 11 | Client 侧 `scales-loader` 查询、元数据一致性、预计用时分档 | ## 错误提示 业务错误统一抛原生 `Error`(框架归一化为 `isError` 结果),文案面向模型、可直接指导下一步操作,例如: - `量表 'XXX' 不存在。可用量表:SDS, SAS, …` - `该会话尚未开始测评。请先调用 start_assessment 并传入此 session_id。` - `测评尚未完成:已作答 3/9 题。请先答完全部题目。` - `题目 1 的答案 'Z' 无效。可选值:A, B, C, D` ## 技术栈 | 层面 | 技术 | |---|---| | 开发语言 | TypeScript(ES2022 / ES2020) | | Host 产物格式 | ESM(`lib/index.js` + `.d.ts`) | | Client 产物格式 | CJS(`lib/client/index.js` + sourcemap,浏览器平台) | | UI 框架 | React 18 / 19 | | 构建工具 | tsdown v0.22.2 | | 包管理器 | pnpm v12.x | | 测试框架 | Vitest v2.x | | 运行时 | Node.js >= 20 | | 插件框架 | @deepseek-ai/cordis v4.0.2 | | 工具系统 | @deepseek-ai/dsh-tools v0.1.5-rc.2 | | 类型定义 | @deepseek-ai/schemastery v3.18.2 | | Client 运行时 | @deepseek-ai/dsh-client-ui-slots / @deepseek-ai/dsh-client-runtime | ## 许可证 MIT