关于 Pacioli — the ERPNext broker you can hand the books
Pacioli 为 ERPNext 提供 API 凭证权限控制,以及面向 Agent 的 MCP/A2A 代理入口。它通过计划、独立同意、执行和可验证收据管理账务操作,并明确列出覆盖边界。
主要功能
- 01按方法和 DocType 限制 API 凭证
- 02通过文档钩子执行提交与取消同意检查
- 03支持 51 种 DocType 和 265 个工具
- 04计划、执行并生成哈希链收据
适用场景
- 01限制集成或自动化凭证可调用的 ERPNext 方法
- 02要求独立授权后提交或取消账务单据
- 03为 Agent 提供受治理的 ERPNext 操作入口
MCP 相关
代理组件通过 MCP 或 A2A 为 Agent 提供 ERPNext 操作入口;两种协议共用同一治理流程。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
为 ERPNext 提供最小权限治理,并在此基础上构建受治理的 Agent 入口——MCP 和 A2A,共用一套底层架构。入口只负责放行,绝不做决定。
1494 年,卢卡·帕乔利在威尼斯印行了《算术大全》——其中收录的论述,教会了世界复式记账。他写下这些,并非因为商人不会算账,而是因为他们无法信任。他的规则只有一句话:**有借必有贷。**每项操作都有金额相等、方向相反且留有记录的对应项——账目若不平,立刻就会暴露。五百年来,始终奉行“证明它,否则就当它没有发生”。
这个仓库就是那篇论述,为新一代账房先生重新写就。商人的问题没有变,账房先生变了。他为那些无法时刻盯着每位账房先生的商人平衡账目。如今,这套方法为无法时刻盯着每个 Agent 的所有者平衡账目。问题相同,相隔五个世纪,解决办法也相同。
继续阅读前先看看实际效果:演示。只需一条命令,在全新的环境中执行 pip install pacioli pacioli-guard,即可看到治理层作出决策,并生成一份无法悄悄篡改的密封回执。每项裁决都由已发布的代码作出;没有任何演示造假。
图版 I——据传为雅各布·德·巴巴里于 1495 年创作:修士坐在石板前。玻璃制菱形立方八面体悬于他左侧;《算术大全》置于他右侧的十二面体下方。本仓库的标志正是那悬挂的几何体。
这座房子里有两种工具,它们彼此配合——而非捆绑。 第一种守护站点上的每一个 api-key 凭据,无论是否涉及 Agent;第二种则是一扇由规则管控、Agent 可以使用的门。第一种是门下的地基;第二种立于其上,并受其约束。以下将按照论著的方式分别阐述这两种工具:它们是什么、拒绝什么,以及如何安装。
地基的覆盖范围,直言不讳,因为夸大其词的地基就不成其为地基。 两个问题,frappe 自身的两个公开钩子,各自在恰当的层面回答相应问题。
哪个凭据正在执行操作,它可以调用什么? 凭据门禁通过 auth_hooks 运行,在 api-key 凭据通过身份验证时触发。它管控所有 REST 挂载点上的 token/Basic api-key 请求,但不管控 OAuth2 Bearer 令牌、desk/cookie 会话、后台作业、调度器、服务器脚本或 bench 控制台。“这是什么凭据”这个问题只在身份验证时才有意义,因此此门禁只能设在这里;这条边界本身是固有的。
此操作是否经过同意? 这是文档本身的属性,因此通过文档上的 doc_events 在 before_submit 和 before_cancel 阶段强制执行;自 guard 0.13.0 起,也包括 before_gl_preview 和 before_sl_preview。后两个钩子把关的是过账的预演,而非过账本身:ERPNext 会执行过账来预览账簿,然后回滚事务,因此预览提交需要与提交本身相同的标记,且不会消耗该标记。所有通过 ORM 记入账簿的路径都会经过此检查:REST、run_doc_method、frappe.client.submit、携带已提交内容的 frappe.client.insert、desk Save/Submit 端点、批量提交、直接写入 docstatus 字段后再保存、后台作业、服务器脚本和 bench 控制台。同意检查不会继承凭据门禁的传输边界。
以下操作仍会绕过两者,在此先行说明,而非留待后来才发现:
- 完全跳过文档生命周期的写入:原始 SQL,以及
db_update/db_set式字段写入。ERPNext 核心代码在重新过账时就会自行这么做。 - 能够破坏授权读取的行为者。同意处理程序会在站点上的每份文档上运行,因此如果读取授权时发生错误,它会直接返回,而不是抛出异常——通配处理程序一旦崩溃,整个站点都会受影响。这是有意为之的取舍,也是实际存在的残余风险。
Document.discard。 它被列入白名单,因此可以调用;它会通过db_set将docstatus = 2,触发的则是before_discard,而非before_cancel。它会拒绝任何非草稿,因此它绝不会冲销任何账簿分录,也不会记入任何内容。风险在于草稿被移出流程,而非账簿发生变动。若要为它设门禁,就需要独立的操作类型,而不能并入“取消”:同意冲销已过账分录,与同意放弃草稿,是两种不同的权限。特此列明,不做含糊带过。
没有任何单一 frappe 扩展点能覆盖全部情况,因此此处的覆盖方案是组合式的,并明确说明其残余风险,而非声称面面俱到——平台无法为任何人做到这一点。
### 守门人——账房的门
除指定经手人外,无人可以在账簿上记账;且只能在分配给自己的账簿上记账。
一个 Frappe/ERPNext bench 应用(在 PyPI 上的发行名称为 pacioli-guard;安装后为
pacioli_guard bench 应用)。它通过 Frappe 的公共
auth_hooks 扩展点,在凭据层以默认拒绝的方式,将任何 API 凭据——集成、Zapier/n8n 工作流、脚本、供应商令牌、cron 任务、AI Agent——绑定到方法允许列表(以及获准时的 DocType 允许列表)。无需分叉核心代码。它管理站点上的每个 API 密钥凭据,无论是否由 Agent 使用——有关其覆盖范围及其局限,请参阅上文的覆盖说明;另请参阅承载同意的独立文档层门控。
# from your bench directory: wheel into the bench env, then install the app
env/bin/pip install pacioli-guard
bench --site <your-site> install-app pacioli_guard
经纪人——把账簿交给它
书记员可以提议;只有商人才能处置。
一个可独立运行、可通过 pip 安装的经纪服务(pip install pacioli),为 AI Agent 提供受管控的方式来操作 ERPNext——入口由你选择:MCP 或 A2A,两者共用同一套底层机制。51 个受管控的 DocType、265 个工具——涵盖 ERPNext 公司完整的可提交交易范围——所有写入都必须经过 PLAN → CONSENT → execute → PROVE;除此之外一律默认拒绝。入口负责准入;底层机制负责决策。
pip install pacioli
各个工具如何协同
Guard 是基础层;经纪服务是绑定到它的一个使用方。 Guard 会限定并管控站点上的任何凭据——不必运行 Agent 才需要它。经纪服务是面向 Agent 的入口,而它自己的 ERPNext 凭据必须通过 pacioli_guard 限定为只允许它实际调用的内容(受管控的 DocType 及其提交/取消操作组合——部署套件会将这些清单作为数据应用)。如果没有这样的限定,任何持有经纪服务原始凭据的人都能直接调用 ERPNext 的 REST API,完全绕过 PLAN、CONSENT 和 PROVE——因此,经纪服务自己的 README 将此限定明确列为硬性前提,而非可选的强化措施。
而且仅有限定还不够——2026-07-25 在一个正在运行的 bench 上已验证这一点,值得明确说明,而不是留给读者自行推断。 经纪服务必须获准提交发票,因为提交发票正是经纪服务的用途。因此,它的凭据获准提交发票,而一次持有该凭据直接调用 run_doc_method 的操作,在没有计划、标记或回执的情况下提交了一张发票。账簿发生了变动。Guard 的行为完全符合设计,允许列表也完全正确:任何允许列表都无法阻止这种情况,因为被调用的操作正是经纪服务存在的目的。持有密钥就意味着获得了过账权限。
Guard 在基础层而非经纪服务内部阻止这种情况:API Key Scope.require_consent(选择启用,默认关闭)会拒绝任何改变 docstatus 的操作,除非该操作携带一个有效且一次性的 Pacioli Consent Marker,该标记必须专为该文档和该操作铸造,并会在使用后失效。同意过账不等于同意冲销:取消操作会更改同一份文档,并写入相反的 GL 分录,因此提交标记绝不会用于取消操作。此外,标记必须由不同于其授权凭据所属主体的主体写入——基础层会将铸造者与调用者进行比对,若两者相同则拒绝,因为能够自行铸造同意标记的凭据,就如同自己签发许可条款的守门人。minted_by 由服务器根据已认证的会话设置,覆盖调用者提供的任何值,因此这种主体分离是实际建立的,而非由调用者自行声明。
此门控作用于文档,而非请求:doc_events 的 before_submit/
before_cancel,以及自 guard 0.13.0 起的 before_gl_preview/before_sl_preview。这是有意为之,也是只管控一个入口与管控实际操作之间的区别。即使凭据持有者完全避开 api-key REST 路径,改用 OAuth 令牌、desk 会话、后台任务、服务器脚本或 bench 控制台,仍会受到此门控。
满足要求和通过检查是两回事,值得明确说明,而不是留给读者自行发现。标记通过 HTTP 标头传递,因此没有 HTTP 请求的路径就无法传递标记:经由后台作业或 bench 控制台操作的、受同意门控的主体,在这些路径上只会被拒绝。这是默认拒绝,也是预期的安全立场,但不要把上文理解为“这些路径的工作方式相同”——它们受到涵盖,指的是会被拦截,而不是可以使用。
选择加入有一个值得明说的后果:仅仅安装应用不会带来任何变化。在有人为重要凭据启用 require_consent 之前,安装后的应用仍会受到上述绕过方式的影响。
这是组合,而非耦合:Guard 本身就有用,而且用途不只限于 Agent。它的两个部分各自覆盖不同范围,而诚实之处就在于明确这种差异——凭据范围绑定任何 api-key 凭据(无论是否属于 Agent),不会触及其他内容;而同意门控在文档层运行,涵盖所有 ORM 路径——desk 会话、后台作业、调度程序、服务器脚本、bench 控制台——适用于已设置门控的凭据。Broker 是唯一选择构建于其上、并遵守其对所有其他内容所执行的同一底线的部分。
这里的复式记账不是比喻,而是设计本身。《Summa》的簿记论述 (Particularis de Computis et Scripturis)将商人的账目真相组织成三本簿册, 而它们与这个系统一一对应。在他的日记簿中,每笔分录都用 per 标记借方, 用 a 标记贷方——没有任何一方会单独发生。请按他划定的方式阅读这张表:
| 页码 | per — 他的簿册(1494 年) | a — 此处 |
|---|---|---|
| ¹ | 备忘簿(memoriale)——先粗略记下每笔交易,再进行任何正式登记 | PLAN — plan_submit / plan_cancel 写入备忘簿:预估的 GL 和风险标记,注明日期并绑定到草稿。不会过账。 |
| ² | 日记簿(giornale)——每笔分录都按固定格式重写,借方标为 per,贷方标为 a | PROVE — 哈希链式回执簿。意图回执是 per,结果是 a;没有结果的意图,就像有借无贷,试算平衡表会将其显示为孤立项。 |
| ³ | 分类账(quaderno)——账目本身 | ERPNext 的 GL — 经纪工具绝不会直接写入其中;每笔分录都要遵循日记簿的规范,才能进入分类账。 |
同一法则贯穿每个支柱——每个操作都有其记录在案的对应项:
| 页码 | 操作(per) | 其记录在案的对应项(a) |
|---|---|---|
| ⁴ | Agent 想要写入 | PLAN — 在操作发生前,先写下预估的 GL 影响。镜像分录先于实际分录。 |
| ⁵ | 已有计划 | CONSENT — 由人类在带外签发标记。Agent 提议,人类决定:每次过账都由双方共同操作,绝不只凭一方。 |
| ⁶ | 写入执行 | PROVE — 写入前有一份 意图 回执,写入后有一份 结果 回执。缺少结果回执的写入就是孤立项,它会像试算平衡不平衡一样显现出来。 |
| ⁷ | 过账已入账 | UNDO — ERPNext 自身的取消/修订操作也遵循复式记账:冲销会过账金额相等、方向相反的行。没有任何内容被抹去,一切都有记录可查。 |
| ⁸ | 凭证已存在 | Guard — 没有明确的授权条目,就没有能力。默认拒绝就是“未经授权不得入账”,并将其应用于凭证层。 |
每一层都遵循同一法则:**没有对应项,任何操作都不会发生。**没有授权的计划不会过账。 没有回执的写入会被标记。没有授权条目的凭证会被拒绝。唯一一个不能信任的系统, 就是允许某项操作孤立发生的系统——没有计划、未经授权、没有回执。Pacioli 所拒绝的一切, 正是如此:孤立分录。
Tavola II — Vigintisex Basium Planum Vacuum:同一立体,由 Leonardo da Vinci 为 Pacioli 的 De divina proportione 绘制成中空形态。Leonardo 为 Luca 的 著作作了插图;此处偿还这份恩情。
将他的忠告应用于凭证层:
- 每笔录入都要两方经手。 文书负责填写,商人负责授权。许可标记在 Agent 无法触及的地方铸造(
pacioli mint)——不能由一方单独授权入账。 - 账房之门。 只有指定的经手人才能记账,而且只能记入分配给他们的账簿——也就是 Guard 按凭证和文档类型逐一授权,默认拒绝。
- 已登记的账簿。 账簿的权威来自账簿管理员无法自行掌控的标记。
pacioli anchor将收据簿的头信息保存在账簿之外——账簿无法改写这项登记,因此账簿遭到截断或替换时会露出破绽。
Distinctio IV — 关于结账
❧ 借方与贷方相等之前,不要入睡。
他将这条规则传授给商人,它也是这里的操作规则。pacioli verify 就是这项睡前检查——运行试算平衡;若意图与结果不匹配,账簿本身会告诉你。
已结账的账簿。 结账是他自己的操作:划线封账,结转余额。账簿经纪人拒绝在已结账的账簿中写入——无论是已关闭的会计期间、期末结账凭证界限,还是冻结账簿的日期——绝不让倒填日期或未来日期的分录越过封账线。
Distinctio V — 关于盘点
盘点之前,不要开账。 他要求商人在首次记账前完成全面盘点——你持有什么、别人欠你什么、缺少什么。pacioli doctor 就是这家商行的盘点:哪些内容可以访问、哪些操作已获授权、哪些操作被拒绝以及原因——只有 doctor 返回 ready.,流程才能继续。之后,普查(pacioli close)会确保每个后续期间的盘点准确无误:报表、对账、响应——每项发现都已核算,否则账簿会明确指出。
状态
(当前版本见各软件包的 pyproject.toml/CHANGELOG.md——本 README 刻意不再重复列出。)
Guard——默认拒绝的凭证范围控制,采用 拒绝未知请求 策略(即使已授权,也会拒绝无法识别的通用 RPC;全部可用范围仅包括按文档类型授权的操作和 4 种经过筛选的安全方法),已在真实的 Frappe v16 工作台上通过实时验证(Gates 1、7、10)。
Broker——51 种受管控的文档类型、265 个工具;其中 51 种有 48 种已在真实 ERPNext v16 工作台上作为受 Guard 范围限制的席位端到端完成实时验证(Gates 2–10、envelopes E1–E8,以及 2026-07 实时验证巡检):受管控的提交/取消/修订,覆盖会计、库存、资产、制造和分包;工作流职责分离(SoD)许可;带依赖图的级联取消(在实时环境中通过单次许可取消 3 节点资产图);受管控的付款对账(比 ERPNext 本身更严格——补上 ERPNext 在对账时跳过的结账保护带,已通过实时验证,PHASE X);已启用预算的控制平面探测(启用 → 工作台拒绝采购订单 → 停用 → 同一采购订单通过,每一步都在许可前披露);账簿外锚点;经认证的最小权限参考席位;以及最初确立的拒绝规则(没有贷方就不能有借方——已通过实时验证)。完整过程已按步骤写入 SCOPED-TOKEN-PROOF.md(PHASES A–X)。有关实际发布内容及未发布内容的准确信息,请参阅 broker/README.md。
许可证
Apache-2.0。
治理大全至此终篇:承卢卡·帕乔利修士之训付梓, 他教导我们,每笔记录皆非孤立;谨以此字体排印成册,论述账簿中的 Agent 治理。
威尼斯,1494 年 · 约翰·布罗德韦工坊 · 2026 年
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。