assistant/docs/archive/DESIGN-school-2026-10-28.md

174 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 智能办事助手:产品定义、架构与决策
> 这份文档回答"我们在做什么、为什么这样做、接下来做什么"。里程碑和实测数据见 `PROGRESS.md`。
> 每次做出新的设计决策,在「已定决策」追加一条并写日期;改变方向时改这里,不要只改对话。
---
## 1. 一句话定义
一个像 Codex 桌面端的办事 Agent:用户对着聊天框说一句"我要办某件事",助手识别业务、追问缺的资料、以对应角色登录学校的网页系统、自主找入口填表、在提交前逐项列出请用户确认,办完后记录结果,并把过程中学到的经验沉淀下来让下一次更快。
## 2. 目标用户与角色
| 角色 | 用什么 | 做什么 |
|---|---|---|
| 学校里的办事人(学生、辅导员、管理员) | 桌面端 | 说需求、补资料、在关键节点确认。**不参与知识审核** |
| 我们团队 | 后台管理(待做) | 写出厂 YAML、审核各校沉淀的经验、看运行监控、维护密码本、交付 |
## 3. 产品原则
1. 只在业务允许的域名内活动。
2. 登录凭据来自密码本或用户手动,模型永远看不到明文(browser-use `sensitive_data`)。
3. 任何正式提交前必须用户确认;用户取消就不提交。
4. 不猜学校没有明说的规则,宁可停下来问。
5. 全程可审计:每步截图、progress.md、run_logs。
6. 经验先成草稿再生效,用户端无感,审核由我们或规则完成。
## 4. 架构
### 4.1 现状(2026-09-17)
```
Electron 桌面端(renderer 聊天 + 右侧状态/截图/时间线)
│ IPC
Python FastAPI 后端(Electron 拉起并独占,端口 8788)
├─ nlu.py 意图识别 + 字段抽取(DeepSeek / 千问,一次调用,JSON)
├─ orchestrator.py 状态机、按业务路由、prompt 组装、暂停/续跑、沉淀触发
├─ browser/runner.py browser-use Agent,独立 Chrome,会话保活,每步截图
├─ learning.py 任务成功后生成 learned 草稿
└─ db/database.py psycopg 3,所有学校数据带 school_id
PostgreSQL 17(Docker,127.0.0.1:15433)
knowledge / tasks / run_logs / system_accounts / learned_entries / skills / settings
```
桌面端直连 PostgreSQL,一台机器一所学校(`.env` 的 `SCHOOL_ID`)。
### 4.2 目标形态
```
桌面端(编排 + 浏览器,必须在本机) ──HTTPS──▶ 服务端 API ◀── 后台管理(我们团队,跨学校)
│
中心 MySQL(按 school_id 隔离)+ 向量库(手册)
```
- 桌面端只保留必须在本机跑的部分:编排、browser-use、Chrome。知识、learned、账号、任务记录都走服务端接口。
- 后台管理和桌面端用同一套接口。
- 学校可以一校一库,也可以共库按 `school_id` 隔离,数据模型两种都支持。
## 5. 知识的三层
| 层 | 内容 | 存在哪 | 谁维护 | 状态 |
|---|---|---|---|---|
| 出厂配置 | 系统入口、登录方式、业务定义(角色、必填项、步骤、规则) | 编写格式是 `knowledge/*.yaml`,启动时灌入 `knowledge` 表,运行时读表 | 我们,来源是学校手册 + 调研;将来后台直接改表,文件只做导入导出 | 已有 |
| 经验 learned | 入口路径、控件技巧、前置阻断、踩过的坑 | `learned_entries` 表,draft → verified / rejected,带 school_id、类别、观察次数、来源任务;YAML 里的 `learned` 只是出厂基线 | 任务成功后自动生成草稿,按内容判重归并;控件技巧 / 入口路径类被 ≥2 个任务观察到自动生效,系统规则类由我们后台人审,用户不参与 | 已有;后台审核队列待做 |
| 参考资料 | 学校手册、通知、FAQ | 向量库(可复用已有 Qdrant),按学校 / 系统打标 | 我们导入 | 待做 |
**YAML 文件与数据库的关系**:文件是给人读写的格式,表是运行时副本。现阶段文件是源头;后台做出来之后反过来,表是源头,文件用于交付。
有后台之后(2026-09-17 确认):业务定义在后台维护(表单编辑 / YAML 文本编辑 / 上传导入 / 手册生成草稿),带学校、版本、发布状态;桌面端启动时从服务端拉本校已发布的定义并本地缓存,不再随安装包带文件;仓库里只保留模板(自家学工系统出厂模板、常见第三方系统骨架),给新学校开户时从模板复制进库再改;导出仍产出 YAML,用于备份、迁移和把 A 校审核过的 learned 打包给 B 校。过渡期加一个"知识来源 = 文件 / 服务端"的配置,服务端模式下启动不再用文件覆盖库,只在该校一条业务都没有时用模板初始化一次。
**有手册**:模型读手册生成 YAML 业务草稿(结构化,价值最高);手册切块进向量库,给 Agent 一个 `lookup_manual` 工具按需查"字段是什么意思、有什么前提"。
**没手册**:上线前按角色做一次建图扫描(枚举菜单、页面、表单字段),生成业务草稿;使用中靠 learned 沉淀;交付前预热多跑几遍,把慢的阶段藏起来。
## 6. 执行模型
```
用户一句话
→ nlu 识别业务、抽字段(今天/明天换算成日期)
→ 缺必填项:对话追问(模型生成追问语),用户下一句再抽取合并
→ 角色准入:业务 role 与用户在该系统密码本账号的 role 不一致 → 直接告知办不了
→ 浏览器 profile 按 人 × 系统 × 角色;任务涉及各系统的凭据按域名分组进 sensitive_data(占位符带系统后缀),没有账号的系统按 YAML fallback(测试环境快捷登录 / 请用户手动)
→ 业务的全部启用步骤按顺序在同一个浏览器会话里执行,前一步产物注入下一步
→ 执行业务的启用步骤:prompt = 业务 + 目标 + 资料 + 登录方式 + 规则 + learned(YAML 基线 + 库里 verified)+ 步骤要求
→ Agent 需要用户时调 ask_user:以 is_done 结束本轮,浏览器保活,状态 paused;用户回答后 add_new_task 续跑同一 Agent
→ 遇到系统提示「必须先办另一件事」时调 report_blocker:按 YAML 声明的 resolution 处理。self = 问用户要不要顺带办,同意则同一会话切去办前置业务再回来;other_role / manual = 说明要找谁怎么办,用户说已处理就重试,说取消就进 blocked(可稍后继续)
→ 提交类步骤:填完 ask_user 逐项确认 → 确认后提交,保存 submitted_record / result_message → 状态 submitted
→ 取消则保存 cancelled → 状态 completed(不算提交)
→ 成功后 learning.draft_learned 生成草稿
→ submitted 任务按 next_check_at 由调度循环拉起只读回查步骤:approval_status 归类 → completed / rejected / 继续排期
```
判定规则:`success_artifacts_any` 覆盖 Agent 自评;`submitted_artifacts_any` 决定是否算提交;`cancelled` 优先。
## 7. 已定决策(按时间)
| 日期 | 决策 | 原因 / 备注 |
|---|---|---|
| 09-16 | 产品形态像 Codex 桌面端;一定是桌面端 | 浏览器和登录态在本机,隐私好 |
| 09-16 | 账号由我们按角色从库里取,自动登录 | 用户不该反复输密码;用 sensitive_data 防泄露 |
| 09-16 | 知识由我们维护交付学校;手册是来源 | 学校只管用 |
| 09-16 | 模型 Key 先放 .env,不做用户级 Key | 阿里 / DeepSeek Key 由我们持有 |
| 09-16 | 学工系统是自家产品,先拿它练 | 快捷登录等便利只在自家系统有,注意别依赖 |
| 09-16 | skill / learned 全校受益;数据层用 MySQL(Docker) | 为中心化做准备 |
| 09-16 | 提交后跟踪要做,但排在链路走通之后 | 提交不等于办完 |
| 09-17 | 演示业务从留校申请换成辅导员查课日志填报 | 测试环境学生端没有留校申请模块 |
| 09-17 | 学校之间数据隔离(school_id) | 不是每所学校都用我们的系统 |
| 09-17 | 用户端不出现采纳 / 忽略;审核靠自动晋升 + 我们后台 | 办事人不该做知识审核 |
| 09-17 | 后台管理和服务端层要做,但桌面端先夯实 | 顺序:真实提交 → 第二业务 → 后台 |
| 09-17 | learned 判重按内容不按 key;自动晋升只对控件技巧 / 入口路径类,系统规则类必须人审 | 模型每次起的 key 不同;记错规则代价大 |
| 09-17 | 有后台后 YAML 只做模板和导入导出,表是源头,桌面端从服务端拉配置 | 见第 5 节 |
| 09-17 | 前置阻断做成标准机制:同角色能办的顺带办(同一会话切换目标再回来),别的角色 / 线下的说明处理方式后 blocked 等待 | 请假被返校核验拦住是真实样例 |
| 09-17 | 桌面端登录走学校统一身份认证;业务系统登录配方要支持「统一认证跳转」,允许域名含认证中心域名 | 用户 09-17 确认 |
| 09-17 | 密码本只服务未接统一认证的系统:学校配发账号由后台导入,个人账号才需要用户绑定。开发期手动维护密码本是权宜之计。「绑定账号界面」从演示版拿掉,换成登录配方支持 sso(登录态在 profile 里复用,缺时请用户完成一次) | 用户 09-17 指出 |
| 09-17 | 前置阻断需要别的角色处理时,是另一个人在自己的端上用自己的账号办(如辅导员办「核实学生返校」),不是同一用户切角色;两端联动(协办请求 / 完成通知 / 自动续跑)走服务端,现阶段学生手动点「继续」 | 纠正此前「跨角色闭环」的说法 |
| 09-17 | 目标系统自身的问题(如测试数据、缺功能)不纠结、不绕过:如实报告并 blocked,等系统方处理。我们会面对各种系统,链路跑通优先 | 学生请假被后端规则拦住是首例 |
| 09-17 | 跨人联动(通知辅导员、协办请求、自动续跑)本阶段不做,归服务端层 | 用户 09-17 决定 |
| 09-17 | 对照同事 PenguinUniOS 后的四项决定:桌面端按其形态重做并连打包链一起对齐;数据库切 PostgreSQL 17;继续独立仓库;执行路线保持 browser-use run() + learned | 用户 09-17 拍板,见 REFERENCE_PENGUINUNIOS.md |
| 09-17 | 身份模型:一个人在一个系统里只有一个身份,跨系统身份可不同。密码本按「人 × 系统」存账号并记录该账号在该系统的角色;业务的 role 是准入条件而不是登录依据;跨系统任务用 browser-use 按域名分组的 sensitive_data,一个会话依次登录各系统 | 用户 09-17 指出;这是跨系统操作的前提 |
| 10-28 | 学校工程整体移入 `browser_use/school-assistant/`,与独立获客项目 `browser_use/assistant/` 并列;各自保留独立 Git 仓库,外层只作工作区导航 | 用户要求分开两个项目,避免代码、配置和文档混在工作区根目录 |
## 8. 待做设计(按建议顺序)
1. ~~真实提交验证~~(09-17 完成:两条查课日志真实提交成功,状态 submitted,日期校验无问题)。
2. ~~第二个业务~~(09-17 完成:每周谈心谈话)。~~前置阻断机制~~(09-17 完成:report_blocker + self / other_role 两条路径 + blocked 状态)。学生请假在测试环境被「返校核验」拦住;09-17 以辅导员和系统管理员探索后确认界面上没有任何角色能解开(后端业务规则作用于历史未返校记录),需学工团队清测试数据或补功能。业务 `counselor_return_verify` 已建,等系统侧有入口再补填报步骤;学生请假填报步骤同样等待。
3. ~~防重复提交~~(09-17 完成:两个填报步骤提交前先查已有记录,重复时 ask_user)。
4. ~~learned 自动晋升~~(09-17 完成:按内容判重,≥2 任务观察且为控件技巧 / 入口路径类自动生效,系统规则类人审;OPERATOR_MODE 控制草稿面板)。失败驱动的自动降级待做。
5. **浏览器展开 / 收起**:~~CDP 停靠 + 最小化 / 恢复~~(09-17 完成,暂停确认时自动展开);下一步验证 Electron WebContentsView + cdp_url 真正内嵌。
6. **密码本升级(跨系统地基)**:~~本地部分 09-17 完成~~(role 列、角色准入、按系统分 profile、域名分组凭据)。目标形态是"用户登录桌面端 → 服务端按人返回他有账号的系统及角色、据此过滤可办业务、密码本条目清单(无密码)→ 任务开始时按需取该任务涉及系统的凭据,用完即丢并记审计"。密码本来源两条:学校配发账号由后台批量导入;个人账号由用户在桌面端「绑定账号」一次写入。当前阶段先做本地能做的部分:`system_accounts` 加 `role` 列;业务执行前做角色准入检查(当前用户在该系统的账号角色 ≠ 业务 role 就直接告知办不了);浏览器 profile 按「人 × 系统」而非「人 × 角色」;多系统凭据按域名分组装进 `sensitive_data`,占位符带系统后缀(`x_username_lib` / `x_password_lib`),登录说明按系统分段;每系统登录配方(表单 / 统一认证 / 扫码);生产环境放中心库并经服务端按任务取用、审计日志。
7. ~~多步串行执行器~~(09-17 完成:步骤顺序执行、每步可声明系统、产物跨步传递、同一会话 keep_session、允许域名取并集;跨系统登录待第二个系统接入后实测)。
8. ~~提交后跟踪~~(09-17 完成:YAML tracking、调度循环、track-now / track-stop、状态 rejected、桌面端提示;真实任务实测两次回查)。后续:回查时静默运行。
9. **手册导入 / 建图扫描**:手册 → YAML 草稿;手册 → 向量库 + lookup_manual 工具;无手册时按角色扫描菜单和表单生成草稿。
10. **后台管理 + 服务端层**(审核队列部分 09-17 完成,见 PROGRESS D17):已做——`server/`(FastAPI,独立库 school_assistant_hub)+ `console/`(React 审核台),各校桌面端带凭证上报草稿、中心按内容跨校合并、智能预审(规则层 + 模型层)、人工采纳 / 驳回 / 改写、按「仅来源学校 / 所有学校」发布、桌面端按版本号增量拉回并落成本地 verified / rejected;`backend/hub_sync.py` 全后台异步,没配 HUB_URL 就是单机模式。智能采纳(09-17,PROGRESS D18):类别硬门槛只放行控件技巧 / 入口路径,证据够硬或模型背书两条路,模型建议改写则留人,自动驳回可复活,先干跑再执行。待做——运行监控(成功率 / 步数 / 时长趋势、失败截图)、知识与密码本管理、按学校分组推送、后台多人账号;桌面端统一身份认证登录、按人过滤业务、按需取凭据;**跨人协办**:一个人的任务被别的角色的前置事项拦住时,向对应角色发协办请求,对方办完后通知并自动续跑。
11. ~~UI 往 Codex 形态靠~~(09-17 完成:三栏布局、任务列表与回放、确认 / 阻断 / 结果卡片、我来接管)。后续:嵌入式浏览器面板。
12. **登录配方支持统一认证**:`login.mode: sso | form | quick_login | manual`;允许域名含认证中心;有登录态直接过,没有则请用户完成一次登录后复用 profile 登录态。测试环境无 SSO,先做配置与提示词,接真实学校再验。绑定账号界面推迟到真遇到非 SSO 的个人账号系统。
13. ~~测试集~~(09-17 完成,见 PROGRESS D16):`npm test` 288 个用例 3.4 秒,覆盖 nlu 退路、状态判定、learned 规则与数据库生命周期、角色准入、凭据分组、阻断问句、执行结果判定、Key 兜底。`@pytest.mark.db` 的用例用一次性 `pytest_xxx` 学校隔离,PostgreSQL 不可达时自动跳过。写测试时挖出并修掉两个缺陷:关键词退路要求整段短语原样出现(模型不可用时真实说法全部误判为通用探索)、单字肯定词在句中任意位置命中(补充资料被当成同意先办前置事项)。
14. **桌面端对齐同事的 PenguinUniOS 形态(09-17 完成,见 PROGRESS D13 / D14)**(见 `docs/REFERENCE_PENGUINUNIOS.md`):已做——React/TS/Vite 渲染层、TS 主进程、`WebContentsView` 内嵌浏览器(browser-use 经 CDP 连 Electron 自带 Chromium,界面页走 `app://ui` 协议、主机名 ui 进白名单,不展示的视图停到窗口边缘外留 2px 在窗内而不能隐藏,否则截图挂死)、任务卡 / 确认卡 / 状态词汇对齐、Lucide 与其视觉规范、uv + PyInstaller + electron-builder 打包链(`npm run package:win` → `release/win-unpacked`,后端冻结成 exe 放 resources/backend,userData 放 .env / data / profiles)。待做——NSIS 安装器 / 图标 / 签名、Pydantic 契约生成 TS。已决:数据库已切 PostgreSQL(09-17 完成迁移);仓库独立;打包态只支持内嵌浏览器,不带 Chromium。
## 9. 实测基线
| 场景 | 配置 | 结果 |
|---|---|---|
| 学生端只读探索 | 无 learned,3 次 | 1 成功 2 自评失败(产物齐全),31 步 / 181 秒 |
| 学生端只读探索 | 修判定 + learned,2 次 | 2/2 成功,23 步 / 99 秒 |
| 辅导员查课日志填报到确认 | 首次 | 47 步 / 372 秒 |
| 辅导员查课日志填报到确认 | 控件技巧 + 每步 6 动作,干跑 2 次 | 2/2,18 步 / 158 秒 |
| 暂停续跑 | 强制先 ask_user | 15 秒暂停,回答后 21 秒完成,同一会话 |
| 辅导员谈心谈话填报到确认 | 首次即带 learned,干跑 1 次 | 15 步 / 81 秒;一句话四个必填全抽出,无追问 |
| 辅导员谈心谈话填报到确认 | 第二次干跑 | 11 步 / 61 秒 |
| 查课日志真实提交 | 用户操作,2 次 | 均「填报成功」,状态 submitted |
| 前置阻断 other_role(学生请假) | 真实环境 | 25 秒识别拦截、标准化提问,取消后 blocked |
| 前置阻断 self(模拟) | 临时业务 | 切子目标并回到原目标完成,约 35 秒 |
| 两步串行(模拟,同系统) | 临时业务 | 54 秒,一个会话,第二步使用第一步产物 |
| 提交后跟踪回查 | 真实提交的查课日志 | 立即回查 73 秒读到「未审批」+ 审批轨迹;调度自动回查 77 秒完成并排期 |
## 10. 目录与关键文件
学校项目根目录为 `browser_use/school-assistant/`(2026-10-28 从工作区根目录整体移入,含独立 Git 历史)。同级 `browser_use/assistant/` 为独立 AI 获客项目。以下路径均相对学校项目根目录。
```
backend/nlu.py 意图识别与字段抽取
backend/orchestrator/orchestrator.py 状态机、路由、prompt、暂停续跑、沉淀触发
backend/browser/runner.py browser-use 执行器、会话保活、每步截图
backend/browser/tools.py ask_user / save_artifact
backend/learning.py learned 草稿生成
backend/security.py 密码本加解密(Fernet,ACCOUNT_ENC_KEY)
backend/db/schema.sql MySQL 表结构(含 school_id)
backend/db/database.py 数据层与旧库迁移
knowledge/xuegong_demo.yaml 出厂业务定义 + 基线 learned
scripts/eval_run.py 评测(默认干跑)
electron/main.js 后端唯一所有者、旧后端接管
renderer/app.js 三栏 UI:任务列表与回放、对话与卡片、状态 / 截图 / 时间线 / 沉淀草稿;无 Electron 时回退 fetch 便于自测
docs/PROGRESS.md 里程碑与实测
```