Files

70 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""管理会计OS — 知识摘要模块
仿 OpenCode 的持久记忆机制(summarizer + SummaryMessageID),但做了三处改进:
1. 分层压缩(日→周→月→全部),不是一次性全量压缩
2. 结构化存储(MySQL 关系表),不是 SQLite JSON 消息
3. 保留版本链,不是覆盖式压缩
参考:OpenCode SummarizeProvider 的 prompt 框架 + CMA OperationLog 的审计日志
"""
from sqlalchemy import Column, Integer, String, Text, DateTime, JSON, ForeignKey, Float, func
from app.database import Base
class KnowledgeEvent(Base):
"""关键事件记录
自动从 OperationLog 和其他数据源抽取的"值得记住"的事件。
每个事件是一个结构化记录,包含类型、级别、关联对象、摘要描述。
这是增量压缩的输入——摘要 agent 只处理"未摘要过"的新事件。
"""
__tablename__ = "knowledge_events"
id = Column(Integer, primary_key=True, index=True)
event_type = Column(String(30), nullable=False, comment="事件类型: kpi_change/alert/decision/plan/map/import/user_action")
event_level = Column(String(20), default="info", comment="info/warning/important/critical")
source = Column(String(50), nullable=True, comment="来源: operation_log/api/erp_sync/manual")
source_id = Column(Integer, nullable=True, comment="源记录ID(如 operation_log.id")
target_type = Column(String(50), nullable=True, comment="关联对象类型: kpi/map/budget/alert/plan")
target_id = Column(Integer, nullable=True, comment="关联对象ID")
title = Column(String(300), nullable=False, comment="事件标题(一句话概括)")
description = Column(Text, nullable=True, comment="事件详细描述")
delta = Column(JSON, nullable=True, comment="变更字段和前后值: {field: {old: X, new: Y}}")
occurred_at = Column(DateTime, nullable=False, comment="事件发生时间")
created_at = Column(DateTime, server_default=func.now())
# 摘要追踪——记录该事件被哪些摘要(id列表)包含
summarized_in = Column(JSON, nullable=True, comment="包含此事件的摘要ID列表")
class KnowledgeSummary(Base):
"""知识摘要
分层存储:daily/weekly/monthly/cumulative
参考 OpenCode 的 summary_message_id 机制,但用结构化字段代替 message 指针。
"""
__tablename__ = "knowledge_summaries"
id = Column(Integer, primary_key=True, index=True)
level = Column(String(20), nullable=False, comment="摘要层级: daily/weekly/monthly/cumulative")
period_key = Column(String(20), nullable=False, comment="期间标识: 2026-06-12 / 2026-W24 / 2026-06 / cumulative")
title = Column(String(300), nullable=False, comment="摘要标题")
content = Column(Text, nullable=False, comment="摘要正文(纯文本/Markdown")
event_ids = Column(JSON, nullable=True, comment="包含的事件ID列表")
# 核心指标变化(精简提取,用于快速问答)
kpi_changes = Column(JSON, nullable=True, comment="摘要期内的KPI变化统计: [{kpi_code, kpi_name, old_value, new_value, direction, alert_level}]")
decision_points = Column(JSON, nullable=True, comment="决策点: [{time, action, actor, result}]")
key_metrics = Column(JSON, nullable=True, comment="摘要期内的关键指标快照: {kpi_code: value}")
# 元信息
prev_summary_id = Column(Integer, nullable=True, comment="上一级摘要ID(如 daily→weekly 的链路)")
next_compressed_by = Column(Integer, nullable=True, comment="被哪个更高层摘要包含")
token_estimate = Column(Integer, default=0, comment="估算token数(用于触发压缩阈值判断)")
model = Column(String(50), nullable=True, comment="生成摘要使用的模型名")
generated_by = Column(String(100), nullable=True, comment="生成方式: auto_scheduler/manual_trigger")
is_stale = Column(Integer, default=0, comment="0=最新 1=已被上层摘要覆盖")
created_at = Column(DateTime, server_default=func.now())