docs: 更新TRA编程培训课程材料、课件与AI技能交付包

- 新增全套考试卷、评估卷、参考答案与考试培训包模板
- 修正slidev培训课件:调整模块标题、链路图缩放,替换工单示例为通用业务对象
- 修正课堂红线表述为证据红线,将小组作业调整为个人作业要求
- 新增权限、状态流转、审计日志、看板指标四类可复用业务规则块
- 整理AI工程技能交付包,迁移旧资产至客户专属交付包并统一术语
- 新增考试输出模板与TRAE使用记录文档
This commit is contained in:
smart
2026-08-08 11:00:36 +08:00
parent e7d6db93c3
commit 67837df8d0
169 changed files with 6618 additions and 2887 deletions
+197
View File
@@ -0,0 +1,197 @@
from copy import deepcopy
from pathlib import Path
from docx import Document
from docx.enum.text import WD_BREAK
ROOT = Path('/Users/smart/Documents/TRAE培训/TRAE课件/exams')
SOURCE = ROOT / 'TRAE编程培训考试评估卷-实操与项目实战.docx'
TARGET = ROOT / 'TRAE编程培训考试评估卷-实操与项目实战(修订版).docx'
def replace_paragraph(paragraph, text, bold=None, size=None):
first_run = paragraph.runs[0] if paragraph.runs else None
old_bold = first_run.bold if first_run else None
old_size = first_run.font.size if first_run else None
paragraph.clear()
run = paragraph.add_run(text)
run.bold = old_bold if bold is None else bold
if size is not None:
run.font.size = size
elif old_size is not None:
run.font.size = old_size
return paragraph
def replace_cell(cell, text, bold=None):
for paragraph in cell.paragraphs[1:]:
paragraph._element.getparent().remove(paragraph._element)
paragraph = cell.paragraphs[0]
first_run = paragraph.runs[0] if paragraph.runs else None
old_bold = first_run.bold if first_run else None
old_size = first_run.font.size if first_run else None
paragraph.clear()
run = paragraph.add_run(text)
run.bold = old_bold if bold is None else bold
if old_size is not None:
run.font.size = old_size
def find_paragraph(document, text):
for paragraph in document.paragraphs:
if text in paragraph.text:
return paragraph
raise ValueError(f'Paragraph not found: {text}')
def set_table_rows(table, rows):
while len(table.rows) < len(rows):
table.add_row()
if len(table.rows) != len(rows):
raise ValueError('Unexpected table row count')
for row, values in zip(table.rows, rows):
if len(row.cells) != len(values):
raise ValueError('Unexpected table column count')
for cell, value in zip(row.cells, values):
replace_cell(cell, value)
def clone_paragraph(document, source_paragraph, text):
paragraph = document.add_paragraph()
placeholder = paragraph._p
element = deepcopy(source_paragraph._element)
placeholder.getparent().replace(placeholder, element)
paragraph._p = paragraph._element = element
replace_paragraph(paragraph, text)
return paragraph
def clone_table(document, source_table):
table = document.add_table(rows=1, cols=1)
placeholder = table._tbl
element = deepcopy(source_table._element)
placeholder.getparent().replace(placeholder, element)
table._tbl = table._element = element
return table
def insert_after(anchor, element):
anchor._element.addnext(element._element)
doc = Document(SOURCE)
replace_paragraph(find_paragraph(doc, '实操 + 公务用车项目实战综合卷'), '实操 + 系统用户状态管理综合卷')
# 1. Fixed time: 20 minutes for A, 60 for B, 10 for submission check.
set_table_rows(doc.tables[0], [
['项目', '说明'],
['适用对象', '参加 TRAE 编程培训并完成 JeecgBoot 实战的技术学员。'],
['考试形式', '允许使用 TRAE,上机完成;提交 Markdown 产物包与 AI 使用记录。'],
['建议时长', '90 分钟:A 卷 20 分钟,B 卷 60 分钟,提交检查 10 分钟。'],
['总分', '100 分:小实操 30 分,项目实战 70 分。'],
['评估目标', '考察学员能否把真实研发问题拆成 TRAE 可执行、团队可验收、后续可复用的工程任务。'],
])
set_table_rows(doc.tables[2], [
['部分', '时间', '分值', '形式', '核心能力'],
['A. 小实操', '20 分钟', '30 分', '个人完成', 'Prompt 改写、证据判断'],
['B. 项目实战', '60 分钟', '70 分', '个人完成', '项目理解、变更边界、测试安全网、Review 与交付'],
['提交检查', '10 分钟', '-', '个人完成', '核对目录、文件名与执行状态'],
['合计', '90 分钟', '100 分', '上机 + 文档交付', '个人基本功 + 项目落地能力'],
])
# 2. Deliverable templates and missing-file treatment.
replace_paragraph(find_paragraph(doc, '建议要求学员提交一个 Markdown 目录包'), '使用 exam-kit/exam-output/ 中的模板提交,目录和文件名不得改动:')
set_table_rows(doc.tables[3], [
['文件', '主要内容'],
['01-project-understanding.md', '结论摘要、证据表、影响与保护边界、待确认项'],
['02-change-and-test.md', '验收标准、校验位置、不改动项、测试矩阵、执行状态'],
['03-pr-and-assets.md', 'Review、PR 说明、可复用 Prompt、Rule/Checklist/Skill 设计卡'],
['trae-usage-record.md', 'Prompt、采纳/否决、人工判断点、证据或验证状态;缺失则任务 3 的如实记录项计 0 分'],
])
# Insert the fixed source / tools contract after the deliverable table.
input_heading = clone_paragraph(doc, find_paragraph(doc, '二、最终提交物'), '考试输入与作答约束')
input_intro = clone_paragraph(doc, find_paragraph(doc, '使用 exam-kit/exam-output/'), 'B 卷仅以 exam-kit/ 中的固定版本、指定 diff 和模板为准。')
input_table = clone_table(doc, doc.tables[3])
set_table_rows(input_table, [
['项目', '统一要求'],
['固定源码', 'GitHub jeecgboot/JeecgBoot;标签 v3.9.2:7df07a823fd558be857d0208ccae96342539fbc1。'],
['指定模块', 'jeecg-module-system;以 SysUserController.frozenBatch 和 SysUserServiceImpl.updateStatus 为事实入口。'],
['指定 diff', 'exam-kit/review-diff.patch;仅以该两文件变更为 Review 输入。'],
['允许材料', 'TRAE、GitHub 固定版本与 exam-kit;版本不一致时记录环境问题并联系监考人员。'],
])
anchor = doc.tables[3]
insert_after(anchor, input_heading)
insert_after(input_heading, input_intro)
insert_after(input_intro, input_table)
replace_paragraph(find_paragraph(doc, '本部分由学员个人完成,重点考察 TRAE 基础使用'), '本部分由学员个人完成,重点考察 TRAE 基础使用与 AI 协作基本功。限时 20 分钟。')
replace_cell(doc.tables[5].cell(0, 0), '原始 Prompt\n帮我看一下系统用户批量冻结/解冻功能有没有问题。')
replace_cell(doc.tables[7].cell(0, 0), 'AI 输出片段\n页面已限制状态值,因此 Service 不需要校验;直接调用接口也不会有问题。')
replace_paragraph(find_paragraph(doc, 'B 卷:公务用车项目实战'), 'B 卷:系统用户状态管理项目实战(70 分)')
replace_paragraph(find_paragraph(doc, '本部分使用 JeecgBoot 现有公务用车模块'), '本部分使用 exam-kit 指定版本的 JeecgBoot 系统用户模块,由学员个人完成。限时 60 分钟。')
replace_cell(doc.tables[9].cell(0, 0), '实战任务背景\n案例使用 GitHub 公开的 JeecgBoot v3.9.2 中系统用户模块。训练改造要求:批量冻结/解冻用户时,后端必须拒绝空白或 1、2 之外的状态值;页面限制不能替代 Service 入口的业务保护。本次考试不要求完整开发所有代码,重点考察如何使用 TRAE 完成项目理解、变更边界分析、测试设计、Review 和资产沉淀。')
# 3. Observable requirements for B tasks.
replace_paragraph(find_paragraph(doc, '使用 TRAE 输出公务用车模块结构说明'), '使用 TRAE 完成 01-project-understanding.md:说明系统用户模块结构、批量冻结/解冻接口、Service 入口、权限、管理员保护、缓存和测试入口。证据表至少含 5 条有效依据,每条必须写文件路径、符号或方法,以及它证明的结论;另说明非法状态值与接口、用户状态写入、缓存失效的影响关系,并区分事实、推断和待确认项。')
replace_paragraph(find_paragraph(doc, '输出最小改造方案和测试安全网'), '完成 02-change-and-test.md:说明应在哪个方法、哪次用户状态写入之前校验非法状态值;列出不改动的接口权限、管理员保护和缓存失效逻辑及依据;为空白或非法状态、正常冻结、正常解冻、无权限调用、管理员保护与缓存/用户状态回归逐项写明预期结果、验证入口、执行状态。已执行项必须给出命令和结果,未执行项必须说明原因和计划验证方式。')
replace_paragraph(find_paragraph(doc, '基于 `main...training/require-cancel-reason`'), '基于 exam-kit/review-diff.patch 的实际两文件 diff,完成 03-pr-and-assets.md:生成 Review 和 PR 说明,再输出一条可复用 Prompt 及一条 Rule、Checklist 或 Skill 设计卡。Review 可以得出“未发现阻断问题”的结论,但必须说明检查范围、证据和残余风险;所有已执行与未执行的验证必须如实标注。')
# 4. Replace vague scoring with observable, non-overlapping criteria.
set_table_rows(doc.tables[6], [
['评分项', '分值'],
['背景与目标清楚', '3'],
['范围可控,限定目录、文件、接口或页面', '2'],
['约束清楚,含项目规则、安全、权限、测试要求及信息不足时先提问', '4'],
['输出格式明确,如表格、清单、报告或 PR 说明', '3'],
['验收标准和证据要求明确', '3'],
])
set_table_rows(doc.tables[10], [
['评分项', '分值'],
['系统用户模块与批量冻结/解冻入口明确', '4'],
['frozenBatch 接口、Controller 与 Service 调用链明确', '5'],
['权限、管理员保护、缓存和测试入口明确', '4'],
['至少 5 条有效证据,每条含路径、符号和证明内容', '7'],
['影响与保护边界完整,并区分事实、推断和待确认项', '5'],
])
set_table_rows(doc.tables[11], [
['评分项', '分值'],
['状态校验改造目标和验收标准清楚', '4'],
['校验方法、位置和副作用前后顺序正确', '5'],
['不改动项及其依据清楚', '4'],
['非法状态、冻结、解冻、权限、管理员保护/缓存五类场景完整', '8'],
['验证命令、执行结果或未执行原因明确', '4'],
])
set_table_rows(doc.tables[12], [
['评分项', '分值'],
['Review 结论、证据、风险和验收方式完整', '5'],
['PR 说明包含背景、实现、测试和风险', '5'],
['可复用 Prompt 的输入、约束、输出和验收清楚', '5'],
['Rule、Checklist 或 Skill 设计卡的边界和验收清楚', '3'],
['AI 使用记录完整;不编造测试结果,能说明人工取舍', '2'],
])
replace_paragraph(find_paragraph(doc, '三、综合评分维度'), '三、阅卷通用原则(不重复计分)')
set_table_rows(doc.tables[13], [
['阅卷原则', '优秀表现', '使用方式'],
['证据意识', '结论能引用文件、函数、接口、日志或数据来源', '仅用于判断对应任务中的证据项,不单独追加扣分'],
['上下文选择', '知道先全局找路,再文件聚焦,再代码细查', '仅用于判断对应任务中的任务表达与证据项'],
['任务表达', '背景、目标、范围、约束、输出、验收完整', '仅用于判断 A 卷 Prompt 改写项'],
['风险意识', '能识别权限、状态、审计、异常态、测试缺口', '仅用于判断 B 卷影响、测试和 Review 项'],
['人机协作', '知道哪些让 AI 做,哪些必须人工确认', '仅用于判断 AI 使用记录与待确认项'],
['资产意识', '能沉淀可复用 Prompt、Rule、Checklist、Skill', '仅用于判断任务 3 的资产项'],
])
set_table_rows(doc.tables[15], [
['环节', '要求'],
['发卷前检查', '发放 exam-kit;核对 GitHub 仓库、v3.9.2 完整提交和 review-diff.patch 均可读取。'],
['考试输入', '考试前完成 GitHub 固定版本下载;版本不一致时由监考人员处理,不得由学员自行替换版本。'],
['过程记录', '必须提交 trae-usage-record.md;缺失时任务 3 的 AI 使用记录项计 0 分。'],
['阅卷方式', '先核对四个交付文件,再按对应评分表打分;通用原则不重复扣分。'],
['独立作答', '抽查关键路径、证据来源和人工取舍,确认交付物与作答者理解一致。'],
])
doc.core_properties.title = 'TRAE 编程培训考试评估卷(修订版)'
doc.core_properties.subject = '实操与系统用户状态管理综合卷'
doc.save(TARGET)
print(TARGET)