7.2 KiB
7.2 KiB
adapter_python_pg 项目指令
基于 ontology(本体)数据基座的 Python 中间层适配器。用 YAML 定义实体、关系、属性、动作、规则,由本适配器驱动 PostgreSQL 完成读写与图查询,对上暴露实体/对象级的语义接口。
这是一份给 AI 编码工具(Claude Code / Codex / Hermes / OpenCode)的项目约定,优先级高于 README 的描述性内容;与本文冲突的实现一律视为错误。
1. 使用形态(硬规则)
| 项 | 约定 |
|---|---|
| 交付物 | pip 包,被已有的 FastAPI 项目 include_router 引用 |
| 不做的事 | 不提供独立 HTTP 服务,不带 uvicorn 启动入口,不假设自己是独立进程 |
| FastAPI 位置 | 只出现在 adapter_python_pg/api/,是 optional extra;核心包禁止 import fastapi |
| 宿主感知边界 | 宿主看不到 SQL / Cypher,只拿到实体对象与语义方法 |
| 契约来源 | 本体 YAML 是唯一事实来源;只加载 YAML 即可工作,不做代码生成、不要求手写 DDL |
宿主项目的典型用法:
from fastapi import FastAPI
from adapter_python_pg import Adapter, build_router
adapter = Adapter.load("ontology/factory-twin.yaml", dsn=settings.pg_dsn)
app = FastAPI()
app.include_router(build_router(adapter), prefix="/ontology")
2. 技术栈
| 层 | 选型 | 约束 |
|---|---|---|
| 语言 | Python 3.11+ | 与 pricing-service、data-mould 对齐 |
| 包管理 | uv(uv.lock 入库) |
不用手工 venv + pip |
| 构建 | hatchling | src/ 布局,带 py.typed |
| 驱动 | psycopg 3(async + psycopg_pool) |
不用 asyncpg、不用任何 ORM |
| 图 | 自研 agtype 解析(graph/agtype.py),注册 psycopg3 loader |
不用 PyPI 的 apache-age-python(0.0.7 依赖 psycopg2+antlr4);AGE master 的 psycopg3 驱动未发布 |
| 对象 | pydantic v2 + create_model() |
运行期按本体动态建类 |
| 本体校验 | pydantic 模型 + 导出 JSON Schema | 本体先自查,schema 供其他语言消费 |
| 表达式 | SQL 下推 + CEL(cel-python) |
不自研表达式解析器,不 eval |
| 批量写 | COPY 协议 |
时序吞吐量级差异 |
| API | FastAPI add_api_route() 动态注册 |
泛化 OpenAPI,不做 codegen |
| 测试 | pytest + pytest-asyncio + testcontainers | 集成测试连真库 |
| 质量 | ruff(line-length 100, py311)+ mypy | 配置写在 pyproject.toml |
依赖分层:[project.dependencies] 只放核心(psycopg / age / pydantic / pyyaml / cel-python);fastapi 放 [project.optional-dependencies].api。
3. 目录结构
src/adapter_python_pg/
├── ontology/ YAML 解析与校验(pydantic spec:entities/links/actions/functions/rules/constraints)
├── compiler/ 本体 → 存储映射:幂等 DDL、索引推导、版本记录
├── storage/ psycopg 连接池、事务边界、动态 SQL 构建(标识符管控)
├── repository/ 实体读写、批量、部分更新、约束校验
├── graph/ AGE 封装:建图、建边、遍历、路径 —— 转义与白名单集中在这里
├── timeseries/ hypertable 读写、COPY 批量、聚合查询
├── expr/ 表达式双轨:SQL 下推编译 + CEL 求值
├── rules/ 派生链接(传递闭包)、派生属性、计算属性
├── api/ FastAPI router 工厂(optional)
└── errors.py 统一错误模型
4. 架构决策(已拍板,勿反复)
- 异步优先 + 同步门面:内部全 async;对同步调用方提供
SyncAdapter薄包装,不复制实现。 - 多本体并存:文档落在
docs.collection = "{ontology}.{entity}";每个本体一个 AGE graph;多本体之间不做跨本体 JOIN。 - 先全 JSONB:属性一律存 JSONB,
column: true(属性提升为真实列)在 spec 里预留字段但不实现,等性能有真实压力再开。 - FastAPI 路由动态生成:
entity作为路径参数,OpenAPI 是泛化 schema;不做逐实体 typed routes、不导出 OpenAPI 代码。 - 发布方式:先支持
pip install git+ssh://git@gitlab.cnsjzn.com/donnyzhang/adapter_python_pg.git;GitLab PyPI registry 后续再接。 - agtype 自己解析,不 vendor、不引包:
graph/agtype.py用json.raw_decode+ 自定义数组扫描消费::vertex/::edge/::path后缀。理由——需要在解析层做自己的类型映射、错误归一与标识符校验,vendor 上游未发布代码等于把维护负担抄一份。测试样本必须是从真实库抓的原文。
5. 硬规则(违反即回退)
- 核心包禁止
import fastapi/import uvicorn。跑python -c "import adapter_python_pg"不应拉起任何 web 依赖。 - Cypher 禁止字符串拼接值。所有进入
cypher()的标识符走白名单、值走转义函数,集中在graph/,并配独立的注入用例矩阵。 - 实体与图必须同事务。一个实体既有 JSONB 行又有图节点,写入在一个事务内完成;禁止 repository 与 graph 各自 commit 造成半实体。
- 禁止
eval/exec/importlib执行 YAML 内容。表达式只允许两条路:编译成 SQL 下推,或交给 CEL 求值。 - DDL 必须幂等(
IF NOT EXISTS);本体演进只做加法,破坏性变更不做自动迁移。 - 默认不建索引。只有
index: true/unique: true才建;GIN 不是默认项。 - 公共接口必须完整类型标注;动态 pydantic 模型只出现在数据边界,不穿透内部函数签名。
- 不用
print,用标准库logging(库不配置 handler,由宿主决定)。 - 测试不得写真实业务库;集成测试用 testcontainers 起的临时实例。
6. 常用命令
uv sync --extra api # 安装(含 FastAPI 可选依赖)
uv sync # 只装核心,验证"无 web 依赖"边界
uv run pytest # 全部测试
uv run pytest -m "not integration" # 只跑不连库的单元测试
uv run ruff check . && uv run ruff format --check .
uv run mypy src
7. 测试约定
| 层 | 做法 |
|---|---|
| 本体解析 / 编译 | 纯单元测试,断言生成的 SQL 字符串与索引清单 |
| 仓储 / 图 / 时序 | 集成测试,testcontainers 起 pg-multimodel:pg18-age-timescale |
| 注入防护 | 专门的用例矩阵:非法标识符、引号、$$、注释符、Unicode 绕过 |
| 回归 | 每个 bug 先写失败用例(RED),再修(GREEN) |
集成测试镜像默认复用本机已构建的 pg-multimodel:pg18-age-timescale;CI 里改为拉 registry 里的预构建镜像,避免每次重新编译 AGE。
8. 与其他仓库的关系
| 仓库 | 关系 |
|---|---|
E:\code\ueos\devops\devos\server-resources\postgres-multimodel |
底层多模型 PostgreSQL 的 Dockerfile / compose / 初始化脚本 |
ueos/backend 的 ontology 模块(Java) |
概念同源(Action / Function / Query / 编排);不共享代码,接口语义对齐即可 |
9. 当前状态
v0.1 设计阶段:README 与本文已定契约,代码未开始。落地顺序见 README「路线」:本体解析 → 文档读写 → 图 → 时序 → 推理 → FastAPI 路由 → Actions。