118 lines
7.2 KiB
Markdown
118 lines
7.2 KiB
Markdown
# 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 |
|
||
|
||
宿主项目的典型用法:
|
||
|
||
```python
|
||
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. 目录结构
|
||
|
||
```text
|
||
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. 架构决策(已拍板,勿反复)
|
||
|
||
1. **异步优先 + 同步门面**:内部全 async;对同步调用方提供 `SyncAdapter` 薄包装,不复制实现。
|
||
2. **多本体并存**:文档落在 `docs.collection = "{ontology}.{entity}"`;每个本体一个 AGE graph;多本体之间不做跨本体 JOIN。
|
||
3. **先全 JSONB**:属性一律存 JSONB,`column: true`(属性提升为真实列)在 spec 里**预留字段但不实现**,等性能有真实压力再开。
|
||
4. **FastAPI 路由动态生成**:`entity` 作为路径参数,OpenAPI 是泛化 schema;**不做**逐实体 typed routes、不导出 OpenAPI 代码。
|
||
5. **发布方式**:先支持 `pip install git+ssh://git@gitlab.cnsjzn.com/donnyzhang/adapter_python_pg.git`;GitLab PyPI registry 后续再接。
|
||
6. **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. 常用命令
|
||
|
||
```bash
|
||
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。
|