sync: 同步服务器最新代码 (2026-01-27)
Some checks failed
continuous-integration/drone/push Build is failing

更新内容:
- 后端 AI 服务优化(能力分析、知识点解析等)
- 前端考试和陪练界面更新
- 修复多个 prompt 和 JSON 解析问题
- 更新 Coze 语音客户端
This commit is contained in:
111
2026-01-27 10:03:28 +08:00
parent fcef140d1a
commit 442ac78b56
28 changed files with 324 additions and 121 deletions

View File

@@ -477,3 +477,8 @@ ability_analysis_service = AbilityAnalysisService()

View File

@@ -154,7 +154,10 @@ class CourseChatServiceV2:
# 6. 更新会话索引
if is_new_conversation:
await self._add_to_conversation_index(user_id, conversation_id, course_id)
await self._add_to_conversation_index(
user_id, conversation_id, course_id,
first_message=query # 使用用户第一条消息作为会话名称
)
else:
await self._update_conversation_index(user_id, conversation_id)
@@ -202,7 +205,7 @@ class CourseChatServiceV2:
Tuple[str, Optional[str]]: (事件类型, 数据)
- ("conversation_started", conversation_id): 会话开始
- ("chunk", text): 文本块
- ("end", None): 结束
- ("done", full_answer): 结束,附带完整回答
- ("error", message): 错误
"""
full_answer = ""
@@ -251,7 +254,7 @@ class CourseChatServiceV2:
yield ("chunk", chunk)
# 6. 发送结束事件
yield ("end", None)
yield ("done", full_answer)
# 7. 保存对话历史
await self._save_conversation_history(
@@ -262,7 +265,10 @@ class CourseChatServiceV2:
# 8. 更新会话索引
if is_new_conversation:
await self._add_to_conversation_index(user_id, conversation_id, course_id)
await self._add_to_conversation_index(
user_id, conversation_id, course_id,
first_message=query # 使用用户第一条消息作为会话名称
)
else:
await self._update_conversation_index(user_id, conversation_id)
@@ -526,7 +532,8 @@ class CourseChatServiceV2:
self,
user_id: int,
conversation_id: str,
course_id: int
course_id: int,
first_message: str = ""
) -> None:
"""
将会话添加到用户索引
@@ -535,6 +542,7 @@ class CourseChatServiceV2:
user_id: 用户ID
conversation_id: 会话ID
course_id: 课程ID
first_message: 用户第一条消息(用于生成会话名称)
"""
try:
from app.core.redis import get_redis_client
@@ -547,12 +555,21 @@ class CourseChatServiceV2:
await redis.zadd(index_key, {conversation_id: timestamp})
await redis.expire(index_key, CONVERSATION_INDEX_TTL)
# 2. 保存会话元数据
# 2. 生成会话名称取用户第一条消息的前30个字符
conversation_name = ""
if first_message:
# 移除换行符截取前30个字符
conversation_name = first_message.replace('\n', ' ').strip()[:30]
if len(first_message) > 30:
conversation_name += "..."
# 3. 保存会话元数据(包含会话名称)
meta_key = f"{CONVERSATION_META_PREFIX}{conversation_id}"
meta_data = {
"conversation_id": conversation_id,
"user_id": user_id,
"course_id": course_id,
"name": conversation_name, # 会话名称
"created_at": timestamp,
"updated_at": timestamp,
}
@@ -563,7 +580,8 @@ class CourseChatServiceV2:
)
logger.debug(
f"会话已添加到索引 - user_id: {user_id}, conversation_id: {conversation_id}"
f"会话已添加到索引 - user_id: {user_id}, conversation_id: {conversation_id}, "
f"name: {conversation_name}"
)
except RuntimeError:
@@ -671,8 +689,10 @@ class CourseChatServiceV2:
"updated_at": time.time(),
}
# 获取最后一条消息作为预览
# 获取历史消息
history = await self._get_conversation_history(conv_id)
# 获取最后一条消息作为预览
last_message = ""
if history:
# 获取最后一条 assistant 消息
@@ -683,8 +703,21 @@ class CourseChatServiceV2:
last_message += "..."
break
# 获取会话名称
# 优先使用元数据中保存的名称,如果没有则从历史消息中提取
conversation_name = meta.get("name", "")
if not conversation_name and history:
# 从第一条用户消息生成名称
for msg in history:
if msg["role"] == "user":
conversation_name = msg["content"].replace('\n', ' ').strip()[:30]
if len(msg["content"]) > 30:
conversation_name += "..."
break
conversations.append({
"id": conv_id,
"name": conversation_name, # 添加会话名称字段
"course_id": meta.get("course_id"),
"created_at": meta.get("created_at"),
"updated_at": meta.get("updated_at"),

View File

@@ -510,3 +510,8 @@ async def generate_exam(

View File

@@ -546,3 +546,8 @@ knowledge_analysis_service_v2 = KnowledgeAnalysisServiceV2()

View File

@@ -256,6 +256,16 @@ def _preprocess_text(text: str) -> str:
# 移除零宽字符
text = re.sub(r'[\u200b\u200c\u200d\ufeff]', '', text)
# 【重要】先替换中文标点为英文标点(在找边界之前做,否则中文引号会破坏边界检测)
cn_punctuation = {
'': ',', '': '.', '': ':', '': ';',
'"': '"', '"': '"', ''': "'", ''': "'",
'': '[', '': ']', '': '(', '': ')',
'': '{', '': '}',
}
for cn, en in cn_punctuation.items():
text = text.replace(cn, en)
# 提取 Markdown 代码块
patterns = [
r'```json\s*([\s\S]*?)\s*```',
@@ -705,3 +715,4 @@ def clean_llm_output(text: str) -> Tuple[str, List[str]]:

View File

@@ -377,3 +377,8 @@ async def prepare_practice_knowledge(

View File

@@ -213,3 +213,8 @@ PRIORITY_LEVELS = ["high", "medium", "low"]

View File

@@ -46,3 +46,8 @@ INCORRECT_KEYWORDS = ["错误", "incorrect", "false", "no", "wrong", "不正确"

View File

@@ -72,3 +72,8 @@ DEFAULT_TEMPERATURE = 0.7

View File

@@ -254,8 +254,11 @@ QUESTION_SCHEMA = {
"description": "知识点ID"
},
"correct": {
"type": "string",
"description": "正确答案"
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
],
"description": "正确答案(单选/判断/填空为字符串,多选为数组)"
},
"analysis": {
"type": "string",
@@ -298,3 +301,8 @@ MAX_DIFFICULTY_LEVEL = 5

View File

@@ -146,3 +146,8 @@ DEFAULT_KNOWLEDGE_TYPE = "理论知识"

View File

@@ -191,3 +191,8 @@ ANNOTATION_TAGS = [

View File

@@ -190,3 +190,8 @@ DEFAULT_DIFFICULTY = "intermediate"