Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
RUOYI_BASE_URLNoBase URL of the RuoYi platform backend.http://localhost:8080
RUOYI_PASSWORDYesPassword for RuoYi platform login.
RUOYI_USERNAMEYesUsername for RuoYi platform login.
TEACHER_MCP_ROLENoRole for tool grouping: all, prep, ingest, or lecture.all

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
extensions
{
  "io.modelcontextprotocol/ui": {}
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
loginA

以真实 teacher 账号登录平台,拿双头 token 注入本会话身份(后续所有工具隐式带该身份、落 RuoYi 权限审计)。

参数: username/password: teacher 账号。留空则用 .env 兜底(RUOYI_USERNAME/RUOYI_PASSWORD)。 返回: {ok, teacher_id, username} —— token 仅驻留 server 侧会话态,不回吐明文给调用方。

login_asA

飞书机器人免密切身份(PRD-007):凭服务密钥用 open_id 换该 teacher 的 token,替换本会话身份。

用途:bot 后端按飞书消息发送者的 open_id 逐消息切身份,让写操作(录题/组卷等)归属各自 teacher。 鉴权:服务密钥走 env BOT_SECRET(只在机器人后端持有、不入 git);调用方不需密码。 401 自动重签:切身份后,后续工具遇 token 失效会按当前 open_id 自动重调 botLogin 重签(不走密码重登)。 参数: openid: 飞书 open_id(如 ou_xxx)。 返回: 成功 → {ok:true, user_id, openid}。 失败 → {ok:false, hint}:openid 未绑定 teacher → hint 含「未绑定」(bot 按此路由拒绝话术); BOT_SECRET 未配置 / 密钥错 / 账号停用等各有对应提示。

list_kg_treeA

查平台知识点树(组卷的知识点白名单源)。返回顶层节点 + children 嵌套。

编排层(Claude Code)据此选要考查的知识点叶子 id,喂给 compose_paper 的 outline.subjectId。 返回: {ok, nodes:[{id,name,children?}, ...]};空树 → {ok:true, nodes:[]} 不报错。

resolve_kgA

KG 锚定查表(确定性只读,数学+科学一套通吃):按名称/节号/父节点在某教材根下查节点,供选锚定叶子。

🔴 叶子=无子节点(is_leaf),别按 level 判——科学 901 树 5 层、902-906 树 4 层,叶深不一(H2 实测)。 用法:先 query 模糊(如「乘方」)看候选 → 挑 is_leaf=true 的最贴切者作 ingest_items 的 kp_id; 同步练习类卷名带节号可 section_num 精确命中(如 "2.5" → 「2.5 有理数的乘方」节点)。 参数: subject_root: 教材根 id(数学七上="100";科学="901".."906")——锚定范围的唯一开关 query : 名称模糊词(LIKE %query%) section_num : 节号精确匹配(如 "2.5",命中名称以「2.5 」开头的节点) parent_id : 只列某节点的直接子节点(浏览下钻用;给了它则忽略 subject_root 前缀过滤) leaves_only : 只返回叶子 返回: {ok, count, nodes:[{id,name,level,parent_id,is_leaf}]};无命中 → count=0 不报错。

search_questionsA

从题库分页检索题目(备课圈题核心)→ POST /teacher/question/page。返回 {ok, total, items}。

🔴 快速找回路径(PRD-O-005 溯源增强):给 batch_id 或 since 任一 → 改走 backends/db 只读检索 (按 import_batch_id / create_time / create_user 查,不依赖 stem LIKE,故不漏 stem_text=NULL 的变式题),可与 mine/subject_id/question_type/difficult 组合。返回 items 附 import_source/batch_id/create_time。

  • batch_id: 精确批次号(ingest_items/ingest_question 返回的 batch_id,如 mcp-20260708-...)

  • since : 时间窗,'24h'/'7d' 或 ISO 日期('2026-07-08');mine=True 限本人。

🔴 subject_id 语义 = 前缀子树匹配:传章节/课时的 biz_subject 节点 id → 召回该节点下 整棵子树所有知识点的题(likeRight 前缀);传叶子 id → 只该叶子的题。非数字 id → BE 静默返空集。 🔴 私有 vs 公共两套口径:

  • 默认(mine=False)= 公共池(status='1' AND is_public=1,仅超管审核过的题);

  • mine=True = 本人已发布题(create_user=登录老师,status='1',含 is_public=0 私有题、 含举一反三变式/据讲义自造题)。🔴 圈自己造的题必须 mine=True。

  • 🔴 两种口径都只返 status='1'(已发布)——草稿 status='0' 永不进列表。 参数: subject_id : 知识点/章节/课次的 biz_subject id(resolve_kg / get_plan_detail.kgNodeIds 来);空=不过滤 question_type: 题型码 1选择/2判断/3应用/4填空/5解答/6作图/7计算/8证明;None=不限 difficult : 难度 1-4 星(按段分层规则挑档);None=不限 keyword : 题干 LIKE %kw% mine : True=只看本人已发布题(含私有池);False=公共池 exam_paper_id: 按出处卷 id 筛(字符串数字);None=不限 label_status : 打标态 0未标/1AI已标/2已审核;None=不限 pattern_id : 题型 id(biz_question_pattern)收窄;None=不限 page_index : 页码(🔴 从 1 起,非 0) page_size : 每页条数(默认 20) 返回: {ok, total, items:[{id(str 雪花), question_type, difficult, subject_id, stem_brief, stem_img, status, label_status, free_tags, patterns, ...}]}。id 直接透给 build_prep_pack。

my_recent_uploadsA

一键找回当前登录老师在最近时间窗内录的东西(题 / 卷 / 讲义片段)——DB 只读,不依赖 stem 关键词。

用途:老师刚用 MCP 录完一批题/组完卷/存完讲义,想快速核对「我刚才录进去了啥」。 参数: hours 时间窗(默认 24 小时;<1 视为 1)。 返回: {ok, hours, questions: {total, batches:[{batch_id, import_source, count, items:[{id, stem_head(30字/占位), import_source, batch_id, create_time}]}]} # 按录入批次分组,倒序 papers: [{id, name, question_count, create_time}] # biz_paper 同窗口本人建的卷 lecture_frags: [{id, title, create_time}] # biz_kg_lecture_frag 同窗口本人 owner 的片段 view_url} # 题库页深链,供浏览器核对 🔴 双管道语义:import_source 带 "mcp-" 前缀 = MCP 机录;'举一反三'=引擎落库;其余=手工/其他管道。

get_questionA

按 id 批量拉题目详情(🔴 装段前人工核对题面/答案/解析,避免盲装误人子弟)→ GET /teacher/question/list?ids=。返回 {ok, items}。

参数: ids : 题 id 列表(字符串雪花号,单次 ≤100);软删题 BE 自动过滤,返回按入参保序。 返回: {ok, items:[{id, stem_text, answer, analyze, question_type, difficult, subject_id, block_json/answer_block_json/analyze_block_json(三端结构化渲染源), stem_img, free_tags, patterns, question_knowledges(U 轨 kp), status, label_status}]}。 🔴 选项内容在 block_json 里(选择题),无独立 options 字段。

get_role_manualA

取角色说明书全文(协议内自带;另有 MCP resource teacher://manual/)。

role 分角色返回(缺省跟随本连接的 TEACHER_MCP_ROLE):

  • role="data"/"ingest"/"lecture"(录入线)= 录入角色说明书(七类来源路由 + IngestItem 契约 + 讲义录入)。

  • role="prep" = 备课角色说明书(备课线路编排 + 私有池铁律 + 变式补题路径)。

  • role="variant" = 举一反三角色说明书(批 3 落笔)。

  • role="all" = 总手册(开场三步 + 四线工具地图 + 四条编排流程 + 铁律盒子 + 自救表)——fresh agent 首选。 🔴 首次以某身份使用本 server 的 agent 先调对应 role:说明书告诉你这条线怎么一步步走、每步调什么工具。 返回: {ok, role, manual}(markdown 全文);文件缺失 → {ok:False, hint}。

health_checkA

探活三依赖(ruoyi BE / toolkit / MySQL),任何异常算 down 不抛。返回 {ruoyi, toolkit, db}。

  • ruoyi: GET /actuator/health(401 也算 up,健康端点要鉴权属正常)

  • toolkit: GET /info

  • db: pymysql SELECT 1 全程 trust_env=False、超时 5s。返回 {ruoyi:{up,url}, toolkit:{up,url}, db:{up}}。

convert_docA

Word(.docx / docx伪装的.doc)→ 结构化文本 + 题图清单。确定性 XML 解析(OMML→$LaTeX$),非 OCR。

产出文本里:数学公式=$LaTeX$;图=〖图:rId〗占位(与 images 的 rid 对应)。 下一步:文本喂 parse_paper_text 确定性拆题;images[].local_path 喂 upload_image 传 OSS。 参数: doc_path 卷子绝对路径;batch 批次名(图文件名前缀+文本落盘名,空=文件名主干)。 返回: {ok, text, paras, text_path, images:[{rid, local_path}]};真 OLE .doc → {ok:false, reason:"另存为 docx"}。

convert_pdfA

PDF → 文字层检测 + 按页转图(多模态拆题的原料)。确定性 pymupdf,非 OCR。

🔴 拆题一律以页图多模态直读为准(H1a:文字层公式不可信);text_layer 仅辅助(题号定位/纯文字题)。 参数: pdf_path 绝对路径;batch 页图文件名前缀(空=文件名主干);dpi 渲染精度(170 实测够);max_pages 限页(0=全部)。 返回: {ok, page_count, has_text_layer, pages:[页图路径...], text_layer_path?}。

parse_paper_textA

规整卷面文本 → 题列表 JSON(确定性规则拆题,零 LLM)。适合 convert_doc 产物 / 规整粘贴文本。

拆题规则:题号「N、/N./N.」+ 章节头判题型 + 【来源】/【答案】/【解析】+ 多小问状态机 + 卷尾参考答案回填。 产出每题含 〖图:rId〗 占位(如有)。🔴 agent 拿到后须核对题数/补漏,再构造 IngestItem[] 喂 ingest_items; 位置序号(数组下标+1)= 入库 sort,别用 num(原卷题号遇杂散「N.」会错位——七上教训)。 参数: text 卷面文本(与 text_path 二选一,text 优先)。 返回: {ok, count, questions:[{num,type,stem,options,answer,analyze,has_fig,source,score}], digest}。

format_questionA

把自然 markdown 题干 + 选项数组确定性转成 blockJson(三端统一渲染格式)。底座永不抛、识别不了降级 markdown 块。

Claude 只产最小内容:markdown 题干(可含小问(1)(2)、图标记 、$LaTeX$、表格)+ 选项内容数组(label 自动 A/B/C…)。 参数: question_type 见字典 biz_question_type(1选择/2判断/3应用/4填空/5解答/6作图/7计算/8证明);options 仅选择题非空。 返回: {ok, block_json, degraded?}。把 block_json 喂给 ingest_question。

upload_imageA

把本地磁盘图片直传 OSS + 去重,返回可塞进 blockJson 图块 / ingest_question.images 的 ossUrl。

参数: local_path 本地绝对路径;asset_kind 资产类型(figure 等)。 返回: {ok, asset_id, oss_url, dedup}。

ingest_questionA

录一道题入库(事务多表),归属当前登录 teacher。返回 {ok, question_id, created, import_source, batch_id}。

free_tags: 自由标签 → biz_free_tag 字典 + biz_question_free_tag 关联(三件套之一,检索锚)。

参数(NOT NULL=必填): subject_id : 科目锚 level1(年级根,如 数学七上 的根 id),NOT NULL question_type: 字典 biz_question_type 1选择/2判断/3应用/4填空/5解答/6作图/7计算/8证明,NOT NULL difficult : 1基础/2提升/3压轴,NOT NULL stem_text : 纯文本题干(全文检索 + 去重 hash),NOT NULL block_json : 来自 format_question 的 blockJson(三端渲染);空则只存 stem_text answer_text / analyze_text: 答案 / 解析文本 knowledge_ids: 锚知识点叶子 [{kpId, isPrimary, source, confidence}](KG 关联,供组卷/举一反三召回) images : 题图 [{ossUrl, assetId, role, ...}](来自 upload_image) external_key: 幂等键(book+节+课时+题号),去重;空则按 stem_text hash 去重 status : '0'草稿 / '1'发布(默认发布) import_source: 双管道来源标记;空则自动打 "mcp-<角色>"(MCP 机录,与手工/其他管道可区分) import_batch_id: 录入批次号;空则自动生成 mcp-YYYYMMDD-HHMMSS-4位随机 🔴 双管道语义:不带 "mcp-" 前缀的 import_source(main/手工导入/'举一反三'引擎…)= 其他/手工管道。 异常: 底座报错 → {ok:false, reason}(不假成功)。

ingest_itemsA

🔴 统一入库口(七类来源殊途同归):一次调用完成 题+图+知识点关系+打标字段+可选成卷,无需事后手补。

每题依次:图代传 OSS(local_path→ossUrl 并替换题面占位)→ blockJson 格式化 → 录题落库 (kp_id 走 knowledgeIds → 底座自动写 biz_question_knowledge + dim1_kp_id)→ 打标字段 (err/scenario/free_tags → biz_question_ai;why → difficulty_reason;models/new_models → 模型链,批尾串行)。 参数: items: IngestItem[](契约见 get_role_manual;未知字段拒绝,缺 stem 单条 fail 不中断批) subject_root: KG 教材根(数学七上="100";科学="901".."906")——无 kp_id 的题 subject_id 落此根 paper: 可选建卷 {name, category_id, total_score, suggest_time};null=散题不成卷 前置信息原样落库不被覆盖(AC3);同题干重复录入自动去重复用(AC4)。 🔴 双管道来源标记(PRD-O-005 溯源增强):本调用自动生成一个批次号,每题打 import_source="mcp-<角色>"(MCP 机录)+ import_batch_id=该批次号。录完记住返回的 batch_id, 日后 search_questions(batch_id=…) 或 my_recent_uploads() 一键找回。 不带 "mcp-" 前缀的 import_source(main/手工导入/'举一反三'引擎…)= 其他/手工管道。 返回: {ok, batch_id, results:[{num, question_id, created, reason?, warnings?}], paper_id?, stats:{ok,reused,fail,img}, view_url?}。

verify_ingestA

🔴 灌库后铁律验证:检查题干开头来源前缀残留(如「(真题·杭州滨江)」「(2025 浙江期末)」)。

ingest_items 已内置预防端自动剥前缀;本工具是验证端——每次灌完卷/批必须跑一次,residue_count 必须=0。 参数: paper_id(查整卷题)或 question_ids(查指定题),二选一。 返回: {ok, residue_count, residues:[{id, head}]};residue_count>0 = 清洗未过,逐题人工处置。

label_questionA

给一道已入库的题写 DNA 打标(难度 + 知识点锚 + 解法骨架 + 变式底料),归当前登录 teacher。

前置:题已 ingest_question 入库(有题干/答案/解析/配图)。Claude 先读题(多模态)+本章 KG 上下文判好,再调本工具落库。 参数(必填 = question_id, difficult;其余按题打满 / 基础题留空): question_id : biz_question.id difficult : ★难度 1基础/2中等/3较难/4压轴(按「难度评级」rubric 判档,非 LLM 自评) dim1_kp_id : 知识点锚叶子 id(同版本精确锚,如浙教七上根 100 下的叶子;不跨版本近似,对不上就留空+need_anchor_review=1) anchor_confidence: 锚定置信 0-1 need_anchor_review: 锚存疑待人审 1/0 dim5_structure: 图形/情境结构指纹 solution_skeleton: 解法骨架(步骤序列,【】标最难步)—— 撑变式①数值②结构算子 assessment_type: 考察类型 hard_points : 难点[](受控词表 biz_anno_ERROR:概念混淆/计算失误/审题偏差/隐含遗漏/分类不全/表达不规范/思路缺失) breakthrough_points: 突破点[](★1/★2 送分/常规题可空) tags : 检索标签 3-6(召回用) scenario : 场景(仅应用题;受控词表 biz_anno_SCENE:纯数学/现实生活/科学跨学科/数学文化) dna_type : DNA 类型 parametric_slots / modeling_frame / conditions / variation_profile: 变式底料(母题打满,普通题可简,应用题才有 modeling_frame) 返回: {ok, question_id, ai_id, difficult, dim1_kp_id};异常 → {ok:false, reason}。

delete_questionsA

🔴 安全删题(只能删散题)。confirm=False(默认)=dry_run 预览:返回 deletable/blocked 清单不动库; confirm=True 才真删——删前自动备份主行+正文 JSON(返回 backup_path),随删全部附属表行并写审计。

阻删闸(命中即拒删该 id):试卷/导入卷/书架书/教辅书/解题模型母题/活血缘母题/讲义引用。 单次上限 500;id 一律字符串传(雪花号截尾)。删除不可逆——真删前先 dry_run 给用户过目。

convert_lecture_docxA

讲义 docx → 忠实 Tiptap 内容 + KG 知识点靶子 + 图清单(确定性,零 login,零落库)。理解式映射的原料台。

🔴 崔崔版式实测=模块(H3)>分组(H3)>知识点(H4),纯确定性 H3 切片对不上 KG 知识点 → 默认 mode='assist': 工具只做忠实转换 + 按顶层 H3 分段 + 给出该课时 KG 知识点靶子,切成知识点由 agent 理解式映射 (读 raw_path 全文,按 kg_targets 重组讲解成片段 IR,习题模块的题走 ingest_items 拿 qid 后挂 kgExample)。 mode='auto':仅当来源 H3 已== KG 知识点名(已清洗源)才用——跑确定性 split_frags + 对齐闸,直接产 IR。 🔴 图不在此上传:content 里 image.src=〖图:rId〗占位 + rid;images[].local_path 喂 upload_image 拿 ossUrl, save_lecture_frag(image_map={rid:ossUrl}) 回填。忠实转换细节:heading/paragraph 留 bold/italic/color、 表 w:shd→背景色、单元格内图、图 EMU→px 内容区 clamp、EMF/WMF 矢量图无 local_path(浏览器不支持)。 参数: docx_path : 讲义 docx 绝对路径 course_subject_id : 课时 L4 subject_id(如 901001002001)——KG 知识点靶子来源 book_id : 教辅套 id(崔崔=CC7S) batch : 产物落盘名前缀(空=docx 文件名主干) mode : 'assist'(默认,理解式原料) | 'auto'(确定性 H3 切片,仅清洗源可用) | 'cuicui'(崔崔适配器) 返回(assist): {ok, mode, course, kg_targets:[{id,name}], sections:[{h3,node_count,preview,start,end}], images:[{rid,local_path}], raw_path, stats, deterministic_hint:{toc,unmatched_h3,frags}} ← 后者是 H3 切片试探,崔崔版必 FAIL 仅供参考。 返回(auto): {ok, mode, course, frag_count, frags, toc, unmatched_h3, images, ir_path, stats}

save_lecture_fragA

片段 IR → :9090 upsert 入库(唯一入库口,UK=subjectId+bookId+owner 幂等覆盖)。需先 login。

图回填:image_map={rid:ossUrl} 把 content 里 〖图:rId〗占位换成 OSS 地址;无 ossUrl 的 image 节点会被剔除 并在 unresolved_images 里报告(不静默丢)。对齐闸:任何 UNMATCHED 片段默认拒绝入库(除非 allow_toc_fail)。 参数: ir_path : convert_lecture_docx 产出的 IR 文件路径(与 frags 二选一,优先) frags : 片段 IR 列表 [{subjectId,title,contentJson,status?}](与 ir_path 二选一) book_id : 教辅套 id(ir_path 里带则以其为准) image_map : {rId: ossUrl}(upload_image 的产物) owner : 归属 uid;省略=登录者(admin 登录省略即官方库覆盖) allow_toc_fail : True 才允许含未匹配 KG 的片段入库(BE 仍会因 subjectId 不存在而单条失败) 返回: BE saveFrags 响应 {ok, owner, results:[{subjectId,action}], stats} + {unresolved_images, view_url?}。

remove_lecture_fragA

删除某 owner 在某 subjectId 前缀下的讲义片段(覆盖录入的「先删」步;打 :9090)。需先 login。

🔴 前缀 LIKE 删除:subject_prefix='901001002001'(课时L4) 会删该课时自身 + 全部子知识点片段—— 连课时级思维导图(kgMindmap)一并删,想保留导图就别用课时前缀,改删到知识点段(或逐个知识点前缀)。 BE 强制 subjectPrefix≥9 位(节级)防误删整册。owner 省略=登录者(admin=uid1 官方库)。 参数: subject_prefix : subjectId 前缀(≥9 位);删该前缀下该 owner 的所有片段 book_id : 教辅套 id owner : 归属 uid;省略=登录者 返回: BE {ok, removed}(removed=删除行数)。

list_lecture_docsA

查讲义目录(哪些课时/知识点有讲义片段,定位「据讲义出题」的锚点)→ :9090 GET /teacher/kg/lecture-catalog。返回 {ok, volume_id, lessons}。

参数: book_id : 教材/书 id(空=服务端默认书 DEFAULT_BOOK)。 返回: {ok, volume_id, lessons:[...]}(lessons 为课时×来源聚合,含各课时 subjectId/标题/ 有无片段/owner 等;结构随 BE getCatalog 演进)。库里无讲义资产时 lessons=[](空态非报错)。

get_lecture_contentA

读某知识点/课次的讲义正文(🔴 据讲义出题的原料入口)→ :9090 GET /teacher/kg/lecture?subjectId=。返回 {ok, subject_id, text, example_qids, ...}。

🔴 text = docJson(Tiptap)递归抽出的纯讲解正文;例题只是 kgExample(qid) 引用——正文里以 【例题 qid=...】 占位、真题面不在讲义片段里。要看例题题面 → 用返回的 example_qids 调 get_question(qids)。据 text 理解知识点讲法后,agent 自己出同源题。 参数: subject_id : 知识点/课次的 biz_subject id(get_plan_detail.kgNodeIds / resolve_kg 来)。 🔴 讲义按前缀树序汇聚:传课时节点会拿到其下片段拼成的整篇。 book_id : 教材/书 id(空=默认书)。 owner : 指定讲义作者 owner(字符串 userId);空=默认视图(我的>本部门管理员>官方兜底)。 返回: {ok, subject_id, node, book_id, owner, has_content, text, example_qids:[qid str]}。 🔴 has_content=False / text="" = 该知识点无讲义资产(空态,agent 应降级为凭 KG + 题库出题)。

compose_paperA

按大纲从真题库确定性组卷并真落库 biz_paper,归属当前登录 teacher。

参数: outline: [{subjectId, subjectName?, questionType, difficult, count}, ...] subjectId 取自 list_kg_tree 的叶子 id;编排层负责选点,本工具不二次解析意图。 title: 卷名(可选,默认"MCP组卷")。 lesson_id / slot_seq: 🔴 PRD-B-101 备课卷位绑定(可选,二者必须同现)——给了则本卷落 【备课卷】(paper_kind='2') 并绑到该课次卷位;只给一个 → 本地报错不发请求; 都省 = 普通卷(一切照旧)。🔴 备课卷私有,绝不 set-public。 返回: {ok, paper_id, item_count, paper, notes};底座不在/无匹配题 → {ok:false, reason}(不假成功)。

create_paperA

按指定题目 id 列表(顺序即试卷内题号顺序)组装成一套试卷入卷库,归属当前登录 teacher。

用于整卷录入:题目已 ingest_question 入库后,把它们按原卷题号顺序串成 biz_paper(建 section + biz_paper_question 关联)。 参数: name: 试卷名(如原卷标题),1-200 字符。 question_ids: 题目 id 列表,顺序 = 试卷内题号顺序,至少 1 题。 paper_category_id: 试卷分类 id(可选,卷库目录树;空=根级)。 lesson_id / slot_seq: 🔴 PRD-B-101 备课卷位绑定(可选,二者必须同现)——给了则本卷落 【备课卷】(paper_kind='2') 并绑到该课次卷位;只给一个 → 本地报错不发请求; 都省 = 普通卷。🔴 备课卷私有,绝不 set-public。 返回: {ok, paper_id, ...};异常 → {ok:false, reason}。

update_paperA

给试卷按**年级标准分(通值)**算每题分值 + 设建议时长,走 /update 落库。

分值规则(不抠原卷,按常规给):选择/判断=3分、填空=3分,大题类(应用/解答/作图/计算/证明)把剩余分铺满到 total_score(余数加在靠后的难题)。 参数: paper_id;total_score 总分(初中数学期末常规 120);suggest_time 建议时长(分钟,常规 120)。 返回: {ok, paper_id, total, per_question:[...]};异常 → {ok:false, reason}。

bind_paper_slotA

🔴 PRD-B-101 备课卷位绑定管理(绑既有卷 / 解绑 / 标记已备好)→ 备课线。返回 {ok, paper_slots, prep_state}。

备课主路 = 组卷时直接带 lesson_id+slot_seq 建卷即自动绑(create_paper/compose_paper); 本工具是事后管理:把已有卷挂到卷位(D7 兜底)、解绑、或手动标记整课次已备好。 🔴 备课卷私有,本工具不含任何公开化能力(绝不 set-public)。 参数: lesson_id : 课次 id(字符串雪花号)——必填。 slot_seq : 卷位序号(≥1)——action=bind/unbind 必填;action=manual_ready 忽略(课次级)。 action : 动作枚举—— 'bind' 绑既有卷到卷位(传 paper_id,必须真实存在且归我;BE 自动置该卷 paper_kind='2') 'unbind' 解绑卷位(卷留库不删;🔴 解绑会自动清该课次 manual_ready=false) 'manual_ready' 手动标记整课次备课态(传 ready;0 卷位课次 → BE 400) paper_id : action=bind 时必填(要挂的既有卷 id,字符串雪花号)。 ready : action=manual_ready 时的目标态(True=已备好,默认 True)。 返回: {ok, paper_slots:[...], prep_state:'0未备/1备课中/2已备好'};异常 → {ok:false, error}。

create_teach_targetA

建教学对象档案(学生或班级)→ :9090。返回 {ok, id}(id 为字符串雪花号)。

对象即「教谁」:一个学生或一个班课,后续排课/备课/回收全挂在它身上。 🔴 R1a 建模口径:年级/教材不再传文本,改传 gradeNo+gradeYear+字典码。 暑期录「升四」= grade_no:4, grade_year:2026(gradeYear=该年级生效学年的起始年, 当前年级由服务端按 9/1 学年进位推导)。 参数: target_type : 'student'(学生一对一)| 'class'(班课)——必填 name : 对象名(学生姓名 / 班级名) grade_no : 年级 1-12(字典 biz_edu_grade;1-6 小学 / 7-9 初中 / 10-12 高中) grade_year : grade_no 生效学年起始年(如 2026 = 2026-09-01 起学年) textbook_edition : 教材版本字典码(biz_edu_edition:'1'浙教/'2'人教/'3'北师大/'4'苏教) subject : 学科字典码(biz_edu_subject:'1'数学/'2'科学/'3'语文/'4'英语) (edition/subject 服务端兼容中文标签归一化,但请按码传) parent_phone : 家长手机号 profile : 肖像 dict(学生画像)——UI 四格 = traits/level.desc/level.target_layer/error_signals, 结构见契约 profile_json:{traits:[str], level:{desc,target_layer}, env:str, history:[{topic,status:'吃透|讲过未吃透',src}], error_signals:[{tag,evidence,session_id,ts,by:'system|teacher',status:'pending|confirmed'}]} color : 色板色(空则服务端从色板轮转分配)

list_teach_targetsA

查我名下的教学对象卡片墙(含实时聚合:排课数/绑定计划进度/下一课/班课学员数)→ :9090。

建对象前查重、选排课对象都走它。返回 {ok, items}。 参数: target_type : 'student' | 'class',省略=两类都查 keyword : 名称模糊过滤 include_archived: True 才含已归档对象(默认只看在用的)

update_teach_targetA

改教学对象档案(学生/班级信息修改)→ :9090。返回 {ok, id, updated:['basic'/'profile']}。

create_teach_target 只能建、建完改不了——本工具补上「改」:改名/年级/教材/学科/家长手机/色,或整体覆写肖像。 🔴 只改传入的字段:不传的保持原值(BE updateById NOT_NULL 跳空);基本维不动肖像,肖像走独立整覆写。 🔴 target_type 必传(BE 靠它选学生表/班级表);改肖像务必先 get_student_profile 取全量再整体回传(传 {} 会清空)。 参数: target_id : 对象 id(字符串雪花号)——必填 target_type : 'student' | 'class'——必填(决定改哪张表) name : 新对象名(不传=不改) grade_no : 年级 1-12(字典 biz_edu_grade) grade_year : grade_no 生效学年起始年(如 2026) textbook_edition : 教材版本码(biz_edu_edition:1浙教/2人教/3北师大/4苏教,兼容中文标签) subject : 学科码(biz_edu_subject:1数学/2科学/3语文/4英语,兼容中文标签) parent_phone : 家长手机号(仅学生) color : 日历着色 profile : 肖像 dict(整体覆写,非增量!结构见 create_teach_target 的 profile 说明)—— 改肖像典型流程 = get_student_profile 读回 → 本地改 → 整体传回

archive_targetA

归档 / 取消归档教学对象(学生·班级的「软删除」)→ :9090。 返回 {ok, id, archived, cancelled_sessions?}。

🔴 归档 = 本系统的「删学生」:对象移出排课选择器、历史保留(非硬删)。 归档会联动取消该对象未来未上的场次,cancelled_sessions 返回联动取消的场次数。 取消归档(archived=False)只解归档标记、不恢复已取消场次(复课需重新排课)。 参数: target_id : 对象 id(字符串雪花号) archived : True=归档(软删,默认)| False=取消归档(复用)

upsert_course_planA

建/改课程计划 + 批量 upsert 课次(一步到位)→ :9090。返回 {ok, plan_id, lesson_ids, view_url}。

计划 = 一段周期(如一个暑假)的课次编排蓝本;排课时按 lesson_seq 顺序自动绑到场次上。 🔴 R1a·S1:计划有归属——新建必传 target_type + target_id(BE 强校验对象存在且归我,缺传 400)。 🔴 PRD-B-101 契约平移:课次内容模型 = 专项卷位 paper_slots(替代旧 seg_template『段模板』)。 一课次 = N 张专项卷,每卷位可绑一张卷。旧字段 seg_template / default_seg_template 收到即报错拒绝 (不静默兼容,防两套字段并存漂移)。 参数: plan : {id?, name, target_type:'student|class', target_id:str(归属对象 id,🔴 新建必传), term_tag:'暑假|上学期|寒假|下学期', year:int, material_note?:str(素材说明,如「学而思 36 周书·挑题制」), default_paper_slots?:list(默认专项卷位模板,lesson 空则继承), status?:'0草稿|1启用|2归档'} —— 带 id = 改计划基本维,空 id = 新建。🔴 无 total_lessons(=课次数实时聚合)。 lessons : 课次列表,每个 dict:{id?(空=新增), lesson_seq:int, title, lesson_type:'0教学|1测试', tag?(自由标签,吃透课走这), source_ref?(素材源,如「学而思第10+11周」), thinking_action?(思维动作), layer_target?(层数目标,如 '2→3'), parent_copy?(家长版口语文案), kg_node_ids?:[str](课内同步锚的 biz_subject id), paper_slots?:list(本课次专项卷位模板,覆盖计划默认): [{slot_seq:int, name:str(必填非空), style:str, rules:str, note:str}] (🔴 绑定字段 paper_id/manual_ready 由服务端管,agent 写入通常只给 slot_seq/name/style/rules/note)}

schedule_sessionsA

批量排课(给某对象铺一串场次)→ :9090。返回 {ok, created, conflicts}。

auto_bind=True 时按 lesson_seq 顺序把未排课次自动绑到这批场次上(items 里也可显式 plan_lesson_id)。 🔴 冲突处理(契约 D6):命中冲突且 force=False → 一条不落、只回 conflicts 明细; 前端弹警告后可 force=True 强存(重发同一批)。冲突口径:老师撞场(create_by 同人时间重叠)/学生撞场。 参数: target_type : 'student' | 'class' target_id : 对象 id(字符串) items : [{date:'YYYY-MM-DD', start:'HH:MM', end:'HH:MM', plan_lesson_id?:str, session_type?:'1正课|2测试|3外部占位', external_title?:str(外部占位标题), note?:str}] plan_id : 绑定的计划 id(auto_bind 用它取课次顺序) auto_bind : 按 lesson_seq 顺序自动绑未排课次(默认 True) force : True = 无视冲突强存(默认 False,先探冲突)

list_scheduleA

查某时间窗的月历场次(对象名/色、时间、课次标题、类型、备课态)→ :9090。返回 {ok, sessions}。

参数: start : 起始日期 'YYYY-MM-DD'(含) end : 结束日期 'YYYY-MM-DD'(含) target_id : 只看某对象的场次(省略=我名下全部)

update_sessionA

改单场次(改期/请假/取消/标已上/锁内容/改绑课次/改备注)→ :9090。返回 {ok, deferred?, overflow?}。

参数: session_id : 场次 id(字符串) action : 动作枚举—— 'reschedule' 改期(传 date/start/end;🔴 改期=只改时间不改状态、不触发顺延) 'leave' 请假 → 🔴 触发顺延:该对象该计划、日期在其后的「已排」场次,绑定课次整体前移补位; lesson_locked='1' 的场次保持原课次被跳过;末位课次悬空 → overflow 提示需补排 'cancel' 取消 → 软取消:置 status='3',行留库(同样触发顺延,口径同 leave) 'delete' 🔴 移除课程 → 物理删除整行、不可恢复(区别于 cancel 软取消);仅本人场次可删 'mark_done' 标记已上(session_status→已上) 'lock' 锁定本场绑定的课次内容(顺延时被跳过、不改绑) 'unlock' 解锁 'rebind' 改绑课次(传 plan_lesson_id;只改本场,不动别的场次) 'note' 改备注(传 note) date/start/end : reschedule 用;plan_lesson_id : rebind 用;note : note 用 返回:leave/cancel 会带 deferred(顺延明细 [{sessionId,newLessonId}])+ overflow(悬空课次提示)。

build_prep_packA

🔴 DEPRECATED(PRD-B-101 已退役):备课不再走「装备课包」,改为按卷位组卷

备课材料模型已从「一包 N 段」升维为「一课次 N 张专项卷位」:逐卷位 compose_paper/create_paper(lesson_id, slot_seq) 建卷(自动落【备课卷】+ 绑卷位)+ bind_paper_slot 管理绑定,PDF 由平台前端导出(MCP 不再出 PDF)。 本工具仅保留返回退役指引,不再执行任何操作。见 get_role_manual(role='prep')。

render_prep_packA

🔴 DEPRECATED(PRD-B-101 已退役):MCP 不再出 PDF。

备课卷 PDF 一律走组卷前端链路(平台「我的卷库·备课卷」导出),服务端简化渲染(纯 Java PDF)退役。 备课改为逐卷位 compose_paper/create_paper(lesson_id, slot_seq) + bind_paper_slot。 本工具仅保留返回退役指引,不再执行任何操作。见 get_role_manual(role='prep')。

submit_reviewA

课后回收(录逐题对错)→ 生成家长反馈 + 肖像增量 → :9090。返回 {ok, parent_msg, portrait_delta}。

提交即标该场次「已上」。parent_msg 服务端模板拼装「家长您好!…思维题:/同步:/拓展奥数:」—— 🔴 R1b S5:parent_msg 即时生成不落库(提交/查详情时都按当时上下文现算,override 也过内部词防线); 🔴 内部词(层/★/素材/挑题/薄弱)一律不进家长文案。portrait_delta = 错/卡题按 cause 聚合出的 error_signals(by=system,status=pending,带 session_id 溯源),自动 append 进对象肖像。重复提交=覆盖+上一版进 prev_json。 参数: session_id : 场次 id item_results : 逐题结果 [{question_id?:str, seg:'段名', seq:int, result:'对|错|卡', cause:'计算|概念辨析|策略|其他'}] teacher_note : 老师备注(可选) parent_msg_override: 传入则用它替换模板文案(LLM 润色位,可选)

get_student_profileA

取对象详情 + 肖像(含 error_signals 易错库)→ :9090。返回 {ok, target, profile}。

备课前读画像、看回收后新增的 pending 易错信号都走它。 参数: target_id : 对象 id(字符串) target_type : 'student'(默认)| 'class'

get_plan_detailA

读某课程计划的全部课次明细(🔴 备课前读「这节课的编排蓝本」)→ :9090 GET /teacher/schedule/plan/{id}。返回 {ok, plan, lessons}。

当前链条最硬的读缺口:现有 upsert_course_plan 只写、list_schedule 只给场次概览,读不到 某课次的分段蓝本 / 课内锚点 → 圈题无依据。本工具补上。 参数: plan_id : 课程计划 id(字符串雪花号;list_schedule 的场次里带 plan_lesson_id 可回溯到 plan)。 返回: plan : {id, name, targetType, targetId, termTag, year, materialNote, defaultPaperSlots(计划默认专项卷位模板), status, createTime, updateTime, lessonCount} lessons : [{id, planId, lessonSeq, title, lessonType('0'教学/'1'测试), tag, sourceRef, thinkingAction, layerTarget(层数目标如 '2→3'), parentCopy(家长版文案,🔴 家长可见、 无内部词), kgNodeIds:[str](🔴 课内锚点,直接喂 search_questions(subject_id=)), paperSlots:[{slot_seq,name,style,rules,note,paper_id,manual_ready}](🔴 PRD-B-101 专项卷位蓝本: 每卷位对应一张专项卷;空卷位=待组,逐卷位走 compose_paper/create_paper(lesson_id,slot_seq)), paperSlotsInherited(true=继承自计划 default_paper_slots), prepState(备课态,服务端按 paper_slots 推导:'0'未备/'1'备课中/'2'已备好)}]

list_feedback_sheetsA

列出当前老师名下的课后反馈单(owner 硬隔离)→ {ok, rows, total}。

rows=[{id,targetId,targetName,batchKey,lessonSeq,title,lessonDate,...}](新→旧)。 🔴 改单前先用它找回目标单的 id,别新建重复单;🔴 接力新课次前先用它看该生 最新批次已到第几节(batchKey+lessonSeq),新单 lesson_seq = 最大值 + 1。 参数: target_id(可选)/ keyword(标题模糊)/ batch_key(只看某批次,PRD-010)。

get_feedback_sheetC

读一张反馈单详情(含五列 rows)→ {ok, sheet}。参数 sheet_id 字符串传。

upsert_feedback_sheetA

建/改课后反馈单(归属当前登录老师)→ {ok, sheet_id}。

🔴 PRD-010 批次模型(用户工作流=批次累积一次性全发):一个学生一段课程 = 一个批次 (batch_key 如「多多五上暑假数学」,独立概念不绑课程计划),批次内课次 lesson_seq 依次递增。接力建新课次单时必须带 batch_key + lesson_seq(先 list_feedback_sheets 看该生最新批次到第几节,新单 = 最大 lesson_seq + 1;title 缺省口径 「{batch_key}第{N}节课上课内容」)。老师说"新开批次/新学期"才换新 batch_key 从 1 重计。

参数: target_id : 学生对象 id(字符串;先用 list_teach_targets 映射,严禁编造) title : 标题(🔴 家长可见,禁内部词) lesson_date: 上课日期 yyyy-MM-dd(可选) rows : 五列行数组 [{seq,module,content,mastery,weakness,kp_id?}] sheet_id : 传了=改这张(PUT),不传=新建 batch_key : 批次键(接力单必带) lesson_seq : 批次内课次号(接力单必带,>0 生效) 🔴 掌握情况写「熟练/基本掌握/待巩固」等家长话术。

export_feedback_batch_pngA

批次全量导出(PRD-010,🔴 发家长用这个不用单张):该学生一个批次 1~N 节全部 反馈单按课次拼一张长图 → {ok, batch_key, sheet_count, local_path, file_marker}。

batch_key 缺省 = 该生最新批次(新建课次后直接调它即可拿到含最新一节的全量图)。 🔴 导出后把 file_marker([[FILE:/tmp/fb_batch_*.png]])原样写进回复, 机器人据此把长图内联发回会话。

export_feedback_pngA

单张反馈单导成家长版 PNG 并下载到本机 → {ok, local_path, file_marker, ...}。

🔴 发家长的常规场景请用 export_feedback_batch_png(批次全量长图,用户实发形态); 本工具只在明确要"单独看某一节"时用。 🔴 导出后必须把返回的 file_marker(形如 [[FILE:/tmp/fb_export_123.png]])原样写进 给用户的回复里(方括号内一字不改),飞书机器人据此把这张图内联发回会话。 参数 sheet_id 字符串传。

compose_specialA

跨多本书选料,一单建成一个"专项"(book_type='special')并批量挑题入区块。

专项 = word/教辅式文档(非试卷):结构 = 区块(sec) → 难度档(tier) → 题。挑题必在 区块框架下(secHint 指定落点,缺则新建)。跨书 = picks 里的题可来自不同源书, 专项只引用题库题 id,不动源书与题库(源书 item 数、题库 stem 全程不变)。

参数: title: 专项名(卷面可见,🔴 只写干净知识点名,绝不含内部词 层/素材/薄弱/★)。 picks: [{questionId?|nodeId?, secHint?, overrideJson?}, ...],逐条挑题。 grade / subject_id: 可选元信息。 返回: {ok, special_id, picked, secs:[{secId,name}], skipped};未登录/空 picks → {ok:false, reason}。

export_specialA

把专项导出成题目卷 / 答案卷双 PDF(苏俊宇卷版式,HTML→无头 Chrome→PDF)。

导出即对专项内每道 item used_count+1(认证计数=拿去上课的信号,不可逆软计数)。 🔴 卷面纪律:★ 仅 with_stars=True 显示(默认隐藏);【解析】仅 with_analysis=True 附带; 卷面绝不出现内部词(层/素材/薄弱)。

参数: special_id: 专项 id(字符串)。 papers: ['question','answer'] 任子集,缺省两卷都出。 with_analysis: 答案卷是否含解析(默认 True)。 with_stars: 是否显示难度星标(默认 False=隐藏)。 返回: {ok, special_id, question_url?, answer_url?, marked_count};空专项(无题) → {ok:false, reason}。

bind_special_to_lessonA

把专项绑到课次材料位(D4 持久绑定),或解绑,或查本课已绑材料。

🔴 只 UPDATE biz_course_plan_lesson.special_ids 单列——绝不整行 upsert(历史事故: 整行重写把 paper_slots 已绑 paper_id 抹掉)。BE 端 partial updateById 只写 special_ids。

参数: lesson_id: 课次 id(字符串)。 special_id: 专项 id(action=materials 时忽略)。 action: 'bind'(默认)/ 'unbind' / 'materials'(查本课已绑专项概要)。 返回: bind/unbind → {ok, lesson_id, special_ids:[...]}; materials → {ok, lesson_id, special_ids:[...], specials:[{id,title,itemCount}]}。

list_calc_typesA

计算题出题器·类型全表(覆盖人教版小学 1-6 年级计算谱系,46 类)。

每类一条 {code, name, grade(1-6), term(1上/2下)}:一年级 5以内加减/10以内/20以内进退位/ 两位数±一位数…,二年级 100以内/表内乘除/有余数除法…,三年级 三位数/乘除一位数/同分母分数/ 一位小数…,四年级 三位数乘两位数/四则混合/简算/小数加减…,五年级 小数乘除/解方程/异分母 分数/公因数公倍数/约分…,六年级 分数乘除混合/百分数互化/化简比/解比例。 返回: {ok, types:[{code,name,grade,term}]}。

generate_calc_paperA

一键生成计算题卷(确定性程序生成非 LLM)→ 题目卷 PDF(口算主场景不出答案卷)。

按 groups 逐组生成(每组一个类型一个多栏区块),约束内置:进退位可控、除法整除/ 有余数分型、分数自动约分/假分数化带分数、全卷跨组去重;题目卷带「姓名/用时/做对」栏。 参数: groups: [{type, count, label?, level?}],type 从 list_calc_types 查(严禁编造)。 title: 卷名(卷面可见,如"口算训练③(10分钟)")。 seed: 随机种子(任意字符串,同串复现同一份卷;空=随机)。 with_group_label: False 时不印组标(整卷混排风格)。🔴 组标卷面只印「一、二」 序号标识、绝不印练习内容名(学生自明,规范 2026-07-19)。 with_answer: True 才附带教师答案卷(🔴 口算卷默认不出,高年级分数/方程需核对时才开)。 fill_rows: 缺省 True = 每组题数向上凑整到栏数倍数(网格每行凑满,故实际题数可能 多于 count);要「填几题出几题」传 False。 返回: {ok, question_url, answer_url?, total, seed};未登录/未知类型 → {ok:false, reason}。

generate_calc_itemsA

只出计算题数据、不渲染 PDF —— 拿 {q,a} 塞进自有版面(每日一练等)。

与 generate_calc_paper 同一套生成器(确定性、跨组去重、难度档、seed 复现), 区别 = 不出 PDF、直接返回题面与答案,排版归 agent。 🔴 题目由程序生成,答案由生成器同步算出——不需要再人工/LLM 验算。 参数: groups: [{type, count, label?, level?}],type 从 list_calc_types 查(严禁编造); level=basic/advanced 出基础版/提高版(同考点不同难度,两版一次做完)。 seed: 随机种子(任意字符串,同串复现同一批题;空=随机)。 fill_rows: 🔴 缺省 False = 要几题给几题(自排版场景不该被静默改数量); True 才向上凑整到栏数倍数。 返回: {ok, total, seed, groups:[{label, cols, mode, items:[{q,a}]}]}。

render_shuzimi_figureA

数字谜竖式图生成器:按结构化 puzzles 确定性画竖式 PNG('?'=待填□方框),可直传 OSS。

🔴 结构规范:puzzles=1~4 个竖式横向并排,每个 {label?, rows:[{op,cells}...]}; cells 一字符一格(数字/汉字均可),'?'=□;op=+ - × ÷ 或空;op="HR"=横线行。 例(6□37+3□□=7183):rows=[{"op":"","cells":"6?37"},{"op":"+","cells":"3??"}, {"op":"HR"},{"op":"","cells":"7183"}]。 参数: upload=True 则直传 OSS(需先 login),False 只落本地;out_name 自定义文件名(可空)。 返回: {ok, local_path, width, height, oss_url?, asset_id?}。 产物用法: oss_url 塞 blockJson 图块(biz_question_block)或 ingest_question.images; 答案版把 '?' 换成实际字符再调一次。

upsert_punch_dayA

把「第 N 天」整天内容灌进打卡书(幂等:同 book+day 重调 = 覆盖,不重复建节点)。

天节点名由 BE 按 day 生成,goals 落节点 meta_json.goals(今日目标条,简短口径, 🔴 学生/家长可见 → 禁内部词:层/★/素材/薄弱/挑题)。modules 原样透传给 BE。

参数: book_id : 打卡书 id(🔴 字符串;list_books(book_type='daily_punch') 查,严禁编造) day : 第几天(1 起正整数;同一 day 重灌 = 整天覆盖,agent 侧批量修正走这条路) goals : 今日目标 ["乘法连续进位","小数退位减","年、月、日"](简短短语,非整句) modules : 模块数组,按卷面顺序排;两类结构 —— ① 计算模块(出题器现产,题目不在题库 → 内容随书存 content_json): {"type":"oral|vertical|stepwise", "title":"口算题", "items":[{"q":"357+276=","a":"633"}, ...]} type 三型 = oral 口算 / vertical 竖式 / stepwise 脱式;q 题面、a 答案成对给全 (答案缺失 = 解析卷开天窗)。题目由 generate_calc_items 产,无需再人工验算。 ② 轮换位(教辅真题,题在题库 → 引用不复制): {"type":"rotating", "title":"解决问题", "qids":["2077057695340310530", ...]} 🔴 qids = 题目 id 的字符串数组(雪花号,search_questions / 生成器给的原样字符串); 🔴 绝不传题面文本——题面由 BE 按 biz_question_block.block_json 渲染, 改题库即改打卡书,零漂移(D5/D11)。传文本 = 制造第二个半源,必被打回。

返回: {ok, node_id(str), item_ids:[str]};未登录/参数不合法/BE 报错 → {ok:false, reason}。

list_punch_daysA

列一本打卡书的全部天(目录 + 审核态)→ {ok, days:[{day,node_id,item_count,review}]}。

review = {status:"pending|passed|issue", issueCount}(审核页目录角标 ○/✓/⚠N 的数据源)。 🔴 重灌某天前先用它看该天是否已存在、审核到哪一步(已 passed 的天重灌会把审核态打回)。 参数 book_id 字符串传。

get_punch_dayA

读回「第 N 天」完整内容 → {ok, goals, modules, review}(展示页/生成器回读共用)。

modules 结构与 upsert_punch_day 入参同构:计算模块带 items:[{q,a}]; 🔴 返回与 upsert 入参同构(round-trip 语义):轮换位返 {type:'rotating',title,qids}—— 不含题面(改字回读→upsert 直接可用);要渲染态题面走 BE /teacher/punch/preview (blocks 由 BE 现取 block_json 组装,agent 无需自行拉题)。 review = {status, issues:[{module,seq,kind,note,resolved}]}。 🔴 局部改一天内容 = 先 get 拿全量 → 改 → 整天 upsert_punch_day 回灌(覆盖语义)。 参数: book_id 字符串;day 第几天(1 起)。

submit_punch_reviewA

提交「第 N 天」人眼审核结论(审核流主闭环)→ {ok, review}。

action 三态: pass 本天通过(🔴 全书每一天都 pass 后,BE 自动置书级审定标记——书由此可挂 SKU 上架) issue 本天记问题(必带 issues;天角标转 ⚠N,进全书问题清单) reopen 销账后重开审核(问题已修复重灌 → 打回 pending 再审一遍)

参数: book_id: 打卡书 id(字符串) day : 第几天(1 起) action : "pass" | "issue" | "reopen"(其余值直接拒,不猜) issues : action=issue 时必带 —— [{module, seq, kind, note}] module = 模块 type(oral/vertical/stepwise/rotating) seq = 模块内题号(1 起) kind = "难度不符" | "题面有误" | "排版" | "其他" note = 问题描述(一句话说清改什么,供重产/重灌时对账) 🔴 大改(换题/调难度)= 记问题 → 重产 → upsert_punch_day 重灌 → reopen 重审; 打卡页对题库题只读,改题库题走题目详情页修改态(D10/D11),不在本工具面内。

create_bookA

新建一本空书(起步)。book_type 开放注册制(BE 不校验,任意 snake_case slug 可传): 已注册值 = lecture讲义 / workbook练习册 / textbook电子课本 / special备课挑题专项(不进书架列表) / variant_special举一反三专项(SOP-2 立项即建) / daily_punch每日打卡。 新资料形态(口算集训、错题重练…)自定新 slug 即可,🔴 但须先到 认知/服务与能力总目录.md「书类型注册表」挂号 + book-ui BOOK_TYPE_LABEL 补中文名,否则前端显示裸 slug。

返回 {ok, book_id(str)};随后用 add_book_node + add_book_item 建目录树与内容, 或整树一次建书走 import(B 线录入直出书交接面,本工具面不含 import)。

list_booksA

我的书列表(owner 归属自动过滤 + type/subject/status 可选筛选)。

返回 {ok, books:[{id,bookType,title,subjectId,grade,nodeCount,questionCount,itemCount,...}]}。 统计字段(nodeCount/questionCount/itemCount)供书架卡片 'N 节 · M 题' 展示。

get_book_structureC

书结构整树(目录树 + 各节点内容项,一次返回可渲染)。

返回 {ok, book, tree:[{id,name,nodeType,kpId?,items:[{id,kind,questionId?,override?,explain?}],children:[...]}]}。 override 优先于题库原题面渲染;kind=explain 走 explain.title/text。

add_book_nodeA

给书加一个目录节点。node_type 自由值(chapter章/lecture讲/qtype_group题型组/tier难度档/sec区块…)。

parent_id 省 = 根层节点;kp_id 可选(KG 锚,仅标签,与树结构解耦 D8)。 节点名 name 卷面可见——🔴 禁内部词(层/★/素材/薄弱),只写干净知识点名。 返回 {ok, node_id(str)}。

add_book_itemA

给节点加一个内容项。kind=question 题引用(传 question_id)/ explain 讲解块(传 explain_*)。

🔴 question_id 一律 str(雪花号 JSON number 会截尾)。讲解块内容书自持(D2,不引用 KG 讲义层)。 返回 {ok, item_id(str)}。

bind_book_node_to_lessonA

把书籍章节(书架书目录节点)绑到课次材料位,或解绑,或查本课已绑书章节。

备课态口径(2026-07-15 扩展):「有专项或有书章节=已备好」——绑上任一书章节, 课次即显已备好(与 bind_special_to_lesson 同为材料位,两者并集推导)。

🔴 只 UPDATE biz_course_plan_lesson.book_node_ids 单列——绝不整行 upsert(历史事故: 整行重写把 paper_slots 已绑 paper_id 抹掉)。BE 端 partial updateById 只写 book_node_ids。

参数: lesson_id: 课次 id(字符串)。 node_id: 书章节节点 id(biz_shelf_node.id,字符串;action=materials 时忽略)。 action: 'bind'(默认)/ 'unbind' / 'materials'(查本课已绑书章节概要)。 返回: bind/unbind → {ok, lesson_id, book_node_ids:[...]}; materials → {ok, lesson_id, book_node_ids:[...], materials:[{nodeId,nodeTitle,bookId,bookTitle,questionCount}]}。

override_itemA

书内改一道题的题面(D3 override 副本):只影响本书,题库原子题不动

override.stem/options 写入 item.override_json;question_id 溯源保留(血缘不断)。 还原原题 = 传空 override({})即清。含数学 <> 不会被 XSS 剥。 返回 {ok}。

make_variantsA

举一反三·母题轮:读图解题打标 → 出母题卡(LLM 轮 ~60s)。图驱动 + D8 渲图旁路。

入参三选一(image_url > stem_text > question_id 优先级):

  • image_url : 公网可达图片 URL(.png/.jpg/.jpeg/.webp)——直接喂入口。

  • stem_text : 纯文本题干(可含 $LaTeX$/markdown)→ MCP 确定性渲图→传 OSS→喂引擎(rendered_stem:true)。

  • question_id: 题库题 id(字符串)→ 先查 biz_question_image 的 oss_url; 无图则走渲图旁路(取该题 stemText 渲图→传 OSS),返回 rendered_stem:true。

  • hint : 追加指令(如「侧重折叠」),默认「帮我把这道题举一反三」。

  • count : 变式数(进 message 文案,实际生成在 generate_variants)。

  • thread_id : 续跑同一母题会话用;缺省自动生成(uuid4)。返回值里带回,后续工具必传它。 返回: {ok, thread_id, status:"ready"|"need_confirm", mother_card, kg_candidates?, reply, rendered_stem?}。

  • rendered_stem=true ⇒ 母题图由渲图旁路生成(题干确定性渲染,opus 读图 OCR)。

  • status=need_confirm(低置信/骨架空分支)→ 读 kg_candidates 挑章 → confirm_variant_chapter。

  • status=ready → 直接 generate_variants。

  • mother_card=None(入口回催图/催登录)→ ok:false,hint=引擎回文(reply)。

confirm_variant_chapterA

确认母题所属章节(need_confirm 分支续跑)→ 回填锚点重锚(~3s)。

参数: thread_id(make_variants 返回);chapter_id(从 kg_candidates 挑的真实章节 id,字符串)。 返回: {ok, thread_id, status:"ready", mother_card}。之后调 generate_variants。

generate_variantsA

触发变式生成(start_variants 续跑,LLM 轮 ~70s)→ 取题组。

参数: thread_id : make_variants/confirm 后的会话 id。 auto_verify: True=生成后每题自动 sympy 验算;False(默认,产品口径)=每题 tier=pending, 按需 verify_variant。透传 agent_config.auto_verify。 返回: {ok, thread_id, count, variants:[{item_id, seq, stem, answer, solution, qtype, difficulty, tier, verify_status, figure_spec, dna, question_id}]}。 🔴 生成偶发首轮空题组(opus 对压轴母题首轮不收尾)——本工具按 AC4「重试≤2」自动再触发一次 start_variants(实测第二次即出题);两次仍空 → ok:false + 引擎回文,交驱动 agent 处置。

verify_variantA

对某道变式独立验算(无状态 sympy 重算,~17s)→ 判决。persist 前须逐题过(说明书铁律)。

参数: thread_id;item_id(generate_variants 返回的变式 id,= seq,字符串)。 返回: {ok, item_id, verdict:"pass"|"fail"|"degrade", reason, computed}。 pass=标答自洽 / fail=标答错(computed=真算值) / degrade=sympy 吃不下转人工(非判错)。

edit_variantA

手动编辑某道变式(零 LLM,只 patch 传入字段)→ 标手动编辑态。

参数: thread_id;item_id(= seq,字符串); patch: {stem?, answer?, analyze?}(analyze 映射 toolkit 的 solution 字段;None 键不改)。 返回: {ok, item_id, item}(编辑后该题快照)。改后建议重跑 verify_variant。

compose_variant_figureA

为某道变式造图(~12s)→ 中性 JSXGraph DSL(bbox+objects),客户端 GeoEngine 渲活图。

参数: thread_id;item_id(= seq,字符串); dsl: 传入=覆盖重绘(把该 DSL 作修正依据令引擎照此重画;不传=从题面/figure_spec 现推)。 返回: {ok, item_id, needs_figure, figure_spec}(figure_spec=JSXGraph DSL:{bbox, objects[]}); 造图降级 → {ok:false, needs_figure:true, reason}。

persist_variantsA

把变式落库拿真实 qid(owner=登录老师)。🔴 落库前须逐题 verify_variant(说明书铁律)。

参数: thread_id;item_ids: 只落指定几题(item_id/seq 字符串列表);缺省=全部入库。 返回: {ok, results:[{item_id, question_id:str}], view_url}(题库页深链,老师视觉验收入口)。

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
_manual_data
_manual_prep
_manual_variant
_manual_all

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/RuntimeAll/teacher-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server