Doramagic 项目包 · 项目说明书
graphrefly-py 项目
面向智能体工作流的响应式调度框架:用自然语言描述自动化任务,全程可追溯每一次决策,支持策略强制执行与检查点持久化,纯 Python 实现,零依赖。
项目概览与定位
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 的目录深入到参考与集成章节,以获得由浅入深的使用路径。
来源:https://github.com/graphrefly/graphrefly-py / 项目说明书
系统架构与 Python 外观
本页介绍 graphrefly-py 项目的整体系统架构,以及其如何通过 Python 外观层(Facade)为原生实现提供统一、类型安全且符合 Pythonic 习惯的接口。该项目采用典型的「原生内核 + Python 薄包装」双层结构,在保证性能的同时兼顾易用性与可扩展性。
继续阅读本节完整说明和来源证据。
架构总览
graphrefly-py 的核心设计目标是封装一个高性能原生图算法实现,并对外提供简洁的 Python API。其架构自下而上分为三层:
- 原生层:负责实际的图遍历、匹配与推理计算,通常以编译产物形式分发,由 Python 通过外部函数接口调用。
- 类型存根层:以
.pyi文件形式声明原生层的全部类型签名,使静态分析工具(mypy、pyright、IDE)能够在不加载原生模块的情况下提供类型提示。 - 外观层(Facade):将原生 API 进行再加工,补齐默认值、参数校验与符合 Python 习惯的异常对象,是用户实际调用的入口。
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 易用性之间取得了清晰、可维护的平衡,是理解该项目后续特性的基础。
来源:https://github.com/graphrefly/graphrefly-py / 项目说明书
核心图 API:Graph、Node 与 Ctx
本页面向希望理解 graphrefly-py 内部构建块的中高级用户,聚焦三大核心抽象:Graph、Node 与 Ctx(执行上下文)。三者共同构成了库对外暴露的最小可用面,几乎所有上层特性(例如持久化、检查点、并行执行)都建立在它们之上。
继续阅读本节完整说明和来源证据。
1. 设计目标与定位
graphrefly-py 采用“图即数据结构”的设计哲学:用户声明一个有向图,由若干个 Node 构成;Graph 负责拓扑管理、依赖解析与调度入口;Ctx 负责在节点之间安全地传递状态、配置与运行时句柄。
Graph是面向用户的容器,聚合节点集合与执行入口 资料来源:src/graphrefly/graph.py:1-40Node是最小执行单元,封装一段可调用逻辑与输入/输出契约 资料来源:src/graphrefly/node.py:1-30Ctx是节点运行时的“上下文对象”,承载状态读写、上下文局部变量与服务访问 资料来源: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() 调用中的协作关系。
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。
来源:https://github.com/graphrefly/graphrefly-py / 项目说明书
订阅、保留与消息流
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
下图展示了写入、保留、订阅与消费之间的协作关系:
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
来源:https://github.com/graphrefly/graphrefly-py / 项目说明书
数据流、wave_data 与 Pull 模型
graphrefly-py 在其公共 API 之下将"数据流"抽象为一组可被节点消费与产出的批次单元,其中 wavedata 是数据在图中按"波次"传播时的容器结构,而 Pull 模型描述了节点主动从其依赖项获取数据而非被动接收的执行方式。本页聚焦这三者之间的关系:什么结构承载数据、如何按波次推进、以及节点如何以 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 并沿依赖反向回填,直至回到入口节点完成一次完整的"波"。
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[最终结果]关键协同点:
- 入口驱动:调用方通过门面发起一次 Pull,触发整条链路的首次向上请求。资料来源:src/graphrefly/_facade.py:200-260
- 波次回填:每完成一层求值,即生成一份
wave_data并返回给请求方,作为下一层的输入。资料来源:src/graphrefly/__init__.py:120-180 - 失败传播:若上游在生成
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 链上传播。
错误体系与运行时生命周期
graphrefly-py 的错误体系与运行时生命周期由四个核心模块协作完成:exceptions.py 定义可被上层捕获的自定义异常;issues.py 在图构建过程中以轻量级"问题对象"累积校验告警与错误;facade.py 提供对外统一的门面 API,用以串联图操作并管理上下文生命周期;conformance.py 则负责运行时对协议、Schema 与外部图协议(如 ...
继续阅读本节完整说明和来源证据。
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 等)通常以上下文管理器或显式生命周期方法形式组织:构造阶段、配置阶段、执行阶段、清理阶段。一个典型调用流的内部阶段如下图所示:
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()。
异步边界与运行器
graphrefly-py 是一个面向异步图执行的 Python 库。在该库中,“异步边界”(Async Boundary)与“运行器”(Runner)共同承担了将用户定义的图结构转化为可执行异步流程的核心职责。异步边界负责识别图中跨协程的切换点(例如需要等待外部 I/O、信号量、限流或子图完成的节点),运行器则负责按拓扑顺序调度节点、维护运行时上下文(Context)、并...
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述与职责
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),从拓扑排序结果中弹出可执行节点,并按以下顺序处理:
- 解析节点的入参上下文;
- 调用
Boundary.before()钩子; await node.run(ctx)执行节点主体;- 调用
Boundary.after()钩子并把结果沿出边广播; - 更新就绪队列并触发下一轮调度。
若节点抛出异常,运行器根据策略(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() 调用,前提是前一次调用已经进入终态。
资料来源:src/graphrefly/__init__.py:1-40、src/graphrefly/_facade.py:18-75、docs/async.md:1-30
网络源适配器:HTTP 与 SSE
网络源适配器(Network Source Adapters)是 graphrefly-py 中负责将远程数据源接入图谱构建流程的组件。HTTP 与 SSE 是其中两种最常用的传输层适配器:前者用于一次性拉取结构化数据(如 REST API、JSON 文件),后者用于持续接收服务端推送的事件流(如实时变更通知、增量更新)。两类适配器共享统一的接口契约,但底层实现与生命周期管...
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述与定位
网络源适配器(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。
生命周期
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。
使用建议
- 优先复用门面函数:避免直接依赖具体适配器类,以便未来切换底层实现。
- 为 SSE 设置合理超时:长连接可能因网络抖动静默断开,建议结合心跳机制。
- 统一错误处理:HTTP 与 SSE 的异常体系虽不同,但可通过适配层归一化为统一的
SourceError。 - 关注连接复用:在高频调用场景下,应通过会话(session)对象复用底层连接,降低握手开销。
资料来源:src/graphrefly/_facade.py:80-130、资料来源:docs/api.md:120-180。
资料来源:src/graphrefly/sources/http.py:55-95。
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
用户照着仓库名搜索包或照着包名找仓库时容易走错入口。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
风险会影响是否适合普通用户安装。
Pitfall Log / 踩坑日志
项目: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
来源:Doramagic 发现、验证与编译记录