204 lines
14 KiB
Markdown
204 lines
14 KiB
Markdown
# 智能办事助手(School Assistant)
|
||
|
||
跨系统智能办事助手 MVP:Electron 聊天 + Python 编排 + Browser Use 自主操作网页。
|
||
|
||
本项目位于 `browser_use/school-assistant/`,与同级的 AI 获客助手 `assistant/` 分开维护;以下命令均在本项目根目录执行。
|
||
|
||
产品形态类似 Codex 桌面端:左侧任务列表与历史(点开可回放过程),中间对话、任务卡与确认卡片,右侧是内嵌的浏览器面板——Agent 就在这个面板里操作学校系统,可以展开 / 收起,需要用户决定时停下来问,回答后在同一页面里继续,随时可以「我来接管」。
|
||
|
||
## 当前能力(D13,2026-09-17)
|
||
|
||
- 一句话发起任务:先识别业务并抽取字段(DeepSeek / 千问一次调用),缺资料时对话追问,再按业务角色登录执行
|
||
- Agent 自主登录、找入口、填表、提交前逐项确认(演示业务:辅导员每月查课日志填报;通用探索兜底)
|
||
- `ask_user` 暂停机制:不受 browser-use 步骤超时限制,浏览器会话保活,用户回答后续跑同一个 Agent
|
||
- 系统账号:`system_accounts` 表按学生 + 系统存加密密码,运行时通过 `sensitive_data` 自动填写,LLM 看不到明文
|
||
- 可观测:每步截图 `data/tasks/{id}/steps/`、进度 `progress.md`、精简执行历史存 `run_logs`
|
||
- 自动沉淀:任务成功后提炼经验草稿(带类别),按内容判重归并;控件技巧 / 入口路径类被多个任务观察到自动生效,系统规则类由团队在运维模式下采纳。用户端无感。YAML 只是出厂基线,`GET /learned/export` 可导出交付
|
||
- 运维模式:`.env` `OPERATOR_MODE=true` 才显示「沉淀草稿」面板,交付学校默认关闭
|
||
- 内嵌浏览器:每个任务一个 `WebContentsView` 挂在应用右侧面板里(独立登录态分区、域名白名单),browser-use 经 Electron 的 CDP 端口直接操作它;Agent 开始操作或停下来问你时自动展开,可随时收起;运行中可「我来接管」(Agent 模式下键盘输入会被拦住并提示先接管),操作完交还给 Agent 继续。单独跑后端(无 Electron)时退回独立 Chrome 停靠模式
|
||
- 提交后跟踪:已提交的任务定时回查审批状态,通过 / 驳回时在对话里提示,可「立即回查」或「停止回查」
|
||
- 前置阻断:系统提示「先办另一件事」时,同角色能办的问你要不要顺带办完再回来,需要别人处理的告诉你找谁,任务进入「被前置事项拦住」等你处理好再继续
|
||
- 多校隔离:所有学校数据带 `school_id`,实例用 `.env` 的 `SCHOOL_ID` 指定所属学校
|
||
- 评测脚本 `scripts/eval_run.py`:同一任务连跑 N 次统计成功率 / 步数 / 时长,默认干跑(到确认即取消)。查课日志填报实测到确认 18 步 / 158 秒
|
||
- Electron 是后端的唯一所有者:启动时自动关掉端口上残留的旧后端,退出时关掉自己的
|
||
|
||
## 环境要求
|
||
|
||
- **Node.js** 20.19+(Vite 8 / Electron 33 构建链)
|
||
- **Python** 3.11+
|
||
- **Docker Desktop**(运行 PostgreSQL 17)
|
||
- **DeepSeek 或千问 API Key**(默认 DeepSeek)
|
||
|
||
## 快速开始
|
||
|
||
### 0. 启动 PostgreSQL(Docker)
|
||
|
||
```powershell
|
||
cd D:\qiekj\browser_use\school-assistant
|
||
docker compose up -d
|
||
```
|
||
|
||
数据落在 `127.0.0.1:15433` 的 PostgreSQL 库 `school_assistant`,账号见 `docker-compose.yml`;后端启动时自动建表并同步 `knowledge/*.yaml`。2026-09-17 从 MySQL 切换而来,旧数据用 `scripts/migrate_mysql_to_pg.py` 迁移。`.env` 的 `SCHOOL_ID` 决定本实例属于哪所学校,学校之间数据互不可见。
|
||
|
||
### 1. 安装 Python 依赖
|
||
|
||
推荐用 uv(自动建 Python 3.12 的 `.venv`,按 `backend/requirements.lock` 精确同步,含打包用的 PyInstaller):
|
||
|
||
```powershell
|
||
cd D:\qiekj\browser_use\school-assistant
|
||
npm install
|
||
npm run backend:setup
|
||
```
|
||
|
||
没有 uv 也行:脚本会用系统 Python 的 pip 把 uv 装进项目的 `native/build/` 里,不碰系统环境。手工方式:`python -m venv .venv && .\.venv\Scripts\pip install -r backend\requirements.txt`。不需要 `playwright install`,浏览器就是 Electron 自带的 Chromium。
|
||
|
||
### 2. 配置 API Key
|
||
|
||
复制 `.env.example` 为 `.env`,填入:
|
||
|
||
```env
|
||
LLM_PROVIDER=deepseek
|
||
DEEPSEEK_API_KEY=sk-你的key
|
||
LLM_MODEL=deepseek-chat
|
||
LLM_BASE_URL=https://api.deepseek.com/v1
|
||
```
|
||
|
||
> 也可切换 `LLM_PROVIDER=qwen` 并使用 `qwen-vl-max`。
|
||
> 可选再配一把兜底 Key `DEEPSEEK_API_KEY_FALLBACK`(千问为 `DASHSCOPE_API_KEY_FALLBACK`):主 Key 返回 402 余额不足时,识别、沉淀和 Agent 执行中的下一次调用都自动切到兜底 Key,任务不中断;之后每 `LLM_PRIMARY_RETRY_HOURS`(默认 1)小时重试主 Key,充值后自动恢复。侧栏状态会显示「兜底 Key」。
|
||
|
||
### 3. 安装并启动 Electron
|
||
|
||
```powershell
|
||
npm install
|
||
npm start
|
||
```
|
||
|
||
`npm start` 先构建(esbuild 打主进程 / preload,Vite 打 React 渲染层,产物在 `dist/`),再启动 Electron;Electron 自己拉起 Python 后端(用 `.venv`),并在启动前关闭端口上残留的旧后端。发送任务后右侧**浏览器面板**自动展开,Agent 在里面操作;停下来提问时,点卡片按钮或直接在聊天框回答即可。只做类型检查用 `npm run check`。
|
||
|
||
### 4. 录入系统账号(可选)
|
||
|
||
密码本按「人 × 系统」存账号并记录该账号的角色;业务要求的角色和账号角色不一致时直接告知办不了。有账号时 Agent 自动登录(多系统时凭据按域名分组,模型看不到明文),没有则按 `knowledge/*.yaml` 里 `login.fallback_mode` 处理(测试环境用快捷登录按钮)。密码用 `.env` 的 `ACCOUNT_ENC_KEY` 加密存储:
|
||
|
||
```powershell
|
||
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8788/api/accounts -ContentType "application/json; charset=utf-8" -Body (@{ student_id = "default"; system_id = "xgxt"; role = "辅导员"; username = "工号"; password = "密码" } | ConvertTo-Json)
|
||
```
|
||
|
||
### 5. 跑测试
|
||
|
||
```powershell
|
||
npm test
|
||
```
|
||
|
||
288 个用例约 3.4 秒。其中 15 个要连本机 PostgreSQL(用一次性的测试学校隔离,跑完自动清理,不会碰到正式数据),数据库没起时自动跳过;`npm run test:fast` 直接跳过它们。覆盖意图识别与退路、状态判定、经验沉淀的判重与自动晋升、角色准入、凭据按域名分组、前置阻断问句、执行结果判定、模型 Key 兜底。
|
||
|
||
### 6. 评测成功率
|
||
|
||
```powershell
|
||
npm run eval -- --n 3 --supplement "课程是高等数学,授课教师是张老师"
|
||
```
|
||
|
||
默认干跑:走到「提交前确认」自动回复取消,不会真提交;加 `--submit` 才会确认提交。`--supplement` 是任务追问时自动补充的话。结果写入 `data/eval/*.jsonl`。
|
||
|
||
## 后台审核台(中心服务端)
|
||
|
||
桌面端办完事会自动沉淀经验草稿。草稿要不要生效,控件技巧和入口路径类被两个任务观察到就自动生效,系统规则类必须人审。人审就在这个后台做。
|
||
|
||
```powershell
|
||
npm run hub:build # 构建界面
|
||
npm run hub # 启动,打开 http://127.0.0.1:8801/
|
||
```
|
||
|
||
在「学校」页给每所学校生成一串上报凭证(只显示一次),写进那台机器的 `.env`:
|
||
|
||
```env
|
||
HUB_URL=http://中心地址:8801
|
||
HUB_TOKEN=生成的凭证
|
||
```
|
||
|
||
之后那台桌面端会在每次沉淀后自动上报,并定时(默认 15 分钟)拉回审核结果。没配这两项就是单机模式,本地照常沉淀和自动晋升。
|
||
|
||
审核台上每条草稿显示它被几所学校观察到过、累计观察多少次,可以采纳、驳回、改写后采纳,也可以批量处理。**跨校观察是最强的可信信号**:同一条知识被多所学校各自撞见,说明它不是某校特例。采纳时选发布范围:只回来源学校,还是推给所有学校。点「预审」会先让模型给建议和理由,建议只是建议,不会自动改状态。
|
||
|
||
驳回也会推回各校:本地可能已经自动晋升过一条错知识,中心驳回后桌面端会停止注入它。
|
||
|
||
### 智能采纳
|
||
|
||
队列一次几十条时,「智能采纳」页可以把能自动定的先处理掉。**只有控件技巧和入口路径可能被自动采纳,系统规则和其他类永远留给人**,因为一条错的规则会让 Agent 以后一直绕开正确操作。
|
||
|
||
自动采纳有两条路:跨校观察够或单校观察够(证据硬);或者证据薄但模型读过内容后明确说可复用(这种只发回来源学校)。模型如果建议改写,就不会自动采纳原文,留给人确认,理由里直接给出改写建议。
|
||
|
||
先点「先看会动哪些」干跑,列出每条的处置和理由,确认后再执行。自动处理过的条目标记来源为 auto,队列里勾「只看自动处理的」可复查,审错了点「退回待审」就会推回各校。
|
||
|
||
**自动驳回只是暂缓**:那条知识再被观察到会退回队列重新排队。只有人工驳回是终局。这样机器误判一次不会永久吃掉一条知识。
|
||
|
||
实测 43 条队列,配了模型 Key 的情况下自动处理掉 21 条(19 采纳、2 驳回,两条驳回都是带任务编号的一次性链接),剩 22 条全是按策略必须人审的。
|
||
|
||
## 打包(Windows)
|
||
|
||
```powershell
|
||
npm run package:win
|
||
```
|
||
|
||
依次执行:构建桌面端(esbuild + Vite → `dist/`)→ 用 `.venv` 的 PyInstaller 把后端冻结成 onedir(`native/release/school-assistant-backend/`,约 100 MB,脚本会跑一遍 `--selfcheck` 并在本机 PostgreSQL 可达时真起一次服务验收)→ electron-builder 产出 `release/win-unpacked/SchoolAssistant.exe`(整目录约 400 MB)。冻结后端放在 `resources/backend`,知识 YAML 放在 `resources/knowledge`。
|
||
|
||
运行安装包形态时,可写目录是 `%APPDATA%\school-assistant\`:首次启动会把 `.env` 模板复制过去,部署方在里面填模型 Key、PostgreSQL 连接、`SCHOOL_ID`、`ACCOUNT_ENC_KEY`;任务进度和登录态分别在其下的 `data/`、`profiles/`。目前只出免安装目录(`dir`),NSIS 安装器、图标、签名待做。
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
school-assistant/
|
||
├── electron/ # Electron 主进程(TypeScript):窗口与 app:// 协议、WebContentsView 宿主、给后端的控制接口、后端进程管理、preload 桥
|
||
├── renderer/ # React + TypeScript + Vite 聊天界面(Sidebar / TopBar / Feed 卡片 / TaskCard / Composer / BrowserPanel / Settings)
|
||
├── dist/ # 构建产物(main.cjs、preload.cjs、renderer/、env.example),不入库
|
||
├── native/ # 打包中间产物:release/school-assistant-backend(PyInstaller onedir)、build/(smoke 结果、uv 引导),不入库
|
||
├── release/ # electron-builder 产出(win-unpacked/SchoolAssistant.exe),不入库
|
||
├── backend/ # FastAPI + 编排 + Browser Use(有 CDP_URL 时走内嵌模式,否则独立 Chrome);requirements.txt / requirements.lock
|
||
├── knowledge/ # 业务配置 YAML(系统、登录方式、步骤、规则、learned 沉淀)
|
||
├── data/ # 任务 progress.md、每步截图、评测结果(运行时生成)
|
||
├── docker-compose.yml # PostgreSQL 17(任务 / 知识 / learned / 密码本 / 设置)
|
||
├── profiles/ # 浏览器登录态(按 student_id 一份,登录一次长期有效)
|
||
├── server/ # 中心服务端(FastAPI):收各校草稿、审核队列、发布;独立库 school_assistant_hub
|
||
├── console/ # 后台审核台界面(React/TS/Vite),构建后由中心服务端直接伺候
|
||
├── shared/ # 桌面端与中心共用的规则(经验判重与归类)
|
||
├── tests/ # pytest 测试集(npm test);@pytest.mark.db 的用例需要 PostgreSQL
|
||
├── scripts/ # eval_run.py、打包与环境脚本
|
||
└── docs/PROGRESS.md # 开发里程碑进度
|
||
```
|
||
|
||
## 进度文档
|
||
|
||
- **开发进度**:`docs/PROGRESS.md`
|
||
- **任务进度**:每个任务自动写入 `data/tasks/{task_id}/progress.md`
|
||
|
||
桌面端右侧可预览并「打开 progress.md」。
|
||
|
||
## API 调试
|
||
|
||
后端 API 端口在 `.env` 的 `API_PORT`(默认 **8788**)。文档:http://127.0.0.1:8788/docs
|
||
|
||
```powershell
|
||
# 单独启动后端
|
||
python backend/main.py
|
||
```
|
||
|
||
## 演示业务与探索发现
|
||
|
||
**当前演示业务**:辅导员工作日志填报(查课)和每周谈心谈话记录,同一入口下两个业务,由意图识别路由。谈心谈话示例:「帮我记一条谈心谈话,今天和计科2301班的李思涵聊了学业压力,内容是……」。说一句「帮我填今天的查课日志,班级计科2301班,听课记录还好」,助手会追问课程名称和授课教师,然后以辅导员身份进入「辅导员工作日志 → 日志填报 → 每月查课要求 → 新建填报」填表,逐项列出后等你确认再点「提交填报」。业务定义和探索沉淀都在 `knowledge/xuegong_demo.yaml` 的 `businesses` 与 `learned` 段。
|
||
|
||
### 学生端探索发现(2026-09-16)
|
||
|
||
测试环境学生端**没有「留校申请」模块**。门户 8 个一级模块:学生管理、综合测评、评奖评优、违纪处分、资助管理、请销假、节假日离返校、辅导员评议。最接近的是「节假日离返校 → 我的报备」(默认在校,离校才需报备)。学生端可提交的申请表单是「请销假 → 发起请假」,建议作为填表里程碑的目标。这些已沉淀在 `knowledge/xuegong_demo.yaml` 的 `learned` 段,运行时注入为「已知信息」。
|
||
|
||
## 测试学工系统
|
||
|
||
默认业务:`knowledge/xuegong_demo.yaml`
|
||
入口:https://school.miaopinai.com:29800/xuegong/
|
||
|
||
在聊天框输入办事目标即可,例如:
|
||
|
||
> 帮我填今天的查课日志,班级计科2301班,听课记录还好
|
||
|
||
## 下一步
|
||
|
||
见 `docs/PROGRESS.md`
|