改成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 FROM python:3.12-slim
# 设置工作目录 # 设置工作目录

View File

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

View File

@ -4,13 +4,13 @@
import sqlite3 import sqlite3
import json import json
import uuid import uuid
import requests
import os import os
import time import time
from datetime import datetime, timezone, timedelta from datetime import datetime, timezone, timedelta
from http.server import HTTPServer, BaseHTTPRequestHandler from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
import threading import threading
from zai import ZhipuAiClient
def get_east8_time(): def get_east8_time():
"""获取东八区时间""" """获取东八区时间"""
@ -37,29 +37,25 @@ def load_env_config():
print(f"加载环境变量失败: {e}") print(f"加载环境变量失败: {e}")
class ReportGenerator: class ReportGenerator:
"""报告生成器 - 调用自定义API生成测评报告""" """报告生成器 - 调用GLM大语言模型API生成测评报告"""
def __init__(self): def __init__(self):
# 加载环境变量 # 加载环境变量
load_env_config() load_env_config()
# 自定义API配置 # GLM API配置
self.api_url = os.getenv("REPORT_API_URL", "http://120.26.23.172:5678/webhook/survey_report") self.api_key = os.getenv("GLM_API_KEY", "")
self.api_key = os.getenv("REPORT_API_KEY", "") self.timeout = int(os.getenv("LLM_API_TIMEOUT", "300"))
self.timeout = int(os.getenv("REPORT_API_TIMEOUT", "300")) self.model_name = "glm-4.5-air"
self.prompt_file = "/Users/moshui/Documents/survey/public/prompt.txt" self.prompt_file = "./public/prompt.md"
# 请求头 # 初始化 Zhipu AI 客户端
self.headers = { self.client = ZhipuAiClient(api_key=self.api_key)
"Content-Type": "application/json"
}
# 添加API密钥如果设置了
if self.api_key:
self.headers["Authorization"] = f"Bearer {self.api_key}"
print(f"报告生成器配置:") 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}") print(f" - 超时时间: {self.timeout}")
def load_prompt(self): def load_prompt(self):
@ -181,23 +177,43 @@ class ReportGenerator:
# 获取会话数据 # 获取会话数据
analysis_data = self.generate_analysis_text({'id': session_id}) analysis_data = self.generate_analysis_text({'id': session_id})
# 调用自定义API生成报告 # 调用GLM API生成报告
# 构造回调URL
report_result = self.call_report_api(analysis_data, session_id) report_result = self.call_report_api(analysis_data, session_id)
if report_result and report_result.get('success'): if report_result and report_result.get('success'):
# API调用成功异步生成中 # API调用成功直接生成报告
# 保存分析数据用于后续回调处理 # 构造完整的报告数据
self.save_analysis_data_for_regeneration(session_id, analysis_data) 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 { return {
'success': True, 'success': True,
'message': '报告异步生成中', 'message': '报告生成成功',
'session_id': session_id, 'session_id': session_id,
'report_id': report_id,
'report_data': complete_report_data,
'analysis_data': analysis_data 'analysis_data': analysis_data
} }
else: else:
raise Exception("API调用失败") raise Exception("GLM API调用失败")
except Exception as e: except Exception as e:
print(f"生成报告失败: {e}") print(f"生成报告失败: {e}")
@ -212,41 +228,73 @@ class ReportGenerator:
} }
def call_report_api(self, analysis_data, session_id): def call_report_api(self, analysis_data, session_id):
"""调用自定义报告API生成报告""" """调用GLM大语言模型API生成报告"""
# 构建请求数据 # 加载提示词
request_data = { prompt = self.load_prompt()
"analysis_text": analysis_data['analysis_text'],
"session_id": session_id
}
# 实现重试机制
max_retries = 3
retry_delay = 2 # 秒
try: try:
print(f"调用报告生成API...") print(f"调用GLM大语言模型SDK...")
print(f" API端点: {self.api_url}")
print(f" Session ID: {session_id}") print(f" Session ID: {session_id}")
print(f" 数据长度: {len(request_data['analysis_text'])} 字符") print(f" 模型: {self.model_name}")
response = requests.post( # 使用ZhipuAiClient调用API设置JSON格式响应
self.api_url, response = self.client.chat.completions.create(
headers=self.headers, model=self.model_name,
json=request_data, messages=[
timeout=10 # 简化超时时间 {
"role": "system",
"content": prompt
},
{
"role": "user",
"content": analysis_data['analysis_text']
}
],
response_format={
"type": "json_object"
}
) )
if response.status_code == 200: print(f"✅ GLM SDK调用成功")
result = response.json() print(f" 响应长度: {len(response.choices[0].message.content)} 字符")
print(f"✅ API调用成功已提交异步报告生成请求")
print(f" 响应内容: {str(result)[:200]}...") # 解析JSON响应
return {"success": True, "message": "异步报告生成请求已提交"} model_response = response.choices[0].message.content
else: parsed_report = self.parse_json_response(model_response)
raise Exception(f"API调用失败: HTTP {response.status_code} - {response.text}")
return {
"success": True,
"message": "报告生成成功",
"report_data": parsed_report,
"raw_response": model_response
}
except Exception as e: except Exception as e:
print(f"API调用出错: {e}") print(f"GLM SDK调用出错: {e}")
raise Exception(f"报告生成API调用失败: {str(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): def save_report_to_db(self, session_id, report_data, analysis_data):
"""保存报告到数据库""" """保存报告到数据库"""
@ -446,22 +494,23 @@ class EnhancedSurveySystem:
analysis_data = json.loads(result[0]) analysis_data = json.loads(result[0])
# 调用API生成报告 # 调用GLM API生成报告
report_result = self.report_generator.call_report_api(analysis_data, session_id) 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'] student_info = analysis_data.get('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')
# 构造报告数据 # 构造报告数据
report_data = { report_data = {
'studentInfo': report_result['studentInfo'], 'studentInfo': {
'report': report_result['report'], '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(), 'generated_at': get_east8_time().isoformat(),
'session_id': session_id, 'session_id': session_id,
'analysis_data': analysis_data, 'analysis_data': analysis_data,
@ -481,7 +530,7 @@ class EnhancedSurveySystem:
'is_regenerated': True 'is_regenerated': True
} }
else: else:
raise Exception("API返回空内容") raise Exception("GLM API返回空内容或失败")
except Exception as e: except Exception as e:
print(f"重新生成报告失败: {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] [package.extras]
trio = ["trio (>=0.31.0)"] 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]] [[package]]
name = "certifi" name = "certifi"
version = "2025.10.5" version = "2025.10.5"
@ -254,6 +266,53 @@ files = [
{file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, {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]] [[package]]
name = "idna" name = "idna"
version = "3.11" version = "3.11"
@ -553,6 +612,24 @@ files = [
[package.dependencies] [package.dependencies]
typing-extensions = ">=4.14.1" 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]] [[package]]
name = "python-multipart" name = "python-multipart"
version = "0.0.20" version = "0.0.20"
@ -682,7 +759,26 @@ h11 = ">=0.8"
[package.extras] [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)"] 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] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12" 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)", "fastapi (==0.120.1)",
"uvicorn (>=0.24.0,<1.0.0)", "uvicorn (>=0.24.0,<1.0.0)",
"python-multipart (>=0.0.6,<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-doc==0.0.3
annotated-types==0.7.0 annotated-types==0.7.0
anyio==4.11.0 anyio==4.11.0
cachetools==6.2.2
certifi==2025.10.5 certifi==2025.10.5
charset-normalizer==3.4.4 charset-normalizer==3.4.4
click==8.3.0 click==8.3.0
et_xmlfile==2.0.0 et_xmlfile==2.0.0
fastapi==0.120.1 fastapi==0.120.1
h11==0.16.0 h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
idna==3.11 idna==3.11
Jinja2==3.1.6 Jinja2==3.1.6
MarkupSafe==3.0.3 MarkupSafe==3.0.3
openpyxl==3.1.5 openpyxl==3.1.5
pydantic==2.12.3 pydantic==2.12.3
pydantic_core==2.41.4 pydantic_core==2.41.4
PyJWT==2.10.1
python-multipart==0.0.20 python-multipart==0.0.20
requests==2.32.5 requests==2.32.5
sniffio==1.3.1 sniffio==1.3.1
@ -21,3 +25,4 @@ typing-inspection==0.4.2
typing_extensions==4.15.0 typing_extensions==4.15.0
urllib3==2.5.0 urllib3==2.5.0
uvicorn==0.38.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)