acmer-coach 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +258 -0
- package/assets/extension/luogu/README.md +104 -0
- package/assets/extension/luogu/content.js +137 -0
- package/assets/extension/luogu/manifest.json +22 -0
- package/assets/knowledge/DEPENDS.yaml +391 -0
- package/assets/knowledge/LICENSE +41 -0
- package/assets/knowledge/MAP.yaml +2540 -0
- package/assets/knowledge/NODE_ENTRY.yaml +465 -0
- package/assets/knowledge/NODE_META.yaml +457 -0
- package/assets/knowledge/skilltree.html +1702 -0
- package/assets/oiwiki/LICENSE +49 -0
- package/assets/oiwiki/chunks.jsonl +2177 -0
- package/assets/oiwiki/node-pages.json +1208 -0
- package/assets/pool/LICENSE +83 -0
- package/assets/pool/problems.jsonl.gz +0 -0
- package/assets/rules/coach-rules.md +425 -0
- package/build-node-meta.mjs +98 -0
- package/coach/analyze_map.py +138 -0
- package/coach/build_map.py +230 -0
- package/coach/build_node_pages.py +105 -0
- package/coach/check_page_syntax.mjs +48 -0
- package/coach/coach-metrics.py +313 -0
- package/coach/export_oiwiki_chunks.py +100 -0
- package/coach/export_problem_pool.py +64 -0
- package/coach/extract_oiwiki.py +110 -0
- package/coach/oiwiki_tree.json +2987 -0
- package/coach/render_map.py +226 -0
- package/coach/render_skilltree.py +197 -0
- package/coach/serve_map.py +181 -0
- package/coach/skilltree_template.html +1702 -0
- package/coach/validate_map.py +184 -0
- package/coach/verify_drag.mjs +51 -0
- package/coach/verify_interact.mjs +82 -0
- package/coach/verify_serve.py +138 -0
- package/cordis.patch.yml +10 -0
- package/dev/parity-check.mjs +169 -0
- package/dev/run-all.sh +70 -0
- package/dev/setup-check.mjs +191 -0
- package/dev/w1-check.mjs +170 -0
- package/dev/wiki-check.mjs +100 -0
- package/dev.sh +142 -0
- package/docs//350/256/276/350/256/241/346/226/271/351/222/210.md +325 -0
- package/index.js +5820 -0
- package/lib/client.js +209 -0
- package/lib/setup.js +455 -0
- package/package.json +33 -0
- package/progress-cli.mjs +67 -0
- package/verify-shape.mjs +315 -0
- package/verify.mjs +3535 -0
package/dev.sh
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# acmer-coach 开发回路:同步 + 验收,一条命令。改完就跑它。
|
|
3
|
+
#
|
|
4
|
+
# 前提:插件以 `file:` 依赖装进了某个 profile。
|
|
5
|
+
# 默认 profile 是 web;要换就设 COACH_PROFILES(空格分隔,可多个)。
|
|
6
|
+
# 要换 dsh 家目录就设 DSH_HOME。
|
|
7
|
+
#
|
|
8
|
+
# 一个坑,两道保险:
|
|
9
|
+
# `file:` 依赖装出来的是**硬链接**。原地写两边同变,但改名式写入(Edit 工具、
|
|
10
|
+
# VS Code 原子保存、vim backupcopy=no)会**断开链接** —— 断开后两边分家,
|
|
11
|
+
# 而同步认不出分家、**不报错**。于是"改完 → 测试绿"可以一路绿到底却验的是
|
|
12
|
+
# 老代码,而且这个坑是静默的。
|
|
13
|
+
# 第一道:下面逐个文件比 inode / 内容,不一致就 cp 过去。
|
|
14
|
+
# 第二道:verify.mjs 第 0 关自己比源码和副本,不一致直接 exit 1。
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
BITSH="${SRC_DIR//\\//}" # Windows 上给 python/node 用的正斜杠形式
|
|
19
|
+
SRC_BASH="$BITSH"
|
|
20
|
+
DSH_HOME="${DSH_HOME:-$HOME/.dsh}"
|
|
21
|
+
DSH_HOME_BASH="${DSH_HOME//\\//}"
|
|
22
|
+
|
|
23
|
+
# ⚠️ 一份也不能漏。半同步的插件会让每一次「代码到底变没变」的判断都不可靠 ——
|
|
24
|
+
# 你会花整晚排查一个"明明改了却不生效"的问题,而它只是副本没跟上。
|
|
25
|
+
# 所以这里列**全部**要同步的 profile。
|
|
26
|
+
read -r -a PROFILES <<< "${COACH_PROFILES:-web}"
|
|
27
|
+
DSTS=()
|
|
28
|
+
for p in "${PROFILES[@]}"; do
|
|
29
|
+
DSTS+=("$DSH_HOME_BASH/profiles/$p/node_modules/acmer-coach")
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
# 要比**全部**文件,不只是 index.js —— 副本里任何一份落后的文件都会让
|
|
33
|
+
# "验的是这一份、跑的是那一份"这种分家看不见。
|
|
34
|
+
# ⚠️ dev.sh 自己也在列表里。它一度不在,于是副本里留着一份**旧版 dev.sh**:
|
|
35
|
+
# 从副本目录跑 `bash dev.sh` 走的是另一条路(`dsh plugin remove/add`,而那条
|
|
36
|
+
# 路不可靠),和这份的行为不一样。换句话说,**同一个命令,从哪个目录按回车
|
|
37
|
+
# 决定跑哪份脚本** —— 两份 dev.sh 就是两份真相,正是这份文件在防的那种坑。
|
|
38
|
+
SYNC_FILES='index.js package.json verify.mjs verify-shape.mjs progress-cli.mjs build-node-meta.mjs cordis.patch.yml README.md lib/client.js dev.sh'
|
|
39
|
+
STALE=''
|
|
40
|
+
for f in $SYNC_FILES; do
|
|
41
|
+
for d in "${DSTS[@]}"; do
|
|
42
|
+
[ -d "$d" ] || continue
|
|
43
|
+
diff -q "$SRC_BASH/$f" "$d/$f" >/dev/null 2>&1 || STALE="$STALE $f"
|
|
44
|
+
done
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
if [ -z "$STALE" ]; then
|
|
48
|
+
echo '· 副本已是最新,跳过同步'
|
|
49
|
+
else
|
|
50
|
+
# 直接 cp,**不走 `dsh plugin remove/add`** —— 那条路不可靠:pnpm 对
|
|
51
|
+
# `file:` 依赖可能只做复制,而且复制的是它 store 里的**缓存版本**,于是
|
|
52
|
+
# add 打印成功、副本却仍是旧的。更阴的是 9 个文件里只有被改名式写入动过的
|
|
53
|
+
# 那个会断链,其余仍是硬链接 —— "有的同步了有的没有",最难查的那种症状。
|
|
54
|
+
# dsh 读的就是这个路径下的文件,cp 过去立刻生效,也不依赖 pnpm 心情。
|
|
55
|
+
echo "· 副本落后($STALE ),同步中…"
|
|
56
|
+
for f in $SYNC_FILES; do
|
|
57
|
+
for d in "${DSTS[@]}"; do
|
|
58
|
+
[ -d "$d" ] || continue
|
|
59
|
+
# 硬链接还活着的(两边是**同一个 inode**)直接跳过:那种情况本来就
|
|
60
|
+
# "改一边两边同变",再 cp 一次是多余的,而且 `cp` 发现自己跟目标是同一
|
|
61
|
+
# 文件会**报错** —— `set -e` 之下这一下就把整个脚本打断了。
|
|
62
|
+
[ "$SRC_BASH/$f" -ef "$d/$f" ] && continue
|
|
63
|
+
mkdir -p "$d/$(dirname "$f")"
|
|
64
|
+
cp "$SRC_BASH/$f" "$d/$f"
|
|
65
|
+
done
|
|
66
|
+
done
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# 同步完**自动比一次哈希**:几份必须逐字节一致,不一致就报错退出。
|
|
70
|
+
# 某个副本静默落后过一次,而"以为同步了"正是那次排查卡住的地方 ——
|
|
71
|
+
# 别让它有第二次。不一致就别往下测,测的会是另一份代码。
|
|
72
|
+
FAIL_SYNC=''
|
|
73
|
+
for f in $SYNC_FILES; do
|
|
74
|
+
a=$(sha256sum "$SRC_BASH/$f" | cut -d' ' -f1)
|
|
75
|
+
for d in "${DSTS[@]}"; do
|
|
76
|
+
[ -d "$d" ] || continue
|
|
77
|
+
# `|| true`:副本缺文件时别让 set -e 把整个脚本掐在一条看不懂的错上
|
|
78
|
+
b=$(sha256sum "$d/$f" 2>/dev/null | cut -d' ' -f1 || true)
|
|
79
|
+
[ "$a" = "$b" ] || FAIL_SYNC="$FAIL_SYNC ${f}@$(basename "$(dirname "$(dirname "$d")")")"
|
|
80
|
+
done
|
|
81
|
+
done
|
|
82
|
+
if [ -n "$FAIL_SYNC" ]; then
|
|
83
|
+
echo "✗ 同步后哈希仍不一致:$FAIL_SYNC"
|
|
84
|
+
echo ' 别往下测 —— 测的会是另一份代码。'
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
echo '· 全部副本哈希一致'
|
|
88
|
+
|
|
89
|
+
# ── 自检的账本闸门 ────────────────────────────────────────────────
|
|
90
|
+
# 起因:verify.mjs 前面几节一度用的是**默认数据目录**(=真账本)。
|
|
91
|
+
# coach_assign 一开始落盘,一条"正常布置"就把幻影任务写进了真 PROGRESS.yaml
|
|
92
|
+
# —— 重启后教练会去追一个根本不存在的活,当场毁信任。
|
|
93
|
+
# 病因已修(那几节改跑临时副本),这条是**兜底闸门**:自检跑完比一次指纹,
|
|
94
|
+
# 一变就报错退出。这种事不靠自觉,靠闸门。
|
|
95
|
+
LEDGER_DIR="$DSH_HOME_BASH/knowledge"
|
|
96
|
+
LEDGERS='PROGRESS.yaml SCHEDULE.yaml'
|
|
97
|
+
ledger_fp() { (cd "$LEDGER_DIR" 2>/dev/null && sha256sum $LEDGERS 2>/dev/null || true); }
|
|
98
|
+
LEDGER_BEFORE=$(ledger_fp)
|
|
99
|
+
|
|
100
|
+
echo
|
|
101
|
+
# `|| RC=$?` 而不是裸跑:set -e 会在第一条命令失败时就掐掉整个脚本,
|
|
102
|
+
# 后面那段提醒就永远不打印 —— 而失败的时候恰恰最需要它。
|
|
103
|
+
node "$SRC_BASH/verify.mjs" || VERIFY_RC=$?
|
|
104
|
+
VERIFY_RC=${VERIFY_RC:-0}
|
|
105
|
+
|
|
106
|
+
echo
|
|
107
|
+
echo '── 全工具形状扫描(每个工具真跑一遍,返回值递归对 schema)──'
|
|
108
|
+
node "$SRC_BASH/verify-shape.mjs" || SHAPE_RC=$?
|
|
109
|
+
SHAPE_RC=${SHAPE_RC:-0}
|
|
110
|
+
|
|
111
|
+
LEDGER_AFTER=$(ledger_fp)
|
|
112
|
+
if [ "$LEDGER_BEFORE" != "$LEDGER_AFTER" ]; then
|
|
113
|
+
echo
|
|
114
|
+
echo '✗ **自检把真账本改了** —— 自检必须跑在临时副本上(COACH_DATA_DIR 指 mkdtemp):'
|
|
115
|
+
printf '%s\n' "$LEDGER_BEFORE" | sed 's/^/ 之前 /'
|
|
116
|
+
printf '%s\n' "$LEDGER_AFTER" | sed 's/^/ 之后 /'
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
echo '· 真账本一字未动'
|
|
120
|
+
|
|
121
|
+
# 提醒"活进程还挂着旧模块"。
|
|
122
|
+
# cordis 的 HMR 只重载配置,不重新 import 模块本体 —— ESM 模块在进程里只求值
|
|
123
|
+
# 一次,之后永远命中缓存。所以磁盘更新了,跑着的 dsh web 还是老版本,
|
|
124
|
+
# **不报错、不报警**。上面 verify.mjs 走的是全新 import,验不到这一层。
|
|
125
|
+
# 不自动重启:重启会打断正在用的会话,得用户自己点头。
|
|
126
|
+
#
|
|
127
|
+
# 探两个端口。3080 是 dsh 的默认值,但 Windows 的**保留端口区**会把它吃掉 ——
|
|
128
|
+
# 报的是 EACCES 不是 EADDRINUSE,因为压根不是"被占用",是系统不让绑。
|
|
129
|
+
# 撞上时得靠 `--port` 换一个,所以这里多探一个 8181:只探 3080 的话,
|
|
130
|
+
# 换端口之后这条提醒会**静默失效**,而它失效的后果是
|
|
131
|
+
# "改完代码以为生效了,其实跑的还是旧模块"。
|
|
132
|
+
DSH_LIVE=''
|
|
133
|
+
for p in 3080 8181; do
|
|
134
|
+
if curl -s -m 2 -o /dev/null "http://127.0.0.1:$p/" 2>/dev/null; then DSH_LIVE="$p"; break; fi
|
|
135
|
+
done
|
|
136
|
+
if [ -n "$DSH_LIVE" ]; then
|
|
137
|
+
echo
|
|
138
|
+
echo "⚠ dsh web 正在跑($DSH_LIVE 有响应),它加载的仍是**旧模块**。"
|
|
139
|
+
echo ' 要验「工具挂进教练的清单里」,得先重启 dsh。'
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
exit $(( VERIFY_RC + SHAPE_RC ))
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# 设计方针
|
|
2
|
+
|
|
3
|
+
> 这份文档回答「**这个教练为什么这么设计**」,不回答「先做哪个功能」。
|
|
4
|
+
> 想看怎么用,去 README;想看代码长什么样,去读源码。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、一句话命题
|
|
9
|
+
|
|
10
|
+
> **教练的价值不是知识,是位置。**
|
|
11
|
+
>
|
|
12
|
+
> 它站在局外、手里有地图、且不投入自尊。
|
|
13
|
+
> 在局里的人看不见河的全貌 —— 这跟聪明程度无关,是位置问题。
|
|
14
|
+
|
|
15
|
+
由此推论:**初学者没法自己给自己当老师。**
|
|
16
|
+
排优先级需要「什么重要、什么先什么后、哪里是坑」——
|
|
17
|
+
而这些恰好是初学者没有的知识。**这是循环依赖,必须由外部打破。**
|
|
18
|
+
|
|
19
|
+
市面上不缺讲解算法的资料,也不缺题。缺的是**替你排顺序**的那个人。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 二、为什么「AI 助手」式的教练会失败(五条诊断)
|
|
24
|
+
|
|
25
|
+
在动手之前,先把这一类系统的常见死法列清楚。以下五条来自一个已退役的
|
|
26
|
+
旧实现(SQL 分析 + LLM 转述那套),每一条都有具体证据。
|
|
27
|
+
|
|
28
|
+
| # | 诊断 | 表现 |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| 1 | **分工反了** —— 程序在做判断,LLM 在做转述 | 用 SQL 判弱点、用规则选平台,LLM 只把 JSON 翻译成人话。这就是它「像个网站」的根因 |
|
|
31
|
+
| 2 | **单位错了** —— 系统数「题」,教练该管「知识点」 | 输出是「DP 44%」「搜索 20%」。但「DP」不是知识点,是一本书 —— 那是学科分类,不是考纲 |
|
|
32
|
+
| 3 | **掌握度靠推断,不靠检测** | 有一道题提交 11 次(9 次 TLE)才 AC。历史数据能说「花了很久」,**说不出「卡在哪」** |
|
|
33
|
+
| 4 | **闭环是虚构的** | 弱点日志全项目只写不读;夜间任务又把标签整体重写回来 —— 闭环活不过一天 |
|
|
34
|
+
| 5 | **自我报告 ≠ 现实** | 定时任务报「上次运行成功」,而消息一条没送到。同一个病:统计说 AC 88 次,另一个口径按 82 算 |
|
|
35
|
+
|
|
36
|
+
**共同点**:每一条都是「系统以为自己在工作,实际没有」。
|
|
37
|
+
|
|
38
|
+
这五条决定了下面所有设计 —— 尤其是「判断归谁」和「什么东西可以信」。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 三、设计原则(八条)
|
|
43
|
+
|
|
44
|
+
### 原则 1:输出是「位置」,不是「池子」
|
|
45
|
+
|
|
46
|
+
> 网站给你一个池子(可无限)。教练给你一个位置(必须唯一)。
|
|
47
|
+
|
|
48
|
+
- 每日指令**只能给一个动作**,禁止候选列表、禁止「你可以考虑」
|
|
49
|
+
- 每条指令必须含:**做什么 + 时间盒 + 交付物**
|
|
50
|
+
- 检验:看到回答后能**直接开始做**,不需要再思考
|
|
51
|
+
|
|
52
|
+
这条是整套设计的分水岭。一旦给出候选列表让人自己挑,这个系统就退回成一个网站了
|
|
53
|
+
—— 那它就**没有存在的理由**。
|
|
54
|
+
|
|
55
|
+
### 原则 2:判断归 LLM,记录归程序
|
|
56
|
+
|
|
57
|
+
| 谁 | 干什么 |
|
|
58
|
+
|---|---|
|
|
59
|
+
| **程序** | 记录事实(做了没、提交几次、耗时、题库检索、抓题面) |
|
|
60
|
+
| **LLM** | 读代码判因、出检测卷、决定推进还是复习、排优先级 |
|
|
61
|
+
|
|
62
|
+
**把判断从程序手里拿回来。**
|
|
63
|
+
|
|
64
|
+
具体的落地:工具**报事实、给出口,但不下结论**。「该不该复习」不由 SQL 打分决定,
|
|
65
|
+
而是把相关事实摆到教练眼前(哪些节点放得最久、做过几题、独立几次),让它判断。
|
|
66
|
+
工具只拦**机械上做不到**的事(前置没满足、块和块叠在一起)。
|
|
67
|
+
|
|
68
|
+
> 推论:**别指望工具把教练的活全干了。** 能机械检查的只是一部分,
|
|
69
|
+
> 剩下的写不进代码。(这条后来被证实了 —— 见原则 5 的边界。)
|
|
70
|
+
|
|
71
|
+
### 原则 3:掌握靠检测,不靠推断
|
|
72
|
+
|
|
73
|
+
| 项 | 做法 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| 标准 | **预先设定**(不是事后按表现调) |
|
|
76
|
+
| 单元 | 单元化,每单元一次形成性检测 |
|
|
77
|
+
| 不达标 | **不是「重学」,是换一种教法** → 重测平行卷 |
|
|
78
|
+
| 达标 | 才推进;不达标不推进 |
|
|
79
|
+
| 参照系 | **自己的目标**,不是别人 |
|
|
80
|
+
|
|
81
|
+
**关键:「对了几题」不该是主指标** —— 题目难度是出卷人控制的,可以操纵。
|
|
82
|
+
扛得住的指标是:**① 独立(无题解无求助)② 限时 ③ 能讲清**。
|
|
83
|
+
|
|
84
|
+
所以进度有**四级**,而且「自评」和「检测」**必须分开**:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
不写 = 未学
|
|
88
|
+
studying = 在学 开了头、还没达标。跨天是常态
|
|
89
|
+
learned = 学过 自评学过,没做检测
|
|
90
|
+
verified = 已验证 过了章节检测,有证据
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
把 learned 和 verified 合成一个「会/不会」,就等于退回原则 2 要治的那个病。
|
|
94
|
+
`verified` 因此**手标不了** —— 它不是靠自觉,是枚举里就没有这个选项。
|
|
95
|
+
|
|
96
|
+
### 原则 4:单一真相源,视图必须生成
|
|
97
|
+
|
|
98
|
+
- 地图只有**一份**(YAML),进度只有**一份**
|
|
99
|
+
- **任何副本都是 bug**(吃过亏:两份标签映射文件导致排序漂移)
|
|
100
|
+
- **视图(网站 / 图)必须由脚本从真相生成,不许手写**
|
|
101
|
+
|
|
102
|
+
推论:工具里同一个查询**不许写两遍**。规范版只有一个,其余都是调用方。
|
|
103
|
+
这条在测试里有专门的关卡盯着 —— 同一个 SQL 在源码里出现两次就是两份真相。
|
|
104
|
+
|
|
105
|
+
### 原则 5:不靠自觉,靠结构强制
|
|
106
|
+
|
|
107
|
+
> 写在文档里的规则是**建议**,写在代码里的规则是**法律**。
|
|
108
|
+
|
|
109
|
+
「今天说了明天又忘」不是记忆问题,是**强制力问题**。治法见第五节。
|
|
110
|
+
|
|
111
|
+
**但这条有边界,得说清楚**:结构只能强制**机械可判**的那一半。
|
|
112
|
+
「新知识点必须先讲」可以做成闸门(没带 `teachMinutes` 就拒绝),
|
|
113
|
+
因为「讲没讲过」是可判的;而「这块该不该复习」判不了,只能靠教练的判断。
|
|
114
|
+
**别把后者也硬做成规则** —— 那会变成一个用错误数据下命令的系统。
|
|
115
|
+
|
|
116
|
+
### 原则 6:命名是终点,不是入口(讲题规范)
|
|
117
|
+
|
|
118
|
+
| 段 | 内容 | 要求 |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| 1 **推导** | 从题目条件出发,一步步推到做法,**含走过的死路** | 不跳步;不用未解释的术语 |
|
|
121
|
+
| 2 **命名** | 推完再给这个结构/技术命名 | **禁用「这题是 X 算法」开头** |
|
|
122
|
+
| 3 **扩展** | 变体、邻居结构、边界 | 孤立点记不住,连成网的才记得住 |
|
|
123
|
+
|
|
124
|
+
**语言标准**:`越简单的语言 + 越完备的逻辑就越好`。
|
|
125
|
+
两者同时成立,不是二选一 —— 简单但跳步 = 听不懂;完备但晦涩 = 看不懂。
|
|
126
|
+
检验:**一个没学过这算法的人,能否复现这条推理链。**
|
|
127
|
+
|
|
128
|
+
**交互禁忌**:禁止苏格拉底式连环提问。提问要**稀疏**,只在最关键那一步问一个。
|
|
129
|
+
|
|
130
|
+
**节奏归他,不归你**:一段 = 一个自然步骤,讲完停一下留插话的口子。
|
|
131
|
+
三段式是**结构**,不是「一条消息发完」。
|
|
132
|
+
|
|
133
|
+
### 原则 7:优先级 = 依赖(硬)× 权重(软)× 缺口(变)
|
|
134
|
+
|
|
135
|
+
| 维度 | 性质 | 用法 |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| **依赖关系** | **不变**(不会线段树就学不了树套树,是逻辑) | **先开这个,且单独开一遍** |
|
|
138
|
+
| 赛场权重 | 会变,且数据源互相打架 | 第二遍叠加 |
|
|
139
|
+
| 个人缺口 | 会变 | 第三遍标位置 |
|
|
140
|
+
|
|
141
|
+
**分三遍画,一遍一个维度。** 第一遍绝不掺权重 ——
|
|
142
|
+
一旦开始想「这个重要不重要」,就会把它排到前面,而依赖会被画错。
|
|
143
|
+
|
|
144
|
+
节点上的两条轴就是这么来的:
|
|
145
|
+
|
|
146
|
+
- `tier`(重不重要)—— 来自人工标注,软的
|
|
147
|
+
- `entry`(学不学得起)—— 来自题池 p25,硬的
|
|
148
|
+
|
|
149
|
+
**两者缺一不可**:地图只会说「**允许**学什么」,不会说「**合适**学什么」。
|
|
150
|
+
后者要看他的当前水平 —— 那是教练的判断。
|
|
151
|
+
|
|
152
|
+
### 原则 8:地图要串成网,不是列成表
|
|
153
|
+
|
|
154
|
+
每个结构必须标:**邻居 / 变体 / 边界**。
|
|
155
|
+
|
|
156
|
+
看到过一个结构之后,**价值不是省掉推导,是扩充库存** ——
|
|
157
|
+
下次见到同族的能认出来。
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 四、架构
|
|
162
|
+
|
|
163
|
+
### 4.1 数据层(单一真相源)
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
MAP.yaml 地图:节点 + 依赖边 + 两条轴(tier / entry)
|
|
167
|
+
DEPENDS.yaml 前置关系的**唯一真相源**(人维护,MAP 由它生成)
|
|
168
|
+
PROGRESS.yaml 进度:游标 + 四级状态 + 检测记录 + 判因台账 + 押着的动作
|
|
169
|
+
SCHEDULE.yaml 日程:忙事 + 排的块 + 实际用时
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**为什么时间要单独一份**:课表会随时改 —— 考试周、临时有事、假期。
|
|
173
|
+
**时间是变量,不是静态输入。** 这决定了「今天做什么」其实是**排程**,不是选点:
|
|
174
|
+
|
|
175
|
+
| 情况 | 计划怎么做 |
|
|
176
|
+
|---|---|
|
|
177
|
+
| 容量缩小(考试周) | **只做复习,不推进** —— 保「不退步」,砍「新内容」 |
|
|
178
|
+
| 容量扩大(假期) | 加速推进,但**不线性加量**(会疲) |
|
|
179
|
+
| 临时打断 | 那几天重排,不是简单堆到后面 |
|
|
180
|
+
|
|
181
|
+
**硬规则:容量变化时重算优先级,但游标只前进不倒退。**
|
|
182
|
+
|
|
183
|
+
### 4.1.1 块结构:一个块 = 一道题的完整循环
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
├─ 40min 自己做(限时,到点必须停) ← 止损点就在这里
|
|
187
|
+
├─ 10min 解决遗留
|
|
188
|
+
├─ 10min 重写
|
|
189
|
+
└──────── 入复习队列
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**这解决了三个问题**:
|
|
193
|
+
|
|
194
|
+
1. **止损点有了** —— 40 分钟到点必须停。治「卡题恋战」靠的是表上写着下一段,
|
|
195
|
+
不是意志力
|
|
196
|
+
2. **复习队列有了自动入口** —— 做完就入队,不靠记得
|
|
197
|
+
3. **「听讲」和「重写」被排进时间表** —— 否则它们永远是"有空再做",实际等于不做
|
|
198
|
+
|
|
199
|
+
**那 20 分钟分两种情形**:
|
|
200
|
+
|
|
201
|
+
| 情况 | 干什么 |
|
|
202
|
+
|---|---|
|
|
203
|
+
| 限时内**独立做出来** | 复盘思路 + 一句话总结(**不重写**) |
|
|
204
|
+
| **没做出来 / 看了题解** | 教练讲 + 重写 |
|
|
205
|
+
|
|
206
|
+
**附带数据价值**:块结构天然产出「独立做出 vs 听了讲才会」这个信号 ——
|
|
207
|
+
这是掌握度最直接的证据,也是 `coach_log` 里那个 `independent` 字段的来历。
|
|
208
|
+
|
|
209
|
+
### 4.1.2 排程规则
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
块长 = 题目循环 60min / 复习一题 25min / 章节检测 90min(3 题)
|
|
213
|
+
填法 = 按地图依赖顺序,从游标往后填
|
|
214
|
+
止损 = 块的时长是硬边界
|
|
215
|
+
记录 = 计划时长 vs 实际时长 → 几周后能知道真实做题速度
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**⚠️ 没有「容量 = 空闲 × 0.75」这条了。** 早期版本有这个硬校验,
|
|
219
|
+
后来**撤掉了**:排多排少是教学判断,工具只该拦物理上不可能的
|
|
220
|
+
(压着忙事、块叠块、出网格)。排太满的后果让「计划 vs 实际」事后说话 ——
|
|
221
|
+
那比一个写死的系数更诚实。
|
|
222
|
+
|
|
223
|
+
### 4.2 逻辑层
|
|
224
|
+
|
|
225
|
+
| 部件 | 输入 | 输出 |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| 每日指令 | MAP + PROGRESS | **一个动作**(含时间盒、交付物) |
|
|
228
|
+
| 章节检测 | 当前节点 | 抽题 + 限时 + 判因 + 过/不过 |
|
|
229
|
+
| 判因 | 代码 + 提交记录 | 「不会 / 不熟 / 不认真」三分类 |
|
|
230
|
+
| 复习决策 | 各节点掌握时间 | 复习 or 推进,带理由 |
|
|
231
|
+
|
|
232
|
+
**判因的三段式**:① 错在哪 → ② 为什么会这么错 → ③ **下次怎么判断**。
|
|
233
|
+
第三段是重点 —— 只写到 ①② 等于告诉他"你错了",他还是不知道下次怎么办。
|
|
234
|
+
|
|
235
|
+
而且每条判因**必须引用代码原文**,工具当场校验那段原文真在代码里。
|
|
236
|
+
抄不出来会被拒绝。这和「指令必须带节点 id」是同一个把戏:
|
|
237
|
+
**让含糊的话物理上说不出口。**
|
|
238
|
+
|
|
239
|
+
### 4.3 形态:一个插件,三合一
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
插件
|
|
243
|
+
├── host 端 工具(能调)+ 钩子(强制)
|
|
244
|
+
├── web 端 进度面板(读同一份数据渲染)
|
|
245
|
+
└── 数据 YAML,唯一真相源
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**一个插件给三样**:工具 + 强制 + 界面。
|
|
249
|
+
|
|
250
|
+
**为什么插件比「让模型去读文件」硬**:后者靠模型自觉,前者由 harness 执行。
|
|
251
|
+
这正是原则 5 的落地。
|
|
252
|
+
|
|
253
|
+
**界面是视图,不存数据**(原则 4)。
|
|
254
|
+
|
|
255
|
+
### 4.4 强制层
|
|
256
|
+
|
|
257
|
+
见下节。
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 五、强制力:怎么让规则真的生效
|
|
262
|
+
|
|
263
|
+
**四层,一层比一层硬。**
|
|
264
|
+
|
|
265
|
+
### 第一层:单一真相源(防漂移)
|
|
266
|
+
|
|
267
|
+
地图一份、进度一份。**删掉所有旧副本。**
|
|
268
|
+
同一件事有两个出处,早晚会打架 —— 而且打架时**没有任何报错**。
|
|
269
|
+
|
|
270
|
+
### 第二层:规则少到能背,且每次会话必加载
|
|
271
|
+
|
|
272
|
+
规则放**每次都会加载的地方**,不放数据文档里(那是数据)。
|
|
273
|
+
每条都能机械检查,写成「违反就是没做好本职工作」的形式。
|
|
274
|
+
|
|
275
|
+
### 第三层:结构上让绕不过去
|
|
276
|
+
|
|
277
|
+
**让正确的行为成为唯一能走通的路。**
|
|
278
|
+
|
|
279
|
+
规则说「指令必须带节点 id」,而节点 id **只能从地图取到** ——
|
|
280
|
+
那它为了产出指令,就**必须先去读地图**。这不是自觉,是结构约束。
|
|
281
|
+
|
|
282
|
+
同理:`coach_assign` **一次只收一个节点**,所以它物理上给不出候选列表。
|
|
283
|
+
`coach_diagnose` 要求引用代码原文,所以「边界没处理好」这种话说不出口。
|
|
284
|
+
|
|
285
|
+
### 第四层:把规则放进 system prompt
|
|
286
|
+
|
|
287
|
+
规则进 **system prompt,不是消息流**:行为约束每轮都得在场,
|
|
288
|
+
而消息流会被长对话把尾巴挤掉。
|
|
289
|
+
|
|
290
|
+
⚠️ **但文本必须静态**:这一段每轮都参与组装,现算内容会打穿 prompt cache
|
|
291
|
+
(DeepSeek 的 cache 是前缀命中,system prompt 每轮变动会让后续 token 全部 miss)。
|
|
292
|
+
所以只在插件加载时读一次 —— 改了规则文件要重启。
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 六、明确不做
|
|
297
|
+
|
|
298
|
+
| 不做 | 原因 |
|
|
299
|
+
|---|---|
|
|
300
|
+
| ❌ 弱点百分比驱动的每日推荐 | 会误导。不同口径下同一个知识点能给出 44% 和 82% |
|
|
301
|
+
| ❌ 从历史数据自动推断掌握度 | **已证伪**:一道题提交 11 次,数据说不出卡在哪 |
|
|
302
|
+
| ❌ 图当主要视图(Mermaid 那类) | 试过,效果差。**技能树是有向图,得专门画** |
|
|
303
|
+
| ❌ 推送作为唯一交付通道 | 死过一次:定时任务报成功,实际零接收 |
|
|
304
|
+
| ❌ 刷题数量当进度单位 | 单位错了,教练管知识点不管题数 |
|
|
305
|
+
| ❌ 计算几何 | 多个数据源一致:低频。投入产出比最差 |
|
|
306
|
+
| ❌ 再写一套「更聪明的」分析引擎 | 问题不在不够聪明,在分工反了 |
|
|
307
|
+
| ❌ 大而全的重构 | 大部分代码是**工具**(抓题面 / 检索 / 题库),那些有用 |
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 七、遗留问题
|
|
312
|
+
|
|
313
|
+
这些是**已知的、还没解决好的**,写出来免得后来者以为是漏掉了:
|
|
314
|
+
|
|
315
|
+
| # | 问题 | 现状 |
|
|
316
|
+
|---|---|---|
|
|
317
|
+
| 1 | **前置边是人工整理的** | 349 节点 / 401 边铺完了,但**没有逐条复核过**。出现「为什么 X 依赖 Y」的疑问是正常的,欢迎提 issue |
|
|
318
|
+
| 2 | **地图的粒度** | 现在到「区间 DP」这一层。「运用方式」还算不算独立知识点 —— 没想清楚 |
|
|
319
|
+
| 3 | **冷启动精度** | 数据只用来缩小范围,位置靠检测。第一次的起点可能偏,要几次检测才收敛 |
|
|
320
|
+
| 4 | **判因的主观性** | 「不熟」和「不认真」的边界靠教练判断,不同模型可能给出不同分类 |
|
|
321
|
+
| 5 | **块时长的普适性** | 40 分钟那个止损点是针对一个人调的,别人可能需要不同的数 |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
*方针变了改这里。实现细节看源码,使用说明看 README。*
|