烟台大学一课堂笔记共享协作Skill

SKILL教育学习
教育学习 未标注权限 许可证不明确
安装这个技能

烟台大学一课堂笔记共享协作Skill。核心是同一门课学生之间的笔记分工与共享协作方法:推荐协作模式(轮流记/各自记重点后合并/在线文档协同)并附优缺点、判断哪些课适合笔记协作、设计避免搭便车的激励与公约机制;同时提供笔记结构化整理、多人笔记合并去重、全文检索等落地工具。每次使用必须同步展示配套交互网页(assets/ytu-notes-collab.html)作为互动入口。当用户提到"课堂笔记共享""笔记分工""轮流记笔记""协作记笔记""合并笔记""笔记协作""搭便车""哪些课适合共享笔记"等场景时触发此 Skill。

一句话选型结论

按资料字段整理

烟台大学一课堂笔记共享协作Skill是教育学习类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类教育学习 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_e51a30c2/sss5468
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

教育学习
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_e51a30c2仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量57.0 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
讲解知识点

按水平把知识点讲清楚。

出练习题

按章节生成练习与答案。

制定学习计划

按目标和时间排学习节奏。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在教育学习场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
烟台大学一课堂笔记共享协作Skill当前 未标注权限 未标注 未标注 未标注 有
食谱创建课堂课程 未标注权限 GitHub 仓库 25.5 万 Codex、Claude、Cursor 有
语言学习 联网访问 ClawHub 市场 23.6 万 OpenClaw 有
learning 效率 只读 ClawHub 市场 19.2 万 OpenClaw 有
学习习惯 未标注权限 ClawHub 市场 10.2 万 OpenClaw 有
日语家教 只读 ClawHub 市场 8.2 万 OpenClaw 有

SKILL.md 全文

57.0 KB
模型实际读到的说明书原文,来自技能仓库

烟台大学一课堂笔记共享协作Skill

概述

本 Skill 的核心是协作方式,不是笔记软件。它解决的是同一门课里"你记了前半段、我漏了后半段"的问题——先帮用户选对笔记分工模式,再评估课程是否适合协作,再设计避免搭便车的机制,最后用工具把协作落地。

工作流程固定为五步:明确场景 → 推荐协作模式 → 评估课程适配性 → 设计反搭便车机制 → 工具落地。

网页交互展示(每次使用必做)

每次使用本 Skill 时,必须同步调用配套交互网页 assets/ytu-notes-collab.html,作为与用户互动的主要入口:

  1. 用 present_files 打开该网页(HTML 会自动在预览面板渲染,标题为「烟台大学 · 课堂笔记共享协作」)
  2. 引导用户直接在网页上操作:30 秒协作模式推荐向导、课程适配五问打分器、三模式对比、反搭便车公约复制
  3. 依据网页上的推荐/评分结果,再结合本文件附录 A–C 的参考文档给出完整方案与公约
  4. 单人整理场景同样先打开网页,跳过协作模块,直接进入「工具落地」

网页内置全部核心内容,与用户沟通时保持网页模块名称与 SKILL.md 一致。

核心工作流

第一步:明确场景

先问清三个问题,再进入推荐环节:
1. 用户是一个人整理,还是多人协作?(单人直接跳到「工具落地」,多人继续)
2. 这门课什么类型、班级多少人、课堂是否允许用电子设备?
3. 小组目前是零基础还是已有协作习惯?

第二步:推荐协作模式(核心)

三种模式各有适用场景,对照下表速选,详细运作方式、优缺点、失败点对策见「附录 A:三种协作模式详解」:

模式 一句话说明 主要优点 主要缺点 优先适用
轮流记 排轮值表,每次课 1–2 人完整记录,全员补漏 单人负担小、质量稳定、管理成本低 依赖当值者、无多视角、怕拖延 大班、内容结构化、进度快的课
各自记重点后合并 每人记不同侧重,课后合并成完整版 覆盖最全、合并即复习、适合理解型课 每人每次都要记、合并成本高 3–6 人小团队、强调理解的课
在线文档协同 共享文档实时多人编辑,边上课边补全 实时性强、贡献透明、免合并 依赖网络设备、易干扰听课、有冲突 小班互动课、机房课、信任度高的小组

快速选择规则:
- 教室不能用电子设备 → 排除在线协同,从轮流记/各自记重点后合并里选
- 课内容结构化、信息量大 → 轮流记
- 课强调理解消化、需要多视角 → 各自记重点后合并
- 团队 ≤5 人、设备允许、都愿意实时动手 → 在线文档协同
- 拿不准 → 推荐混合模式:平时轮流记 + 章节末各自划重点合并(详见附录 A「混合模式」一节)

给出推荐时必须附带该模式的优缺点与适用条件,让用户能自行权衡,不得只丢一个结论。

第三步:评估课程适配性

不是所有课都值得协作。按三个维度判断:内容结构化程度、信息完整 vs 过程完整、班级规模与考核方式。完整分级表(高/中/低适配)与五问评估清单见「附录 B:课程适配性评估」。

核心结论直接引用:
- 高适配:公共理论课、基础课、语言类编程课、记忆密集型课
- 中适配:数学推导密集课、编程实践课、外语课(用针对性对策)
- 低适配:纯讨论课、实验课、体育艺术课、无结构口述课、老师抽查个人笔记的小班

第四步:设计反搭便车机制

搭便车是协作第一死因,用机制而非道德解决。三大原则:贡献可验证(轮值表公开/署名/版本历史)、收益与贡献挂钩(全量笔记分层开放/考前权益)、降低贡献门槛(模板化、脚本化、1 条补充也算贡献)。可直接复制使用的《协作公约》模板与常见错误清单见「附录 C:反搭便车机制设计」。

第五步:工具落地

选定模式后,用以下工具把协作落地(脚本使用说明见下方「工具支撑」,源码见「附录 G:脚本源码」):

  1. 初始化课程笔记本:init_notebook.py 创建「学期 > 课程 > 章节」目录结构
  2. 每次课按标准模板记录:见「附录 F:标准模板」
  3. 各自记重点后合并:merge_notes.py 自动去重、标注来源、生成贡献报告(兼作搭便车核查依据)
  4. 复习检索:search_notes.py 按关键词/课程/章节全文搜索

目录结构

笔记库采用统一的目录组织方式:

notes/                          # 笔记库根目录
├── 2025-春季/                  # 按学期分目录
│   ├── 高等数学/               # 按课程分目录
│   │   ├── README.md           # 课程信息(课程名、教师、学分等)
│   │   ├── 第01章-函数与极限/
│   │   │   └── notes.md        # 章节笔记正文
│   │   ├── 第02章-导数与微分/
│   │   │   └── notes.md
│   │   └── merged/             # 合并笔记输出目录
│   │       └── merged_notes.md
│   └── C语言程序设计/
│       ├── README.md
│       └── ...
└── 2025-秋季/
    └── ...

工具支撑

1. 笔记整理与结构化

将零散课堂记录整理为结构化 Markdown 笔记,模板见「附录 F:标准模板」。

# 第X章 章节名称

## 课程信息
- 课程:课程名
- 日期:YYYY-MM-DD
- 记录人:姓名

## 知识点

### 知识点1名称
**定义:** ...
**要点:**
- 要点1

**例题:**
> 题目内容
> 解答过程

## 课堂重点
- ⭐ 重点1

## 疑问与待办
- [ ] 疑问1

整理规则:每个知识点含「定义/概念」和「要点」;有例题加「例题」;末尾汇总「课堂重点」与「疑问与待办」;⭐ 标重点,❓ 标疑问。

2. 多人协作合并

将多位同学的同节课笔记合并为完整版本:

python merge_notes.py \
  alice_notes.md bob_notes.md charlie_notes.md \
  --output merged/merged_notes.md \
  --names "张三,李四,王五"

合并策略:标题匹配归并、重复内容去重、独有内容标注 [贡献者: 姓名]、仅一人记录的知识点标注 [仅 XX 记录]。合并报告(参与人数、合并知识点数、新增内容数)兼作贡献记录,可用于搭便车核查。

3. 笔记搜索与检索

python search_notes.py "极限" --path ./notes            # 基本搜索
python search_notes.py "导数" --path ./notes --course "高等数学"  # 限课程
python search_notes.py "拉格朗日" --path ./notes --context 3     # 带上下文

4. 初始化课程笔记本

python init_notebook.py \
  --course "高等数学" \
  --semester "2025-春季" \
  --chapters "函数与极限,导数与微分,中值定理,不定积分,定积分" \
  --path ./notes

文档结构说明

本 Skill 包精简为两个文件,全部内容均内联于下:

ytu-notes-share/
├── SKILL.md                       # 主文档:五步工作流 + 全部附录
└── assets/
    └── ytu-notes-collab.html      # 配套交互网页(每次使用必开)
  • SKILL.md — 主文档:正文为五步工作流,附录 A–G 收纳全部参考文档、标准模板与脚本源码
  • assets/ytu-notes-collab.html — 交互网页:内置模式推荐向导、课程适配打分、三模式对比、反搭便车公约,随 Skill 同步打开使用

附录索引:
- 附录 A:三种协作模式详解(运作方式、优缺点对比表、失败点与对策、混合模式、30 秒模式选择决策)
- 附录 B:课程适配性评估(三维度判断、高/中/低适配分级表、五问评估清单)
- 附录 C:反搭便车机制设计(三大原则、可复制的《协作公约》模板、常见错误清单)
- 附录 D:笔记结构规范(笔记结构、格式标准、命名规范、协作标记)
- 附录 E:多人协作合并指南(合并流程、策略详解、最佳实践、常见问题)
- 附录 F:标准模板(单节课笔记模板、课程 README 模板)
- 附录 G:脚本源码(init_notebook.py、merge_notes.py、search_notes.py)

使用建议

  • 每次使用先打开 assets/ytu-notes-collab.html 交互网页,让用户在上面作答/打分,再给详细方案
  • 多人协作场景,先走「第二步:推荐协作模式」再谈工具,不要一上来就跑合并脚本
  • 推荐模式时始终附优缺点与适用条件,尊重用户对团队规模的判断
  • 学期初就用「附录 C」的公约模板把规则立起来,别等出问题再补
  • 合并后的笔记存放在课程的 merged/ 子目录下,不覆盖原始个人笔记

附录 A:三种协作模式详解

同一门课多人协作记笔记,本质是回答三个问题:谁来记、怎么记、何时合并。三种主流模式各有利弊,没有绝对最优,关键是匹配课程特点和团队情况。

模式一:轮流记(Rotation)

运作方式

  • 按座位/学号/抽签排定轮值表,每次课由 1–2 人负责完整记录
  • 当值者课后 24 小时内按统一模板整理并发布到共享位置
  • 其他人收到笔记后只做「补漏 + 纠错」,不重写
  • 轮值表固定公开,缺课/请假需提前换班并告知小组

优点

  • 单人负担小:一学期按 30 次课算,10 人小组每人只需完整记 3 次
  • 质量容易把控:每次只有一个责任主体,笔记结构统一、无重复劳动
  • 管理成本低:不需要实时协调,适合课程排布紧、大家时间不同步的情况
  • 出勤保障:当值者必须到场,客观上降低逃课率

缺点

  • 笔记质量取决于当值者:某次轮到记性差/懒得写的人,全组这周笔记就垮
  • 理解断层:非当值者可能只"存"不"看",到考前才发现某次笔记看不懂
  • 时效脆弱:当值者拖延或丢失,整周复习材料就缺一块
  • 不产生"多方视角":本质是轮流单人记录,缺了相互补充的化学反应

适用场景

  • 内容密度高、课堂节奏快、板书/PPT 结构清晰的课(高数、C语言、马原等)
  • 团队规模 4–10 人、成员时间难以同步
  • 有稳定的共享渠道(QQ群/网盘/在线文档)

常见失败点与对策

失败点 对策
当值者笔记潦草或缺漏 轮值表 + 匿名补漏制度:非当值者必须提交至少 1 条补充/修正
当值者拖延 约定发布截止时间(课后当晚),超时由下一顺位补位
笔记"存而不看" 每章结束后轮值者出一页「本章速览」,逼大家真正阅读

模式二:各自记重点,课后合并(Divide & Merge)

运作方式

  • 每人上课时都记(可自由记,不限格式),但刻意分工记不同侧重
  • 例 1:按位置分——前 1/3 的人记黑板左侧、中间记右侧、后排记提问与补充
  • 例 2:按内容分——一人盯定义公式、一人盯例题步骤、一人盯老师口头强调的考点提示
  • 例 3:按能力分——记性好的记框架,写字快的记细节,理解深的标注疑问
  • 课后(建议 48 小时内)把各自笔记交给整理人,用合并脚本合成一份完整版
  • 合并时保留「独有内容 + 来源署名」,分歧点并列展示

优点

  • 覆盖最全:多人视角互相补漏,漏记概率显著低于单人
  • 产生二次理解:合并过程本身就是一次复习,整理人获益最大
  • 灵活性高:个人笔记仍是自己的,不依赖别人;某次缺席不影响记录覆盖
  • 适合"理解型"课程:能把各自不同的解题思路、联想记忆都沉淀下来

缺点

  • 人力成本最高:每人每次都要记,课后还要合并,总耗时≈单人记录 × 2–3
  • 合并是瓶颈:需要有人承担整理角色,且合并质量决定最终质量
  • 风格冲突:不同人笔记结构、详略、符号体系不一致,合并时需统一
  • 易演变为"各记各的":如果没有明确分工,最后就是三份独立笔记,没形成合力

适用场景

  • 需要理解消化、人人参与度高的课(专业课理论课、经济类、法学类)
  • 团队人数 3–6 人为宜,再多合并成本失控
  • 大家都能坚持上课、愿意花时间的人

常见失败点与对策

失败点 对策
分工形同虚设,各记各的 课前 5 分钟明确分工口令("前两排记左侧板书"),课后点名核对分工是否执行
合并拖到考前一周 定死合并节奏:每次课后 48 小时内合并,由固定「整理人」轮值
合并后没人看 整理人出「差异摘要」:本次合并新增了哪些别人没记到的点

模式三:在线文档协同(Live Co-editing)

运作方式

  • 创建共享在线文档(腾讯文档/飞书/语雀/WPS 云文档),按章节分页
  • 上课时多人同时在线编辑:主讲人记主干,其他人实时补充、提问写评论
  • 课后收敛:标注待确认点,教师答疑后修正
  • 文档历史版本自动留存,可回溯任何人的贡献

优点

  • 实时性强:边上课边补全,当场解决漏记
  • 贡献透明:编辑记录、评论、版本历史天然可见,搭便车者一目了然
  • 无需课后二次合并:省掉合并环节的时间成本
  • 沉淀即成果:文档本身就是最终笔记,考前直接复习

缺点

  • 强依赖网络与设备:教室网络差/不让带电脑的课直接失效
  • 课堂干扰:多人同时敲字可能影响听课专注,也可能被老师视为不专心
  • 协调成本前置:需要有人建文档、分章节、维护结构
  • 实时冲突:同页多人编辑易互相覆盖,需要约定分区(按标题分块)
  • 期末留存问题:在线文档权限过期、账号注销等,资料可能丢失

适用场景

  • 小班课、讨论课、实验课等互动型课堂
  • 机房/网络条件好的课程
  • 团队小(2–5 人)、彼此信任、行动力强

常见失败点与对策

失败点 对策
同页编辑冲突 按标题分块各管一段,或按"先到先写、评论补充"规则
全员在线但全员不写 指定「主讲记录人」,其余人只做评论补充,职责仍然明确
课堂变成"打字比赛" 约定只记关键信息(公式/例题/考点),不逐字记

三模式速查对比表

维度 轮流记 各自记重点后合并 在线文档协同
单人单次负担 低(几周一次) 高(每次都要) 中(边听边记)
笔记完整性 中(依赖当值者) 高(多视角互补) 高(实时补全)
理解深度 低(非当值者只看) 高(合并即复习) 中
课后耗时 低 高(需合并) 最低(实时完成)
管理复杂度 低 中 中
对设备网络依赖 无 无 强
贡献可追溯性 中(轮值表) 高(署名+合并报告) 最高(版本历史)
团队规模上限 10 人左右 3–6 人 2–5 人

混合模式(推荐实践)

不要把自己锁死在一种模式里,按阶段切换:

  1. 学期中(平时):轮流记为主,保证低负担 + 稳定产出;当值者之外的成员课后在共享文档里补 1–2 条自己听到的补充点(成本极低,但保住了"多方视角")
  2. 章节末(复习期):切换到"各自记重点后合并"——每人对着整章笔记划自己的重点/疑问,交给整理人合成「复习冲刺版」
  3. 考前:在线文档汇总全组疑问清单,统一找老师答疑

这套组合拳里,平时用轮流的低负担保证"有人记",复习期用合并的深度保证"记得住"。

模式选择决策问题(30 秒快速判断)

按顺序回答,落到哪个分支就选哪个模式:

  1. 教室网络和电子设备允许吗?→ 不允许,跳到 3
  2. 团队是否 ≤5 人且大家都愿意实时动手?→ 是:在线文档协同
  3. 这门课内容是否结构化(有明确板书/PPT 骨架)?→ 是:轮流记
  4. 这门课是否强调理解消化、需要多视角?→ 是:各自记重点后合并
  5. 都拿不准?→ 默认轮流记 + 章节末合并的混合模式

附录 B:课程适配性评估

笔记协作不是所有课都值得做。判断标准不是"课重不重要",而是这门课的内容形态是否适合多人分工记录。

三个核心判断维度

1. 内容结构化程度

  • 有清晰板书/PPT 骨架、知识点边界明确的课 → 适合分工
  • 内容碎片化、跳跃性强、以讨论为主的课 → 分工难以执行

2. 信息完整性 vs 过程完整性的要求

  • 信息完整型:记住结论、公式、考点就够了(马原、毛概、经管类)→ 协作收益高,因为多人互补能补全信息
  • 过程完整型:必须看到完整推导/实验步骤才能复习(数学推导、编程排错、实验流程)→ 协作风险高,一人记漏一步,整份笔记就废

3. 班级规模与考核方式

  • 大班(60+ 人)、老师不互动 → 内容单一线性,轮流记最划算
  • 小班(<30 人)、互动多 → 需要捕捉提问与口头补充,多人/实时协作更优
  • 考核:闭卷背诵型 → 笔记全面性重要;开卷/理解应用型 → 笔记的"别人视角"更重要

课程适配分级

高适配(强烈推荐协作)

特征:内容结构化、信息量大、进度快、有明确知识点边界

课程类型 原因 推荐模式
公共理论课(马原/毛概/史纲/思修) 内容量大、考点固定、背诵为主 轮流记
基础课(高数/线代/概率论) 定义定理例题结构清晰 轮流记为主,例题多需合并补全
语言类(C语言/Python/Java) 概念+语法+例题分层清晰 轮流记 + 章节末合并
大班专业课(经济学原理/管理学) 知识点多、老师照PPT讲 轮流记
记忆密集型(法律、医学基础、历史类) 覆盖面广,多人互补收益大 各自记重点后合并

中适配(有条件的协作)

特征:内容有一定结构化,但存在过程性/实践性内容

课程类型 风险 对策
数学推导密集课(数分/高代) 推导过程易记漏 轮流记时当值者重点记"步骤 + 卡点";课后合并补例题
编程实践课(数据结构实验/Web开发) 上课以写代码为主,笔记少 只协作记"作业要求 + 常见坑 + 老师口头提示",代码各自留档
外语课 口语听力为主,文字笔记有限 协作记"生词表 + 句型 + 课堂例句",轮流分担
考研/考证课 内容重要但每个人复习目标不同 各自记重点后合并,保留个人扩展空间

低适配(不建议/慎用协作)

特征:内容无法前置结构化,或协作成本远超收益

课程类型 原因
纯讨论课/研讨课 观点流动,笔记是个人思考记录,合并价值低
体育/艺术实践课 动作要领靠练不靠记
实验课 过程个性化,数据各自生成,共享易引发抄袭嫌疑
老师不按套路讲、全程口述跳跃的课 结构化成本极高,单人录播/录音更靠谱
10 人以下小班且老师课堂抽查笔记 抽查按人头,共享笔记可能被认定未完成个人任务

五问评估清单

对任何一门课,答完这 5 题即可定级:

  1. 这门课有没有稳定的内容骨架(PPT/板书/教材章节)?——有得 1 分
  2. 复习时"知道结论"够不够用,还是必须看懂完整推导/步骤?——够用得 1 分
  3. 班级人数是否 ≥30 人、课堂互动少?——是得 1 分
  4. 课程考核是否以闭卷知识点记忆为主?——是得 1 分
  5. 全组是否都能稳定出勤?——是得 1 分
  • 4–5 分:高适配,放心协作
  • 2–3 分:中适配,用对策表中的针对性方案
  • 0–1 分:放弃协作,改用录音+个人整理

一个反直觉的提醒

越"难"的课越不要全员各自记然后合并——推导过程一人漏一步全组崩溃。
越"简单"的课反而越适合协作——因为信息量大、考点机械,多人互补的边际收益最高。

正确的分配是:把协作精力投在"量大但不难"的课上,把个人精力留给"难但量小"的课。


附录 C:反搭便车机制设计

搭便车(free-rider)是笔记协作的第一死因:几个人认真记,其他人白嫖,认真的人渐渐也不干了,协作解体。解决思路不是"道德教育",而是机制设计——让"不贡献"的成本高于"贡献"的成本,让贡献可验证、可追溯、有回报。

搭便车为什么必然发生

  • 笔记是公共物品:我不记也能看别人的,收益不排他
  • 贡献成本即时(每次上课都要花时间),收益延迟(考前才用得上)——短期理性的人会倾向不干
  • 课堂场景没有惩罚机制,不贡献没有直接后果

明白了成因,对策就清楚了:让收益排他一点、让贡献可验证、让不贡献有代价。

机制设计原则

原则一:贡献可验证(让"记没记"看得见)

手段 做法
轮值表公开 全组可见的排班表,谁记哪次一目了然,缺席/换班必须公示
署名制 每份笔记、每个补充点都标注贡献者姓名;合并脚本自动生成「贡献者+独有内容」报告
版本历史 用在线文档的编辑记录,谁写了什么有据可查
发布截止 约定课后 24 小时发布,超时记录在案

原则二:收益与贡献挂钩(让"不记"吃亏)

手段 做法
全量笔记 vs 精简版分层 完整笔记只对贡献者开放;旁观者只能拿到「要点速览」,想要全量就必须参与
考前答疑权 合并版笔记的疑问清单只整理给贡献者;白嫖者自己去找老师
换班/补位规则 缺席当值必须提前找人换班并说明原因,临时放鸽子者承担下轮双倍记录
互评积分 每章末小组互相打分(笔记质量、补充数量),积分低的在组内公示

原则三:降低贡献门槛(让"记"不难)

搭便车很多时候不是坏,是懒/忙。把贡献成本降下来,搭便车自然减少:

  • 轮流记模式下,非当值者的贡献只需 1 条补充(一句话都行),别要求每人写完整笔记
  • 提供标准模板(见「附录 F:标准模板」),填空式记录,10 分钟能搞定
  • 用合并脚本自动去重、自动标注来源,整理人不做苦力
  • 允许"贡献非笔记形式":整理合并、做章节速览、找老师答疑也算贡献,扩大参与面

落地规则包(可直接复制给小组用)

课堂笔记协作公约(示例)
1. 成员:___、___、___(共 __ 人),共享位置:___
2. 协作模式:平时轮流记,章节末各自划重点合并
3. 轮值表:见下表,换班需提前 24 小时在群内说明
4. 当值者义务:课后 24 小时内按模板发布笔记,逾期记一次
5. 非当值者义务:每次课后补至少 1 条【补充/纠错/疑问】,记入贡献表
6. 贡献表每月公示:记录数、补充数、合并次数
7. 全量笔记与疑问清单仅对贡献者开放(按贡献表核实)
8. 连续 3 次无贡献且无正当理由者,移出共享群(可观察期后回归)
9. 期末每位贡献者按贡献表排名,前三名获得全组最全的「考前冲刺包」

软性方案:比机制更重要的三件事

  1. 控制规模:3–6 人最优。人越多越匿名,搭便车越难察觉。人多的班拆成多个小组。
  2. 目标对齐:开组会时讲清楚"这不是帮忙,是共同生产复习资料",让每个人有主人翁感而非受惠感。
  3. 及时止损:发现 1–2 人持续白嫖,第一时间私聊沟通,别等情绪积累。沟通无效就按公约处理,别让个别人拖垮全组。

需要避免的常见错误

错误做法 为什么错
把公约写得像讨伐檄文 激起逆反,好成员也流失
完全依靠自觉和人情 短期有效,学期中后期必然崩塌
白嫖者也能拿到全量笔记 等于为搭便车发奖金,认真的人最先退出
一人身兼记录+整理+发布 单点负担过重,最容易撂挑子,务必轮换
考前才检查贡献 木已成舟,机制应在学期初就建立并公示

附录 D:笔记结构规范

本文档定义课堂笔记的标准结构和格式规范,确保所有笔记风格统一、便于协作合并和搜索检索。

标准笔记结构

一篇完整的课堂笔记包含以下部分:

# 第X章 章节名称
├── 课程信息(课程、教师、日期、记录人)
├── 知识点
│   ├── 知识点1
│   │   ├── 定义/概念
│   │   ├── 要点列表
│   │   └── 例题(可选)
│   ├── 知识点2
│   └── ...
├── 课堂重点(⭐ 标记汇总)
└── 疑问与待办(❓ 标记 + checkbox)

元素说明

1. 课程信息

每个笔记文件开头必须包含课程信息块:

## 课程信息
- 课程:高等数学
- 教师:张教授
- 日期:2025-03-15
- 记录人:张三

2. 知识点

知识点是笔记的核心单元,每个知识点必须包含「定义」和「要点」:

### 极限的定义

**定义:** 当自变量 x 无限趋近于 a 时,函数 f(x) 无限趋近于常数 L,
则称 L 为 x→a 时 f(x) 的极限,记作 lim(x→a) f(x) = L。

**要点:**
- 极限描述的是趋近行为,不要求 f(a) 有定义
- 左极限和右极限都存在且相等时,极限才存在
- 极限值唯一

**例题:**
&gt; 求 lim(x→2) (x² + 3x - 1)
&gt; 解:代入 x=2,得 4 + 6 - 1 = 9
&gt; 所以 lim(x→2) (x² + 3x - 1) = 9

3. 课堂重点

课程结束时,汇总本节课的重点内容:

## 课堂重点
- ⭐ 极限的 ε-δ 定义是分析基础
- ⭐ 左右极限相等是极限存在的充要条件
- ⭐ 夹逼定理用于求复杂极限

4. 疑问与待办

记录课堂上的疑问和课后待办事项:

## 疑问与待办
- [ ] ❓ ε-δ 定义中 ε 和 δ 的关系如何直观理解?
- [ ] 复习夹逼定理的证明过程
- [ ] 完成课后习题 1-5

格式规范

标题层级

层级 用途 示例
# 章节标题 # 第01章 函数与极限
## 主要部分 ## 知识点 / ## 课堂重点
### 知识点标题 ### 极限的定义
#### 子知识点 #### 左极限与右极限

标记符号

符号 含义 使用场景
⭐ 重点内容 课堂重点汇总、重要公式/定理
❓ 疑问 不理解的内容、需要查阅的疑问
✅ 已解决 疑问已解决时勾选
📌 注意事项 易错点、特殊情况
💡 技巧/方法 解题技巧、记忆方法

公式书写

简单公式直接用行内/行间 Markdown:

行内公式:lim(x→a) f(x) = L

行间公式:
&gt; ∫(a→b) f(x)dx = F(b) - F(a)

复杂公式可使用 LaTeX 语法(如果渲染环境支持):

$$\lim_{x \to a} f(x) = L \iff \forall \varepsilon &gt; 0, \exists \delta &gt; 0, \text{s.t.} |f(x) - L| &lt; \varepsilon$$

列表格式

使用 - 作为无序列表标记,有序列表使用 1.:

**要点:**
- 第一点
- 第二点
  - 子要点
  - 子要点

引用格式

例题和重要内容使用引用块:

**例题:**
&gt; 题目:求 lim(x→0) sin(x)/x
&gt; 解答:根据夹逼定理,... 
&gt; 结论:lim(x→0) sin(x)/x = 1

命名规范

文件命名

  • 笔记文件统一命名为 notes.md
  • 合并笔记命名为 merged_notes.md
  • 章节目录命名:第01章-函数与极限(两位数编号 + 章节名)

目录命名

  • 学期目录:2025-春季 / 2025-秋季
  • 课程目录:使用课程全称,如 高等数学 / C语言程序设计
  • 章节目录:第XX章-章节名称

协作标记

多人协作时,在笔记中添加贡献者标记:

### 知识点名称
**定义:** ...  [贡献者: 张三]

**要点:**
- 要点1  [贡献者: 张三]
- 要点2  [贡献者: 李四]

合并笔记中,仅一位同学记录的内容会自动标注 [仅 XX 记录]。


附录 E:多人协作合并指南

本文档详细说明多人课堂笔记合并的工作流程、策略和最佳实践。

合并流程概览

各自记录 → 收集笔记 → 运行合并脚本 → 审阅冲突 → 定稿

详细流程

1. 各自记录

每位同学在各自的 notes.md 中独立记录课堂笔记:
- 使用标准笔记结构(参见「附录 D:笔记结构规范」)
- 保持标题层级一致(便于自动匹配)
- 记录人填写自己的姓名

2. 收集笔记

将所有同学的笔记文件收集到同一目录:

merge_workspace/
├── 张三_notes.md
├── 李四_notes.md
└── 王五_notes.md

文件命名建议:姓名_notes.md,便于脚本自动识别贡献者。

3. 运行合并脚本

python merge_notes.py \
  张三_notes.md 李四_notes.md 王五_notes.md \
  --output merged/merged_notes.md \
  --names &quot;张三,李四,王五&quot;

如果文件名已包含姓名,可省略 --names:

python merge_notes.py \
  张三_notes.md 李四_notes.md 王五_notes.md \
  --output merged/merged_notes.md

4. 审阅合并结果

合并后需要人工审阅以下内容:

4.1 冲突内容

当不同同学对同一知识点有不同理解时,合并脚本会并列展示。需要:
- 判断哪个版本更准确
- 查阅教材或询问老师确认
- 保留正确版本,删除错误版本

4.2 独有内容

仅一位同学记录的内容会被标注 [仅 XX 记录]。需要:
- 判断该内容是否属于本节课范围
- 确认内容准确性
- 保留有价值的补充内容

4.3 格式统一

合并后可能出现格式不一致的情况:
- 统一公式书写格式
- 统一标记符号使用
- 调整标题层级

5. 定稿

审阅修改后,将最终版本保存到 merged/merged_notes.md。

合并策略详解

标题匹配

脚本通过 (标题层级, 标题文本) 二元组匹配知识点:

  • ### 极限的定义 在不同笔记中会匹配到一起
  • ### 极限定义(缺少"的")也会通过模糊匹配合并
  • #### 极限的定义(层级不同)不会匹配

建议: 所有同学使用统一的章节标题,避免匹配失败。

内容去重

对于相同知识点下的内容行:
1. 标准化文本(去除空格、统一标点、转小写)
2. 完全相同的行 → 保留首次出现
3. 高度相似的行(一方包含另一方且长度比 > 80%)→ 视为重复
4. 不重复的行 → 添加到合并结果,标注 [贡献者: XX]

独有内容标注

仅出现在一位同学笔记中的知识点:
- 标题后添加 [仅 XX 记录]
- 合并报告中列出所有独有知识点

最佳实践

课前准备

  • 约定统一的标题格式和知识点划分
  • 分工记录不同部分(如一人记定义,一人记例题)
  • 使用相同的笔记模板

课后合并

  • 尽快合并,趁记忆新鲜时审阅
  • 轮流担任"合并者"角色
  • 合并后发到群里让所有同学确认

版本管理

  • 合并笔记保留合并时间和贡献者信息
  • 原始个人笔记不覆盖
  • 考前复习时以合并版本为准

常见问题

Q: 标题不完全一致怎么办?

A: 脚本会进行模糊匹配(忽略大小写和空格差异)。如果标题差异较大(如"极限" vs "数列极限"),会被视为不同知识点。建议课前约定统一标题。

Q: 某位同学笔记格式不规范怎么办?

A: 合并前先手动整理格式,确保标题层级正确。格式严重不规范的笔记会影响合并质量。

Q: 合并后内容太多太长怎么办?

A: 可以按章节拆分合并:

# 只合并某一章的笔记
python merge_notes.py ch01_张三.md ch01_李四.md -o merged/ch01_merged.md

Q: 如何处理录音转写的笔记?

A: 录音转写的文本通常缺乏结构,建议先用本 Skill 的「笔记整理与结构化」功能(见正文「工具支撑」第 1 节)整理为标准格式,再参与合并。


附录 F:标准模板

F.1 单节课笔记模板(notes.md)

# 第XX章 章节名称

## 课程信息
- 课程:课程名称
- 教师:教师姓名
- 日期:YYYY-MM-DD
- 记录人:你的姓名

## 知识点

### 知识点1名称

**定义:**
在此处填写知识点的定义或概念描述。

**要点:**
- 要点1
- 要点2
- 要点3

**例题:**
&gt; 题目:在此填写题目内容
&gt; 
&gt; 解答:
&gt; 步骤1...
&gt; 步骤2...
&gt; 
&gt; 结论:最终答案

### 知识点2名称

**定义:**
...

**要点:**
- ...

## 课堂重点
- ⭐ 重点1
- ⭐ 重点2
- ⭐ 重点3

## 疑问与待办
- [ ] ❓ 疑问1
- [ ] ❓ 疑问2
- [ ] 复习要点
- [ ] 待做习题

---

&gt; 📝 笔记整理提示:
&gt; 1. 每个知识点包含「定义」和「要点」两个部分
&gt; 2. 有例题的知识点加「例题」部分
&gt; 3. 用 ⭐ 标记重点,❓ 标记疑问
&gt; 4. 课后及时补充「疑问与待办」
&gt; 5. 多人协作时使用 merge_notes.py 合并笔记

F.2 课程 README 模板(README.md)

# 课程名称

## 课程信息
- 课程名称:课程名称
- 任课教师:教师姓名
- 学期:YYYY-春季/秋季
- 学分:X
- 考核方式:考试/考查
- 上课时间:周X 第X-X节
- 上课地点:教学楼-教室

## 章节目录
- 第01章 章节名称1
- 第02章 章节名称2
- 第03章 章节名称3

## 笔记说明
- 每个章节目录下有一个 `notes.md` 文件,记录该章节的课堂笔记
- 多人协作合并后的笔记存放在 `merged/` 目录下
- 使用 `search_notes.py` 可搜索本课程笔记内容

## 参与人员
| 姓名 | 学号 | 负责章节 | 联系方式 |
|------|------|----------|----------|
|      |      |          |          |

## 学习资源
- 教材:
- 参考书:
- 课程网站:
- 习题集:

附录 G:脚本源码

三个脚本相互独立,仅依赖 Python 3 标准库。使用方式:从下方代码块复制内容,分别保存为 init_notebook.py、merge_notes.py、search_notes.py,再用 python xxx.py 运行(示例命令见正文「工具支撑」与「附录 E」)。

G.1 init_notebook.py —— 初始化课程笔记本

#!/usr/bin/env python3
&quot;&quot;&quot;
课堂笔记笔记本初始化脚本
为指定课程创建标准目录结构、README 和章节笔记模板。
&quot;&quot;&quot;

import argparse
import os
import sys
from datetime import datetime
from pathlib import Path


def get_default_semester():
    &quot;&quot;&quot;根据当前月份推断学期&quot;&quot;&quot;
    now = datetime.now()
    year = now.year
    month = now.month
    if 2 &lt;= month &lt;= 7:
        return f&quot;{year}-春季&quot;
    else:
        return f&quot;{year}-秋季&quot;


def sanitize_dirname(name: str) -&gt; str:
    &quot;&quot;&quot;清理目录名,移除文件系统不允许的字符&quot;&quot;&quot;
    invalid_chars = &#x27;&lt;&gt;:&quot;/\\|?*&#x27;
    for ch in invalid_chars:
        name = name.replace(ch, &quot;_&quot;)
    return name.strip()


def format_chapter_dir(index: int, name: str) -&gt; str:
    &quot;&quot;&quot;格式化章节目录名:第01章-章节名称&quot;&quot;&quot;
    return f&quot;第{index:02d}章-{sanitize_dirname(name)}&quot;


def create_chapter_note(chapter_name: str, course_name: str, teacher: str) -&gt; str:
    &quot;&quot;&quot;生成章节笔记模板内容&quot;&quot;&quot;
    today = datetime.now().strftime(&quot;%Y-%m-%d&quot;)
    return f&quot;&quot;&quot;# {chapter_name}

## 课程信息
- 课程:{course_name}
- 教师:{teacher or &#x27;待填写&#x27;}
- 日期:{today}
- 记录人:待填写

## 知识点

### 知识点1
**定义:**

**要点:**
-

**例题:**
&gt; 

## 课堂重点
- ⭐ 

## 疑问与待办
- [ ] 

---

&gt; 笔记整理提示:
&gt; 1. 每个知识点包含「定义」和「要点」两个部分
&gt; 2. 有例题的知识点加「例题」部分
&gt; 3. 用 ⭐ 标记重点,❓ 标记疑问
&gt; 4. 课后及时补充「疑问与待办」
&quot;&quot;&quot;


def create_course_readme(course_name: str, teacher: str, semester: str, chapters: list) -&gt; str:
    &quot;&quot;&quot;生成课程 README 内容&quot;&quot;&quot;
    chapter_list = &quot;\n&quot;.join(
        f&quot;- 第{i:02d}章 {ch}&quot; for i, ch in enumerate(chapters, 1)
    ) if chapters else &quot;- (待添加章节)&quot;
    return f&quot;&quot;&quot;# {course_name}

## 课程信息
- 课程名称:{course_name}
- 任课教师:{teacher or &#x27;待填写&#x27;}
- 学期:{semester}
- 学分:待填写
- 考核方式:待填写

## 章节目录
{chapter_list}

## 笔记说明
- 每个章节目录下有一个 `notes.md` 文件,记录该章节的课堂笔记
- 多人协作合并后的笔记存放在 `merged/` 目录下
- 使用 `search_notes.py` 可搜索本课程笔记内容

## 参与人员
| 姓名 | 学号 | 负责章节 |
|------|------|----------|
|      |      |          |
&quot;&quot;&quot;


def main():
    parser = argparse.ArgumentParser(
        description=&quot;初始化课程笔记本目录结构&quot;
    )
    parser.add_argument(
        &quot;--course&quot;, &quot;-c&quot;,
        required=True,
        help=&quot;课程名称(必填)&quot;
    )
    parser.add_argument(
        &quot;--semester&quot;, &quot;-s&quot;,
        default=None,
        help=&quot;学期,格式 YYYY-春季/秋季(默认自动推断)&quot;
    )
    parser.add_argument(
        &quot;--chapters&quot;, &quot;-ch&quot;,
        default=None,
        help=&quot;章节列表,逗号分隔(可选)&quot;
    )
    parser.add_argument(
        &quot;--path&quot;, &quot;-p&quot;,
        default=&quot;./notes&quot;,
        help=&quot;笔记库根目录(默认 ./notes)&quot;
    )
    parser.add_argument(
        &quot;--teacher&quot;, &quot;-t&quot;,
        default=None,
        help=&quot;任课教师(可选)&quot;
    )

    args = parser.parse_args()

    semester = args.semester or get_default_semester()
    chapters = (
        [ch.strip() for ch in args.chapters.split(&quot;,&quot;) if ch.strip()]
        if args.chapters
        else []
    )

    # 构建目录路径
    base_path = Path(args.path) / sanitize_dirname(semester) / sanitize_dirname(args.course)
    merged_path = base_path / &quot;merged&quot;

    # 检查是否已存在
    if base_path.exists():
        print(f&quot;⚠️  课程目录已存在:{base_path}&quot;)
        response = input(&quot;是否覆盖?(y/N): &quot;).strip().lower()
        if response != &quot;y&quot;:
            print(&quot;已取消。&quot;)
            sys.exit(0)

    # 创建目录
    base_path.mkdir(parents=True, exist_ok=True)
    merged_path.mkdir(exist_ok=True)
    print(f&quot;✅ 创建课程目录:{base_path}&quot;)

    # 创建 README
    readme_path = base_path / &quot;README.md&quot;
    readme_content = create_course_readme(args.course, args.teacher, semester, chapters)
    readme_path.write_text(readme_content, encoding=&quot;utf-8&quot;)
    print(f&quot;✅ 创建课程 README:{readme_path}&quot;)

    # 创建章节目录和笔记模板
    if chapters:
        for i, chapter in enumerate(chapters, 1):
            chapter_dir = base_path / format_chapter_dir(i, chapter)
            chapter_dir.mkdir(exist_ok=True)
            note_path = chapter_dir / &quot;notes.md&quot;
            note_content = create_chapter_note(chapter, args.course, args.teacher)
            note_path.write_text(note_content, encoding=&quot;utf-8&quot;)
            print(f&quot;✅ 创建章节笔记:{note_path}&quot;)
    else:
        print(&quot;ℹ️  未指定章节,可后续手动添加章节目录&quot;)

    print(f&quot;\n📝 笔记本初始化完成!&quot;)
    print(f&quot;   路径:{base_path}&quot;)
    print(f&quot;   学期:{semester}&quot;)
    print(f&quot;   课程:{args.course}&quot;)
    if chapters:
        print(f&quot;   章节:{len(chapters)} 个&quot;)
    print(f&quot;\n下一步:&quot;)
    print(f&quot;  1. 编辑 README.md 补充课程信息&quot;)
    print(f&quot;  2. 在各章节的 notes.md 中记录课堂笔记&quot;)
    print(f&quot;  3. 使用 merge_notes.py 合并多人笔记&quot;)


if __name__ == &quot;__main__&quot;:
    main()

G.2 merge_notes.py —— 多人笔记合并

#!/usr/bin/env python3
&quot;&quot;&quot;
多人课堂笔记合并脚本
将多位同学各自记录的同一节课/章节笔记合并为一份完整版本。
合并策略:标题匹配、内容去重、独有内容保留、冲突标注。
&quot;&quot;&quot;

import argparse
import re
import sys
from pathlib import Path
from collections import defaultdict
from dataclasses import dataclass, field
from typing import List, Optional


@dataclass
class NoteSection:
    &quot;&quot;&quot;笔记章节:一个标题及其下的所有内容&quot;&quot;&quot;
    level: int                # 标题层级 (1-6)
    title: str               # 标题文本
    content_lines: list = field(default_factory=list)  # 标题下的内容行
    contributors: list = field(default_factory=list)   # 贡献者列表


def parse_markdown(text: str) -&gt; List[NoteSection]:
    &quot;&quot;&quot;解析 Markdown 文本,按标题分割为 Section 列表&quot;&quot;&quot;
    sections = []
    current_section = None

    for line in text.split(&quot;\n&quot;):
        # 匹配标题行:# ~ ######
        heading_match = re.match(r&#x27;^(#{1,6})\s+(.+)$&#x27;, line)
        if heading_match:
            # 保存上一个 section
            if current_section:
                sections.append(current_section)
            level = len(heading_match.group(1))
            title = heading_match.group(2).strip()
            current_section = NoteSection(level=level, title=title)
        else:
            if current_section is None:
                # 标题之前的内容,创建一个虚拟 section
                current_section = NoteSection(level=0, title=&quot;(文档头部)&quot;)
            current_section.content_lines.append(line)

    if current_section:
        sections.append(current_section)

    return sections


def normalize_text(text: str) -&gt; str:
    &quot;&quot;&quot;标准化文本以便比较:去除空格、标点差异&quot;&quot;&quot;
    # 去除多余空白
    text = re.sub(r&#x27;\s+&#x27;, &#x27;&#x27;, text)
    # 去除常见标点差异(直/弯双引号、直/弯单引号)
    text = text.replace(&#x27;&quot;&#x27;, &#x27;&#x27;).replace(&#x27;\u201c&#x27;, &#x27;&#x27;).replace(&#x27;\u201d&#x27;, &#x27;&#x27;)
    text = text.replace(&quot;&#x27;&quot;, &#x27;&#x27;).replace(&#x27;\u2018&#x27;, &#x27;&#x27;).replace(&#x27;\u2019&#x27;, &#x27;&#x27;)
    return text.lower()


def is_duplicate(line1: str, line2: str) -&gt; bool:
    &quot;&quot;&quot;判断两行内容是否为重复&quot;&quot;&quot;
    n1 = normalize_text(line1)
    n2 = normalize_text(line2)
    if not n1 or not n2:
        return False
    # 完全相同
    if n1 == n2:
        return True
    # 一方包含另一方且长度差异小
    if len(n1) &gt; 10 and len(n2) &gt; 10:
        shorter = min(n1, n2, key=len)
        longer = max(n1, n2, key=len)
        if shorter in longer and len(shorter) / len(longer) &gt; 0.8:
            return True
    return False


def merge_sections(sections_list: List[List[NoteSection]], names: List[str]) -&gt; List[NoteSection]:
    &quot;&quot;&quot;
    合并多份笔记的 Section 列表。
    sections_list: 每位同学的 Section 列表
    names: 对应的姓名列表
    &quot;&quot;&quot;
    # 用 (level, normalized_title) 作为 key 匹配
    merged_map = {}  # key -&gt; NoteSection
    merged_order = []  # 保持出现顺序

    for sections, name in zip(sections_list, names):
        for section in sections:
            key = (section.level, normalize_text(section.title))

            if key not in merged_map:
                # 新 section
                new_section = NoteSection(
                    level=section.level,
                    title=section.title,
                    content_lines=list(section.content_lines),
                    contributors=[name]
                )
                merged_map[key] = new_section
                merged_order.append(key)
            else:
                # 已有 section,合并内容
                existing = merged_map[key]
                if name not in existing.contributors:
                    existing.contributors.append(name)

                # 逐行合并:去除重复
                for line in section.content_lines:
                    is_dup = False
                    for existing_line in existing.content_lines:
                        if is_duplicate(line, existing_line):
                            is_dup = True
                            break
                    if not is_dup:
                        # 非空行才添加
                        if line.strip():
                            existing.content_lines.append(f&quot;{line}  [贡献者: {name}]&quot;)

    return [merged_map[k] for k in merged_order]


def render_merged(sections: List[NoteSection], contributor_names: List[str]) -&gt; str:
    &quot;&quot;&quot;将合并后的 Section 列表渲染为 Markdown 文本&quot;&quot;&quot;
    lines = []
    lines.append(&quot;# 合并笔记&quot;)
    lines.append(&quot;&quot;)
    lines.append(f&quot;&gt; 本笔记由 {len(contributor_names)} 位同学合并:{&#x27;, &#x27;.join(contributor_names)}&quot;)
    lines.append(f&quot;&gt; 合并时间:{__import__(&#x27;datetime&#x27;).datetime.now().strftime(&#x27;%Y-%m-%d %H:%M&#x27;)}&quot;)
    lines.append(&quot;&quot;)

    # 统计信息
    total_sections = len([s for s in sections if s.level &gt; 0])
    lines.append(f&quot;**合并统计:** {total_sections} 个知识点,{len(contributor_names)} 位贡献者&quot;)
    lines.append(&quot;&quot;)
    lines.append(&quot;---&quot;)
    lines.append(&quot;&quot;)

    for section in sections:
        if section.level == 0:
            # 文档头部内容,跳过或直接输出
            for line in section.content_lines:
                if line.strip():
                    lines.append(line)
            continue

        # 输出标题
        prefix = &quot;#&quot; * section.level
        contributor_tag = &quot;&quot;
        if len(section.contributors) == 1:
            contributor_tag = f&quot;  [仅 {section.contributors[0]} 记录]&quot;
        lines.append(f&quot;{prefix} {section.title}{contributor_tag}&quot;)
        lines.append(&quot;&quot;)

        # 输出内容
        for line in section.content_lines:
            lines.append(line)
        lines.append(&quot;&quot;)

    # 合并报告
    lines.append(&quot;---&quot;)
    lines.append(&quot;&quot;)
    lines.append(&quot;## 合并报告&quot;)
    lines.append(f&quot;- 参与人数:{len(contributor_names)}&quot;)
    lines.append(f&quot;- 贡献者:{&#x27;, &#x27;.join(contributor_names)}&quot;)
    lines.append(f&quot;- 合并知识点数:{total_sections}&quot;)

    # 统计每位贡献者的独有内容
    solo_sections = [s for s in sections if s.level &gt; 0 and len(s.contributors) == 1]
    if solo_sections:
        lines.append(f&quot;- 独有知识点数:{len(solo_sections)}&quot;)
        for s in solo_sections:
            lines.append(f&quot;  - {s.title}({s.contributors[0]})&quot;)

    lines.append(&quot;&quot;)

    return &quot;\n&quot;.join(lines)


def main():
    parser = argparse.ArgumentParser(
        description=&quot;合并多人课堂笔记&quot;
    )
    parser.add_argument(
        &quot;files&quot;,
        nargs=&quot;+&quot;,
        help=&quot;多个笔记文件路径&quot;
    )
    parser.add_argument(
        &quot;--output&quot;, &quot;-o&quot;,
        default=&quot;./merged_notes.md&quot;,
        help=&quot;合并后输出路径(默认 ./merged_notes.md)&quot;
    )
    parser.add_argument(
        &quot;--names&quot;, &quot;-n&quot;,
        default=None,
        help=&quot;贡献者姓名列表,逗号分隔,与文件顺序对应(默认使用文件名)&quot;
    )

    args = parser.parse_args()

    # 检查文件
    file_paths = []
    for f in args.files:
        path = Path(f)
        if not path.exists():
            print(f&quot;❌ 文件不存在:{f}&quot;)
            sys.exit(1)
        file_paths.append(path)

    # 解析姓名
    if args.names:
        names = [n.strip() for n in args.names.split(&quot;,&quot;)]
        if len(names) != len(file_paths):
            print(f&quot;❌ 姓名数量({len(names)})与文件数量({len(file_paths)})不匹配&quot;)
            sys.exit(1)
    else:
        names = [path.stem for path in file_paths]

    # 读取并解析所有笔记
    sections_list = []
    for path, name in zip(file_paths, names):
        text = path.read_text(encoding=&quot;utf-8&quot;)
        sections = parse_markdown(text)
        sections_list.append(sections)
        print(f&quot;📄 已读取:{path.name}({name})- {len(sections)} 个段落&quot;)

    # 合并
    print(f&quot;\n🔄 正在合并 {len(file_paths)} 份笔记...&quot;)
    merged_sections = merge_sections(sections_list, names)

    # 渲染
    output_text = render_merged(merged_sections, names)

    # 写入
    output_path = Path(args.output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(output_text, encoding=&quot;utf-8&quot;)

    # 统计
    total = len([s for s in merged_sections if s.level &gt; 0])
    solo = len([s for s in merged_sections if s.level &gt; 0 and len(s.contributors) == 1])

    print(f&quot;\n✅ 合并完成!&quot;)
    print(f&quot;   输出文件:{output_path}&quot;)
    print(f&quot;   合并知识点:{total} 个&quot;)
    print(f&quot;   独有知识点:{solo} 个(仅一位同学记录的内容)&quot;)
    print(f&quot;   贡献者:{&#x27;, &#x27;.join(names)}&quot;)
    print(f&quot;\n💡 建议审阅合并结果,特别是标注了 [仅 XX 记录] 的独有知识点,以及 [贡献者: XX] 的补充内容。&quot;)


if __name__ == &quot;__main__&quot;:
    main()

G.3 search_notes.py —— 笔记搜索检索

#!/usr/bin/env python3
&quot;&quot;&quot;
课堂笔记搜索脚本
在笔记库中按关键词、课程、章节搜索笔记内容。
支持模糊匹配、上下文显示、多关键词 OR 搜索。
&quot;&quot;&quot;

import argparse
import re
import sys
from pathlib import Path
from dataclasses import dataclass
from typing import List, Optional


@dataclass
class SearchResult:
    &quot;&quot;&quot;单条搜索结果&quot;&quot;&quot;
    file_path: Path
    course: str          # 课程名
    chapter: str         # 章节名
    line_number: int     # 匹配行号
    matched_line: str    # 匹配行内容
    context_before: list  # 上文行
    context_after: list   # 下文行


def find_notes_files(root: Path) -&gt; List[Path]:
    &quot;&quot;&quot;递归查找笔记库中所有 .md 文件(跳过 merged 合并笔记目录)&quot;&quot;&quot;
    files = []
    for path in root.rglob(&quot;*.md&quot;):
        # 跳过 merged 目录中的合并笔记,避免结果重复
        if &quot;merged&quot; in path.parts:
            continue
        files.append(path)
    return sorted(files)


def extract_course_chapter(file_path: Path, root: Path) -&gt; tuple:
    &quot;&quot;&quot;从文件路径推断课程名和章节名&quot;&quot;&quot;
    try:
        rel_path = file_path.relative_to(root)
        parts = rel_path.parts
        # 结构:semester/course/chapter/notes.md
        # 或:course/chapter/notes.md
        course = &quot;&quot;
        chapter = &quot;&quot;
        if len(parts) &gt;= 3:
            course = parts[-3] if len(parts) &gt;= 3 else &quot;&quot;
            chapter = parts[-2] if len(parts) &gt;= 2 else &quot;&quot;
        elif len(parts) &gt;= 2:
            course = parts[-2] if len(parts) &gt;= 2 else &quot;&quot;
            chapter = parts[-1] if len(parts) &gt;= 1 else &quot;&quot;
        return course, chapter
    except ValueError:
        return &quot;&quot;, &quot;&quot;


def fuzzy_match(text: str, keyword: str, exact: bool = False) -&gt; bool:
    &quot;&quot;&quot;检查文本是否匹配关键词&quot;&quot;&quot;
    if exact:
        return keyword in text
    # 模糊匹配:忽略大小写,允许部分匹配
    return keyword.lower() in text.lower()


def search_in_file(
    file_path: Path,
    keywords: List[str],
    root: Path,
    context_lines: int,
    exact: bool,
    course_filter: Optional[str],
    chapter_filter: Optional[str]
) -&gt; List[SearchResult]:
    &quot;&quot;&quot;在单个文件中搜索关键词&quot;&quot;&quot;
    course, chapter = extract_course_chapter(file_path, root)

    # 课程过滤
    if course_filter:
        if not fuzzy_match(course, course_filter, exact=False):
            return []

    # 章节过滤
    if chapter_filter:
        if not fuzzy_match(chapter, chapter_filter, exact=False):
            return []

    results = []
    try:
        lines = file_path.read_text(encoding=&quot;utf-8&quot;).split(&quot;\n&quot;)
    except Exception as e:
        print(f&quot;⚠️  无法读取文件 {file_path}: {e}&quot;, file=sys.stderr)
        return []

    for i, line in enumerate(lines):
        for keyword in keywords:
            if fuzzy_match(line, keyword, exact):
                # 提取上下文
                start = max(0, i - context_lines)
                end = min(len(lines), i + context_lines + 1)
                context_before = lines[start:i]
                context_after = lines[i + 1:end]

                results.append(SearchResult(
                    file_path=file_path,
                    course=course,
                    chapter=chapter,
                    line_number=i + 1,
                    matched_line=line,
                    context_before=context_before,
                    context_after=context_after,
                ))
                break  # 同一行不重复报告

    return results


def highlight_keyword(text: str, keywords: List[str]) -&gt; str:
    &quot;&quot;&quot;高亮关键词(用 &lt;&lt;&gt;&gt; 包裹)&quot;&quot;&quot;
    for kw in keywords:
        # 大小写不敏感替换
        pattern = re.compile(re.escape(kw), re.IGNORECASE)
        text = pattern.sub(f&quot;&lt;&lt;{kw}&gt;&gt;&quot;, text)
    return text


def format_result(result: SearchResult, keywords: List[str], show_context: bool) -&gt; str:
    &quot;&quot;&quot;格式化单条搜索结果&quot;&quot;&quot;
    lines = []
    rel_path = result.file_path

    # 文件位置
    location = f&quot;📄 {result.course}/{result.chapter}&quot; if result.course else f&quot;📄 {rel_path.name}&quot;
    lines.append(f&quot;  {location}  (第 {result.line_number} 行)&quot;)

    # 上下文 - 之前
    if show_context and result.context_before:
        for line in result.context_before:
            if line.strip():
                lines.append(f&quot;    │ {line.rstrip()}&quot;)

    # 匹配行
    highlighted = highlight_keyword(result.matched_line, keywords)
    lines.append(f&quot;    ▶ {highlighted.rstrip()}&quot;)

    # 上下文 - 之后
    if show_context and result.context_after:
        for line in result.context_after:
            if line.strip():
                lines.append(f&quot;    │ {line.rstrip()}&quot;)

    lines.append(&quot;&quot;)  # 空行分隔
    return &quot;\n&quot;.join(lines)


def main():
    parser = argparse.ArgumentParser(
        description=&quot;搜索课堂笔记库&quot;
    )
    parser.add_argument(
        &quot;keywords&quot;,
        nargs=&quot;+&quot;,
        help=&quot;搜索关键词(多个关键词为 OR 关系)&quot;
    )
    parser.add_argument(
        &quot;--path&quot;, &quot;-p&quot;,
        default=&quot;.&quot;,
        help=&quot;笔记库根目录(默认当前目录)&quot;
    )
    parser.add_argument(
        &quot;--course&quot;, &quot;-c&quot;,
        default=None,
        help=&quot;限定课程名称(支持模糊匹配)&quot;
    )
    parser.add_argument(
        &quot;--chapter&quot;, &quot;-ch&quot;,
        default=None,
        help=&quot;限定章节名称(支持模糊匹配)&quot;
    )
    parser.add_argument(
        &quot;--context&quot;, &quot;-C&quot;,
        type=int,
        default=2,
        help=&quot;显示匹配行上下文的行数(默认 2)&quot;
    )
    parser.add_argument(
        &quot;--exact&quot;, &quot;-e&quot;,
        action=&quot;store_true&quot;,
        help=&quot;精确匹配模式(关闭模糊匹配)&quot;
    )

    args = parser.parse_args()

    root = Path(args.path)
    if not root.exists():
        print(f&quot;❌ 路径不存在:{root}&quot;)
        sys.exit(1)

    if not root.is_dir():
        print(f&quot;❌ 不是目录:{root}&quot;)
        sys.exit(1)

    # 查找所有笔记文件
    md_files = find_notes_files(root)
    if not md_files:
        print(f&quot;❌ 在 {root} 中未找到 Markdown 文件&quot;)
        sys.exit(1)

    print(f&quot;🔍 搜索关键词:{&#x27;, &#x27;.join(args.keywords)}&quot;)
    if args.course:
        print(f&quot;   课程过滤:{args.course}&quot;)
    if args.chapter:
        print(f&quot;   章节过滤:{args.chapter}&quot;)
    print(f&quot;   搜索范围:{len(md_files)} 个文件&quot;)
    print(f&quot;   {&#x27;=&#x27; * 50}&quot;)
    print()

    # 搜索
    all_results = []
    for md_file in md_files:
        results = search_in_file(
            md_file,
            args.keywords,
            root,
            args.context,
            args.exact,
            args.course,
            args.chapter
        )
        all_results.extend(results)

    if not all_results:
        print(&quot;❌ 未找到匹配结果。&quot;)
        print(f&quot;\n💡 建议:&quot;)
        print(f&quot;  - 尝试使用更短的关键词&quot;)
        print(f&quot;  - 去除 --course / --chapter 过滤条件&quot;)
        print(f&quot;  - 使用模糊匹配(不加 --exact)&quot;)
        sys.exit(0)

    # 输出结果
    show_context = args.context &gt; 0
    for result in all_results:
        print(format_result(result, args.keywords, show_context))

    # 统计
    print(f&quot;{&#x27;=&#x27; * 50}&quot;)
    print(f&quot;✅ 找到 {len(all_results)} 条匹配结果&quot;)

    # 按课程统计
    course_counts = {}
    for r in all_results:
        course_counts[r.course] = course_counts.get(r.course, 0) + 1
    if len(course_counts) &gt; 1:
        print(f&quot;   按课程分布:&quot;)
        for course, count in sorted(course_counts.items(), key=lambda x: -x[1]):
            print(f&quot;     {course or &#x27;(未知)&#x27;}: {count} 条&quot;)


if __name__ == &quot;__main__&quot;:
    main()

常见问题

5 问
答案由本站依据条目字段整理
烟台大学一课堂笔记共享协作Skill怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_e51a30c2/sss5468。安装方式为未标注。装完刷新技能列表或重开客户端。

烟台大学一课堂笔记共享协作Skill支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

烟台大学一课堂笔记共享协作Skill权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

烟台大学一课堂笔记共享协作Skill收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

烟台大学一课堂笔记共享协作Skill用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。