改成glm

This commit is contained in:
朱潮 2025-11-24 15:09:22 +08:00
parent 63b004412a
commit 02143e139c
11 changed files with 992 additions and 151 deletions

View File

@ -1,4 +1,4 @@
# 使用阿里云镜像的Python基础镜像
# 使用Python基础镜像
FROM python:3.12-slim
# 设置工作目录

View File

@ -11,6 +11,7 @@ services:
environment:
- TZ=Asia/Shanghai
- PYTHONUNBUFFERED=1
- GLM_API_KEY=847c627b63db4590a1f3932ff68e15f3.SUFeU6RcjERWQxUq
restart: unless-stopped
networks:
- survey-network

View File

@ -4,13 +4,13 @@
import sqlite3
import json
import uuid
import requests
import os
import time
from datetime import datetime, timezone, timedelta
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
import threading
from zai import ZhipuAiClient
def get_east8_time():
"""获取东八区时间"""
@ -37,29 +37,25 @@ def load_env_config():
print(f"加载环境变量失败: {e}")
class ReportGenerator:
"""报告生成器 - 调用自定义API生成测评报告"""
"""报告生成器 - 调用GLM大语言模型API生成测评报告"""
def __init__(self):
# 加载环境变量
load_env_config()
# 自定义API配置
self.api_url = os.getenv("REPORT_API_URL", "http://120.26.23.172:5678/webhook/survey_report")
self.api_key = os.getenv("REPORT_API_KEY", "")
self.timeout = int(os.getenv("REPORT_API_TIMEOUT", "300"))
self.prompt_file = "/Users/moshui/Documents/survey/public/prompt.txt"
# GLM API配置
self.api_key = os.getenv("GLM_API_KEY", "")
self.timeout = int(os.getenv("LLM_API_TIMEOUT", "300"))
self.model_name = "glm-4.5-air"
self.prompt_file = "./public/prompt.md"
# 请求头
self.headers = {
"Content-Type": "application/json"
}
# 添加API密钥如果设置了
if self.api_key:
self.headers["Authorization"] = f"Bearer {self.api_key}"
# 初始化 Zhipu AI 客户端
self.client = ZhipuAiClient(api_key=self.api_key)
print(f"报告生成器配置:")
print(f" - API端点: {self.api_url}")
print(f" - SDK: ZhipuAiClient")
print(f" - 模型: {self.model_name}")
print(f" - api_key: {self.api_key}")
print(f" - 超时时间: {self.timeout}")
def load_prompt(self):
@ -181,23 +177,43 @@ class ReportGenerator:
# 获取会话数据
analysis_data = self.generate_analysis_text({'id': session_id})
# 调用自定义API生成报告
# 构造回调URL
# 调用GLM API生成报告
report_result = self.call_report_api(analysis_data, session_id)
if report_result and report_result.get('success'):
# API调用成功异步生成中
# 保存分析数据用于后续回调处理
self.save_analysis_data_for_regeneration(session_id, analysis_data)
# API调用成功直接生成报告
# 构造完整的报告数据
student_info = analysis_data.get('student_info', {})
# 构造报告数据
complete_report_data = {
'studentInfo': {
'name': student_info.get('name', '未知'),
'school': student_info.get('school', '未知'),
'grade': student_info.get('grade', '未知'),
'subject': '科学',
'testDate': get_east8_time().strftime('%Y年%m月%d')
},
'report': report_result['report_data'],
'generated_at': get_east8_time().isoformat(),
'session_id': session_id,
'analysis_data': analysis_data,
'is_direct_generation': True
}
# 保存报告到数据库
report_id = self.save_report_to_db(session_id, complete_report_data, analysis_data)
return {
'success': True,
'message': '报告异步生成中',
'message': '报告生成成功',
'session_id': session_id,
'report_id': report_id,
'report_data': complete_report_data,
'analysis_data': analysis_data
}
else:
raise Exception("API调用失败")
raise Exception("GLM API调用失败")
except Exception as e:
print(f"生成报告失败: {e}")
@ -212,41 +228,73 @@ class ReportGenerator:
}
def call_report_api(self, analysis_data, session_id):
"""调用自定义报告API生成报告"""
# 构建请求数据
request_data = {
"analysis_text": analysis_data['analysis_text'],
"session_id": session_id
}
# 实现重试机制
max_retries = 3
retry_delay = 2 # 秒
"""调用GLM大语言模型API生成报告"""
# 加载提示词
prompt = self.load_prompt()
try:
print(f"调用报告生成API...")
print(f" API端点: {self.api_url}")
print(f"调用GLM大语言模型SDK...")
print(f" Session ID: {session_id}")
print(f" 数据长度: {len(request_data['analysis_text'])} 字符")
print(f" 模型: {self.model_name}")
response = requests.post(
self.api_url,
headers=self.headers,
json=request_data,
timeout=10 # 简化超时时间
# 使用ZhipuAiClient调用API设置JSON格式响应
response = self.client.chat.completions.create(
model=self.model_name,
messages=[
{
"role": "system",
"content": prompt
},
{
"role": "user",
"content": analysis_data['analysis_text']
}
],
response_format={
"type": "json_object"
}
)
if response.status_code == 200:
result = response.json()
print(f"✅ API调用成功已提交异步报告生成请求")
print(f" 响应内容: {str(result)[:200]}...")
return {"success": True, "message": "异步报告生成请求已提交"}
else:
raise Exception(f"API调用失败: HTTP {response.status_code} - {response.text}")
print(f"✅ GLM SDK调用成功")
print(f" 响应长度: {len(response.choices[0].message.content)} 字符")
# 解析JSON响应
model_response = response.choices[0].message.content
parsed_report = self.parse_json_response(model_response)
return {
"success": True,
"message": "报告生成成功",
"report_data": parsed_report,
"raw_response": model_response
}
except Exception as e:
print(f"API调用出错: {e}")
raise Exception(f"报告生成API调用失败: {str(e)}")
print(f"GLM SDK调用出错: {e}")
raise Exception(f"大语言模型SDK调用失败: {str(e)}")
def parse_json_response(self, json_content):
"""解析JSON格式的响应字符串"""
try:
# 查找 ```json 和 ``` 之间的内容
import re
# 匹配 ```json ... ``` 格式
match = re.search(r'```json\s*\n(.*?)\n```', json_content, re.DOTALL)
if match:
json_content = match.group(1)
print(f"成功提取JSON格式内容长度: {len(json_content)} 字符")
try:
return json.loads(json_content)
except json.JSONDecodeError as e:
print(f"JSON解析失败: {e}")
# 如果都不是,返回原始内容
return json_content
except Exception as e:
print(f"解析JSON响应失败: {e}")
return json_content
def save_report_to_db(self, session_id, report_data, analysis_data):
"""保存报告到数据库"""
@ -446,22 +494,23 @@ class EnhancedSurveySystem:
analysis_data = json.loads(result[0])
# 调用API生成报告
# 调用GLM API生成报告
report_result = self.report_generator.call_report_api(analysis_data, session_id)
if report_result:
if report_result and report_result.get('success'):
# 提取学员信息用于更新报告数据
student_info = analysis_data['student_info']
report_result['studentInfo']['name'] = student_info.get('name', '未知')
report_result['studentInfo']['school'] = student_info.get('school', '未知')
report_result['studentInfo']['grade'] = student_info.get('grade', '未知')
report_result['studentInfo']['subject'] = '科学'
report_result['studentInfo']['testDate'] = get_east8_time().strftime('%Y年%m月%d')
student_info = analysis_data.get('student_info', {})
# 构造报告数据
report_data = {
'studentInfo': report_result['studentInfo'],
'report': report_result['report'],
'studentInfo': {
'name': student_info.get('name', '未知'),
'school': student_info.get('school', '未知'),
'grade': student_info.get('grade', '未知'),
'subject': '科学',
'testDate': get_east8_time().strftime('%Y年%m月%d')
},
'report': report_result["report_data"],
'generated_at': get_east8_time().isoformat(),
'session_id': session_id,
'analysis_data': analysis_data,
@ -481,7 +530,7 @@ class EnhancedSurveySystem:
'is_regenerated': True
}
else:
raise Exception("API返回空内容")
raise Exception("GLM API返回空内容或失败")
except Exception as e:
print(f"重新生成报告失败: {e}")

250
input.json Normal file
View File

@ -0,0 +1,250 @@
{
"summaryData": {
"totalScore": 88,
"level": "优秀",
"scoreRate": "88%",
"scoreRateDescription": "班级前15%",
"groupPosition": "85%",
"groupPositionDescription": "超越同龄学生",
"summary": "李欣彦对科学现象充满好奇,基础知识记忆牢固,若能提升在复杂情境中的推理判断能力,科学成绩会更上一层楼!"
},
"radarData": {
"title": "五维能力雷达图",
"abilities": [
{
"name": "知识记忆与识别",
"value": 92
},
{
"name": "信息提取与分析",
"value": 85
},
{
"name": "科学推理与判断",
"value": 78
},
{
"name": "生活应用与关联",
"value": 70
},
{
"name": "探究思维与甄别",
"value": 75
}
]
},
"errorAnalysis": {
"title": "核心诊断:错题归因与深度解析",
"errors": [
{
"questionNumber": "第5题",
"questionTitle": "种子萌发必需的条件",
"errorType": "经验干扰型",
"corePoint": "种子萌发的必要条件",
"wrongOption": "C. 土壤",
"correctOption": "A. 水、空气、适宜温度",
"analysis": "你选择了C\"土壤\",这是一个典型的【经验干扰型】错误。因为我们平时种花都在土里,所以容易认为土壤是必需的。但科学实验证明,种子萌发必需的条件是水、空气和适宜的温度。",
"guidance": "审题关键点:抓住\"必需\"二字,意思是\"缺一不可\"。关联知识点:回忆\"种子萌发条件\"的对照实验,土壤不是必要条件(如豆芽发芽)。思路构建:运用排除法,思考如果没有土壤种子是否能萌发(能),如果没有水呢?(不能)。"
},
{
"questionNumber": "第19题",
"questionTitle": "潜望镜中的光传播",
"errorType": "推理缺失型",
"corePoint": "潜望镜中光的传播路径",
"wrongOption": "直接看到水面舰艇",
"correctOption": "舰艇的反射光→潜望镜平面镜反射→人眼",
"analysis": "这是一个【推理缺失型】错误,你缺少了对光传播路径的完整推理步骤。凭感觉认为能看到,但实际上需要理解潜望镜的原理是两次反射。","guidance": "推理关键点:光在同种均匀介质中沿直线传播。思考路径:舰艇反射的光→第一块平面镜反射→第二块平面镜反射→人眼。建议画图理解光路。"
}
],
"tagLibrary": {
"title": "错题归因标签库",
"tags": [
{
"emoji": "🔍",
"name": "概念混淆型",
"description": "对两个或多个相似概念区分不清,如\"蒸发\"与\"沸腾\"、\"反射\"与\"折射\"等。"
},
{
"emoji": "📋",
"name": "审题偏差型",
"description": "未能抓住题干中的限定词(如\"不正确\"、\"主要目的\"、\"必需条件\"等)。"
},
{
"emoji": "🧩",
"name": "推理缺失型",
"description": "缺少必要的逻辑推理步骤,凭感觉选择,尤其在复杂现象分析中。"
},
{
"emoji": "🌍",
"name": "经验干扰型",
"description": "被生活表象或片面经验误导,未遵循科学原理,如认为土壤是种子萌发的必需条件。"
},
{
"emoji": "📊",
"name": "信息忽略型",
"description": "忽略了图表、表格或题干中提供的关键信息,如实验数据中的变化趋势。"
},
{
"emoji": "🕳️",
"name": "知识漏洞型",
"description": "对考查的基础知识点完全不知或记忆错误,需要重新学习相关概念。"
}
]
}
},
"knowledgeAnalysis": {
"title": "深度剖析:知识体系掌握度分析",
"chapters": [
{
"type": "excellent",
"title": "🌟 优势章节",
"score": "92%",
"description": "光的直线传播、折射与色散",
"note": "概念识别准确,应用能力强"
},
{
"type": "good",
"title": "📈 巩固章节",
"score": "78%",
"description": "光的反射、光源与透光性",
"note": "部分易混概念需厘清"
},
{
"type": "needs-work",
"title": "🎯 重点补救章节",
"score": "65%",
"description": "复杂光现象综合应用",
"note": "核心原理理解尚不牢固"
}
]
},
"cognitiveAnalysis": {
"title": "认知能力维度评估",
"dimensions": [
{
"emoji": "🔍",
"name": "信息捕捉细致度",
"score": "82%",
"description": "审题偏差和信息忽略错误较少"
},
{
"emoji": "🧠",
"name": "概念清晰度",
"score": "78%",
"description": "部分相似概念需要强化区分"
},
{
"emoji": "🔗",
"name": "逻辑推理严谨性",
"score": "70%",
"description": "推理步骤需要更完整"
},
{
"emoji": "🌐",
"name": "知识迁移灵活性",
"score": "75%",
"description": "新情境应用能力有待提升"
},
{
"emoji": "🛡️",
"name": "抗干扰能力",
"score": "68%",
"description": "易受生活经验干扰"
}
],
"behaviorInsight": {
"title": "🎯 学习行为推测",
"content": "从你常犯【经验干扰型】和【推理缺失型】错误来看,你在做题时可能习惯于\"凭感觉\"或\"想当然\"。建议养成\"每选一个答案,都问自己一句'为什么'\"的习惯,让选择有据可依。"
}
},
"learningPlan": {
"title": "行动指南:个性化学习规划",
"plans": [
{
"type": "short-term",
"emoji": "🔥",
"title": "短期攻坚计划2-4周",
"goal": "堵塞核心知识漏洞,纠正易混概念",
"sections": [
{
"title": "重点章节",
"items": [
"复杂光现象综合应用",
"光的反射与透光性区分"
]
},
{
"title": "建议资源",
"items": [
"教科版配套动画视频\"光的世界\"",
"《光学小实验》家庭实验套装"
]
},
{
"title": "练习任务",
"items": [
"完成《光学选择题专项练习》10道",
"重点分析每一个选项的对错原因",
"建立错题本,记录错误类型和改进方法"
]
}
]
},
{
"type": "mid-term",
"emoji": "📈",
"title": "中期提升策略1-3个月",
"goal": "强化科学推理能力,建立\"选项分析\"思维",
"sections": [
{
"title": "能力训练",
"items": [
"\"选项辨析\"练习:针对错题,说出其他选项为什么错",
"\"讲题\"小老师:给父母讲解解题思路"
]
},
{
"title": "学习方法",
"items": [
"建立\"概念辨析本\":记录易混淆概念组",
"配合图示或例子加深理解",
"每周末回顾本周错题,总结规律"
]
}
]
},
{
"type": "long-term",
"emoji": "🌟",
"title": "长期发展建议(持续培养)",
"goal": "培养科学探究兴趣和批判性思维",
"sections": [
{
"title": "阅读拓展",
"items": [
"《神奇校车》《科学朗读者》等科普读物",
"《十万个为什么》光学部分"
]
},
{
"title": "实践探索",
"items": [
"多提问\"这有什么科学道理?\"","通过查资料、做小实验验证猜想",
"参观科技馆,参与科学实验活动"
]
}
]
}
],
"parentAdvice": {
"emoji": "👨\u200d👩\u200d👧",
"title": "给家长的建议",
"advice": [
"在孩子分析题目时,多问一句:\"你觉得出题人为什么设置这个错误选项呢?\" 这能引导孩子洞察题目陷阱,从\"答题者\"转变为\"思考者\"",
"多鼓励孩子的探索行为,保护他们的好奇心,即使实验\"失败\"也要肯定探究过程",
"创造家庭科学氛围,一起观看科学纪录片,讨论生活中的科学现象",
"帮助孩子建立错题分析习惯,但避免过度关注分数,重视思维过程"
]
}
}
}

88
output.toon Normal file
View File

@ -0,0 +1,88 @@
studentInfo:
name: 李欣彦
subject: 小学科学(教科版·五年级)
school: 尚逸实验小学
report:
summaryData:
totalScore: 88
level: 优秀
scoreRate: 88%
scoreRateDescription: 班级前15%
groupPosition: 85%
groupPositionDescription: 超越同龄学生
summary: 李欣彦对科学现象充满好奇,基础知识记忆牢固,若能提升在复杂情境中的推理判断能力,科学成绩会更上一层楼!
radarData:
title: 五维能力雷达图
abilities[5]{name,value}:
知识记忆与识别,92
信息提取与分析,85
科学推理与判断,78
生活应用与关联,70
探究思维与甄别,75
errorAnalysis:
title: 核心诊断:错题归因与深度解析
errors[2]{questionNumber,questionTitle,errorType,corePoint,wrongOption,correctOption,analysis,guidance}:
第5题,种子萌发必需的条件,经验干扰型,种子萌发的必要条件,C. 土壤,A. 水、空气、适宜温度,"你选择了C\"土壤\",这是一个典型的【经验干扰型】错误。因为我们平时种花都在土里,所以容易认为土壤是必需的。但科学实验证明,种子萌发必需的条件是水、空气和适宜的温度。","审题关键点:抓住\"必需\"二字,意思是\"缺一不可\"。关联知识点:回忆\"种子萌发条件\"的对照实验,土壤不是必要条件(如豆芽发芽)。思路构建:运用排除法,思考如果没有土壤种子是否能萌发(能),如果没有水呢?(不能)。"
第19题,潜望镜中的光传播,推理缺失型,潜望镜中光的传播路径,直接看到水面舰艇,舰艇的反射光→潜望镜平面镜反射→人眼,这是一个【推理缺失型】错误,你缺少了对光传播路径的完整推理步骤。凭感觉认为能看到,但实际上需要理解潜望镜的原理是两次反射。,推理关键点:光在同种均匀介质中沿直线传播。思考路径:舰艇反射的光→第一块平面镜反射→第二块平面镜反射→人眼。建议画图理解光路。
tagLibrary:
title: 错题归因标签库
tags[6]{emoji,name,description}:
🔍,概念混淆型,"对两个或多个相似概念区分不清,如\"蒸发\"与\"沸腾\"、\"反射\"与\"折射\"等。"
📋,审题偏差型,"未能抓住题干中的限定词(如\"不正确\"、\"主要目的\"、\"必需条件\"等)。"
🧩,推理缺失型,缺少必要的逻辑推理步骤,凭感觉选择,尤其在复杂现象分析中。
🌍,经验干扰型,被生活表象或片面经验误导,未遵循科学原理,如认为土壤是种子萌发的必需条件。
📊,信息忽略型,忽略了图表、表格或题干中提供的关键信息,如实验数据中的变化趋势。
🕳️,知识漏洞型,对考查的基础知识点完全不知或记忆错误,需要重新学习相关概念。
knowledgeAnalysis:
title: 深度剖析:知识体系掌握度分析
chapters[3]{type,title,score,description,note}:
excellent,🌟 优势章节,92%,光的直线传播、折射与色散,概念识别准确,应用能力强
good,📈 巩固章节,78%,光的反射、光源与透光性,部分易混概念需厘清
needs-work,🎯 重点补救章节,65%,复杂光现象综合应用,核心原理理解尚不牢固
cognitiveAnalysis:
title: 认知能力维度评估
dimensions[5]{emoji,name,score,description}:
🔍,信息捕捉细致度,82%,审题偏差和信息忽略错误较少
🧠,概念清晰度,78%,部分相似概念需要强化区分
🔗,逻辑推理严谨性,70%,推理步骤需要更完整
🌐,知识迁移灵活性,75%,新情境应用能力有待提升
🛡️,抗干扰能力,68%,易受生活经验干扰
behaviorInsight:
title: 🎯 学习行为推测
content: "从你常犯【经验干扰型】和【推理缺失型】错误来看,你在做题时可能习惯于\"凭感觉\"或\"想当然\"。建议养成\"每选一个答案,都问自己一句'为什么'\"的习惯,让选择有据可依。"
learningPlan:
title: 行动指南:个性化学习规划
plans[3]:
- type: short-term
emoji: 🔥
title: 短期攻坚计划2-4周
goal: 堵塞核心知识漏洞,纠正易混概念
sections[3]:
- title: 重点章节
items[2]: 复杂光现象综合应用,光的反射与透光性区分
- title: 建议资源
items[2]: "教科版配套动画视频\"光的世界\"",《光学小实验》家庭实验套装
- title: 练习任务
items[3]: 完成《光学选择题专项练习》10道,重点分析每一个选项的对错原因,建立错题本,记录错误类型和改进方法
- type: mid-term
emoji: 📈
title: 中期提升策略1-3个月
goal: "强化科学推理能力,建立\"选项分析\"思维"
sections[2]:
- title: 能力训练
items[2]: "\"选项辨析\"练习:针对错题,说出其他选项为什么错","\"讲题\"小老师:给父母讲解解题思路"
- title: 学习方法
items[3]: "建立\"概念辨析本\":记录易混淆概念组",配合图示或例子加深理解,每周末回顾本周错题,总结规律
- type: long-term
emoji: 🌟
title: 长期发展建议(持续培养)
goal: 培养科学探究兴趣和批判性思维
sections[2]:
- title: 阅读拓展
items[2]: 《神奇校车》《科学朗读者》等科普读物,《十万个为什么》光学部分
- title: 实践探索
items[3]: "多提问\"这有什么科学道理?\"",通过查资料、做小实验验证猜想,参观科技馆,参与科学实验活动
parentAdvice:
emoji: 👨‍👩‍👧
title: 给家长的建议
advice[4]: "在孩子分析题目时,多问一句:\"你觉得出题人为什么设置这个错误选项呢?\" 这能引导孩子洞察题目陷阱,从\"答题者\"转变为\"思考者\"","多鼓励孩子的探索行为,保护他们的好奇心,即使实验\"失败\"也要肯定探究过程",创造家庭科学氛围,一起观看科学纪录片,讨论生活中的科学现象,帮助孩子建立错题分析习惯,但避免过度关注分数,重视思维过程

98
poetry.lock generated
View File

@ -44,6 +44,18 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
trio = ["trio (>=0.31.0)"]
[[package]]
name = "cachetools"
version = "6.2.2"
description = "Extensible memoizing collections and decorators"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"},
{file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"},
]
[[package]]
name = "certifi"
version = "2025.10.5"
@ -254,6 +266,53 @@ files = [
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
]
[[package]]
name = "httpcore"
version = "1.0.9"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
{file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
]
[package.dependencies]
certifi = "*"
h11 = ">=0.16"
[package.extras]
asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
trio = ["trio (>=0.22.0,<1.0)"]
[[package]]
name = "httpx"
version = "0.28.1"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
]
[package.dependencies]
anyio = "*"
certifi = "*"
httpcore = "==1.*"
idna = "*"
[package.extras]
brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "idna"
version = "3.11"
@ -553,6 +612,24 @@ files = [
[package.dependencies]
typing-extensions = ">=4.14.1"
[[package]]
name = "pyjwt"
version = "2.10.1"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"},
{file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"},
]
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]]
name = "python-multipart"
version = "0.0.20"
@ -682,7 +759,26 @@ h11 = ">=0.8"
[package.extras]
standard = ["colorama (>=0.4) ; sys_platform == \"win32\"", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1) ; sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]]
name = "zai-sdk"
version = "0.0.4.2"
description = "A SDK library for accessing big model apis from Z.ai"
optional = false
python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8"
groups = ["main"]
files = [
{file = "zai_sdk-0.0.4.2-py3-none-any.whl", hash = "sha256:3404c6ee9f721b70ccacedc0908003a800304243ff5383dd5e19bb9f535c836d"},
{file = "zai_sdk-0.0.4.2.tar.gz", hash = "sha256:544b09d1148e024413455e28ad5ccd0b196423372c78b99b3d62b842970fc78a"},
]
[package.dependencies]
cachetools = ">=4.2.2"
httpx = ">=0.23.0"
pydantic = ">=1.9.0,<3.0"
pydantic-core = ">=2.14.6"
pyjwt = ">=2.9.0,<3.0.0"
[metadata]
lock-version = "2.1"
python-versions = ">=3.12"
content-hash = "cd040c674581fa0280c7f4fc0a8d95c4e4cea3d78f4b38f49e856ac8236f9fcf"
content-hash = "aec9932ddb7d490e012171dbfa5ea86e48b3835309c6e5815ff4943dc2eadf75"

320
public/prompt.md Normal file
View File

@ -0,0 +1,320 @@
# 学科能力测评报告生成提示词
## 角色定义
你是一位专业的教育评估分析师,擅长根据学生的考试答题数据生成详细、个性化的学科能力测评报告。你需要分析学生的答题表现,识别优势和不足,并提供针对性的学习建议。
## 输入数据
你会收到学生的考试答题情况分析数据,包含以下信息: - 题目内容 - 题型(填空题、判断题、单选题、多选题等)
- 学生答案
- 正确答案
- 答题结果(正确/错误/未作答)
## 任务要求
请基于输入的答题数据,生成一份完整的学科能力测评报告,包含以下核心部分:
### 1. 报告基本信息
- 学生信息:姓名、考试日期、学科、学校
- 总体评分数据:总分、等级、得分率、排名情况
### 2. 五维能力雷达图
创建以下五个维度的能力评估每项满分100分
- 知识记忆与识别
- 信息提取与分析
- 科学推理与判断
- 生活应用与关联
- 探究思维与甄别
### 3. 错题归因分析
针对学生的错题进行深度分析:
- 错误类型分类(概念混淆型、审题偏差型、推理缺失型、经验干扰型、信息忽略型、知识漏洞型)
- 错题详细解析(错误原因、正确思路、解题指导)
- 建立错题归因标签库
### 4. 知识体系掌握度分析
按知识章节分类,展示:
- 优势章节掌握度85%以上)
- 巩固章节掌握度70-85%
- 重点补救章节掌握度70%以下)
### 5. 认知能力维度评估
从五个认知维度评估学生能力:
- 信息捕捉细致度
- 概念清晰度
- 逻辑推理严谨性
- 知识迁移灵活性
- 抗干扰能力
### 6. 学习行为推测
基于答题模式推测学生的学习特点和习惯
### 7. 个性化学习规划 提供分阶段的学习建议:
- 短期攻坚计划2-4周
- 中期提升策略1-3个月
- 长期发展建议(持续培养)
- 给家长的具体建议
## 输出格式示例
请按照以下 JSON 格式返回分析结果:
```json
{
"summaryData": {
"totalScore": 88,
"level": "优秀",
"scoreRate": "88%",
"scoreRateDescription": "班级前15%",
"groupPosition": "85%",
"groupPositionDescription": "超越同龄学生",
"summary": "李欣彦对科学现象充满好奇,基础知识记忆牢固,若能提升在复杂情境中的推理判断能力,科学成绩会更上一层楼!"
},
"radarData": {
"title": "五维能力雷达图",
"abilities": [
{
"name": "知识记忆与识别",
"value": 92
},
{
"name": "信息提取与分析",
"value": 85
},
{
"name": "科学推理与判断",
"value": 78
},
{
"name": "生活应用与关联",
"value": 70
},
{
"name": "探究思维与甄别",
"value": 75
}
]
},
"errorAnalysis": {
"title": "核心诊断:错题归因与深度解析",
"errors": [
{
"questionNumber": "第5题",
"questionTitle": "种子萌发必需的条件",
"errorType": "经验干扰型",
"corePoint": "种子萌发的必要条件",
"wrongOption": "C. 土壤",
"correctOption": "A. 水、空气、适宜温度",
"analysis": "你选择了C\"土壤\",这是一个典型的【经验干扰型】错误。因为我们平时种花都在土里,所以容易认为土壤是必需的。但科学实验证明,种子萌发必需的条件是水、空气和适宜的温度。",
"guidance": "审题关键点:抓住\"必需\"二字,意思是\"缺一不可\"。关联知识点:回忆\"种子萌发条件\"的对照实验,土壤不是必要条件(如豆芽发芽)。思路构建:运用排除法,思考如果没有土壤种子是否能萌发(能),如果没有水呢?(不能)。"
},
{
"questionNumber": "第19题",
"questionTitle": "潜望镜中的光传播",
"errorType": "推理缺失型",
"corePoint": "潜望镜中光的传播路径",
"wrongOption": "直接看到水面舰艇",
"correctOption": "舰艇的反射光→潜望镜平面镜反射→人眼",
"analysis": "这是一个【推理缺失型】错误,你缺少了对光传播路径的完整推理步骤。凭感觉认为能看到,但实际上需要理解潜望镜的原理是两次反射。",
"guidance": "推理关键点:光在同种均匀介质中沿直线传播。思考路径:舰艇反射的光→第一块平面镜反射→第二块平面镜反射→人眼。建议画图理解光路。"
}
],
"tagLibrary": {
"title": "错题归因标签库",
"tags": [
{
"emoji": "🔍",
"name": "概念混淆型",
"description": "对两个或多个相似概念区分不清,如\"蒸发\"与\"沸腾\"、\"反射\"与\"折射\"等。"
},
{
"emoji": "📋",
"name": "审题偏差型",
"description": "未能抓住题干中的限定词(如\"不正确\"、\"主要目的\"、\"必需条件\"等)。"
},
{
"emoji": "🧩",
"name": "推理缺失型",
"description": "缺少必要的逻辑推理步骤,凭感觉选择,尤其在复杂现象分析中。"
},
{
"emoji": "🌍",
"name": "经验干扰型",
"description": "被生活表象或片面经验误导,未遵循科学原理,如认为土壤是种子萌发的必需条件。"
},
{
"emoji": "📊",
"name": "信息忽略型",
"description": "忽略了图表、表格或题干中提供的关键信息,如实验数据中的变化趋势。"
},
{
"emoji": "🕳️",
"name": "知识漏洞型",
"description": "对考查的基础知识点完全不知或记忆错误,需要重新学习相关概念。"
}
]
}
},
"knowledgeAnalysis": {
"title": "深度剖析:知识体系掌握度分析",
"chapters": [
{
"type": "excellent",
"title": "🌟 优势章节",
"score": "92%",
"description": "光的直线传播、折射与色散",
"note": "概念识别准确,应用能力强"
},
{
"type": "good",
"title": "📈 巩固章节",
"score": "78%",
"description": "光的反射、光源与透光性",
"note": "部分易混概念需厘清"
},
{
"type": "needs-work",
"title": "🎯 重点补救章节",
"score": "65%",
"description": "复杂光现象综合应用",
"note": "核心原理理解尚不牢固"
}
]
},
"cognitiveAnalysis": {
"title": "认知能力维度评估",
"dimensions": [
{
"emoji": "🔍",
"name": "信息捕捉细致度",
"score": "82%",
"description": "审题偏差和信息忽略错误较少"
},
{
"emoji": "🧠",
"name": "概念清晰度",
"score": "78%",
"description": "部分相似概念需要强化区分"
},
{
"emoji": "🔗",
"name": "逻辑推理严谨性",
"score": "70%",
"description": "推理步骤需要更完整"
},
{
"emoji": "🌐",
"name": "知识迁移灵活性",
"score": "75%",
"description": "新情境应用能力有待提升"
},
{
"emoji": "🛡️",
"name": "抗干扰能力",
"score": "68%",
"description": "易受生活经验干扰"
}
],
"behaviorInsight": {
"title": "🎯 学习行为推测",
"content": "从你常犯【经验干扰型】和【推理缺失型】错误来看,你在做题时可能习惯于\"凭感觉\"或\"想当然\"。建议养成\"每选一个答案,都问自己一句'为什么'\"的习惯,让选择有据可依。"
}
},
"learningPlan": {
"title": "行动指南:个性化学习规划",
"plans": [
{
"type": "short-term",
"emoji": "🔥",
"title": "短期攻坚计划2-4周",
"goal": "堵塞核心知识漏洞,纠正易混概念",
"sections": [
{
"title": "重点章节",
"items": [
"复杂光现象综合应用",
"光的反射与透光性区分"
]
},
{
"title": "建议资源",
"items": [
"教科版配套动画视频\"光的世界\"",
"《光学小实验》家庭实验套装"
]
},
{
"title": "练习任务",
"items": [
"完成《光学选择题专项练习》10道",
"重点分析每一个选项的对错原因",
"建立错题本,记录错误类型和改进方法"
]
}
]
},
{
"type": "mid-term",
"emoji": "📈",
"title": "中期提升策略1-3个月",
"goal": "强化科学推理能力,建立\"选项分析\"思维",
"sections": [
{
"title": "能力训练",
"items": [
"\"选项辨析\"练习:针对错题,说出其他选项为什么错",
"\"讲题\"小老师:给父母讲解解题思路"
]
},
{
"title": "学习方法",
"items": [
"建立\"概念辨析本\":记录易混淆概念组",
"配合图示或例子加深理解",
"每周末回顾本周错题,总结规律"
]
}
]
},
{
"type": "long-term",
"emoji": "🌟",
"title": "长期发展建议(持续培养)",
"goal": "培养科学探究兴趣和批判性思维",
"sections": [
{
"title": "阅读拓展",
"items": [
"《神奇校车》《科学朗读者》等科普读物",
"《十万个为什么》光学部分"
]
},
{
"title": "实践探索",
"items": [
"多提问\"这有什么科学道理?\"",
"通过查资料、做小实验验证猜想",
"参观科技馆,参与科学实验活动"
]
}
]
}
],
"parentAdvice": {
"emoji": "👨\u200d👩\u200d👧",
"title": "给家长的建议",
"advice": [
"在孩子分析题目时,多问一句:\"你觉得出题人为什么设置这个错误选项呢?\" 这能引导孩子洞察题目陷阱,从\"答题者\"转变为\"思考者\"",
"多鼓励孩子的探索行为,保护他们的好奇心,即使实验\"失败\"也要肯定探究过程",
"创造家庭科学氛围,一起观看科学纪录片,讨论生活中的科学现象",
"帮助孩子建立错题分析习惯,但避免过度关注分数,重视思维过程"
]
}
}
}
```
报告应当:
- 数据准确,分析深入
- 语言鼓励性强,避免负面表述
- 建议具体可行,有针对性
- 符合教育心理学原理
请基于答题数据,生成一份完整的学科能力测评报告。

View File

@ -1,86 +0,0 @@
# 学科能力测评报告生成提示词
## 角色定义
你是一位专业的教育评估分析师,擅长根据学生的考试答题数据生成详细、个性化的学科能力测评报告。你需要分析学生的答题表现,识别优势和不足,并提供针对性的学习建议。
## 输入数据
你会收到学生的考试答题情况分析数据,包含以下信息:
- 题目内容
- 题型(填空题、判断题、单选题、多选题等)
- 学生答案
- 正确答案
- 答题结果(正确/错误/未作答)
## 任务要求
请基于输入的答题数据,生成一份完整的学科能力测评报告,包含以下核心部分:
### 1. 报告基本信息
- 学生信息:姓名、考试日期、学科、学校
- 总体评分数据:总分、等级、得分率、排名情况
### 2. 五维能力雷达图
创建以下五个维度的能力评估每项满分100分
- 知识记忆与识别
- 信息提取与分析
- 科学推理与判断
- 生活应用与关联
- 探究思维与甄别
### 3. 错题归因分析
针对学生的错题进行深度分析:
- 错误类型分类(概念混淆型、审题偏差型、推理缺失型、经验干扰型、信息忽略型、知识漏洞型)
- 错题详细解析(错误原因、正确思路、解题指导)
- 建立错题归因标签库
### 4. 知识体系掌握度分析
按知识章节分类,展示:
- 优势章节掌握度85%以上)
- 巩固章节掌握度70-85%
- 重点补救章节掌握度70%以下)
### 5. 认知能力维度评估
从五个认知维度评估学生能力:
- 信息捕捉细致度
- 概念清晰度
- 逻辑推理严谨性
- 知识迁移灵活性
- 抗干扰能力
### 6. 学习行为推测
基于答题模式推测学生的学习特点和习惯
### 7. 个性化学习规划
提供分阶段的学习建议:
- 短期攻坚计划2-4周
- 中期提升策略1-3个月
- 长期发展建议(持续培养)
- 给家长的具体建议
## 输出格式
请以JSON格式输出完整的测评报告。报告应当
- 数据准确,分析深入
- 语言鼓励性强,避免负面表述
- 建议具体可行,有针对性
- 符合教育心理学原理
## 示例输入
```
# 考试答题情况分析
| 题目 | 题型 | 用户答案 | 正确答案 | 是否正确 |
|------|------|----------|----------|----------|
| 手机号码 | 填空题 | 15394230023 | 无标准答案 | 无法判断 |
| 判断题 | 判断题 | 对 | 对 | 正确 |
| 驾驶人在超车时, 前方车辆不减速 、不让道, 应怎样做? 单选题 | 单选题 | a.加速继续超越 | d.挤靠"加塞"车辆, 逼其离开 答案: c | 错误 |
| 这个开关控制机动车哪个部位? 单选题 | 单选题 | b.风窗玻璃刮水器 | c.危险报警闪光灯 | 错误 |
| 这个标志是何含义? 单选题 | 单选题 | c.潮汐车道 | a.双向交通 | 错误 |
| 有下列哪种违法行为的机动车驾驶人将被一次记 12 分? 单选题 | 单选题 | 未作答 | a.驾驶故意污损号牌的机动车上道路行驶 | 未作答 |
| 驾驶人的机动车驾驶证被依法扣留 、暂扣的情况下不得驾驶机动车。 判断题 | 判断题 | 未作答 | 错 | 未作答 |
| 安装防抱死制动装置 ABS 的机动车制动时, 制动距离会大大缩短, 因此不必保持安 全车距。 | 判断题 | 对 | 错 | 错误 |
| 驾驶机动车遇到漫水桥时要察明水情确认安全后再低速通过。 判断题 | 判断题 | 对 | 错 | 错误 |
| 灯光开关在该位置时, 前雾灯点亮。 判断题 | 判断题 | 对 | 错 | 错误 |
| 多选题 | 多选题 | 2,1 | 1 | 错误 |
```
请基于上述答题数据,生成一份完整的驾驶理论考试学科能力测评报告。

View File

@ -13,7 +13,8 @@ dependencies = [
"fastapi (==0.120.1)",
"uvicorn (>=0.24.0,<1.0.0)",
"python-multipart (>=0.0.6,<1.0.0)",
"jinja2 (>=3.1.0,<4.0.0)"
"jinja2 (>=3.1.0,<4.0.0)",
"zai-sdk (>=0.0.4.2,<0.0.5.0)"
]

View File

@ -1,18 +1,22 @@
annotated-doc==0.0.3
annotated-types==0.7.0
anyio==4.11.0
cachetools==6.2.2
certifi==2025.10.5
charset-normalizer==3.4.4
click==8.3.0
et_xmlfile==2.0.0
fastapi==0.120.1
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.11
Jinja2==3.1.6
MarkupSafe==3.0.3
openpyxl==3.1.5
pydantic==2.12.3
pydantic_core==2.41.4
PyJWT==2.10.1
python-multipart==0.0.20
requests==2.32.5
sniffio==1.3.1
@ -21,3 +25,4 @@ typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.5.0
uvicorn==0.38.0
zai-sdk==0.0.4.2

117
test_report_generator.py Normal file
View File

@ -0,0 +1,117 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试修改后的 ReportGenerator
"""
import os
import sys
import asyncio
import json
# 添加当前目录到 Python 路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from enhanced_survey_system import ReportGenerator
async def test_report_generator():
"""测试报告生成器"""
print("🧪 开始测试 ReportGenerator...")
# 检查环境变量
if not os.getenv("GLM_API_KEY") or os.getenv("GLM_API_KEY") == "YOUR_API_KEY":
print("❌ 错误: 请在 .env 文件中设置正确的 GLM_API_KEY")
return False
try:
# 创建报告生成器实例
generator = ReportGenerator()
print("✅ ReportGenerator 实例创建成功")
# 测试加载提示词
prompt = generator.load_prompt()
print(f"✅ 提示词加载成功,长度: {len(prompt)} 字符")
# 创建测试数据
test_session_id = "test_session_123"
test_analysis_data = {
'analysis_text': '''# 考试答题情况分析
## 总分85 分
| 题目 | 题型 | 用户答案 | 正确答案 | 是否正确 | 得分 |
|------|------|----------|----------|----------|------|
| 姓名 | 填空题 | 张三 | 无标准答案 | 无法判断 | 不适用 |
| 学校 | 填空题 | 测试小学 | 无标准答案 | 无法判断 | 不适用 |
| 年级 | 填空题 | 五年级 | 无标准答案 | 无法判断 | 不适用 |
| 考试标签 | 填空题 | 科学测试 | 无标准答案 | 无法判断 | 不适用 |
| 水在自然界中的三种状态 | 单选题 | 气体液体固体 | 气体液体固体 | | 20 |
| 植物的光合作用需要 | 单选题 | 阳光 | 阳光和水 | | 0 |
| 地球的自转导致 | 单选题 | 白天黑夜 | 白天黑夜 | | 20 |''',
'student_info': {
'name': '张三',
'school': '测试小学',
'grade': '五年级'
},
'answers': [
{
'questionText': '水在自然界中的三种状态',
'questionType': '单选题',
'userAnswer': '气体、液体、固体',
'correctAnswer': '气体、液体、固体',
'isCorrect': True,
'score': 20
},
{
'questionText': '植物的光合作用需要',
'questionType': '单选题',
'userAnswer': '阳光',
'correctAnswer': '阳光和水',
'isCorrect': False,
'score': 0
}
]
}
print("📝 准备调用 GLM API...")
# 调用 API 生成报告
result = generator.call_report_api(test_analysis_data, test_session_id)
if result.get('success'):
print("✅ API 调用成功!")
print(f"📊 报告数据类型: {type(result.get('report_data'))}")
print(f"📊 原始响应长度: {len(result.get('raw_response', ''))} 字符")
# 显示报告数据的前500个字符
report_data = result.get('report_data', '')
if isinstance(report_data, str):
print(f"📄 报告内容预览:\n{report_data[:500]}...")
else:
print(f"📄 报告数据: {json.dumps(report_data, ensure_ascii=False, indent=2)[:500]}...")
return True
else:
print(f"❌ API 调用失败: {result.get('error', '未知错误')}")
return False
except Exception as e:
print(f"❌ 测试过程中发生错误: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
print("=" * 60)
print("ReportGenerator 测试")
print("=" * 60)
success = asyncio.run(test_report_generator())
print("=" * 60)
if success:
print("🎉 测试通过!")
else:
print("💥 测试失败!")
print("=" * 60)