Files

118 lines
7.2 KiB
Markdown
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.
# 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。