# 架构

本项目以 Pi Extension 为运行入口，由本地 Web 工作台与 TUI 接收学习者作答。浏览器不启动模型，也不解析 Tutor 自然语言来猜测题型。

## 模块与依赖

```mermaid
flowchart TD
  Pi[Pi 运行时] --> Entry[扩展装配]
  Entry --> Commands[学习命令]
  Entry --> Tools[学习工具]
  Commands --> Session[LearningSession]
  Tools --> Session
  Session --> State[LearningStateStore]
  Session --> Broker[InteractionBroker]
  Tools --> Presenter[Web / TUI 呈现]
  Presenter --> Broker
  Broker --> Transport[本地 HTTP / SSE]
  Transport --> Web[React 工作台]
  State --> Persistence[Pi 分支快照]
  Shared[shared 学习协议] -.类型.-> Tools
  Shared -.类型.-> Transport
  Shared -.类型.-> Web
```

| 位置 | 职责 |
| --- | --- |
| shared/protocol.ts、shared/learning-state.ts | 题目、答案、事件与快照的唯一类型定义；无 Node、Pi 或 React 运行时依赖 |
| extension/learning-session.ts | 评分资格、掌握度、停止、重置与会话清理的协调规则 |
| extension/state/learning-state.ts | 掌握度算法、已计分账本、快照校验与状态变更 |
| extension/state/session-persistence.ts | 从当前 Pi 分支恢复最新有效 version=1 快照 |
| extension/server/interaction-broker.ts | 等待 Promise、答案校验、跳过、取消与有界完成凭据 |
| extension/server/learning-server.ts | loopback HTTP/SSE、token、请求体限制与包内静态资源 |
| extension/tools/ | Pi 参数与结果翻译，题目在 Web/TUI 间选择和回退 |
| web/src/state/store.ts | 连接生命周期、权威快照与事件重放、提交去重、Tutor 流式文本 |

## 作答与评分

四个作答工具顺序执行：呈现题目 → Broker 等待 → Web POST 或 TUI 输入 → 校验 → 返回结构化 tool result。只有用户主动执行的学习命令会发出教学启动消息。

评分工具要求提供非空白的 `explanation`，说明正确答案及关键理由。成功结果将讲解保留在 details，同步层通过 `tutor.message` 将中文判定和讲解一起发送到工作台，因此模型只调用工具时也能显示反馈。讲解不改变掌握度算法或持久化快照；旧评分结果缺少讲解时，仅回退显示已有误区说明。修改加载到 Pi 扩展后用于后续评分，历史记录不会自动补回。

状态提示对应真实教学阶段：仅 `learning_ask_*` 作答工具开始时发送 `waiting`，仅 `agent_settled` 发送 `idle`。评分开始不要求学习者作答，工具返回也不代表整轮讲解结束。

完成凭据仅保留题型和是否跳过，最多 1,000 条，不留存整份代码答案。评分时由 LearningSession 检查题目已完成且没有跳过；LearningStateStore 检查是否重复计分。掌握度算法保持既有规则：初始 0.20、按证据形式调整、限制在 0..1，超过 0.75 需要近期连续正确中出现不同证据形式。

重置学习时清空掌握度/计分账本与完成凭据，并取消等待。停止学习只取消等待、关闭学习模式，保留记录。

切换课程或主题时，命令层先中止旧教学轮次并等待宿主空闲；会话层清除旧题等待和完成凭据后设置新目标。已保存的累计概念进度继续保留。评分证据必须与完成凭据的实际题型一致，错误参数不会消耗评分资格。

## Pi 生命周期与持久化

session_start 与 session_tree 共用恢复路径：取消旧等待、清空进程内题目资格和旧学习状态，再从 ctx.sessionManager.getBranch() 恢复最新有效快照，最后刷新工作台。Pi 原生 /tree 不会把上一分支状态写入新分支。

快照版本仍为 1，兼容缺少可选历史字段的旧数据。完成凭据不持久化；恢复进度不代表恢复旧题的计分资格。shutdown 取消等待并关闭 HTTP/SSE。

关闭服务时同时终止活动 HTTP 连接，响应断开通过 AbortSignal 通知注入的 runner 取消。连接关闭不等于 runner 自身的异步资源清理已经完成。

## 浏览器同步

启动先读取 health 与学习快照，再连接 SSE。连接代次使停止后返回的旧请求失效；请求序号阻止旧刷新覆盖新刷新。

重连使用服务器 pending 快照作为基准，重放请求期间的 presented/resolved/cancelled 和本地提交成功操作。因此既能清除断线时漏掉的旧题，也不会重新显示刚完成的题目。会话字段独立保留请求之后到达的更新。POST 与 SSE 到达顺序不会重复添加 transcript。

提交记录在 SSE 到达时保留当前题干，POST 返回后补齐元数据。Markdown 与数学 token 共用一次解析，统一进行 HTML 消毒，保留代码转义及原段落/列表结构。

Monaco 只读区域跟随装饰范围移动；范围和对应代码作为伴随草稿元数据保存，恢复时检查代码一致及范围有效。旧纯文本草稿仍可读取，缺少有效元数据时沿用题目原始范围。Reset 直接恢复编辑器模型并重建原始只读区，避免显示代码与提交内容分离。

## 分发与验证

pi.extensions 定位入口，files 包含 extension、shared、构建后的 Web 和维护文档。Pi 内置库使用 peerDependencies，浏览器依赖打入静态资源。

正式支持 npm 预构建包与本地源码构建。Pi 0.84.0 的 Git 安装省略 devDependencies，不承诺直接 Git 安装能构建 Web。本地 runner 的限制见 SECURITY.md。

日常测试与归档检查分别运行。公开工具/命令、分支恢复、HTTP/SSE 往返、浏览器输入与同步有自动测试；发布检查真实离线安装 tarball，经 Pi 加载包内入口并验证首页、脚本和 Monaco worker。真实模型 E2E 保持独立。
