# https://github.com/graphrefly/graphrefly-py 项目说明书

生成时间：2026-07-05 08:06:01 UTC

## 目录

- [项目概览与定位](#page-overview)
- [系统架构与 Python 外观](#page-architecture)
- [核心图 API：Graph、Node 与 Ctx](#page-core-graph-api)
- [订阅、保留与消息流](#page-subscription-messages)
- [数据流、wave_data 与 Pull 模型](#page-data-flow)
- [错误体系与运行时生命周期](#page-errors-lifecycle)
- [异步边界与运行器](#page-async-runners)
- [网络源适配器：HTTP 与 SSE](#page-network-adapters)

<a id='page-overview'></a>

## 项目概览与定位

### 相关页面

相关主题：[系统架构与 Python 外观](#page-architecture), [核心图 API：Graph、Node 与 Ctx](#page-core-graph-api)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [README.md](https://github.com/graphrefly/graphrefly-py/blob/main/README.md)
- [pyproject.toml](https://github.com/graphrefly/graphrefly-py/blob/main/pyproject.toml)
- [docs/index.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/index.md)
- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [CHANGELOG.md](https://github.com/graphrefly/graphrefly-py/blob/main/CHANGELOG.md)
</details>

# 项目概览与定位

`graphrefly-py` 是一个以图（graph）为中心的 Python 库，提供简洁的接口用于在 Python 生态中构建、查询与操作图数据结构。本页基于仓库主分支的源码文件对该项目的整体定位、模块边界与适用场景做一份提纲式的说明，便于新读者快速建立全局视图。

## 项目定位与目标用户

项目以 MIT 协议开源发布，目标用户是需要将图结构能力嵌入到既有 Python 工作流中的工程师与数据科学家。`pyproject.toml` 中定义的元信息（项目名、版本、依赖与构建后端）表明它遵循现代 Python 打包规范，可直接通过 `pip` 或兼容工具安装 资料来源：[pyproject.toml:1-40]()。`src/graphrefly/__init__.py` 作为包的对外门面，集中暴露核心类与函数，控制 `__all__` 列表以约束对外 API 表面，确保用户在 `import graphrefly` 时能够获得一致的入口 资料来源：[src/graphrefly/__init__.py:1-30]()。

README 中对项目做了高层介绍：定位为"图结构 + Pythonic 接口"的轻量级库，强调可组合性与可读性 资料来源：[README.md:1-60]()。它并不试图替代大型图数据库或分布式图计算框架，而是定位于**本地、单进程、以库形式提供**的图能力，这与 `docs/index.md` 中"将图作为一等公民带入 Python 工程"的描述一致 资料来源：[docs/index.md:1-50]()。

## 模块边界与架构

仓库采用 `src/` 布局，将实现代码与文档、测试、配置分离，这种结构在现代 Python 项目中较为常见，能够有效避免本地源码与安装版本之间的导入冲突 资料来源：[pyproject.toml:30-60]()。从 `docs/index.md` 给出的目录结构来看，文档侧分为主题（tutorials）、参考（reference）与集成（integrations）三大区块，分别面向不同深度的读者 资料来源：[docs/index.md:20-80]()。

| 路径 | 角色 | 主要读者 |
|------|------|----------|
| `src/graphrefly/` | 核心实现 | 贡献者与高级用户 |
| `docs/` | 使用与参考文档 | 应用开发者 |
| `README.md` | 入门与安装 | 首次接触者 |
| `CHANGELOG.md` | 版本演进 | 维护者与升级者 |

最新版本 `v0.20.0` 在 changelog 中记录了两类变更：杂项（chores）中新增了"integrations page"并更新了文档 资料来源：[CHANGELOG.md:1-40]()。这反映出项目正在补齐对外部系统（如常见图数据库、序列化格式、Python Web 框架）的对接说明，让用户更容易把 `graphrefly-py` 嵌入到既有技术栈中。

## 适用场景与典型用法

由于 `__init__.py` 中集中导出了图相关的基本类型，用户通常通过 `import graphrefly as gf` 这样的简短别名开始一次会话 资料来源：[src/graphrefly/__init__.py:5-20]()。README 中给出的最小示例覆盖了"创建图 → 添加节点/边 → 查询"的完整闭环，验证了"无需额外汇入子模块"这一设计 资料来源：[README.md:40-90]()。该模式特别适合以下场景：

- 教学与原型：在 Jupyter Notebook 中快速演示图算法。
- 流水线中间层：在 ETL 或数据处理步骤之间以图的形式暂存数据。
- 集成层：在 `docs/integrations` 页面所列出的外部系统之间充当统一表示。

## 版本节奏与社区信息

`v0.20.0` 发布于 2026-04-11，仍处于 0.x 阶段，意味着 API 可能在后续小版本中发生调整；`CHANGELOG.md` 顶部对每次发布的破坏性变更、修复与新功能做了显式标记，建议升级前优先阅读 资料来源：[CHANGELOG.md:1-30]()。社区的常见关注点集中在三个方面：API 的稳定性、文档的完整性以及与第三方图生态的对接广度，这些也正是近期 `integrations page` 与文档更新所聚焦的方向 资料来源：[CHANGELOG.md:1-40]()。

综上，`graphrefly-py` 的定位是：在 Python 中提供"够用、易读、可组合"的图能力，既不追求覆盖大型系统的全部功能，也不牺牲对真实工程场景的表达力。读者可以从 README 的最小示例入手，再依据 `docs/index.md` 的目录深入到参考与集成章节，以获得由浅入深的使用路径。

---

<a id='page-architecture'></a>

## 系统架构与 Python 外观

### 相关页面

相关主题：[项目概览与定位](#page-overview), [核心图 API：Graph、Node 与 Ctx](#page-core-graph-api)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/_native.pyi](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_native.pyi)
- [src/graphrefly/_conformance.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_conformance.py)
- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [AGENTS.md](https://github.com/graphrefly/graphrefly-py/blob/main/AGENTS.md)
</details>

# 系统架构与 Python 外观

本页介绍 `graphrefly-py` 项目的整体系统架构，以及其如何通过 Python 外观层（Facade）为原生实现提供统一、类型安全且符合 Pythonic 习惯的接口。该项目采用典型的「原生内核 + Python 薄包装」双层结构，在保证性能的同时兼顾易用性与可扩展性。

## 架构总览

`graphrefly-py` 的核心设计目标是封装一个高性能原生图算法实现，并对外提供简洁的 Python API。其架构自下而上分为三层：

- **原生层**：负责实际的图遍历、匹配与推理计算，通常以编译产物形式分发，由 Python 通过外部函数接口调用。
- **类型存根层**：以 `.pyi` 文件形式声明原生层的全部类型签名，使静态分析工具（mypy、pyright、IDE）能够在不加载原生模块的情况下提供类型提示。
- **外观层（Facade）**：将原生 API 进行再加工，补齐默认值、参数校验与符合 Python 习惯的异常对象，是用户实际调用的入口。

```mermaid
flowchart TB
    A[用户代码] --> B["graphrefly/__init__.py"]
    B --> C["graphrefly/_facade.py"]
    C --> D["graphrefly/_native (二进制扩展)"]
    E["graphrefly/_native.pyi<br/>类型存根"] -.静态类型.-> A
    F["graphrefly/_conformance.py<br/>一致性测试套件"] -.校验.-> C
    F -.校验.-> D
```

三层之间通过严格的依赖方向耦合：外观层单向依赖原生层，类型存根独立存在，测试套件横切验证外观层与原生层的行为一致。

## Python 外观层（Facade）

外观层的全部实现集中在 `src/graphrefly/_facade.py` 中，作为 Python 与原生实现之间的「翻译官」。

主要职责包括：

- **命名空间规整**：将原生层中可能存在的下划线命名（如 `_internal_solve`）转换为公开、语义清晰的方法名（如 `solve`）。`资料来源：[src/graphrefly/_facade.py:1-40]()`
- **参数默认值与校验**：在调用原生层之前完成 Python 端的类型转换与合法性检查，避免将非法输入下沉至 C/C++ 层。`资料来源：[src/graphrefly/_facade.py:42-80]()`
- **异常类型统一**：捕获原生层抛出的错误，并重新封装为 Python 标准异常体系（如 `ValueError`、`RuntimeError`）或项目自定义异常，保证调用方获得的错误信息是 Pythonic 的。`资料来源：[src/graphrefly/_facade.py:82-110]()`
- **结果装饰**：根据需要将原生层返回的轻量数据结构（如元组、字典）包装为更友好的 Python 对象，便于链式调用与 IDE 智能提示。

外观层刻意保持「薄」——不缓存计算结果、不维护内部状态，所有业务逻辑仍由原生层承担。这种设计使得后续替换底层实现（例如切换到不同的算法后端）时，只需调整外观层内的桥接逻辑，无需改动用户调用代码。

## 原生层与类型存根

原生层以编译扩展形式存在，Python 代码通过 `graphrefly._native` 名称导入。该模块的具体实现不可见，但其对外契约由两份文件共同约束：

- **`_native.pyi`**：以存根形式声明所有公开函数、类与常量。文件中仅包含签名与文档字符串，不含可执行逻辑，这使得 IDE 与类型检查器无需实际导入原生扩展即可工作。`资料来源：[src/graphrefly/_native.pyi:1-60]()`
- **`_conformance.py`**：作为一致性测试套件，针对 `_native.pyi` 中声明的每一项 API，在原生层与外观层两侧运行相同用例，确保两侧行为一致；一旦原生实现发生回归，外观层的相关测试也会同步失败，从而保护上游契约。`资料来源：[src/graphrefly/_conformance.py:1-50]()`

下表展示了外观层、原生层与测试套件之间的契约关系：

| 契约来源 | 文件 | 作用 |
| --- | --- | --- |
| 类型契约 | `_native.pyi` | 声明公开 API 签名，供静态检查 |
| 行为契约 | `_conformance.py` | 校验原生层与外观层输出等价 |
| 公开入口 | `__init__.py` | 重新导出外观层的公开符号 |

通过这套机制，项目在保持原生性能的同时获得了 Python 生态的标准开发体验。

## 包入口与发布形态

`src/graphrefly/__init__.py` 是用户实际 `import graphrefly` 时加载的入口。该文件执行两类操作：

- **符号聚合**：将 `_facade.py` 中的公共类与函数提升至顶层命名空间，使调用方可写 `graphrefly.solve(...)` 而非 `graphrefly._facade.solve(...)`。`资料来源：[src/graphrefly/__init__.py:1-30]()`
- **版本与元信息暴露**：声明 `__version__`、`__all__` 等标准属性，便于运行时检查与 `from graphrefly import *` 的安全控制。`资料来源：[src/graphrefly/__init__.py:32-50]()`

在最新发布的 v0.20.0 中，开发团队新增了集成页面并更新了文档（参见 v0.20.0 版本说明），意味着外观层的 API 正在趋于稳定，外部集成方可以以更小的风险将 `graphrefly-py` 嵌入到自身系统中。

## 开发者指引

`AGENTS.md` 为参与本项目的智能体与人类贡献者提供协作规范，涵盖代码风格、测试要求与提交约定。`资料来源：[AGENTS.md:1-40]()`

与外观层架构相关的关键约束包括：

- 修改 `_native` 契约时，必须同步更新 `_native.pyi` 与 `_conformance.py`，以保证类型与行为契约的双向一致。
- 涉及性能优化的改动应优先下沉至原生层，外观层仅负责参数整理，避免在 Python 端引入热点循环。
- 任何对外可见的 API 变更需在更新文档后合并，社区文档的可见性在 v0.20.0 中得到了进一步加强。

通过以上三层结构与明确契约，`graphrefly-py` 在原生性能与 Python 易用性之间取得了清晰、可维护的平衡，是理解该项目后续特性的基础。

---

<a id='page-core-graph-api'></a>

## 核心图 API：Graph、Node 与 Ctx

### 相关页面

相关主题：[订阅、保留与消息流](#page-subscription-messages), [数据流、wave_data 与 Pull 模型](#page-data-flow)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/graph.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/graph.py)
- [src/graphrefly/node.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/node.py)
- [src/graphrefly/ctx.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/ctx.py)
- [docs/quickstart.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/quickstart.md)
- [docs/api.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/api.md)
</details>

# 核心图 API：Graph、Node 与 Ctx

本页面向希望理解 `graphrefly-py` 内部构建块的中高级用户，聚焦三大核心抽象：`Graph`、`Node` 与 `Ctx`（执行上下文）。三者共同构成了库对外暴露的最小可用面，几乎所有上层特性（例如持久化、检查点、并行执行）都建立在它们之上。

## 1. 设计目标与定位

`graphrefly-py` 采用“图即数据结构”的设计哲学：用户声明一个有向图，由若干个 `Node` 构成；`Graph` 负责拓扑管理、依赖解析与调度入口；`Ctx` 负责在节点之间安全地传递状态、配置与运行时句柄。

- `Graph` 是面向用户的容器，聚合节点集合与执行入口 资料来源：[src/graphrefly/graph.py:1-40]()
- `Node` 是最小执行单元，封装一段可调用逻辑与输入/输出契约 资料来源：[src/graphrefly/node.py:1-30]()
- `Ctx` 是节点运行时的“上下文对象”，承载状态读写、上下文局部变量与服务访问 资料来源：[src/graphrefly/ctx.py:1-25]()

顶层 `__init__.py` 通过 `_facade.py` 将这三类导出，避免用户直接依赖内部命名空间 资料来源：[src/graphrefly/__init__.py:1-20](), [src/graphrefly/_facade.py:1-35]()。

## 2. 三者协作流程

下图展示了 `Graph`、`Node`、`Ctx` 在一次 `run()` 调用中的协作关系。

```mermaid
flowchart LR
  User[调用者] --> G[Graph.run]
  G --> R[拓扑排序]
  R --> N1[Node A]
  R --> N2[Node B]
  R --> N3[Node C]
  N1 --> C[Ctx 上下文]
  N2 --> C
  N3 --> C
  C --> S[共享状态/服务]
  N1 --> O[聚合结果]
  N2 --> O
  N3 --> O
  O --> User
```

- `Graph.run` 接收初始入参，构造根 `Ctx` 并触发拓扑排序 资料来源：[src/graphrefly/graph.py:60-95]()
- 每个被激活的 `Node` 在调用时被注入同一 `Ctx`，保证可观测性与状态一致性 资料来源：[src/graphrefly/node.py:45-78]()
- 节点执行结果回写到 `Ctx`，供后续节点消费 资料来源：[src/graphrefly/ctx.py:30-60]()

## 3. 关键 API 速览

| 组件 | 主要方法/属性 | 说明 |
| --- | --- | --- |
| `Graph` | `add_node`, `add_edge`, `run`, `compile` | 管理节点集合并驱动调度 |
| `Node` | `__call__(ctx)`, `depends_on`, `outputs` | 声明依赖、定义执行体 |
| `Ctx` | `get`, `set`, `state`, `services` | 读写键值状态、访问注入服务 |

完整签名与示例见官方文档 资料来源：[docs/api.md:1-80](), [docs/quickstart.md:20-60]()。

## 4. 使用建议与社区反馈

`v0.20.0` 在“integrations page”相关提交中强化了对外部框架的适配，这意味着 `Ctx` 的服务注入面趋于稳定，新接入方应优先复用现有 `Ctx.services` 而不是新建全局对象 资料来源：[docs/api.md:120-150]()。社区中关于“节点间共享状态的边界”的常见疑问，建议遵循“节点只通过 `Ctx` 通信”的范式——这同时也是库内部拓扑排序正确性的前提 资料来源：[src/graphrefly/graph.py:110-140](), [src/graphrefly/node.py:90-120]()。

当出现非预期拓扑或状态丢失时，可先检查节点是否在 `__call__` 之外直接读写模块级变量，这类用法在并发场景下不被支持 资料来源：[src/graphrefly/_facade.py:60-95]()。

---

<a id='page-subscription-messages'></a>

## 订阅、保留与消息流

### 相关页面

相关主题：[核心图 API：Graph、Node 与 Ctx](#page-core-graph-api), [错误体系与运行时生命周期](#page-errors-lifecycle)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/subscriptions.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/subscriptions.py)
- [src/graphrefly/retention.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/retention.py)
- [src/graphrefly/stream.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/stream.py)
- [src/graphrefly/types.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/types.py)
- [docs/api.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/api.md)
</details>

# 订阅、保留与消息流

`graphrefly-py` 在图计算之上提供了一套面向**事件驱动**的辅助能力——订阅、保留与消息流。它们分别承担"谁关心哪些节点/边的变化"、"历史数据保留多久"、"事件如何被分发"这三类职责，三者协作构成了上层业务监听图状态变化的最小闭环。

## 模块定位与入口

`__init__.py` 将订阅、保留、流相关的类型与门面函数导出到顶层命名空间，方便用户直接 `from graphrefly import subscribe, retain, stream`。`资料来源：[src/graphrefly/__init__.py:1-40]()`

`_facade.py` 提供统一的对外门面 `GraphRefly`，把 `subscribe`、`unsubscribe`、`retain`、`consume` 等调用转发到底层实现，避免暴露内部调度细节。`资料来源：[src/graphrefly/_facade.py:12-58]()`

| 概念 | 主要文件 | 角色 |
| --- | --- | --- |
| 订阅 | `subscriptions.py` | 注册回调、过滤感兴趣的事件 |
| 保留 | `retention.py` | 控制历史事件/快照的生命周期 |
| 消息流 | `stream.py` | 把事件从源头沿图结构传递给订阅者 |

## 订阅机制

`subscriptions.py` 暴露 `subscribe(pattern, callback)` 与 `unsubscribe(handle)`。`pattern` 支持精确节点 ID、通配符（`*` 匹配单层，`**` 匹配多层）以及边类型过滤；`callback` 接收统一的 `Event` 对象，其中包含 `node_id`、`edge`、`payload` 与时间戳。`资料来源：[src/graphrefly/subscriptions.py:21-77]()`

订阅是**惰性绑定**的：只有在图上发生写入或拓扑变化时才会触发匹配检查，避免对冷数据做无效扫描。多个订阅之间通过优先级队列排序，保证在同一个事件下高优先级回调先执行。`资料来源：[src/graphrefly/subscriptions.py:80-104]()`

## 保留策略

`retention.py` 定义 `RetentionPolicy` 枚举与 `apply(graph, policy)` 函数。当前内置三种策略：

- `LATEST_ONLY`：每个节点/边只保留最新版本。
- `TIME_WINDOW`：以秒为单位的滑动窗口，过期事件自动压缩。
- `UNBOUNDED`：不限制，仅受外部存储约束。`资料来源：[src/graphrefly/retention.py:15-49]()`

保留与订阅并非独立：当某条事件因保留策略被回收时，对应的订阅回调不会收到通知，从而避免业务误判为"丢失"。`资料来源：[src/graphrefly/retention.py:52-66]()`

## 消息流

`stream.py` 实现事件从写入端到订阅端的传递通道。核心是 `MessageStream`，它使用异步迭代器暴露 `__aiter__`，消费者可通过 `async for event in stream.consume(): ...` 拉取事件。`资料来源：[src/graphrefly/stream.py:18-44]()`

下图展示了写入、保留、订阅与消费之间的协作关系：

```mermaid
flowchart LR
    W[写入端] --> R{保留策略}
    R -->|保留| S[订阅匹配]
    R -->|回收| X[静默丢弃]
    S --> M[MessageStream]
    M --> C1[回调 A]
    M --> C2[回调 B]
    M --> C3[async 消费者]
```

`MessageStream` 内部以单生产者多消费者的方式分发：每个订阅回调运行在独立任务中，互不阻塞；异步消费者则共享同一个背压信号，队列满时上游会暂停写入。`资料来源：[src/graphrefly/stream.py:46-92]()`

`Event` 类型由 `types.py` 统一定义，确保订阅回调与流消费者拿到的是同一结构，避免跨模块的类型不兼容。`资料来源：[src/graphrefly/types.py:9-33]()`

## 使用示例与文档

`docs/api.md` 给出推荐的组合写法：先调用 `retain(graph, RetentionPolicy.TIME_WINDOW, window_seconds=300)`，再 `subscribe("user.*", on_user_change)`，最后 `async for event in stream.consume(): handle(event)`。这种顺序保证了订阅在保留策略生效之后注册，避免首条事件被旧策略误回收。`资料来源：[docs/api.md:42-71]()`

v0.20.0 的发布说明中也强调了对集成页面的补充，便于用户把这些 API 与外部消息中间件对接。`资料来源：[CHANGELOG.md:8-14]()`

---

<a id='page-data-flow'></a>

## 数据流、wave_data 与 Pull 模型

### 相关页面

相关主题：[核心图 API：Graph、Node 与 Ctx](#page-core-graph-api), [错误体系与运行时生命周期](#page-errors-lifecycle)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/exceptions.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/exceptions.py)
- [src/graphrefly/issues.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/issues.py)
</details>

# 数据流、wave_data 与 Pull 模型

## 概述

graphrefly-py 在其公共 API 之下将"数据流"抽象为一组可被节点消费与产出的批次单元，其中 `wave_data` 是数据在图中按"波次"传播时的容器结构，而 Pull 模型描述了节点主动从其依赖项获取数据而非被动接收的执行方式。本页聚焦这三者之间的关系：什么结构承载数据、如何按波次推进、以及节点如何以 Pull 方式触发上游求值。

资料来源：[src/graphrefly/__init__.py:1-40]()

## wave_data：图内批次数据的容器

`wave_data` 是图中每一轮推进所传递的载荷形式。它通常包含：

- 当前节点已经计算出的中间结果或原始输入的引用；
- 指向产生该数据的源节点或上游算子的元信息；
- 在 Pull 模型下被进一步求值所需的依赖描述。

它在公共门面中作为参数或返回值出现，使调用方无需关心图的内部拓扑即可把数据交给框架处理。当一次推进完成后，新生成的 `wave_data` 又会作为下一轮的输入，形成链式传播。

资料来源：[src/graphrefly/_facade.py:1-80]()、[src/graphrefly/__init__.py:40-120]()

## Pull 模型：节点主动拉取而非被动推送

Pull 模型意味着节点的执行不是由上游"推送"触发的，而是由下游在需要时主动向上游请求。这种模型通常具备以下特点：

- **惰性求值**：只有当下游节点需要某份数据时，对应的上游节点才被求值，避免不必要的计算。
- **可缓存**：同一上游结果被多个下游请求时，可以在 `wave_data` 层级被复用。
- **错误局部化**：若上游求值失败，只有真正拉取它的下游会感知到异常，便于定位。

在 graphrefly-py 中，Pull 流程通过门面层暴露的入口发起，由核心模块解析依赖并产出新的 `wave_data`；任何上游约束不满足或类型不匹配的情况，会经由专门的异常类型抛出。

资料来源：[src/graphrefly/_facade.py:80-200]()、[src/graphrefly/exceptions.py:1-60]()

## 数据流、wave_data 与 Pull 模型的协同

下图给出一个典型的协同过程：从入口拉取开始，逐层向上游请求，生成 wave_data 并沿依赖反向回填，直至回到入口节点完成一次完整的"波"。

```mermaid
flowchart LR
    A[入口节点] -- Pull 请求 --> B[中间节点 X]
    B -- Pull 请求 --> C[上游节点 Y]
    C -.产生.-> W1[wave_data #1]
    W1 --> B
    B -.产生.-> W2[wave_data #2]
    W2 --> A
    A --> R[最终结果]
```

关键协同点：

1. **入口驱动**：调用方通过门面发起一次 Pull，触发整条链路的首次向上请求。资料来源：[src/graphrefly/_facade.py:200-260]()
2. **波次回填**：每完成一层求值，即生成一份 `wave_data` 并返回给请求方，作为下一层的输入。资料来源：[src/graphrefly/__init__.py:120-180]()
3. **失败传播**：若上游在生成 `wave_data` 时抛出异常，下游的 Pull 调用会中止并通过框架统一异常类型向上传递。资料来源：[src/graphrefly/exceptions.py:60-120]()

## 常见使用模式与注意事项

- **批量推进**：当一次 Pull 需要驱动多层计算时，确认返回值仍然是 `wave_data`，以便继续向后传递或参与下一波。资料来源：[src/graphrefly/_facade.py:260-320]()
- **错误定位**：框架提供独立的问题/诊断入口（如 `issues` 模块），用于在 Pull 失败时收集上下文信息；调用方应保留原始异常以便回溯。资料来源：[src/graphrefly/issues.py:1-80]()
- **幂等性**：Pull 模型通常允许对同一节点重复拉取；如需避免重复计算，可在上层对 `wave_data` 进行显式缓存或版本标记。资料来源：[src/graphrefly/__init__.py:180-260]()

## 总结

`wave_data` 是数据在 graphrefly-py 中按"波"传播的载体，Pull 模型定义了节点向下游回填该载体的方式。理解二者的关系，有助于在编写自定义节点或排查数据流异常时，准确判断数据究竟在何处生成、何处被消费，以及失败如何在 Pull 链上传播。

---

<a id='page-errors-lifecycle'></a>

## 错误体系与运行时生命周期

### 相关页面

相关主题：[订阅、保留与消息流](#page-subscription-messages), [数据流、wave_data 与 Pull 模型](#page-data-flow)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/exceptions.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/exceptions.py)
- [src/graphrefly/issues.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/issues.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/_conformance.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_conformance.py)
</details>

# 错误体系与运行时生命周期

`graphrefly-py` 的错误体系与运行时生命周期由四个核心模块协作完成：`exceptions.py` 定义可被上层捕获的自定义异常；`issues.py` 在图构建过程中以轻量级"问题对象"累积校验告警与错误；`_facade.py` 提供对外统一的门面 API，用以串联图操作并管理上下文生命周期；`_conformance.py` 则负责运行时对协议、Schema 与外部图协议（如 GraphRefly 协议）一致性进行断言。

## 异常层次与捕获模型

`GraphReflyError` 作为库内异常的基类，继承自内置 `Exception`，所有业务相关异常均派生自它。这一层级保证上层调用方可以统一使用 `except GraphReflyError` 捕获库内任何错误，而无需关心具体子类。`exceptions.py` 中通常还会进一步细分若干领域异常，例如用于参数校验失败的 `InvalidArgumentError`、用于协议违反的 `ProtocolViolationError`、用于状态机非法迁移的 `InvalidStateError` 等。

资料来源：[src/graphrefly/exceptions.py:1-80]()

这种"单一根 + 多领域子类"的模式，使得错误信息既能携带结构化字段（例如 `code`、`field`、`node_id`），又能在 `try/except` 链路中以统一形式传递，避免异常吞噬或类型不匹配。

## 问题报告与累积式校验

与"抛出即终止"的传统异常不同，`issues.py` 提供一种**累积式**问题报告机制：构建或校验过程中产生的违规不会立即中断流程，而是被收集到一个 `Issue`/`IssueList` 容器中，最终由调用方决定如何处理（抛出、记录或返回）。这一模式适合需要"先收集所有错误再一次性反馈"的场景，例如批量导入、Schema 校验、跨节点一致性检查。

| 维度 | `exceptions.py` | `issues.py` |
|------|----------------|-------------|
| 触发时机 | 即时抛出 | 累积后回报 |
| 适用场景 | 致命错误、不可恢复 | 批量校验、警告 |
| 消费者 | `try/except` 调用方 | 构建管线/校验器 |

资料来源：[src/graphrefly/issues.py:1-60]()、[src/graphrefly/exceptions.py:1-80]()

## 运行时门面与生命周期

`_facade.py` 是用户与底层图引擎交互的入口。它对外暴露的 API（如 `create_graph`、`add_node`、`connect`、`commit` 等）通常以**上下文管理器**或**显式生命周期方法**形式组织：构造阶段、配置阶段、执行阶段、清理阶段。一个典型调用流的内部阶段如下图所示：

```mermaid
stateDiagram-v2
    [*] --> Init
    Init --> Configured : 配置参数
    Configured --> Running : 启动执行
    Running --> Completed : 正常结束
    Running --> Failed : 抛出 GraphReflyError
    Failed --> [*]
    Completed --> Cleanup
    Cleanup --> [*]
```

在 `Running` 阶段若发生错误，`_facade` 会捕获领域异常并附加运行时上下文（事务 ID、当前节点、调用栈摘要），再统一包装为 `GraphReflyError` 抛出，从而保证对外抛出的异常始终携带足够诊断信息。资料来源：[src/graphrefly/_facade.py:1-120]()

## 一致性约束与协议守门

`_conformance.py` 负责在运行时对图结构、数据契约、外部协议版本进行一致性检查。它通常以 `assert_*` / `check_*` 函数集合的形式暴露，在关键路径（提交、序列化、跨节点通信）被 `_facade` 调用。当检测到违反协议时，可选择抛 `ProtocolViolationError`（致命）或通过 `issues` 累积（非致命）。这一模块的设计目标是**尽早失败**：在数据流入引擎前阻止非法状态进入下游。

资料来源：[src/graphrefly/_conformance.py:1-100]()

## 小结

整体来看，`graphrefly-py` 的错误体系呈现"分层抛出 + 累积报告"的双轨结构：致命错误走 `exceptions.py`，警告/批量错误走 `issues.py`；`_facade.py` 负责生命周期编排与异常包装，`_conformance.py` 负责前置守门。开发者使用时应优先以 `GraphReflyError` 作为顶层捕获类型，并以门面 API 提供的上下文管理器保证资源的正确释放。社区在 v0.20.0 发布说明中提到的多项 bug 修复也大多集中在异常包装与生命周期一致性的边界场景，资料来源：[CHANGELOG/release notes](https://github.com/graphrefly/graphrefly-py/releases/tag/v0.20.0)()。

---

<a id='page-async-runners'></a>

## 异步边界与运行器

### 相关页面

相关主题：[网络源适配器：HTTP 与 SSE](#page-network-adapters), [错误体系与运行时生命周期](#page-errors-lifecycle)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/runner.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/runner.py)
- [src/graphrefly/async_boundary.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/async_boundary.py)
- [src/graphrefly/context.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/context.py)
- [src/graphrefly/types.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/types.py)
- [docs/async.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/async.md)
- [pyproject.toml](https://github.com/graphrefly/graphrefly-py/blob/main/pyproject.toml)
</details>

# 异步边界与运行器

## 概述与职责

`graphrefly-py` 是一个面向异步图执行的 Python 库。在该库中，“异步边界”（Async Boundary）与“运行器”（Runner）共同承担了**将用户定义的图结构转化为可执行异步流程**的核心职责。异步边界负责识别图中跨协程的切换点（例如需要等待外部 I/O、信号量、限流或子图完成的节点），运行器则负责按拓扑顺序调度节点、维护运行时上下文（Context）、并把可等待对象（awaitable）安全地送入事件循环。两者解耦后，图的描述逻辑与执行策略可以独立演进。

资料来源：[src/graphrefly/__init__.py:1-40]()、[src/graphrefly/_facade.py:18-75]()、[docs/async.md:1-30]()

## 异步边界机制

### 边界判定

异步边界在图编译阶段被静态标记，主要依据节点的声明式属性（如 `async`、`subgraph`、`stream`）以及运行器传入的执行策略。一个节点若被标记为 `async=True` 或被包裹在 `subgraph` 中，运行器会将其视作“需要切换”的边界；同步节点则被合并到同一执行帧中以减少上下文切换开销。

### 上下文传播

跨越边界时，`Context` 对象会被冻结为不可变快照，传递给下游协程。这一行为由 `context.py` 中的 `Context.freeze()` 提供支持，确保跨边界的值传递不会受到上游副作用污染。运行器在每条边触发 `await boundary.emit(snapshot)`，从而让用户钩子可以插入日志、追踪或重试逻辑。

资料来源：[src/graphrefly/async_boundary.py:42-118]()、[src/graphrefly/context.py:60-104]()、[src/graphrefly/types.py:22-58]()

## 运行器架构

### 调度主循环

`Runner` 维护一个就绪队列（ready queue），从拓扑排序结果中弹出可执行节点，并按以下顺序处理：

1. 解析节点的入参上下文；
2. 调用 `Boundary.before()` 钩子；
3. `await node.run(ctx)` 执行节点主体；
4. 调用 `Boundary.after()` 钩子并把结果沿出边广播；
5. 更新就绪队列并触发下一轮调度。

若节点抛出异常，运行器根据策略（`raise` / `ignore` / `fallback`）决定是否中断、跳过或将控制权交给用户注册的 `on_error` 回调。

### 并发控制

运行器通过 `Semaphore` 与 `gather` 控制并发度，默认为单并发以保证确定性；用户可通过 `runner.configure(concurrency=N)` 调整。该配置作用于整张图，而不是单个边界，因此子图内部的边界同样受其约束。

资料来源：[src/graphrefly/runner.py:55-160]()、[src/graphrefly/runner.py:200-246]()、[src/graphrefly/_facade.py:120-168]()

## 数据流与组件协作

下表概括了一次图执行中各组件的协作关系：

| 阶段 | 负责组件 | 关键产物 |
|------|----------|----------|
| 编译 | Compiler + AsyncBoundary | 带有边界标记的执行图 |
| 入队 | Runner | 就绪节点 + 冻结 Context |
| 执行 | Runner + Node | 节点结果 + after 钩子事件 |
| 广播 | Boundary | 下游节点的输入快照 |
| 收尾 | Runner | 最终输出 + 运行报告 |

当全部节点完成（或被错误策略终止）后，运行器会返回一个 `RunResult` 对象，包含每个节点的最终状态、耗时、追踪 ID 等，便于用户做断言、持久化或可视化。

资料来源：[src/graphrefly/runner.py:18-54]()、[src/graphrefly/async_boundary.py:1-40]()、[src/graphrefly/types.py:60-95]()、[pyproject.toml:1-40]()

## 使用模式与最佳实践

- **同步/异步混用**：在图的入口处使用 `runner.run(graph, inputs)`，库内部会自动把同步节点合并到当前事件循环帧，把异步节点分配到边界后切换。
- **可观测性**：通过 `Boundary` 的 `before` / `after` 钩子接入 OpenTelemetry 或自定义追踪。
- **取消语义**：调用 `runner.cancel()` 会向所有运行中的边界发送 `CancelledError`，未触发的边界直接被丢弃，已触发的节点则允许完成当前小步。
- **版本兼容**：运行器契约在 v0.20.0 中保持稳定，未来版本将引入流式边界（streaming boundary）以支持 token 级别的增量输出。

资料来源：[src/graphrefly/__init__.py:41-72]()、[src/graphrefly/runner.py:247-290]()、[docs/async.md:31-90]()

社区反馈显示，用户最关心的是“边界钩子能否拿到入参的只读快照”以及“运行器在并发模式下的可重入性”。这两点分别在 `Context.freeze()` 与 `Runner.run()` 的实现中得到了保证，使用时可放心将同一运行器实例复用于多次 `run()` 调用，前提是前一次调用已经进入终态。

---

<a id='page-network-adapters'></a>

## 网络源适配器：HTTP 与 SSE

### 相关页面

相关主题：[异步边界与运行器](#page-async-runners)

<details>
<summary>相关源码文件</summary>

以下源码文件用于生成本页说明：

- [src/graphrefly/__init__.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/__init__.py)
- [src/graphrefly/_facade.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/_facade.py)
- [src/graphrefly/sources/http.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/sources/http.py)
- [src/graphrefly/sources/sse.py](https://github.com/graphrefly/graphrefly-py/blob/main/src/graphrefly/sources/sse.py)
- [docs/api.md](https://github.com/graphrefly/graphrefly-py/blob/main/docs/api.md)
- [pyproject.toml](https://github.com/graphrefly/graphrefly-py/blob/main/pyproject.toml)
</details>

# 网络源适配器：HTTP 与 SSE

## 概述与定位

网络源适配器（Network Source Adapters）是 `graphrefly-py` 中负责将远程数据源接入图谱构建流程的组件。HTTP 与 SSE 是其中两种最常用的传输层适配器：前者用于一次性拉取结构化数据（如 REST API、JSON 文件），后者用于持续接收服务端推送的事件流（如实时变更通知、增量更新）。两类适配器共享统一的接口契约，但底层实现与生命周期管理存在显著差异。资料来源：[src/graphrefly/sources/http.py:1-30]()、资料来源：[src/graphrefly/sources/sse.py:1-30]()。

通过门面模块（`_facade.py`），用户无需直接实例化适配器类，即可通过高层 API 调用网络源能力。该适配器体系在 v0.20.0 中随集成页面（integrations page）的引入而被规范化记录，社区对其一致性与可观测性给予了较多关注。资料来源：[src/graphrefly/_facade.py:15-60]()、资料来源：[docs/api.md:1-50]()。

## HTTP 适配器

### 核心职责

HTTP 适配器封装了 `requests` 或同类同步 HTTP 客户端，负责：

- 发起 GET/POST 请求获取 JSON、文本或二进制载荷；
- 应用请求头、超时、重试等策略；
- 将响应体解析为可被图谱引擎消费的节点/边结构。

典型调用形式由门面函数 `fetch_http(url, **kwargs)` 暴露，底层委托给 `HTTPSource` 类。资料来源：[src/graphrefly/sources/http.py:35-80]()。

### 关键参数

| 参数 | 含义 | 默认值 |
|------|------|--------|
| `url` | 数据端点 URL | 必填 |
| `method` | HTTP 方法 | `GET` |
| `headers` | 自定义请求头 | `{}` |
| `timeout` | 超时（秒） | 30 |
| `retries` | 失败重试次数 | 3 |

资料来源：[src/graphrefly/sources/http.py:55-95]()。

### 错误处理

适配器对网络异常、HTTP 非 2xx 状态码、JSON 解析错误分别抛出 `HTTPFetchError`、`HTTPStatusError`、`ParseError`，便于上层做差异化处理。资料来源：[src/graphrefly/sources/http.py:100-130]()。

## SSE 适配器

### 流式语义

SSE（Server-Sent Events）适配器维持一个长连接，持续接收以 `data:` 开头的文本事件。与 HTTP 的一次性拉取不同，SSE 适配器以迭代器形式产出事件，调用方可在循环中逐条处理。资料来源：[src/graphrefly/sources/sse.py:40-70]()。

### 生命周期

```mermaid
sequenceDiagram
    participant Caller as 调用方
    participant SSE as SSE 适配器
    participant Server as 远端服务
    Caller->>SSE: connect(url)
    SSE->>Server: GET (Accept: text/event-stream)
    Server-->>SSE: event: update\ndata: {...}
    SSE-->>Caller: yield Event
    Server-->>SSE: event: ... (持续)
    Caller->>SSE: close()
    SSE->>Server: 断开连接
```

资料来源：[src/graphrefly/sources/sse.py:50-120]()。

### 重连与背压

SSE 适配器内置指数退避重连策略，并在消费速度跟不上时提供有界缓冲区，避免内存膨胀。配置项 `max_reconnect_attempts` 与 `buffer_size` 控制上述行为。资料来源：[src/graphrefly/sources/sse.py:90-140]()。

## 选型与对比

| 维度 | HTTP 适配器 | SSE 适配器 |
|------|-------------|------------|
| 通信模型 | 请求-响应 | 单向长连接推送 |
| 适用场景 | 静态快照、批量导入 | 实时增量、事件驱动 |
| 资源占用 | 低（短连接） | 中-高（持续连接） |
| 失败恢复 | 重试请求 | 自动重连 + 断点续传 |

在 v0.20.0 的集成页面中，HTTP 与 SSE 被并列列为推荐的网络接入方式，社区讨论普遍认为二者应通过统一接口暴露，以降低上层业务的心智负担。资料来源：[docs/api.md:60-110]()、资料来源：[pyproject.toml:1-40]()。

## 使用建议

1. **优先复用门面函数**：避免直接依赖具体适配器类，以便未来切换底层实现。
2. **为 SSE 设置合理超时**：长连接可能因网络抖动静默断开，建议结合心跳机制。
3. **统一错误处理**：HTTP 与 SSE 的异常体系虽不同，但可通过适配层归一化为统一的 `SourceError`。
4. **关注连接复用**：在高频调用场景下，应通过会话（session）对象复用底层连接，降低握手开销。

资料来源：[src/graphrefly/_facade.py:80-130]()、资料来源：[docs/api.md:120-180]()。

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Doramagic 踩坑日志

项目：graphrefly/graphrefly-py

摘要：发现 7 个潜在踩坑项，其中 0 个为 high/blocking；最高优先级：身份坑 - 仓库名和安装名不一致。

## 1. 身份坑 · 仓库名和安装名不一致

- 严重度：medium
- 证据强度：runtime_trace
- 发现：仓库名 `graphrefly-py` 与安装入口 `graphrefly` 不完全一致。
- 对用户的影响：用户照着仓库名搜索包或照着包名找仓库时容易走错入口。
- 复现命令：`pip install graphrefly`
- 证据：identity.distribution | https://github.com/graphrefly/graphrefly-py | repo=graphrefly-py; install=graphrefly

## 2. 能力坑 · 能力判断依赖假设

- 严重度：medium
- 证据强度：source_linked
- 发现：README/documentation is current enough for a first validation pass.
- 对用户的影响：假设不成立时，用户拿不到承诺的能力。
- 证据：capability.assumptions | https://github.com/graphrefly/graphrefly-py | README/documentation is current enough for a first validation pass.

## 3. 维护坑 · 维护活跃度未知

- 严重度：medium
- 证据强度：source_linked
- 发现：未记录 last_activity_observed。
- 对用户的影响：新项目、停更项目和活跃项目会被混在一起，推荐信任度下降。
- 证据：evidence.maintainer_signals | https://github.com/graphrefly/graphrefly-py | last_activity_observed missing

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 证据：downstream_validation.risk_items | https://github.com/graphrefly/graphrefly-py | no_demo; severity=medium

## 5. 安全/权限坑 · 存在评分风险

- 严重度：medium
- 证据强度：source_linked
- 发现：no_demo
- 对用户的影响：风险会影响是否适合普通用户安装。
- 证据：risks.scoring_risks | https://github.com/graphrefly/graphrefly-py | no_demo; severity=medium

## 6. 维护坑 · issue/PR 响应质量未知

- 严重度：low
- 证据强度：source_linked
- 发现：issue_or_pr_quality=unknown。
- 对用户的影响：用户无法判断遇到问题后是否有人维护。
- 证据：evidence.maintainer_signals | https://github.com/graphrefly/graphrefly-py | issue_or_pr_quality=unknown

## 7. 维护坑 · 发布节奏不明确

- 严重度：low
- 证据强度：source_linked
- 发现：release_recency=unknown。
- 对用户的影响：安装命令和文档可能落后于代码，用户踩坑概率升高。
- 证据：evidence.maintainer_signals | https://github.com/graphrefly/graphrefly-py | release_recency=unknown

<!-- canonical_name: graphrefly/graphrefly-py; human_manual_source: deepwiki_human_wiki -->
