feat: persist cleaned question insights

This commit is contained in:
2026-07-31 16:50:11 +08:00
parent 6c03faf10c
commit a589a25bdc
10 changed files with 505 additions and 21 deletions

View File

@@ -4,6 +4,7 @@ from app.models.base import Base
from app.models.chat import ChatMessage, ChatSession, TopicSession
from app.models.entitlement import EntitlementPlan, UserEntitlement, UserEntitlementLog
from app.models.growth import GrowthProfileRevision, PeriodicReport, ShareDraft, TeacherHelpCard, TopicSummary, UserGrowthProfile
from app.models.insight import QuestionInsightCleanedQuestion
from app.models.knowledge import (
HumanAttentionHistory,
HumanAttentionRecord,
@@ -54,6 +55,7 @@ __all__ = [
"TopicSession",
"TopicSummary",
"Prompt",
"QuestionInsightCleanedQuestion",
"Role",
"SystemConfig",
"ShareDraft",

View File

@@ -0,0 +1,55 @@
from __future__ import annotations
from datetime import datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base
class QuestionInsightCleanedQuestion(Base):
"""用户问题清洗后的持久化结果。
每个原始消息至少写入一条记录。没有有效问题的消息会写入 accepted=0
的占位记录,使增量清洗无需重复读取已经处理过的聊天原文。
"""
__tablename__ = "sys_question_insight_cleaned_question"
__table_args__ = (
UniqueConstraint(
"message_id",
"cleaner_version",
"part_index",
name="uq_question_insight_message_version_part",
),
)
id: Mapped[int] = mapped_column(
BigInteger().with_variant(Integer, "sqlite"),
primary_key=True,
autoincrement=True,
)
message_id: Mapped[int] = mapped_column(
ForeignKey("sys_chat_message.id", ondelete="CASCADE"),
nullable=False,
)
session_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False)
user_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False)
part_index: Mapped[int] = mapped_column(Integer, nullable=False)
cleaner_version: Mapped[str] = mapped_column(String(20), nullable=False)
source_hash: Mapped[str] = mapped_column(String(64), nullable=False)
cleaned_text: Mapped[str] = mapped_column(Text, nullable=False, default="")
normalized_text: Mapped[str] = mapped_column(Text, nullable=False, default="")
category: Mapped[str] = mapped_column(String(50), nullable=False, default="other")
tokens_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
accepted: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
filtered_reason: Mapped[str | None] = mapped_column(String(100), nullable=True)
source_created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)