""" LLM报告引擎:分段上下文 + 并行调用 + 报告文字生成 核心设计: 1. 报告拆分为~20个独立段落(segment),每段有独立prompt 2. 每段只喂该段需要的结构化数据(JSON),节省token 3. 10路并发调用LLM,~2分钟完成全部段落 4. Jinja2模板渲染prompt,保证格式统一 5. 支持缓存,同一数据不重复调用 """ import asyncio import hashlib import json import logging import re import time from pathlib import Path from typing import Dict, List, Optional, Callable from openai import OpenAI from ..config import ( LLM_BASE_URL, LLM_API_KEY, LLM_MODEL, LLM_MAX_CONCURRENCY, DIMENSION_FRAMEWORK, LEVEL_DESCRIPTIONS, SCHOOL_TYPE_MAP, ) # 维度概念定义(来自参考报告,每个维度分析时作为开头段落的背景) DIMENSION_DEFINITIONS = { "课程领导力": "课程领导力是指学校在高质量落实国家课程,根据学校培养目标构建核心素养导向校本课程体系上的关键能力,强调对课程多样性及特色性的重视,强调在核心素养引领下建构富有学校特色的课程育人体系。具体可分为国家标准遵循、课程结构建设和课程规范落实。", "教学变革力": "教学变革力是指学校由知识本位转向素养本位的变革力量,它在一定程度上决定了学生核心素养培育的成效,强调对过分重视接受学习、死记硬背、机械训练现状的转变。具体可分为教学方式变革和作业设计与管理变革。", "学生发展指导力": "学生发展指导力是指学校为学生提供全面、个性化发展指导的能力,涵盖学科学习的精准辅导和面向未来的生涯规划。具体可分为学科发展的个性化辅导和学生生涯发展指导。", "教师发展支持力": "教师发展支持力是指学校在教师专业成长方面的制度保障与资源投入能力,强调为教师提供多元化的发展路径与支持平台。具体可分为培训支持、教研支持和项目支持。", "教育质量评估力": "教育质量评估力是指学校建立科学评价体系、全面监测教育质量的能力,强调以核心素养为导向,综合运用多种评价方式。具体可分为科学评价观、学业质量评估、综合素质评估和实践活动评估。", "教育条件保障力": "教育条件保障力是指学校在区域政策支持、硬件环境和资源配置方面为课程实施提供保障的能力。具体可分为区域推进、环境支持和资源支持。", "数字化赋能力": "数字化赋能力是指学校运用数字技术推动教学创新、评价精准化和课程持续优化的能力,反映学校数字化转型的深度与广度。具体可分为教学方式创新、评价精准化与个性化和课程迭代优化。", } # 三级维度概念定义(来自参考报告) SUB_DIMENSION_DEFINITIONS = { "国家标准遵循": "国家标准遵循是课程领导力的首要条件,强调学校在校内开足开齐开好国家课程的重要性,以《普通高中课程方案(2017年版2020年修订)》《上海市普通高中课程实施方案》为标准,反映学校在各类型课程课时、学分上的达标情况。", "课程结构建设": "课程结构建设强调学校在课程设置中需着重考虑各学科、各类型的课程结构与比例,强调课程设置的科学性与合理性,反映学校在三类课程中的结构合理性与内容丰富性。", "课程规范落实": "课程规范落实强调学校课程规范落实作为课程的价值定位和制度保证,关注建设规范文本的完备性和过程性档案记录的建成与使用情况。", "教学方式变革": "教学方式变革强调学生主动参与和探究合作,引导学生在实践中学习、在教师指导下深度学习,强调跨学科和信息技术常态化应用的学习。", "作业设计与管理变革": "作业设计与管理变革关注学校在创新性作业设计、作业时长管理、批改反馈和属性标注等方面的系统化程度。", "学科发展的个性化辅导": "学科发展的个性化辅导关注学校为学生提供学科学习方面的精准化、个性化辅导的能力,包括辅导时长、辅导内容确定方式和辅导形式。", "学生生涯发展指导": "学生生涯发展指导关注学校为学生提供生涯规划教育的实施方式、覆盖率、师资队伍和资源支持情况。", "培训支持": "培训支持反映学校为教师提供外出培训机会的力度,以各学科教师平均外出培训人数为核心指标。", "教研支持": "教研支持反映学校教研活动的数量和质量,体现学校教研文化的深度和教研机制的有效性。", "项目支持": "项目支持反映学校以课题项目引领教师专业发展的情况,以各学科负责校级以上项目的覆盖情况为核心指标。", "科学评价观": "科学评价观反映学校在课程教学各方面对学生核心素养发展的关注程度和广度。", "学业质量评估": "学业质量评估关注学校在校本化评价工具研制、使用以及学期考试质量分析的系统性。", "综合素质评估": "综合素质评估关注学校在校本化综合素质评价体系的建设、平台支持和评价结果运用情况。", "实践活动评估": "实践活动评估关注学校在研究性学习、社会考察和学科实践活动等领域校本化评价工具的研制与使用。", "区域推进": "区域推进反映学校所在区教育局对高中课程教学工作的推动力度,包括会议频次、管理文件和配套措施。", "环境支持": "环境支持反映学校信息化环境和硬件设施对课程教学的支撑情况。", "资源支持": "资源支持反映学校校内外资源配置和师资水平的综合状况。", "教学方式创新": "教学方式创新关注信息技术与教学融合的深度以及教师常态化使用信息技术开展教学的情况。", "评价精准化与个性化": "评价精准化与个性化关注学校运用信息技术平台支持学科诊断与综合素质评价的能力层次。", "课程迭代优化": "课程迭代优化关注学校教学数字化转型的规划制度和信息化系统在业务流程中的应用程度。", } logger = logging.getLogger(__name__) # ========== System Prompt ========== SYSTEM_PROMPT_ZH = """你是一位资深的教育评估专家,正在撰写上海市高中课程实施监测数据分析报告。本报告由上海市教师教育学院(上海市教育委员会教学研究室)指导,面向学校管理层。 ## 写作风格参照 请严格参照以下范文风格撰写。范文来自教育部门正式报告: <范文示例-总体表现> 测评显示,贵校课程实施总体得分是56.69分。高于上海市均值6.73分,高于XX区均值5.15分,高于市实验性示范高中均值4.22分。通过基于学校领导力视角的各维度聚类分析发现,上海市学校课程实施状况共有2类,分别是:课程实施较好类、课程实施待提高类。贵校属于课程实施较好类。 综合来看,贵校在教育条件保障力和数字化赋能力上表现好,得分为60.15和57.76。在教学变革力和学生发展指导力上表现相对较差,得分为52.41和54.05。 范文示例> <范文示例-维度整体> 课程领导力是指学校在高质量落实国家课程,根据学校培养目标构建核心素养导向校本课程体系上的关键能力,强调对课程多样性及特色性的重视。具体可分为国家标准遵循、课程结构建设和课程规范落实。 从课程领导力的得分情况来看,贵校课程领导力的得分为57.51分,高于区均值4.41分,高于市实验性示范性高中均值4.82分。 分维度来看,课程结构建设的得分最高为63.93分;其次为课程规范落实54.42分,国家标准遵循的得分最低为54.17分。 通过对课程领导力的三个子维度进行聚类发现,贵校课程领导力类型为高领导能力型。 范文示例> <范文示例-子维度> 从国家标准遵循的得分情况来看,贵校国家标准遵循的得分为54.17分,高于区均值0.20分,高于市实验性示范性高中均值1.90分。 总体来看,贵校国家标准遵循水平处于水平三,区内处于水平三及以上的学校比例为66.6%,同类学校处于水平三及以上的比例为42.0%。 范文示例> ## 写作规范 1. 称谓:统一使用"贵校"指代被分析的学校,不要使用学校名称 2. 语言风格:客观、平实、严谨的学术报告体,避免修辞性、评价性、主观感受性表述 3. 数据引用:所有分析必须基于提供的数据,引用具体数值,使用"高于/低于XX均值X.XX分"的句式 4. 对比结构:按"贵校得分→与区均值对比→与同类学校对比→水平分布情况"的顺序展开 5. 水平分布:引用区内处于各水平的学校数量或比例 6. 改进建议:针对薄弱维度给出具体、可操作的建议,对不同类型学校有差异化要求 7. 图表引用:不生成图表,但可用"如图X-X所示"等引用(图表由模板渲染) 8. 篇幅控制:总体表现400-600字,维度整体300-500字,子维度200-350字,总结400-600字 ## 学校类型差异化要求 - 市实验性示范性高中:对标更高要求,强调引领示范作用,改进建议要求迈向卓越 - 区实验性示范性高中:强调在区域内的标杆作用,改进建议着眼特色发展 - 特色高中:突出其特色定位,分析特色与各维度的关联 - 公办普通高中/民办高中:关注基础达标情况,改进建议务实可操作 ## HTML输出格式规范(必须严格遵守) - 直接输出HTML片段,不要包裹在```html代码块中 - 只使用以下标签:
、、 标签包裹
- 关键数据用加粗
- 第一个 直接开始正文分析,不要写"一、""(一)"等编号前缀
- 禁止使用任何emoji符号(如🎯📊⚠️🔴等),全文必须为纯文字+HTML标签
"""
# ========== System Prompt (English) ==========
SYSTEM_PROMPT_EN = """You are a senior educational-evaluation expert preparing a formal data-analysis report on senior secondary school curriculum implementation in Shanghai. The work is supervised by the Shanghai Academy of Educational Sciences (Shanghai Curriculum Research Office) and addressed to school leadership. Your writing must adopt the formal, evidence-based academic register used by OECD/PISA reports and national education-monitoring publications.
## Style Reference (mandatory)
Adopt the formal, restrained, and rigorously evidence-based register used by OECD/PISA reports.
, , ... (分析文字生成失败:{str(e)}) (generation failed: {e}) (生成失败:{e}) 、、 )
cleaned = re.sub(r' \1 / 五、总结与改进建议 \s*\s*[一二三四五六七八九十]+[、..]\s*[^<]{2,20}\s*\s* \s*\s*[((][一二三四五六七八九十]+[))]\s*[^<]{2,20}\s*\s* I. Overall Performance \s*\s*[IVX]+\.\s*[A-Z][A-Za-z\s\-]{2,40}\s*\s* 【...】 开头的行
cleaned = re.sub(
r' \s*【',
' 【',
cleaned
)
# 10. 清理多余空行
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned)
return cleaned.strip()
def _generate_segment(self, segment: Dict, cache_key: str, lang: str = "zh") -> str:
"""生成单个段落"""
prompt = segment["prompt"]
content, truncated = self._call_llm(prompt, lang=lang)
# 后处理:去除代码块标记 + 清洗不规范HTML
content = self._normalize_html(content)
# 英文模式校验:若输出包含大量中文(除已知专有名词),自动重试一次(强化指令)
if lang == "en" and content:
cn_count = self._count_chinese_excluding_proper_nouns(content)
if cn_count > 10:
logger.warning(
f"段落 {segment['id']} 英文模式下出现 {cn_count} 个中文字符,自动重试..."
)
retry_prompt = (
"REMINDER: Your previous attempt contained Chinese characters. "
"This time, you MUST output exclusively in formal English. "
"Translate every Chinese phrase, label, tag, level marker, and bracketed annotation. "
"Apply the translation rules exactly as specified.\n\n"
+ prompt
)
content2, truncated2 = self._call_llm(retry_prompt, lang=lang)
content2 = self._normalize_html(content2)
cn2 = self._count_chinese_excluding_proper_nouns(content2)
if cn2 < cn_count:
content, truncated = content2, truncated2
logger.info(
f"段落 {segment['id']} 重试后中文字符 {cn_count} → {cn2}"
)
# 只有未截断且有效的内容才写入缓存
if content and "生成失败" not in content:
if truncated:
logger.warning(f"段落 {segment['id']} 被截断,不写入缓存(下次会重新生成)")
else:
self._set_cache(cache_key, content)
return content
@staticmethod
def _count_chinese_excluding_proper_nouns(text: str) -> int:
"""统计文本中的中文字符数(排除可能出现的专有名词如学校名/区名)。
用于英文模式下的输出校验。
"""
# 简单做法:剥离常见专有名词后统计 CJK 字符
cleaned = text
# 已在 i18n DISTRICTS 中映射的区名(中文形式可能在数据中出现)
for district in [
"长宁区", "杨浦区", "闵行区", "浦东新区", "嘉定区",
"宝山区", "金山区", "静安区", "奉贤区", "普陀区", "徐汇区",
]:
cleaned = cleaned.replace(district, "")
return len(re.findall(r"[\u4e00-\u9fa5]", cleaned))
def _build_segments(self, report_data: Dict) -> List[Dict]:
"""构建所有报告段落的prompt"""
school = report_data["school"]
school_info = report_data["school_info"]
segments = []
# ===== 第二部分:总体表现 =====
segments.append({
"id": "part2_overview",
"prompt": self._prompt_overview(report_data),
"data": report_data["overall"],
})
# 第二部分图表解读
chart_caption_specs_p2 = [
("part2_caption_profile", "profile", "图2-0a 学校课程实施画像总览 / 图2-0b 三级维度优势-短板象限分析"),
("part2_caption_scores", "scores", "图2-1 课程实施七维度得分对比 / 图2-2 课程实施七维度雷达图"),
("part2_caption_cluster", "cluster", "图2-3 学校课程实施总体类型分布 / 图2-4 两类学校课程实施特征对比"),
("part2_caption_ranking", "ranking", "图2-5 学校课程实施类型特征对比(雷达图) / 图2-6 区内各校总体得分排名"),
("part2_caption_correlation", "correlation", "图2-7 课程实施维度间相关性分析"),
]
for seg_id, chart_type, chart_title in chart_caption_specs_p2:
segments.append({
"id": seg_id,
"prompt": self._prompt_chart_caption_overview(report_data, chart_type, chart_title),
"data": {"chart_type": chart_type},
})
# ===== 第三~九部分:七个维度 =====
dim_order = [
("课程领导力", "part3"),
("教学变革力", "part4"),
("学生发展指导力", "part5"),
("教师发展支持力", "part6"),
("教育质量评估力", "part7"),
("教育条件保障力", "part8"),
("数字化赋能力", "part9"),
]
for dim_name, part_id in dim_order:
# 维度整体分析
segments.append({
"id": f"{part_id}_overall",
"prompt": self._prompt_dimension_overall(report_data, dim_name),
"data": report_data["dimensions"].get(dim_name, {}),
})
# 维度图表解读
part_num = part_id.replace('part', '')
dim_chart_specs = [
(f"{part_id}_caption_dim_score", "dim_score", f"图{part_num}-1 {dim_name}得分情况 / 图{part_num}-2 {dim_name}各子维度得分情况"),
(f"{part_id}_caption_sub_detail", "sub_detail", f"图{part_num}-3 {dim_name}各子维度对比详情 / 图{part_num}-4 {dim_name}各子维度水平分布"),
(f"{part_id}_caption_scatter", "scatter", f"图{part_num}-5 {dim_name}聚类分析散点图"),
]
for seg_id, chart_type, chart_title in dim_chart_specs:
segments.append({
"id": seg_id,
"prompt": self._prompt_chart_caption_dimension(report_data, dim_name, chart_type, chart_title),
"data": {"dim": dim_name, "chart_type": chart_type},
})
# 各三级维度分析
sub_dims = DIMENSION_FRAMEWORK[dim_name]["sub_dimensions"]
for i, sub_dim in enumerate(sub_dims):
sub_data = report_data["sub_dimensions"].get(sub_dim, {})
if sub_data:
segments.append({
"id": f"{part_id}_sub{i+1}_{sub_dim}",
"prompt": self._prompt_sub_dimension(report_data, dim_name, sub_dim),
"data": sub_data,
})
# ===== 紧急预警(全区垫底子维度) =====
alert_subs = self._find_alert_sub_dimensions(report_data)
if alert_subs:
segments.append({
"id": "part2_alert",
"prompt": self._prompt_alert(report_data, alert_subs),
"data": {"type": "alert", "subs": [s["name"] for s in alert_subs]},
})
# ===== 学校定位差距分析(所有学校均生成,按类型差异化措辞) =====
segments.append({
"id": "part2_positioning_gap",
"prompt": self._prompt_positioning_gap(report_data),
"data": {"type": "positioning_gap"},
})
# ===== 总结与建议 =====
# 瀑布图解读
segments.append({
"id": "part10_caption_waterfall",
"prompt": self._prompt_chart_caption_waterfall(report_data),
"data": {"type": "waterfall"},
})
# 跨维度因果链分析
segments.append({
"id": "part10_cross_dimension",
"prompt": self._prompt_cross_dimension_analysis(report_data),
"data": {"type": "cross_dimension"},
})
# 最紧迫的三件事
segments.append({
"id": "part10_top3_priorities",
"prompt": self._prompt_top3_priorities(report_data),
"data": {"type": "top3"},
})
segments.append({
"id": "part10_conclusion",
"prompt": self._prompt_conclusion(report_data),
"data": {"overall": report_data["overall"], "dimensions": report_data["dimensions"]},
})
# ===== 第十一部分:实践落地行动指南(拆分为5个并行segment) =====
action_data = self._classify_sub_dimensions(report_data)
# 水平1:急需突破
if action_data["critical"]:
segments.append({
"id": "part11_critical",
"prompt": self._prompt_action_critical(report_data, action_data),
"data": {"type": "critical", "subs": [s["name"] for s in action_data["critical"]]},
})
# 水平2:重点攻关
if action_data["attention"]:
segments.append({
"id": "part11_attention",
"prompt": self._prompt_action_attention(report_data, action_data),
"data": {"type": "attention", "subs": [s["name"] for s in action_data["attention"]]},
})
# 水平3:稳步巩固
if action_data["maintain"]:
segments.append({
"id": "part11_maintain",
"prompt": self._prompt_action_maintain(report_data, action_data),
"data": {"type": "maintain", "subs": [s["name"] for s in action_data["maintain"]]},
})
# 水平4:深化引领
if action_data["excellent"]:
segments.append({
"id": "part11_excel",
"prompt": self._prompt_action_excel(report_data, action_data),
"data": {"type": "excel", "subs": [s["name"] for s in action_data["excellent"]]},
})
# 学期行动时间表(汇总前面所有板块的要点)
segments.append({
"id": "part11_timeline",
"prompt": self._prompt_action_timeline(report_data, action_data),
"data": {"type": "timeline"},
})
return segments
# ========== 辅助:学校背景信息 ==========
@staticmethod
def _school_background(data: Dict) -> str:
"""构建学校背景信息文本,供各 prompt 引用"""
school = data["school"]
info = data.get("school_info", {})
school_type = info.get("type", "")
nature = info.get("nature", "")
feature = info.get("feature", "")
lines = [f"- 学校:{school}"]
lines.append(f"- 学校类型:{school_type}")
if nature:
lines.append(f"- 办学性质:{nature}")
if feature and feature != "非特色高中":
lines.append(f"- 办学特色:{feature}")
# 类型定位说明
if "市实验性示范性" in school_type:
lines.append("- 定位要求:作为市实验性示范性高中,应在区域乃至全市发挥课程改革的引领示范作用,各维度表现应对标更高要求")
elif "区实验性示范性" in school_type:
lines.append("- 定位要求:作为区实验性示范性高中,应在区域内发挥标杆引领作用")
elif "特色高中" in school_type:
lines.append(f"- 定位要求:作为{feature},应在特色领域形成课程品牌,带动相关维度发展")
elif "民办" in nature:
lines.append("- 定位要求:作为民办高中,应在办学灵活性方面发挥优势,重点关注基础达标和特色发展")
return "\n".join(lines)
# ========== Prompt生成函数 ==========
def _prompt_overview(self, data: Dict) -> str:
"""第二部分:总体表现"""
school = data["school"]
overall = data["overall"]
dims = data["dimensions"]
dims_text = "\n".join([
f" - {name}: {info['score']:.2f}分 (区均值{info['district_avg']:.2f},差值{info['diff_district']:+.2f}分,同类学校均值{info['same_type_avg']:.2f}分,区内排名第{info['rank_in_district']}名/{overall['total_schools']}所)"
for name, info in dims.items()
])
# 按与区均值差值排序找出相对强弱维度
sorted_by_diff = sorted(dims.items(), key=lambda x: x[1]["diff_district"], reverse=True)
above_avg = [(n, d) for n, d in sorted_by_diff if d['diff_district'] > 0]
below_avg = [(n, d) for n, d in sorted_by_diff if d['diff_district'] < 0]
strong_text = ", ".join([f"{n}({d['score']:.2f}分, 高于区均{d['diff_district']:+.2f}分, 区内第{d['rank_in_district']}名)" for n, d in above_avg]) if above_avg else "无"
weak_text = ", ".join([f"{n}({d['score']:.2f}分, 低于区均{d['diff_district']:+.2f}分, 区内第{d['rank_in_district']}名)" for n, d in below_avg]) if below_avg else "无"
bg = self._school_background(data)
return f"""请撰写"学校课程实施总体表现"分析段落。
## 学校背景
{bg}
## 核心数据
- 总体得分:{overall['score']:.2f}分
- {data.get('district', '本区')}均值:{overall['district_avg']:.2f}分(差异{overall['score'] - overall['district_avg']:+.2f}分)
- 同类学校均值:{overall['same_type_avg']:.2f}分(差异{overall['score'] - overall['same_type_avg']:+.2f}分)
- 区内排名:第{overall['rank_in_district']}名 / {overall['total_schools']}所
- 聚类类型:课程实施{overall['cluster']}类
## 七个维度得分(按区均值差值排序)
{dims_text}
## 相对强弱维度(以与区均值的差值为判断依据)
- 高于区均值的维度:{strong_text}
- 低于区均值的维度:{weak_text}
## 写作要求与约束
1. 第一段:报告贵校总体得分及与区均值、同类学校均值的对比关系,说明聚类类型
2. 第二段:综合各维度得分,指出表现较好和相对较弱的维度
3. 第三段:简要分析维度间的结构性差异所反映的学校课程实施特点
4. 全文使用"贵校"指代学校,语言客观平实
5. 【关键约束】判断维度强弱的唯一依据是"与区均值的差值"和"区内排名",而非得分绝对值
- 一个维度得分高但低于区均值,说明区内整体水平更高,该维度不能说"较好"
- 一个维度得分低但高于区均值,说明在区内相对领先,可以客观描述
6. 引用数据时必须使用上面提供的精确数值,不要自行计算
7. 禁止使用"突出""亮眼""领先"等主观修辞;使用"高于区均值X.XX分""区内排名第X"等客观句式
8. 直接输出HTML片段"""
def _prompt_dimension_overall(self, data: Dict, dim_name: str) -> str:
"""维度整体分析"""
school = data["school"]
dim_data = data["dimensions"][dim_name]
sub_dims = DIMENSION_FRAMEWORK[dim_name]["sub_dimensions"]
dim_def = DIMENSION_DEFINITIONS.get(dim_name, "")
sub_text = ""
for sd in sub_dims:
sd_data = data["sub_dimensions"].get(sd, {})
if sd_data:
sub_text += f" - {sd}: {sd_data['score']:.2f}分, 水平{sd_data['level']}, 区内排名第{sd_data['rank_in_district']}名\n"
# 获取各学校在该维度的得分用于对比
all_scores = data.get("all_schools_dim_scores", {}).get(dim_name, {})
ranking_text = "\n".join([
f" {s}: {round(v, 2)}分" for s, v in sorted(all_scores.items(), key=lambda x: x[1], reverse=True)
])
bg = self._school_background(data)
return f"""请撰写"{dim_name}"维度的整体分析段落。
## 维度定义
{dim_def}
## 学校背景
{bg}
## 核心数据
- {dim_name}得分:{dim_data['score']:.2f}分
- {data.get('district', '本区')}均值:{dim_data['district_avg']:.2f}分(差异{dim_data['diff_district']:+.2f}分)
- 同类学校均值:{dim_data['same_type_avg']:.2f}分
- 区内排名:第{dim_data['rank_in_district']}名 / {data['overall']['total_schools']}所
- 聚类类型:{dim_data.get('cluster', '')}
## 子维度得分概览
{sub_text}
## 区内各校该维度排名
{ranking_text}
## 写作要求与约束
1. 第一段简要引出维度定义(1-2句),然后报告贵校得分及与区均值、同类学校对比
2. 第二段:分析各子维度的得分分布,指出最高和最低的子维度
3. 第三段:与区内其他学校横向对比(引用具体学校得分)
4. 第四段:基于数据给出1-2条改进建议(针对最弱子维度)
5. 注意:子维度的详细分析将在后续独立段落中展开,此处只需概述分布和整体特征
6. 【关键约束】
- 得分低于区均值或排名在区内后1/3的维度,不能使用"较好""相对突出"等正面描述
- 区内排名倒数的子维度,必须用"显著落后""亟需改进"等直接措辞指出严重性
- 如果子维度间存在巨大分差(>10分),必须指出这种内部分化现象
7. 全文使用"贵校"指代学校,语言客观平实但对问题不回避
8. 直接输出HTML片段,不要添加标题或编号"""
def _prompt_sub_dimension(self, data: Dict, dim_name: str, sub_dim: str) -> str:
"""三级维度详细分析"""
school = data["school"]
sd_data = data["sub_dimensions"][sub_dim]
level = sd_data["level"]
level_desc = sd_data["level_description"]
sub_def = SUB_DIMENSION_DEFINITIONS.get(sub_dim, "")
# 区内各校在该子维度的分数和水平
all_sub = data.get("all_schools_sub_scores", {}).get(sub_dim, {})
ranking_text = ""
if all_sub:
# 为各学校构建含水平的排名
ranking_items = []
for s, v in sorted(all_sub.items(), key=lambda x: x[1], reverse=True):
ranking_items.append(f" {s}: {round(v, 2)}分")
ranking_text = "\n".join(ranking_items)
else:
ranking_text = "暂无"
# 水平分布
level_dist = sd_data.get("level_distribution", {})
total_schools = sum(level_dist.values()) if level_dist else data['overall']['total_schools']
dist_text = ", ".join([f"{k}: {v}所({round(v/total_schools*100, 1)}%)" for k, v in level_dist.items()]) if level_dist else "暂无"
bg = self._school_background(data)
return f"""请撰写"{sub_dim}"(属于{dim_name}维度)的详细分析段落。
## 子维度定义
{sub_def}
## 学校背景
{bg}
## 核心数据
- {sub_dim}得分:{sd_data['score']:.2f}分
- {data.get('district', '本区')}均值:{sd_data['district_avg']:.2f}分(差异{sd_data['diff_district']:+.2f}分)
- 水平等级:水平{level}
- 水平含义:{level_desc}
- 区内排名:第{sd_data['rank_in_district']}名 / {total_schools}所
## 区内水平分布
{dist_text}
## 区内各校得分排名
{ranking_text}
## 各水平的含义
水平4: {LEVEL_DESCRIPTIONS.get(sub_dim, {}).get(4, '')}
水平3: {LEVEL_DESCRIPTIONS.get(sub_dim, {}).get(3, '')}
水平2: {LEVEL_DESCRIPTIONS.get(sub_dim, {}).get(2, '')}
水平1: {LEVEL_DESCRIPTIONS.get(sub_dim, {}).get(1, '')}
## 写作要求与约束
1. 第一段:报告贵校得分及与区均值的对比,说明处于水平几及该水平的具体含义
2. 第二段:引用区内水平分布数据,说明处于该水平及以上的学校占比,与区内具体学校对比
3. 第三段:根据水平高低给出针对性建议——低于水平3的给出提升路径,达到水平4的肯定做法并建议深化
4. 【关键约束——对低水平/垫底子维度必须追问根因】
- 如果处于水平1且区内排名末位,语气必须严肃,不能轻描淡写
- 如果与区均值差距超过10分,必须追问可能的结构性原因(如"是否师资编制不足""是否缺乏相关制度"等)
- 如果同一维度内其他子维度表现好、但该子维度很差,必须指出这种不对称,分析可能的原因
- 差距超过20分的属于极端情况,用"需要管理层立即排查原因并制定专项整改方案"等严肃措辞
5. 全文使用"贵校"指代学校,篇幅控制在200-400字(低水平子维度可适当增加篇幅)
6. 直接输出HTML片段,不要添加标题"""
def _prompt_conclusion(self, data: Dict) -> str:
"""总结与建议"""
school = data["school"]
overall = data["overall"]
dims = data["dimensions"]
# 找出最强和最弱维度
sorted_dims = sorted(dims.items(), key=lambda x: x[1]["score"], reverse=True)
top3 = sorted_dims[:3]
bottom2 = sorted_dims[-2:]
# 找出水平1-2的三级维度
weak_subs = []
for sd_name, sd_data in data["sub_dimensions"].items():
if sd_data.get("level", 0) <= 2:
weak_subs.append(f"{sd_name}(水平{sd_data['level']}, {sd_data['score']:.2f}分)")
# 找出水平4的三级维度
strong_subs = []
for sd_name, sd_data in data["sub_dimensions"].items():
if sd_data.get("level", 0) >= 4:
strong_subs.append(f"{sd_name}(水平4, {sd_data['score']:.2f}分)")
bg = self._school_background(data)
return f"""请撰写"总结与改进建议"章节。
## 学校背景
{bg}
## 核心数据
- 总体得分:{overall['score']:.2f}分
- 区内排名:第{overall['rank_in_district']}名 / {overall['total_schools']}所
- 聚类类型:课程实施{overall['cluster']}类
## 最强维度(Top 3)
{chr(10).join([f" - {name}: {info['score']:.2f}分 (高于区均值{info['diff_district']:+.2f}分)" for name, info in top3])}
## 最弱维度(Bottom 2)
{chr(10).join([f" - {name}: {info['score']:.2f}分 (vs区均值{info['diff_district']:+.2f}分)" for name, info in bottom2])}
## 达到水平4的三级维度(优势项)
{chr(10).join([f" - {s}" for s in strong_subs]) if strong_subs else " 无"}
## 需要重点关注的三级维度(水平1-2)
{chr(10).join([f" - {s}" for s in weak_subs]) if weak_subs else " 无"}
## 写作要求
1. 第一段:综合概述贵校课程实施的总体特点和主要矛盾(如"整体排名靠后但个别维度有亮点"或"维度间发展严重不平衡"等)
2. 第二段:提炼核心问题主线(不超过2条),指出各薄弱维度之间的内在关联(如"教师专业发展不足是根本制约"),避免逐维度罗列
3. 第三段:针对核心问题主线,给出3-5条优先级明确的改进方向(标注"第一优先""第二优先"),每条说明:当前状态→目标→核心举措
4. 最后一段:展望未来发展方向,呼应学校类型定位
5. 【关键约束】
- 不要"每个维度都给三条建议"——这样等于什么都没说
- 要区分主次:什么是"必须立即做的",什么是"可以下学期再推进的"
- 如果有水平1的子维度,必须在建议中置顶为第一优先
6. 全文使用"贵校"指代学校,语言客观平实但对问题直言不讳
7. 篇幅控制在400-600字
8. 直接输出HTML片段,不要添加标题或编号"""
# ========== 图表解读Prompt ==========
def _prompt_chart_caption_overview(self, data: Dict, chart_type: str, chart_title: str) -> str:
"""第二部分(总体表现)各组图表的简短解读"""
school = data["school"]
overall = data["overall"]
dims = data["dimensions"]
# 按得分排序
sorted_dims = sorted(dims.items(), key=lambda x: x[1]["score"], reverse=True)
if chart_type == "profile":
# 画像卡 + 象限图
# 找出优势和短板的三级维度
strong_subs = []
weak_subs = []
for sd_name, sd_data in data["sub_dimensions"].items():
diff = sd_data.get("diff_district", 0)
if diff > 3:
strong_subs.append(f"{sd_name}(+{diff:.1f})")
elif diff < -3:
weak_subs.append(f"{sd_name}({diff:.1f})")
context = f"""贵校总体得分{overall['score']:.1f}分,属于"课程实施{overall['cluster']}类"。
优势三级维度(高于区均值3分以上):{', '.join(strong_subs) if strong_subs else '无'}
短板三级维度(低于区均值3分以上):{', '.join(weak_subs) if weak_subs else '无'}"""
elif chart_type == "scores":
# 得分对比 + 雷达图
# 【重要】每个维度单独列出得分、区均值和差值,避免LLM自行计算出错
above_avg = []
below_avg = []
for n, d in sorted_dims:
diff = d['diff_district']
entry = f"{n}({d['score']:.1f}分, 区均值{d['district_avg']:.1f}分, {diff:+.1f}分, 区内排名第{d['rank_in_district']})"
if diff > 0:
above_avg.append(entry)
else:
below_avg.append(entry)
context = f"""七维度得分、区均值和差异(从高到低排列):
{';'.join([f"{n}({d['score']:.1f}分, 区均值{d['district_avg']:.1f}分, 差值{d['diff_district']:+.1f}分)" for n, d in sorted_dims])}
高于区均值的维度:{'、'.join(above_avg) if above_avg else '无'}
低于区均值的维度:{'、'.join(below_avg) if below_avg else '无'}
【写作约束】
- 必须使用上面提供的精确数值(区均值、差值),严禁自行计算或推测
- 判断维度表现好坏时,必须同时看"与区均值的差值"和"区内排名",而非仅看得分高低
- 得分高但低于区均值或排名靠后的维度,不能描述为"突出"或"较好"
- 得分低但高于区均值或排名靠前的维度,应客观肯定"""
elif chart_type == "cluster":
# 聚类分布 + 特征对比
# 找出真正高于区均值且排名靠前的维度
truly_good = [f"{n}({d['score']:.1f}分, 高于区均{d['diff_district']:+.1f})"
for n, d in sorted_dims if d['diff_district'] > 0 and d['rank_in_district'] <= overall['total_schools'] // 2]
truly_weak = [f"{n}({d['score']:.1f}分, 低于区均{d['diff_district']:+.1f})"
for n, d in sorted_dims if d['diff_district'] < 0]
context = f"""贵校属于"课程实施{overall['cluster']}类",区内排名第{overall['rank_in_district']}名(共{overall['total_schools']}所)。
高于区均值且排名靠前的维度:{'、'.join(truly_good) if truly_good else '无'}
低于区均值的维度:{'、'.join(truly_weak) if truly_weak else '无'}
【写作约束】只有同时"高于区均值"且"区内排名前50%"的维度才能描述为"表现较好",不能仅凭得分绝对值高低判断"""
elif chart_type == "ranking":
# 类型雷达 + 区内排名
# 找出排名最优和最弱的维度(按排名而非得分)
rank_sorted = sorted(dims.items(), key=lambda x: x[1]['rank_in_district'])
context = f"""贵校总体得分{overall['score']:.1f}分,区内排名第{overall['rank_in_district']}/{overall['total_schools']}。
排名最优维度:{rank_sorted[0][0]}({rank_sorted[0][1]['score']:.1f}分,区均值{rank_sorted[0][1]['district_avg']:.1f}分,区内排名第{rank_sorted[0][1]['rank_in_district']})。
排名最弱维度:{rank_sorted[-1][0]}({rank_sorted[-1][1]['score']:.1f}分,区均值{rank_sorted[-1][1]['district_avg']:.1f}分,区内排名第{rank_sorted[-1][1]['rank_in_district']})。
【写作约束】引用具体数据时必须使用上面给出的精确数值"""
elif chart_type == "correlation":
# 相关性热力图
corr = data.get("correlation", {})
# 找出相关性最高的几对
high_corrs = []
dims_list = list(corr.keys())
for i, d1 in enumerate(dims_list):
for j, d2 in enumerate(dims_list):
if i < j:
val = corr.get(d1, {}).get(d2, 0)
if val is not None:
high_corrs.append((d1, d2, val))
high_corrs.sort(key=lambda x: abs(x[2]), reverse=True)
top_corrs = high_corrs[:3] if high_corrs else []
corr_text = "、".join([f"{c[0]}与{c[1]}({c[2]:.2f})" for c in top_corrs]) if top_corrs else "数据不足"
context = f"""维度间相关性最强的组合为:{corr_text}。
高相关性意味着这些维度在学校间表现趋势一致,可协同提升。"""
else:
context = ""
return f"""请为以下图表撰写一段简短的解读说明(80-120字即可)。
## 图表标题
{chart_title}
## 图表关键数据
{context}
## 写作要求
1. 用1-2句话概括该图表呈现的核心信息,帮助读者快速理解图表要点
2. 必须引用具体数据(分数、排名等)
3. 使用"贵校"指代学校
4. 语言简洁客观,不重复图表标题
5. 直接输出一个 标签包裹的段落,不要添加标题
6. 不要使用"如图所示"等表述"""
def _prompt_chart_caption_dimension(self, data: Dict, dim_name: str, chart_type: str, chart_title: str) -> str:
"""维度部分各组图表的简短解读"""
dim_data = data["dimensions"][dim_name]
sub_dims = DIMENSION_FRAMEWORK[dim_name]["sub_dimensions"]
# 子维度数据
sub_info = []
for sd in sub_dims:
sd_data = data["sub_dimensions"].get(sd, {})
if sd_data:
sub_info.append((sd, sd_data))
sub_sorted = sorted(sub_info, key=lambda x: x[1]["score"], reverse=True)
if chart_type == "dim_score":
# 得分 + 子维度雷达
sub_text = "、".join([f"{n}({d['score']:.1f}分)" for n, d in sub_sorted])
context = f"""{dim_name}总体得分{dim_data['score']:.1f}分,区均值{dim_data['district_avg']:.1f}分(差异{dim_data['diff_district']:+.1f}分)。
各子维度得分:{sub_text}。
最高与最低子维度分差为{sub_sorted[0][1]['score'] - sub_sorted[-1][1]['score']:.1f}分。"""
elif chart_type == "sub_detail":
# 对比详情 + 水平分布
level_text = "、".join([f"{n}(水平{d['level']})" for n, d in sub_info])
above_avg = [n for n, d in sub_info if d.get("diff_district", 0) > 0]
below_avg = [n for n, d in sub_info if d.get("diff_district", 0) < 0]
context = f"""各子维度水平分布:{level_text}。
高于区均值的子维度:{'、'.join(above_avg) if above_avg else '无'}。
低于区均值的子维度:{'、'.join(below_avg) if below_avg else '无'}。"""
elif chart_type == "scatter":
# 聚类散点图
diff = dim_data.get("diff_district", 0)
rank = dim_data['rank_in_district']
total = data['overall']['total_schools']
if diff > 0 and rank <= total // 2:
position_desc = "处于区内中上水平"
elif diff < -3 or rank > total * 2 // 3:
position_desc = "处于区内靠后位置"
else:
position_desc = "处于区内中等位置"
context = f"""贵校{dim_name}得分{dim_data['score']:.1f}分(区均值{dim_data['district_avg']:.1f}分,差值{diff:+.1f}分),区内排名第{rank}/{total},{position_desc}。
散点图中各点代表区内各学校,贵校位置反映其在该维度各子指标上的综合表现。"""
else:
context = ""
return f"""请为以下图表撰写一段简短的解读说明(80-120字即可)。
## 图表标题
{chart_title}
## 图表关键数据
{context}
## 写作要求
1. 用1-2句话概括该图表呈现的核心信息,帮助读者快速理解图表要点
2. 必须引用具体数据(分数、排名、水平等)
3. 使用"贵校"指代学校
4. 语言简洁客观,不重复图表标题
5. 直接输出一个 标签包裹的段落,不要添加标题
6. 不要使用"如图所示"等表述"""
def _prompt_chart_caption_waterfall(self, data: Dict) -> str:
"""结论部分瀑布图的简短解读"""
# 找出水平低于3的三级维度
weak_subs = []
for sd_name, sd_data in data["sub_dimensions"].items():
if sd_data.get("level", 0) < 3:
weak_subs.append(f"{sd_name}(当前水平{sd_data['level']},得分{sd_data['score']:.1f})")
context = f"""当前低于水平3的三级维度有{len(weak_subs)}个:
{'、'.join([s for s in weak_subs]) if weak_subs else '无'}。
若将这些维度均提升至水平3,预计可带来整体课程实施水平的显著改善。"""
return f"""请为以下图表撰写一段简短的解读说明(80-120字即可)。
## 图表标题
图10-1 进步空间分析:提升至水平3的潜在收益
## 图表关键数据
{context}
## 写作要求
1. 用1-2句话概括瀑布图所展示的核心信息——哪些子维度有提升空间,提升后的预期收益
2. 引用具体数据
3. 使用"贵校"指代学校
4. 语言简洁客观
5. 直接输出一个 标签包裹的段落,不要添加标题
6. 不要使用"如图所示"等表述"""
# ========== 第十一部分:实践落地行动指南(拆分为5个并行segment) ==========
def _classify_sub_dimensions(self, data: Dict) -> Dict:
"""将子维度按水平分类,供各 action prompt 共用"""
sub_dims = data["sub_dimensions"]
result = {"critical": [], "attention": [], "maintain": [], "excellent": []}
for sd_name, sd_data in sub_dims.items():
level = sd_data.get("level", 0)
parent_dim = ""
for dim_name, dim_info in DIMENSION_FRAMEWORK.items():
if sd_name in dim_info["sub_dimensions"]:
parent_dim = dim_name
break
entry = {
"name": sd_name, "parent": parent_dim,
"score": sd_data["score"], "level": level,
"level_desc": sd_data.get("level_description", ""),
"district_avg": sd_data.get("district_avg", 50),
"diff": sd_data.get("diff_district", 0),
"rank": sd_data.get("rank_in_district", 0),
}
if level <= 1:
result["critical"].append(entry)
elif level == 2:
result["attention"].append(entry)
elif level == 3:
result["maintain"].append(entry)
else:
result["excellent"].append(entry)
result["critical"].sort(key=lambda x: x["diff"])
result["attention"].sort(key=lambda x: x["diff"])
return result
@staticmethod
def _format_sub_list(subs):
if not subs:
return " 无"
return "\n".join([
f" - {s['name']}({s['parent']}):{s['score']:.1f}分,水平{s['level']},区内排名第{s['rank']}名,{s['level_desc']}"
for s in subs
])
# ----- 共用的 system 约束片段 -----
_ACTION_SYSTEM_RULES = """## 内容底线
1. 禁止"建议进一步…""需要加强…""应当重视…""可以考虑…"等方向性空话——每条必须回答"做什么、谁做、何时做、做到什么程度"
2. 必须出现:责任部门(教务处/教研组/学科组长等)、时间节点(本学期末/每月/每两周等)、量化目标(每学科每学期至少X次/覆盖率X%等)
3. 可引用《普通高中课程方案(2017年版2020年修订)》《上海市普通高中课程实施方案》;不编造不存在的文件
4. 根据学校类型调整定位高度
## HTML格式(严格遵守)
- 只用 、、 【所属二级维度·子维度名称】——当前X.X分,区内第X名{f'/{total}' if total else ''} 问题诊断:一句话直指问题本质——不是"得分较低",而是"为什么会这样"(如"这意味着贵校各学科几乎没有校级以上项目在推进")。如果与区均值差距超过15分,必须追问可能的结构性根因。 【所属二级维度·子维度名称】——当前X.X分,区内第X名 一句话诊断当前状态。如果与水平1维度存在因果关联,在此指出。 【子维度名称】({{'水平3→水平4'}}):对照水平4标准,关键差距是XXX,下学期重点推进XXX(责任部门;时间节点)。 段落概括所有水平4维度的共同优势特征
- 再用一个 段落简要建议如何将经验转化为可推广的成果(如编写案例集、承办区域研讨等),安排在下学年
- 总计不超过150字
- 直接输出HTML片段"""
def _prompt_action_timeline(self, data: Dict, action_data: Dict) -> str:
"""学期行动时间表——以Top3为核心框架"""
bg = self._school_background(data)
overall = data["overall"]
# 汇总各优先级的维度名
critical_names = [f"{s['name']}({s['parent']}, {s['score']:.1f}分)" for s in action_data["critical"]]
attention_names = [f"{s['name']}({s['parent']}, {s['score']:.1f}分)" for s in action_data["attention"]]
maintain_names = [s["name"] for s in action_data["maintain"]]
excellent_names = [s["name"] for s in action_data["excellent"]]
return f"""请为该校撰写一份"学期行动时间表"。核心原则:**聚焦、聚焦、再聚焦**——本学期只做最紧迫的事。
## 学校背景
{bg}
- 总体得分:{overall['score']:.2f}分,区内第{overall['rank_in_district']}/{overall['total_schools']}
## 各优先级维度清单
- [紧急] 急需突破(水平1):{', '.join(critical_names) if critical_names else '无'}
- [重点] 重点攻关(水平2):{', '.join(attention_names) if attention_names else '无'}
- [巩固] 稳步巩固(水平3):{', '.join(maintain_names) if maintain_names else '无'}
- [引领] 深化引领(水平4):{', '.join(excellent_names) if excellent_names else '无'}
## 输出格式
用 标签),不加标题"""
def _prompt_positioning_gap(self, data: Dict) -> str:
"""学校定位与实际表现差距分析(所有学校类型均适用,按类型差异化措辞)"""
bg = self._school_background(data)
school_type = data.get("school_info", {}).get("type", "")
overall = data["overall"]
dims = data["dimensions"]
# 找出排名在后1/3的维度
total = overall["total_schools"]
weak_dims = [(n, d) for n, d in dims.items() if d["rank_in_district"] > total * 2 / 3]
weak_text = "\n".join([
f" - {n}: {d['score']:.1f}分, 区均值{d['district_avg']:.1f}分, 区内排名第{d['rank_in_district']}/{total}"
for n, d in weak_dims
]) if weak_dims else " 无"
# 找出水平1-2的子维度
bottom_subs = [(n, sd) for n, sd in data["sub_dimensions"].items() if sd.get("level", 0) <= 2]
bottom_text = "\n".join([
f" - {n}({sd.get('parent_dimension', '')}): {sd['score']:.1f}分, 水平{sd['level']}, 区内排名第{sd['rank_in_district']}/{total}"
for n, sd in bottom_subs
]) if bottom_subs else " 无"
# 根据学校类型差异化定位要求和分析角度
if "市实验性示范性" in school_type:
positioning = "作为市实验性示范性高中,贵校应在全市发挥课程改革的引领示范作用,各维度表现理应对标全市前列"
gap_framing = f"然而数据显示,贵校总体排名区内第{overall['rank_in_district']}/{total},部分维度处于区内后段。这与市实验性示范性高中的办学定位之间存在显著落差"
pressure = "如果不正视这些差距,市实验性示范性高中的金字招牌将与实际办学水平越来越脱节"
elif "区实验性示范性" in school_type:
positioning = "作为区实验性示范性高中,贵校应在区域内发挥标杆引领作用,各维度表现理应位于区内前列"
gap_framing = f"然而数据显示,贵校总体排名区内第{overall['rank_in_district']}/{total},部分维度排名靠后。这与区实验性示范性高中应有的引领地位不相匹配"
pressure = "如果不正视这些差距,区实验性示范性高中的定位将难以服众"
elif "特色" in school_type:
feature = data.get("school_info", {}).get("feature", "特色高中")
positioning = f"作为{feature},贵校应在特色领域形成课程品牌,带动相关维度发展"
gap_framing = f"但数据显示,贵校总体排名区内第{overall['rank_in_district']}/{total},特色定位与实际课程实施能力之间存在差距"
pressure = "特色发展不能脱离基础课程实施能力的支撑,否则特色将成为空中楼阁"
elif "民办" in data.get("school_info", {}).get("nature", ""):
positioning = "作为民办高中,贵校在办学机制上具有灵活性优势,应在课程实施基础达标的前提下追求差异化发展"
gap_framing = f"数据显示,贵校总体排名区内第{overall['rank_in_district']}/{total}。需要客观审视当前各维度表现与区域平均水平的差距"
pressure = "民办学校的竞争力最终体现在教育质量上,课程实施能力是核心基本功"
else:
positioning = "每所高中都承载着为学生提供高质量课程教育的责任,需要定期审视自身课程实施水平在区域中的位置"
gap_framing = f"数据显示,贵校总体排名区内第{overall['rank_in_district']}/{total}。以下分析将帮助学校管理层客观认识当前各维度表现与区域平均水平之间的差距"
pressure = "不正视差距,就无法确定改进的优先级和着力点"
return f"""请撰写"学校定位与实际表现差距分析"专节。
## 学校背景
{bg}
## 定位要求
{positioning}
## 核心矛盾
{gap_framing}
- 学校类型:{school_type if school_type else '公办普通高中'}
- 总体排名:区内第{overall['rank_in_district']}/{total}
## 排名在区内后1/3的二级维度
{weak_text}
## 处于水平1-2的三级维度
{bottom_text}
## 写作要求
1. 开篇点明学校定位与实际数据之间的落差
2. 逐一分析排名靠后的维度——提出可能的结构性追问:
- 是管理层对课程方案掌握不够准确?
- 是师资编制不足导致无法支撑?
- 是学校发展重心过度偏向某些方面而忽视了基础建设?
- 还是评价体系和激励机制尚未与课程改革要求对齐?
3. 总结压力:{pressure}
4. 如果排名在后1/3的维度为"无"且无水平1-2的子维度,则改为肯定当前表现与定位的匹配度,同时指出仍可精进的方向
5. 语气严肃但专业,是诊断而非批评
6. 篇幅300-400字
7. 直接输出HTML片段,不加标题"""
def _prompt_cross_dimension_analysis(self, data: Dict) -> str:
"""跨维度因果链分析"""
bg = self._school_background(data)
dims = data["dimensions"]
subs = data["sub_dimensions"]
correlation = data.get("correlation", {})
# 找出排名靠后的维度对
weak_dims = sorted(dims.items(), key=lambda x: x[1]["rank_in_district"], reverse=True)[:3]
# 从相关性矩阵找到高相关的维度对
high_corrs = []
dim_list = list(correlation.keys())
for i, d1 in enumerate(dim_list):
for j, d2 in enumerate(dim_list):
if i < j:
val = correlation.get(d1, {}).get(d2, 0)
if val and abs(val) > 0.5:
high_corrs.append((d1, d2, val))
high_corrs.sort(key=lambda x: abs(x[2]), reverse=True)
# 找出同一维度内部分化严重的情况
internal_splits = []
for dim_name, info in DIMENSION_FRAMEWORK.items():
sub_dims_list = info["sub_dimensions"]
sub_scores = [(sd, subs[sd]["score"]) for sd in sub_dims_list if sd in subs]
if len(sub_scores) >= 2:
max_s = max(sub_scores, key=lambda x: x[1])
min_s = min(sub_scores, key=lambda x: x[1])
gap = max_s[1] - min_s[1]
if gap > 10:
internal_splits.append(f" - {dim_name}内部:{max_s[0]}({max_s[1]:.1f}分) vs {min_s[0]}({min_s[1]:.1f}分),分差{gap:.1f}分")
corr_text = "\n".join([f" - {c[0]} ↔ {c[1]} (r={c[2]:.2f})" for c in high_corrs[:5]]) if high_corrs else " 数据不足"
weak_text = "\n".join([f" - {n}: 得分{d['score']:.1f}分, 区内排名第{d['rank_in_district']}" for n, d in weak_dims])
split_text = "\n".join(internal_splits) if internal_splits else " 无显著内部分化"
return f"""请撰写"跨维度综合分析"段落,揭示各维度之间的内在关联和因果链。
## 学校背景
{bg}
## 排名最靠后的维度
{weak_text}
## 高相关维度对(来自相关性分析)
{corr_text}
## 维度内部分化情况(子维度分差>10分)
{split_text}
## 各子维度完整数据
{chr(10).join([f" - {n}: {sd['score']:.1f}分, 水平{sd.get('level',0)}, 区内第{sd['rank_in_district']}" for n, sd in subs.items()])}
## 写作要求
1. 重点分析:排名靠后的几个维度之间是否存在因果链?例如:
- "教师培训机会匮乏(培训支持垫底) → 教师专业能力受限 → 课程领导力不足 → 教学变革力受限"
- "缺少项目支持 → 教师无科研抓手 → 教研质量难以提升"
2. 指出维度内部分化的深层原因,例如:
- "作业管理做得好但课堂教学方式没跟上,说明教研重心偏向了管理而非教学本身"
3. 最终提炼出1-2条核心改进主线(如"教师专业发展是当前最根本的发动机问题")
4. 如果存在高相关的维度对同时排名靠后,必须指出这种"双低共振"现象
5. 篇幅300-500字,语言深入有洞察力
6. 直接输出HTML片段,不加标题"""
def _prompt_top3_priorities(self, data: Dict) -> str:
"""最紧迫的三件事"""
bg = self._school_background(data)
overall = data["overall"]
dims = data["dimensions"]
subs = data["sub_dimensions"]
# 找出所有子维度,按严重程度排序
scored_subs = []
total = overall["total_schools"]
for sd_name, sd_data in subs.items():
level = sd_data.get("level", 0)
diff = sd_data.get("diff_district", 0)
rank = sd_data.get("rank_in_district", 0)
rank_pct = rank / total if total else 1
severity = (4 - level) * 30 + max(0, -diff) * 2 + rank_pct * 20
scored_subs.append({
"name": sd_name,
"parent": sd_data.get("parent_dimension", ""),
"score": sd_data["score"],
"level": level,
"diff": diff,
"rank": rank,
"severity": severity,
})
scored_subs.sort(key=lambda x: x["severity"], reverse=True)
top_issues = scored_subs[:6]
issues_text = "\n".join([
f" {i+1}. {s['name']}({s['parent']}):{s['score']:.1f}分, 水平{s['level']}, 差距{s['diff']:+.1f}分, 区内第{s['rank']}/{total}, 严重性指数{s['severity']:.0f}"
for i, s in enumerate(top_issues)
])
return f"""请为学校管理层撰写"最紧迫的三件事"。这是整份报告最核心的产出——让校长看完后知道"先做哪三件事"。
## 学校背景
{bg}
- 总体得分:{overall['score']:.2f}分,区内第{overall['rank_in_district']}/{total}
## 按严重程度排序的薄弱子维度(候选清单)
{issues_text}
## 输出格式
为什么这是最紧迫的?(1-2句话点明严重性和紧迫原因) 本学期核心行动:(1句话概括关键举措,指明责任部门和时间节点) ... ...、
、
~
、
,
,
-
,
~
、
、
、
~
标签(保留内容转为
/
/
或直接移除
# 匹配独立成行的 --- 或 ----(前后可有空行)
cleaned = re.sub(r'\n*^-{3,}\s*$\n*', '\n', cleaned, flags=re.MULTILINE)
# 9. 给行动指南的子维度标题行加 class="action-title",使其顶格显示
# 匹配 、
、
~
、
## 写作约束(在原有规则基础上追加)
- 语气严肃直接,这是"必须做"而非"建议做"
- 差距超过20分的子维度(如项目支持),需要专门分析"区内绝大多数学校是如何做到的,贵校为何未能做到"
- 如果多个水平1子维度属于同一二级维度,必须指出这代表该维度存在系统性问题,不是修修补补能解决的
{self._ACTION_SYSTEM_RULES}"""
def _prompt_action_attention(self, data: Dict, action_data: Dict) -> str:
"""水平2维度:重点攻关——加入与水平1的关联分析"""
subs = action_data["attention"]
bg = self._school_background(data)
# 目标:从水平2提升到水平3
targets = []
for s in subs:
target_desc = LEVEL_DESCRIPTIONS.get(s["name"], {}).get(3, "")
current_desc = LEVEL_DESCRIPTIONS.get(s["name"], {}).get(2, "")
targets.append(f" - {s['name']}: 当前水平2({s['score']:.1f}分,{current_desc})→ 目标水平3:{target_desc}")
# 找出水平1的维度(用于关联分析)
critical_names = [s["name"] + f"({s['parent']})" for s in action_data["critical"]]
critical_note = ""
if critical_names:
critical_note = f"""
## 与水平1维度的关联
当前水平1的子维度有:{', '.join(critical_names)}
请分析:以下水平2的子维度是否与上述水平1的子维度存在因果关联?例如:
- 教学方式变革(水平2)可能受制于培训支持(水平1)——教师缺乏培训自然难以变革教学
- 教研支持(水平2)可能受制于项目支持(水平1)——没有项目引领,教研缺乏抓手
如果存在这种关联,在行动方案中应指出"本维度的提升有赖于XX维度的先行改善"。"""
return f"""请为以下处于水平2的维度,逐一撰写行动路径(每个3-4步)。目标是提升到下一个水平(水平3)。
## 学校背景
{bg}
{critical_note}
## 水平2维度(重点攻关)
{self._format_sub_list(subs)}
## 各维度的提升目标(当前水平2 → 目标水平3)
{chr(10).join(targets)}
## 每个维度的输出格式
{self._ACTION_SYSTEM_RULES}"""
def _prompt_action_maintain(self, data: Dict, action_data: Dict) -> str:
"""水平3维度:稳步巩固——精简版,每个维度1-2步"""
subs = action_data["maintain"]
bg = self._school_background(data)
# 目标:从水平3提升到水平4
targets = []
for s in subs:
target_desc = LEVEL_DESCRIPTIONS.get(s["name"], {}).get(4, "")
current_desc = LEVEL_DESCRIPTIONS.get(s["name"], {}).get(3, "")
targets.append(f" - {s['name']}: 水平3({s['score']:.1f}分,{current_desc})→ 水平4:{target_desc}")
return f"""请为以下处于水平3的维度,用精简的方式给出从水平3迈向水平4的关键一步。
注意:这些维度当前不是最紧迫的问题,不宜占用过多篇幅。每个维度用1-2步概括即可。
## 学校背景
{bg}
## 水平3维度(稳步巩固)
{self._format_sub_list(subs)}
## 各维度的提升目标
{chr(10).join(targets)}
## 输出格式(精简版,每个维度不超过3行)
嵌套列表,分四个时间段。本学期必须以水平1的维度为核心,不能平均分配:
## 写作约束
- 本学期的行动项中,[紧急]水平1的维度至少占60%的条目数,这是底线
- 不要在本学期塞入水平3/4的行动——那些可以等
- 每个时间段2-4条,总计不超过12条行动项
- 用[紧急][重点][巩固][引领]标记各条目的优先级来源
- 禁止使用emoji符号
{self._ACTION_SYSTEM_RULES}"""
# ========== 新增:紧急预警 + 定位差距 + 跨维度分析 + Top3优先级 ==========
def _find_alert_sub_dimensions(self, data: Dict) -> List[Dict]:
"""找出需要紧急预警的子维度:区内垫底或差距超过15分"""
alerts = []
total = data["overall"]["total_schools"]
for sd_name, sd_data in data["sub_dimensions"].items():
rank = sd_data.get("rank_in_district", 0)
diff = sd_data.get("diff_district", 0)
is_last = rank == total
is_severe_gap = diff < -15
is_bottom_and_big_gap = rank >= total - 1 and diff < -5
if is_last or is_severe_gap or is_bottom_and_big_gap:
parent = sd_data.get("parent_dimension", "")
alerts.append({
"name": sd_name,
"parent": parent,
"score": sd_data["score"],
"district_avg": sd_data["district_avg"],
"diff": diff,
"rank": rank,
"total": total,
"level": sd_data.get("level", 0),
"reason": "全区末位" if is_last else ("差距悬殊" if is_severe_gap else "接近末位且差距显著"),
})
# 按差距严重程度排序
alerts.sort(key=lambda x: x["diff"])
return alerts
def _prompt_alert(self, data: Dict, alert_subs: List[Dict]) -> str:
"""紧急预警段落"""
bg = self._school_background(data)
alert_text = "\n".join([
f" - [预警] {s['name']}({s['parent']}):{s['score']:.1f}分,区均值{s['district_avg']:.1f}分,差距{s['diff']:+.1f}分,区内排名第{s['rank']}/{s['total']}({s['reason']}),水平{s['level']}"
for s in alert_subs
])
return f"""请为以下处于"紧急预警"状态的子维度撰写一段严肃的警示分析(200-300字)。这些子维度要么在全区排名垫底,要么与区均值存在悬殊差距,需要引起学校管理层高度重视。
## 学校背景
{bg}
## 紧急预警子维度
{alert_text}
## 写作要求
1. 开篇直接点明:以下子维度存在严重短板,需立即引起管理层关注
2. 逐一说明每个预警子维度的严重程度——用"全区排名最后""差距高达XX分"等直接措辞
3. 对差距特别大(>15分)的子维度,追问可能的结构性原因(如师资不足、制度缺失、资源匮乏等假设),用"是否存在……?""需要追查……"等追问句式
4. 语气严肃但建设性,目标是让校长立刻意识到问题的紧迫性
5. 禁止"建议关注""可以考虑"等委婉措辞,直接说"必须立即排查""需要在本学期内制定专项整改方案"
6. 直接输出HTML片段(用
## 写作要求
1. 从候选清单中选出最关键的3件事——不一定是严重性指数最高的,而是考虑:
- 改进的杠杆效应(改好一个能带动多个的优先)
- 可操作性(学校能在本学期内启动的优先)
- 底线要求(涉及办学底线的优先)
2. 每件事必须有具体的行动指向,不能只是"加强XX"
3. 如果前面的跨维度分析发现了因果链,优先级应反映"发动机问题"——先解决根因
4. 语气果断直接,适合校长会议上传达
5. 直接输出HTML片段"""