18 KiB
18 KiB
心理咨询室互动终端 — 心理小游戏聚合系统
项目代号:Psycho-Games-Hub 定位:青少年/儿童心理辅导互动终端的核心子系统 阶段:架构设计 + 原型阶段(v0.1)
一、产品定位
1.1 整体场景
┌─────────────────────────────────────────────┐
│ 心理咨询室互动终端 │
│ │
│ 🗡️ 数字人入口(语音 + 触摸) │
│ │ │ │
│ Agent 主对话 游戏弹窗 │
│ 策略召回 半透明遮罩 │
│ 风险评估 轻交互 │
│ │ │ │
│ 谈话式量表 ←─── Agent 动态编排 ───→ 激励解读│
└─────────────────────────────────────────────┘
- 载体:一体机 / 触摸屏 kiosk
- 目标用户:青少年 / 儿童(来访者)
- 核心理念:数字人是入口,小游戏是「药」,只是吃起来像糖
- 交互方式:语音对话 + 触摸,Agent 主动驱动轻量干预
1.2 用户故事
场景 A(游戏触发)
来访者:「最近压力好大,考试考砸了。」 Agent:「你这个困扰,看起来困扰你很久了。我知道个有效的缓解方式——我给你算个塔罗牌吧。」→ 弹出游戏弹窗 → 抽卡 → 积极方向解读 → 关闭弹窗
场景 B(量表编排)
Agent 后台监测到近期对话出现「失眠」「焦虑」关键词 → StrategyDispatcher 入队 → Agent 在自然节点插入引导 → 用户同意 → 谈话式问答 → 生成摘要 → 更新风险标签
场景 C(直接入口)
用户双指下滑 → 解锁底部工具栏 → 展示所有游戏图标网格 → 主动点击进入
二、系统架构(MVC)
2.1 模块总览
┌──────────────────────────────────────────────────────┐
│ VIEW 层 │
│ AvatarView │ GameModal │ ChatPanel │ ResultCard │
└──────────────────────────────────────────────────────┘
▲
│ 事件 / 回调
┌──────────────────────────────────────────────────────┐
│ CONTROLLER 层 │
│ AgentController │ GameController │ AssessmentCtrl │
│ InterpretationEngine │ StrategyDispatcher │
└──────────────────────────────────────────────────────┘
▲
│ 读写
┌──────────────────────────────────────────────────────┐
│ MODEL 层 │
│ AgentBrain │ GameCatalog │ AssessmentFlows │
│ UserSession │ InterpretationPool │
└──────────────────────────────────────────────────────┘
三、Model 层
3.1 AgentBrain
职责:维护策略记忆 + 风险词召回
数据结构:
{
// 标注语料库(心理咨询师标注)
corpus: [
{ keyword: "失眠", riskLevel: 3, relatedScale: "SAS", strategy: "soft_inquiry" },
{ keyword: "压力大", riskLevel: 2, relatedGame: "tarot", strategy: "game_offer" },
// ...
],
// 策略路由表
strategies: {
"game_offer": { trigger: "anxiety_level > 2", gameType: "tarot" },
"soft_inquiry": { trigger: "riskLevel >= 3", prompt: "询问近期睡眠状况" },
// ...
},
// 召回方法(当前为规则版,待升级 RAG)
recall(text) → matchedCorpus[]
}
3.2 GameCatalog
职责:游戏配置清单 + 触发条件注册
数据结构:
{
games: [
{
id: "tarot",
name: "塔罗牌占卜",
triggerKeywords: ["压力", "迷茫", "选择", "未来"],
completionKeywords: ["抽卡", "算一算"],
viewComponent: "TarotGame",
difficulty: "low",
estimatedTime: "2min",
icon: "🃏"
},
{
id: "rorschach",
name: "罗夏墨迹",
triggerKeywords: ["看到了什么", "感觉", "想象"],
viewComponent: "RorschachGame",
difficulty: "medium",
estimatedTime: "3min",
icon: "🎭"
},
{
id: "mbti",
name: "人格测试",
triggerKeywords: ["我是谁", "性格", "了解自己"],
viewComponent: "MBTIGame",
difficulty: "medium",
estimatedTime: "5min",
icon: "🔮"
},
{
id: "chimp_memory",
name: "记忆挑战",
triggerKeywords: ["试试", "挑战"],
viewComponent: "ChimpMemoryGame",
difficulty: "low",
estimatedTime: "1min",
icon: "🐵"
}
// 更多游戏...
],
// 查询方法
matchByKeywords(text) → games[]
getById(id) → game
}
3.3 AssessmentFlows
职责:量表的对话化编排
数据结构:
{
flows: [
{
id: "SAS_anxiety",
name: "焦虑自评",
condition: "riskKeywords includes ['焦虑', '失眠', '心慌']",
questionCount: 3, // 精简至 3 题,不做完整量表
questions: [
{
id: "q1",
text: "你最近有没有感到特别紧张或者担心的事情?",
type: "single_choice",
options: ["完全没有", "偶尔", "经常", "一直都在"],
scores: [1, 2, 3, 4]
},
// ...
],
scoringMethod: "sum",
resultRanges: {
low: { min: 0, max: 5, label: "状态良好", interpretation: "positive" },
medium: { min: 6, max: 8, label: "轻度波动", interpretation: "neutral" },
high: { min: 9, max: 12, label: "值得关注", interpretation: "caution" }
}
}
// 更多量表...
],
// 查询方法
matchByCondition(riskKeywords) → flows[]
getQuestions(flowId) → questions[]
}
3.4 UserSession
职责:维护当前来访者会话上下文
数据结构:
{
sessionId: "uuid-xxx",
startTime: "2026-05-31T10:00:00Z",
userProfile: {
age: 14, // 年龄(首访时采集)
gender: "M", // 性别
firstVisit: true
},
riskTags: [], // 动态风险标签,如 ["焦虑", "压力", "学业"]
riskScore: 0, // 综合风险评分(0-100)
gameHistory: [
{ gameId: "tarot", timestamp: "...", result: "positive_outlook" }
],
assessmentResults: [
{ flowId: "SAS_anxiety", score: 5, timestamp: "..." }
],
recentUtterances: [
{ role: "user", text: "最近压力好大", timestamp: "..." },
{ role: "agent", text: "...", timestamp: "..." }
]
}
3.5 InterpretationPool
职责:积极心理学话术库,按游戏类型 × 情境分组
数据结构:
{
pools: {
"tarot": {
"压力": [
"你抽到的这张牌显示,你正在经历一个重要的转变期。压力其实是身体在告诉你,它愿意为你付出更多,只是需要一点喘息的空间。",
"这张牌提醒你:不是所有事情都需要马上解决。有时候,『放下』也是一种前进的方式。",
],
"迷茫": [
"牌面显示你正处于一个探索期,迷茫不是坏事——它意味着你还在寻找答案,而答案正在来的路上。",
]
// ...
},
"rorschach": {
"学业压力": [
"你在这张墨迹中看到了很多变化的形状,这说明你的内心正在经历很多调整。你比自己想象的更有弹性。",
]
// ...
}
// 更多游戏...
},
// 查询方法
get(gameType, contextTags) → interpretations[]
}
四、View 层
4.1 AvatarView
职责:数字人主界面
- 全屏占位,作为系统主页背景
- 实现方式:Live2D / 3D 虚拟人 / 纯 2D 立绘 + 表情差分(待选)
- 接收 AgentController 的表情指令(
setExpression("happy")、speak("你好呀")) - 触摸响应区域:点击数字人触发语音输入
4.2 GameModal
职责:游戏弹窗遮罩
展示规格:
- 层级:覆盖在 AvatarView 上方,z-index 最高
- 样式:
backdrop-filter: blur(8px)+ 半透明黑底(rgba(0,0,0,0.6)) - 尺寸:宽度 90vw,高度 80vh(不超出屏幕),居中
- 关闭行为:允许用户点击关闭按钮退出(游戏中途关闭记录为「中断」)
子区域:
┌─────────────────────────┐
│ [游戏名称] [× 关闭] │ ← 顶栏
├─────────────────────────┤
│ │
│ 游戏内容区 │ ← 动态组件 slot
│ │
├─────────────────────────┤
│ [进度指示] │ ← 底栏(如有需要)
└─────────────────────────┘
4.3 ChatPanel
职责:对话气泡流
- 底部/侧边展开,可折叠
- 展示最近 N 条对话记录
- Agent 消息和来访者消息气泡区分样式
- 接入 AgentController 的实时消息流
4.4 AssessmentCard
职责:量表对话式问答
- 每题一屏,全屏卡片流
- 选项以卡片按钮形式展示(触摸友好)
- 支持进度条展示(1/N)
- 动画过渡切换
4.5 ResultCard
职责:结果展示
内容:
- 游戏结果解读(来自 InterpretationEngine)
- 雷达图/进度条(可选)
- 鼓励文案(来自 InterpretationPool)
- 建议下一步行动
五、Controller 层
5.1 AgentController(主控协调器)
职责:系统中央调度
核心流程:
1. 监听对话输入(用户 utterance)
2. 追加到 UserSession.recentUtterances
3. 触发 AgentBrain.recall() → 策略匹配
4. 根据匹配结果:
a. 若匹配到 game_offer → 触发 GameController
b. 若匹配到 soft_inquiry → 直接生成对话回复
c. 若风险等级高 → 触发 StrategyDispatcher
5. 将回复追加到 ChatPanel
6. 若 AgentController 内部有 StrategyDispatcher 队列非空 → 插入引导语
5.2 GameController
职责:游戏生命周期管理
状态机:
IDLE → LOADING → PLAYING → RESULT → CLOSING → IDLE
核心方法:
{
// 触发游戏
trigger(gameId) → 弹出 GameModal,加载 viewComponent
// 收集游戏结果
collectResult(data) → 传递给 InterpretationEngine
// 关闭游戏
close(interrupt: boolean) → 写回 UserSession.gameHistory → 关闭 GameModal
}
5.3 AssessmentController
职责:量表执行器
核心流程:
1. 接收 AgentController 的激活指令(flowId)
2. 从 AssessmentFlows 加载 questions
3. 按顺序展示 AssessmentCard(每题一屏)
4. 收集 answers + 计算 scores
5. 生成摘要 → 传递给 InterpretationEngine
6. 结果写回 UserSession.assessmentResults
7. 更新 UserSession.riskScore
5.4 InterpretationEngine
职责:解读编排引擎(最体现积极心理学设计的模块)
核心流程:
1. 接收 GameController 或 AssessmentController 的结果数据
2. 查询 UserSession 获取当前上下文(riskTags / recentUtterances)
3. 从 InterpretationPool 按 gameType + contextTags 获取候选解读
4. 拼接上下文敏感内容(注入来访者提到过的关键词)
5. 选取最积极的解读变体(interpretation = "positive" 优先)
6. 生成 ResultCard 内容
7. 决定是否在 ResultCard 中插入下一个游戏/量表建议
5.5 StrategyDispatcher
职责:策略分发队列
数据结构:
{
queue: [
{
id: "strategy_001",
type: "assessment",
scale: "SAS_anxiety",
priority: 2,
reason: "检测到焦虑风险词 3 次",
suggestedQuestions: ["失眠情况", "紧张频率"],
createdAt: "..."
},
{
id: "strategy_002",
type: "game",
gameId: "chimp_memory",
priority: 3,
reason: "缓解轻度压力的轻量游戏"
}
],
// 方法
enqueue(strategy),
dequeue() → strategy, // 优先级最高者出队
peek() → strategy, // 只查看不下队
isEmpty() → boolean
}
入队触发条件:
- AgentBrain 后台监测到风险关键词累计 N 次
- 用户在对话中主动提到相关话题
- 游戏/量表完成后自动推荐下一个
六、关键交互流
流 1:游戏触发(核心)
用户说"最近压力好大"
↓
AgentController.utterance()
↓
AgentBrain.recall("最近压力好大")
→ 匹配 corpus: { keyword: "压力大", strategy: "game_offer" }
↓
AgentController 决策 → StrategyDispatcher.enqueue({ type: "game", gameId: "tarot" })
↓
AgentController 在对话中插入引导语:
"你这个困扰,看起来困扰你很久了。我知道个有效的缓解方式——我给你算个塔罗牌吧。"
↓
用户确认 / 自然延续
↓
GameController.trigger("tarot")
→ 弹出 GameModal → TarotGame 组件加载
↓
用户抽卡 → TarotGame 交互完成
↓
GameController.collectResult({ card, userChoice })
↓
InterpretationEngine.generate({ gameType: "tarot", result: {...}, context: UserSession })
→ 从 InterpretationPool 选取积极解读
→ 生成 ResultCard
↓
展示 ResultCard(解读文案 + 鼓励 + 建议)
↓
GameController.close()
→ 写回 UserSession.gameHistory
↓
AgentController 追加对话:"感觉怎么样?塔罗牌给你的启示有没有帮到你?"
流 2:量表编排(PSI 等)
AgentBrain 后台监测:
recentUtterances 检测到 "焦虑"/"失眠"/"心慌" 关键词累计 3 次
↓
StrategyDispatcher.enqueue({
type: "assessment",
scale: "SAS_anxiety",
priority: 2,
reason: "检测到焦虑风险词 3 次",
suggestedQuestions: ["你最近睡眠状况如何?", "有没有经常感到紧张?"]
})
↓
AgentController 感知队列非空
↓
在对话自然节点插入:
"我注意到你说最近睡得不太好,我有个小工具能帮你整理一下现在的状态,愿意试试吗?"
↓
用户同意
↓
AssessmentController.activate("SAS_anxiety")
→ 从 AssessmentFlows 加载 3 道题目
↓
逐题展示 AssessmentCard → 用户作答
↓
收集评分 → 计算总分
↓
InterpretationEngine.generate({ flowType: "assessment", score, context })
↓
展示 ResultCard + 更新 UserSession.riskScore
↓
AgentBrain 更新 riskTags(加入 "焦虑")
↓
影响后续 StrategyDispatcher 召回权重
流 3:直接入口(用户主动触发)
数字人下方手势区域(双指下滑 / 长按)
↓
解锁底部工具栏 ToolBar
↓
展示游戏图标网格 GridView
↓
用户点击图标 → GameController.trigger(gameId)
(与流 1 的后半段一致)
七、项目结构(原型阶段)
psycho-games-hub/
├── docs/
│ ├── SPEC.md # 本文档
│ ├── RESEARCH.md # GitHub 调研报告(稍后补充)
│ └── PROTOTYPE_NOTES.md # 原型实现笔记
├── src/
│ ├── models/
│ │ ├── AgentBrain.js
│ │ ├── GameCatalog.js
│ │ ├── AssessmentFlows.js
│ │ ├── UserSession.js
│ │ └── InterpretationPool.js
│ ├── views/
│ │ ├── AvatarView.html # 数字人主界面(单文件原型用)
│ │ ├── GameModal.vue # 游戏弹窗
│ │ ├── ChatPanel.vue
│ │ └── ResultCard.vue
│ ├── controllers/
│ │ ├── AgentController.js
│ │ ├── GameController.js
│ │ ├── AssessmentController.js
│ │ └── StrategyDispatcher.js
│ ├── components/ # 各游戏独立组件
│ │ ├── TarotGame.vue
│ │ ├── RorschachGame.vue
│ │ └── MBTIGame.vue
│ └── App.vue
└── README.md
八、待决策问题(悬置)
| # | 问题 | 选项 | 当前倾向 |
|---|---|---|---|
| 1 | 数字人技术路线 | A: Live2D / B: 3D 虚拟人 / C: 2D 立绘+差分 | C(最轻量) |
| 2 | 策略召回引擎 | A: 规则版(关键词匹配)/ B: RAG(向量化召回) | A先行,B后续 |
| 3 | 第一批游戏优先级 | 塔罗牌 / MBTI / 罗夏墨迹 | 塔罗牌优先 |
| 4 | 游戏关闭策略 | A: 可随时关闭 / B: 完成才关闭 | A |
| 5 | 遮罩样式 | A: blur + 黑底 / B: 纯黑底半透明 | A |
| 6 | Agent 接入方式 | 接入 MiniMax / 本地小模型 | 待确认 |
九、版本记录
| 版本 | 日期 | 变更 |
|---|---|---|
| v0.1 | 2026-05-31 | 初稿:MVC 模块划分 + 交互流设计 |
本文档为架构设计文档,随着原型开发推进会持续更新。 关键决策待在大头确认后锁定。