12 类实体统一表 + 状态机 + version 递增 + kb_relation 递归 CTE 1~3 跳
可解释路径(防环)+ kb_color 色板(T-P2-01/05,TD-09/18)。
实现:
- packages/kb/schemas.py:12 类 EntityType 与状态 Literal 校验、
EntityCreate/Update/Out、RelationCreate、GraphNode/Hop/Path/QueryResult、
ColorOut;雪花 ID 全部字符串出参(JS 精度)。
- packages/kb/repo.py:实体 CRUD/按名查重/过滤分页搜索;traverse 递归
CTE(path 数组 + NOT @> 防环,锚点与递归段 varchar[] 显转型);色板读写。
- packages/kb/service.py:状态机矩阵(draft→reviewing→published→archived
+ draft/reviewing→archived 撤稿,非法跃迁 40900,矩阵单测 16 组全覆盖)、
内容修改仅 draft/reviewing 且 version+1、published 只读;关系写入
(自环/三元组重复 400、端点缺失 404);色板 role/hex(#RRGGBB) 校验。
- apps/api/routers/kb.py:GET/POST /kb/items、GET/PATCH /kb/items/{id}、
GET /kb/items/{id}/graph?max_hops=1..3(超界 422);KbService 装配就近
放路由文件(apps/api/deps.py 本轮禁改)。
测试:单测 77 例(跃迁矩阵、12 类 Literal、非法类型拒绝、hex/role/hops
校验);集成 5 例(真实 PG:生命周期/改 published 被拒/版本递增、关系
约束、2 跳可解释路径+防环、列表过滤分页、色板读写)。全套门禁绿:
ruff check/format、mypy(指定包列表)、lint-imports、单测 315 通过、
集成 21 通过。
偏差说明:
1. 新增 tests/unit/__init__.py 与 tests/integration/__init__.py(仅注释
的包标记):任务文档要求单测/集成同名 test_kb_entity.py,pytest
prepend 导入模式按 basename 注册模块会撞名,加包标记唯一化。
2. 关系与色板未挂 HTTP 路由:文档 §3.4 路由清单仅列 items+graph,
录入入口属运营后台 T-P2-09,本批在 service 层落地并集成直连验证。
3. published→draft 按文档字面矩阵(其余跃迁非法)拦为 40900:改已发布
内容的既定路径是撤稿归档后重录新草稿(错误 hint 已说明),未私开
回退通道。
apps/ — 可部署的进程
| 目录 | 进程 |
|---|---|
api/ |
FastAPI 应用:路由、依赖注入、中间件 |
worker/ |
Celery 任务定义与四队列配置(gpu / cpu / io / beat) |
admin/ |
极简运营后台(占位,FR-ADM-01~05) |
API 层的职责边界(架构 §3.1 L3)
只做编排:认证鉴权、参数校验、幂等、配额校验与预扣、统一响应、OpenAPI。
不能放什么
- 业务规则。路由函数里出现
if判断业务条件,几乎总是应该下沉到packages/<域>/service.py或packages/domain/。 - 直接写 SQL 或用 ORM 查表。走域的
service。 - 直接调用外部 API。LLM 走
packages/llm;未来的审核、视觉、换装同理。 - 裸
dict返回。所有响应必须经过packages.shared.response.ok()/fail()。
契约先行(NFR-MAINT-01)
先在 apps/api/schemas.py 定义 Pydantic schema,再写实现。客户端类型由
OpenAPI 生成,schema 一变客户端立刻可见。
Worker 的规矩
- 每个长耗时任务都必须落
tk_task,走packages/task的状态机。 不要自己维护状态字段。 - 失败必须退还配额(
billing.refund(),FR-BILL-05)。apps/worker/tasks.py里的ping_task是形状示例。 - 队列按资源画像分:GPU 推理进
gpu,视觉/渲染进cpu,外部调用进io。 混队列会让「换装排队把报告拖慢」。