chore(prep): shared groundwork for the Sprint-2 parallel batch

第二批(T6~T11)协调提交:
- 迁移 0005:kb 七表(entity/relation/rule/chunk vector1024+tsv 生成列/
  asset/color/gap),HNSW+GIN 索引,FK 级联
- ORM:七个 Kb* 模型(pgvector Vector/TSVECTOR/Computed)
- 配置:检索与 embedding、华为 JWKS、短信全参数组
- 错误码:40110/40111/42910/42911/61000
- 骨架路由:kb.py + auth_sms.py(已注册);依赖:tencentcloud-sdk-python-sms
  + spike extra(onnxruntime/opencv);gitignore:models/ 与 spike 输出
This commit is contained in:
xsl
2026-08-14 17:01:43 +08:00
parent ddec3973ad
commit d5f366fa4c
11 changed files with 656 additions and 4 deletions
+176 -1
View File
@@ -18,9 +18,11 @@ from datetime import datetime
from decimal import Decimal
from typing import Any
from pgvector.sqlalchemy import Vector
from sqlalchemy import (
BigInteger,
Boolean,
Computed,
DateTime,
Index,
Integer,
@@ -31,8 +33,9 @@ from sqlalchemy import (
Text,
UniqueConstraint,
func,
text,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.dialects.postgresql import JSONB, TSVECTOR
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
@@ -304,3 +307,175 @@ class ObEvent(Base):
Index("ix_ob_event_name_ts", "event_name", "server_ts"),
Index("ix_ob_event_user_ts", "user_id", "server_ts"),
)
# ---------------------------------------------------------------------------
# kb 域(知识库,架构文档 §6.2)
# ---------------------------------------------------------------------------
class KbEntity(Base):
"""12 类实体统一表:attrs JSONB 承载类目差异化字段(架构 §6.2).
状态机 draft→reviewing→published→archived 由 service 层校验;
published 实体不可直接改(version 递增需回 draft 重走评审)。
"""
__tablename__ = "kb_entity"
entity_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
entity_type: Mapped[str] = mapped_column(String(32), nullable=False)
name: Mapped[str] = mapped_column(String(128), nullable=False)
attrs: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
status: Mapped[str] = mapped_column(String(16), nullable=False, default="draft")
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
source: Mapped[str | None] = mapped_column(String(255))
license: Mapped[str | None] = mapped_column(String(64))
updated_by: Mapped[str | None] = mapped_column(String(64))
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (
UniqueConstraint("entity_type", "name", name="uq_kb_entity_type_name"),
Index("ix_kb_entity_type_status", "entity_type", "status"),
)
class KbRelation(Base):
"""邻接表 + 递归 CTE 做 1~3 跳(TD-09:不上 Neo4j)."""
__tablename__ = "kb_relation"
relation_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
from_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
rel_type: Mapped[str] = mapped_column(String(64), nullable=False)
to_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
weight: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False, default=Decimal("1"))
confidence: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False, default=Decimal("1"))
evidence: Mapped[str | None] = mapped_column(String(255))
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (
UniqueConstraint("from_id", "rel_type", "to_id", name="uq_kb_relation_triple"),
Index("ix_kb_relation_to", "to_id"),
)
class KbRule(Base):
"""决策表(TD-10 自研规则引擎)。conditions/effects 为 JSONB DSL."""
__tablename__ = "kb_rule"
rule_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
rule_key: Mapped[str] = mapped_column(String(64), nullable=False)
category: Mapped[str] = mapped_column(String(32), nullable=False)
version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
status: Mapped[str] = mapped_column(String(16), nullable=False, default="draft")
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=60)
conditions: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
effects: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
is_hard: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
confidence: Mapped[Decimal] = mapped_column(
Numeric(4, 3), nullable=False, default=Decimal("0.9")
)
evidence: Mapped[str | None] = mapped_column(String(255))
author: Mapped[str | None] = mapped_column(String(64))
reviewer: Mapped[str | None] = mapped_column(String(64))
published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (
UniqueConstraint("rule_key", name="uq_kb_rule_key"),
Index("ix_kb_rule_status_priority", "status", "priority"),
)
class KbChunk(Base):
"""向量 + 全文双索引,支撑混合检索(架构 §8.3).
embedding_model/version 必填:没有版本号的向量不可重跑(T-P2-03)。
tsv 是生成列——写入时不要手工填。
"""
__tablename__ = "kb_chunk"
chunk_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
entity_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
chunk_type: Mapped[str] = mapped_column(String(32), nullable=False, default="text")
text: Mapped[str] = mapped_column(Text, nullable=False)
embedding = mapped_column(Vector(1024), nullable=False)
embedding_model: Mapped[str] = mapped_column(String(64), nullable=False)
embedding_version: Mapped[str] = mapped_column(String(16), nullable=False)
tsv = mapped_column(
TSVECTOR, Computed("to_tsvector('simple', text)", persisted=True), nullable=False
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (Index("ix_kb_chunk_entity", "entity_id"),)
class KbAsset(Base):
"""图像资产。tryon_ready 是换装闭环的关键标志位(架构 §6.2)."""
__tablename__ = "kb_asset"
asset_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
entity_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
asset_type: Mapped[str] = mapped_column(String(32), nullable=False)
cos_key: Mapped[str] = mapped_column(String(255), nullable=False)
spec_check: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
tryon_ready: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
review_status: Mapped[str] = mapped_column(String(16), nullable=False, default="pending")
license: Mapped[str | None] = mapped_column(String(64))
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (
Index("ix_kb_asset_entity", "entity_id"),
Index("ix_kb_asset_tryon_ready", "tryon_ready", postgresql_where=text("tryon_ready")),
)
class KbColor(Base):
"""自建色板(TD-18:规避「四季色彩/CMB」商标)。Lab 用于 ΔE 数值匹配."""
__tablename__ = "kb_color"
color_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
season_code: Mapped[str] = mapped_column(String(16), nullable=False)
role: Mapped[str] = mapped_column(String(8), nullable=False)
lab_l: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False)
lab_a: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False)
lab_b: Mapped[Decimal] = mapped_column(Numeric(6, 2), nullable=False)
hex: Mapped[str] = mapped_column(String(7), nullable=False)
name_cn: Mapped[str] = mapped_column(String(64), nullable=False)
__table_args__ = (Index("ix_kb_color_season", "season_code"),)
class KbGap(Base):
"""知识缺口埋点:无检索结果的查询,驱动内容生产(T-P2-06)."""
__tablename__ = "kb_gap"
gap_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=False)
query_text: Mapped[str] = mapped_column(Text, nullable=False)
filter_ctx: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
hit_count: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
first_seen_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
last_seen_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)
__table_args__ = (Index("ix_kb_gap_query", "query_text"),)