关于 ERP Report Engine
ERP Report Engine 为需要从 ERP 数据库生成周报及供 AI Agent 查询数据的团队提供只读 SQL 数据层。它通过语义配置映射规范实体,并以四层防护和审计记录约束查询。
主要功能
- 01生成含 KPI、趋势和数据质量检查的 HTML 报告
- 02映射订单、订单明细和库存等规范实体
- 03四层只读 SQL 防护
- 04记录查询、参数、行数和耗时
- 05支持 MSSQL、PostgreSQL 和 SQLite
适用场景
- 01定期生成 ERP 经营周报
- 02让 AI Agent 安全查询 ERP 数据
- 03检查重复、负数等数据质量问题
MCP 相关
MCP 服务通过规范实体向 AI Agent 暴露 ERP 数据,并使用与报告引擎相同的只读防护。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
erp-report-engine
周一会议要用的数据已经存放在 ERP 的数据库里了。那么,为什么还要有人手动写报告?
为 ERP 背后的 SQL 数据库提供只读、自验证的数据层——既能生成周报,也能为 AI Agent 提供带防护的 MCP 服务。每条查询都能证明只读:经 28 种攻击测试,而非仅凭文字承诺。
🇹🇷 土耳其语版:README.tr.md
一次定时执行 run,就会运行 9 条经过审计的 SELECT 语句,并生成一份独立完整的 HTML 报告:包含与 8 周基线对比的四项 KPI、注明具体驱动因素的发现、数据质量门控,以及与源数据核对一致的行数。不需要 BI 许可证,不需要在 ERP 服务器上安装 Agent,而且绝不写入——通过四层防护(词法、解析树、副作用函数防护和只读会话)强制执行,而非仅凭文字承诺。

这份报告由一条命令根据捆绑的演示数据库生成,其中包括特意植入的三个数据质量问题,全部都被门控捕获。
同一个带防护的引擎也能与 AI Agent 通信。 erp-report-engine mcp 通过标准实体(orders,而非 LG_001_01_ORFICHE)向 Agent 暴露 ERP,并由同一只读防护层守护——MCP 生态系统一直在这方面做得不够。参考 PostgreSQL MCP 服务的只读模式曾被 COMMIT; DROP SCHEMA public CASCADE; 绕过,随后已被归档;Supabase 的 MCP 则成了经典的“致命三要素”案例。这里的防护会检查语句调用的函数,而不只是语句的结构——你也不必只听信形容词:
▶ 运行 28 种攻击信任基准测试 · 在浏览器中亲自尝试突破防护(通过 Pyodide 运行真实的 guard.py,不会向任何地方发送数据)。
测试运行的正是这份 guard.py,并在浏览器中执行——拒绝文件读取和逃逸只读事务,允许真实聚合查询。粘贴你自己的攻击 →
60 秒演示(无需 ERP)
# install (pipx or uv keep it isolated; plain pip works too)
pipx install erp-report-engine # or: uv tool install erp-report-engine
# from source instead: pip install .
erp-report-engine init-demo # builds demo.db + config.demo.yaml here
erp-report-engine run -c config.demo.yaml
每条命令也可以通过 python -m erp_report_engine … 运行。通过 extras 添加数据库驱动:pipx install "erp-report-engine[mssql]"(Logo Tiger / Netsis / Mikro — SQL Server)或 [postgres]。
更喜欢 Docker? 先运行 docker build -t erp-report-engine .,然后运行 docker run --rm -v "$PWD:/work" erp-report-engine run -c config.demo.yaml。SQLite 和 PostgreSQL 开箱即用;MSSQL 需添加 Microsoft 的 ODBC 驱动(参见 Dockerfile)。只需发布一个 GitHub Release 即可上传到 PyPI——publish.yml 会通过 Trusted Publishing 构建并上传(OIDC,无需存储令牌),CI 也已配置为构建 wheel 并确认每个内置配置文件都随包发布。
打开 reports/erp_report_<week>.html。你会看到引擎捕捉到收入激增并将其归因于某个区域,标记准时率下降 2 个百分点,列出库存覆盖不足两周的商品——还会坦承运行过程中发现的所有重复行和负值行。
▶ 查看实时示例报告:gulmezeren2-byte.github.io/erp-report-engine(也已提交至 docs/sample-report.html)。
或者运行 run --dashboard,查看高级版 Command Center——一个深色、现代、独立运行的仪表板,带有动画 KPI 和发光的 SPC 控制带图表(实时演示):

单次运行会生成什么
| 部分 | 解答的问题 |
|---|---|
| KPI 卡片 | 收入、订单、准时率、低库存数量——分别与上周以及8 周基线对比 |
| 发现 | “收入环比增长 6.5%——主要驱动因素:区域 ‘Ege’(占本周变动的 54%,部分被 Marmara 抵消(−14,697))”——指出驱动因素,也指出走势相反的细分项,并给出建议措施 |
| 信号(SPC) | “收入信号:148,291 高于控制限(UCL 143,078 = 均值 93,168 ± 2.66 × 平均移动极差 18,763,基线 n=25 周)”——识别真实变化,并将其与逐周噪声区分开,同时显示计算过程和样本量 |
| 趋势 | 13 个完整周的收入和准时率,使用内嵌 SVG(无外部资源) |
| 库存关注清单 | 低于库存覆盖阈值的商品,按情况最严重者排在前面 |
| 数据质量关卡 | 重复 ID、无法解析的日期、负总额、先发货后下单的行 |
| 来源核对 | 获取的行数与对同一查询独立执行的 COUNT(*) 结果对比——✓ 或不匹配 |
| SQL 审计记录 | 执行的每条语句及其参数、行数和耗时 |
| 运行状态记忆 | “收入已连续 3 周下降”——提供超出回溯窗口的上下文 |
工作原理
flowchart LR
DB[("ERP database<br/>MSSQL · PostgreSQL · SQLite")]
PROF["Semantic profile<br/>(YAML contract)"]
GUARD["safe_read<br/>read-only guard + audit"]
GATE["Data-quality gate<br/>+ source reconciliation"]
KPI["KPI engine<br/>ISO weeks, 8-wk baseline"]
INS["Insight rules<br/>driver attribution"]
HTML["Self-contained<br/>HTML report"]
STATE[("state.db<br/>run memory")]
PROF --> GUARD
DB --> GUARD --> GATE --> KPI --> INS --> HTML
STATE --- HTML
GATE --> PBI["Power BI Command Center<br/>(TMDL + PBIR, code-authored)"]
让这一方案具备可移植性的层是语义配置文件:一个带版本的 YAML 契约,用于将某个 ERP 的晦涩架构映射到三个标准实体——orders、order_lines、inventory——以及一个可选的 receivables 实体(未结应收账款,用于账龄分析)。如果能够访问账本,配置文件就会映射该实体;如果不能,下游所有环节都会优雅地跳过它。引擎始终只接触标准列。更换配置文件,保留报告。
安全模型
将软件连接到生产 ERP 数据库是一项信任决策。此引擎对此严肃对待——这些保障由代码强制执行,并通过测试覆盖:
只读模式通过四层机制强制执行,因此任何单一失误都不会让引擎具备写入能力:
| 层 | 强制执行方式 |
|---|---|
| 词法防护 | 单条语句,以 SELECT/WITH 开头,不含注释(--、/*、#),不含写入/DDL 关键字,也不含会升级写入权限的锁提示(TABLOCKX、UPDLOCK、XLOCK)。扫描时会将字符串字面量置空,因此 SELECT 'please delete this note' 是读取操作,而不是威胁 |
| 解析树防护 | sqlglot 会解析语句——而且必须解析成功,否则查询会被拒绝:无法读取查询的防护机制,就无法为它背书。查询必须是单条读取查询,其 AST 中不得包含 INSERT/UPDATE/DELETE/CREATE/DROP/ALTER/MERGE/EXEC/INTO 节点(可捕获隐藏在 CTE 中的写入操作) |
| 函数防护 | 许多看似纯粹的 SELECT 并非读取操作。 pg_read_file、lo_export(会写入文件)、dblink(会连接外部)、OPENROWSET、LOAD_FILE、load_extension(可执行任意代码)、query_to_xml(可执行任意 SQL)、set_config(会关闭会话级后备防护)、SLEEP/BENCHMARK(拒绝服务)——全部会被拒绝,既通过 AST 检查,也通过词法检查,因为 OPENROWSET 正是 sqlglot 无法解析的内容 |
| 只读会话 | PostgreSQL 的 default_transaction_read_only=on、SQLite 的 PRAGMA query_only、MySQL 的 SET SESSION TRANSACTION READ ONLY + max_execution_time,以及所有数据库上的单语句超时 |
临时 SQL——Agent 使用的路径——限制更严格。 query 和 guarded_query 在严格模式下运行,默认拒绝防护机制无法识别的所有函数:sqlglot 的函数注册表就是允许列表,因为它只了解可移植的分析函数,不包含任何读取文件或打开套接字的函数。所有四种内置配置都能通过检查——它们完全不调用无法识别的函数。
还有一层不由我们控制。 MSSQL 没有会话级只读开关,因此请使用权限最小化的只读登录账号运行引擎(MSSQL 上使用 db_datareader,PostgreSQL 上授予仅 SELECT 权限——最好使用只读副本)。防护机制提供纵深防御;如果防护机制存在漏洞,权限授予才是能够兜底的那一层。过去确实出现过漏洞:审计本仓库时发现了这些函数绕过方式,目前已按名称、按方言记录在 tests/test_guard.py 中。
别只听我说——亲自测量。 防护机制附带一套可复现的可信度基准测试:在四种方言上测试 28 个格式正确、仅凭语句形态检查的防护机制会放行的攻击,全部都会被拒绝;同时也测试必须通过的合法读取操作。测试得分来自实际运行防护机制的结果,而不是写在说明里的断言,并且 CI 会在每次提交时对其进行验证。
而且,“28/28”只有与较弱防护机制在相同的 28 个案例上的得分对照才有意义。因此,基准测试还会用实际工具常用的捷径来测试这组语料——检查语句是否以 SELECT 开头,以及屏蔽写入关键字——并展示差距:它们分别会拒绝 28 个案例中的 6 个和 9 个;关键字扫描甚至会拦截一条文本中含有 “delete” 一词的合法读取操作,而本防护机制会拒绝全部 28 个攻击,且不会拦截任何合法读取。测试会确保它严格优于所有这类捷径。
erp-report-engine trust-benchmark
# this guard 28/28 attacks refused · 8/8 reads allowed
# starts-with-SELECT 6/28 · 8/8 (waves 22 through)
# write-keyword blocklist 9/28 · 7/8 (leaky AND breaks a real read)
▶ 查看结果:可信度基准测试——查看每个案例、其严重程度以及实际效果。或者**亲自试试能否绕过**:将 SQL 粘贴到浏览器中运行的真实防护机制里(无需安装,也不会向任何地方发送数据——这是测试实际运行的 guard.py,通过 Pyodide 执行)。
只想为你自己的 MCP 服务或数据库工具获取防护机制?它已提取为独立软件包:readonly-sql-guard(pip install readonly-sql-guard)——仅包含一个函数,只依赖 re + sqlglot。
再往上一层也是同样的思路:ossie-guard 会检查 Apache Ossie 语义模型,找出不同方言会悄然产生分歧的指标(一个数据仓库使用 SUM,另一个使用 AVG),以及并非纯粹、可复现读取的表达式,并以 SARIF 格式报告到 GitHub 代码扫描。
延伸阅读:为什么“文字上只读”并不代表只读(两个著名的 MCP 数据库故障案例,以及背后的攻击类型) · AI Agent 的只读数据库访问方式对比(事务与角色、语句守卫、语义层的对比——如实说明各自的优势所在)。
此外:配置文件变量符合标识符安全规则(^[A-Za-z0-9_]{1,16}$,因此 "001; DROP TABLE x" 会在建立任何连接前触发错误);密钥绝不会存放在配置文件中(加载器会拒绝任何形式的内嵌凭据——password、passwd、pwd、sslpassword、ODBC PWD=——请使用 url_env);每条执行过的语句都会写入报告的审计记录;行数上限(默认 500k)限制任何单条查询的结果。
测试套件会向守卫发起一系列注入尝试——多语句、三种语法的注释隐藏、事务控制拼接(ROLLBACK/COMMIT)、SELECT INTO、锁提示,以及隐藏在 CTE 中的 DELETE——并要求每一种都触发错误。请参阅 SECURITY.md。
接入你自己的 ERP
- 将连接 URL 放入环境变量(绝不要写入文件):
# Windows (System Properties → Environment Variables, or:)
setx ERP_DB_URL "mssql+pyodbc://readonly_user:***@SERVER/LOGODB?driver=ODBC+Driver+17+for+SQL+Server"
- 将
config.example.yaml复制为config.yaml:
connection:
url_env: ERP_DB_URL # the engine reads the URL from this env var
profile: logo_tiger # a bundled profile name, or a path to your own YAML
profile_vars:
firm_no: "001" # identifier-safe values only, validated
period_no: "01"
report:
company_alias: "Company" # display name — use an alias if you prefer
lookback_weeks: 13
low_cover_weeks: 2.0
limits:
row_cap: 500000
query_timeout_s: 60
- 先进行试运行——
validate会建立连接、检查配置文件契约并核对计数,除此之外不会触碰任何内容:
python -m erp_report_engine validate -c config.yaml
python -m erp_report_engine run -c config.yaml
内置配置文件
配置文件随软件包提供,并通过名称引用(generic、logo_tiger)——配置文件旁边无需存在 profiles/ 文件夹。
generic— 规范架构;也是编写自定义配置文件的模板。logo_tiger— 适用于 MSSQL 上的 Logo Tiger / GO:通过LG_{firm}_{period}_ORFICHE订单头连接CLCARD客户、ORFLINE明细行和STINVTOT库存总量;使用TRCODE = 2筛选销售;并可选择启用应收账款账龄分析,数据来自PAYTRANS(分期付款TOTAL − PAID,MODULENR = 4)。Logo 架构会因版本而异——配置文件中附有字段说明,指出在信任结果前需要针对你的版本核实哪些内容。netsis— 适用于 MSSQL 上的 Logo Netsis 3(每家公司使用独立数据库):TBLSIPAMAS/TBLSIPATRA销售订单(FTIRSIP = '6')、TBLCASABIT客户、TBLSTOKPH库存总量,并可选择启用应收账款账龄分析,数据来自TBLCAHAR(其中关于未结项目与滚动余额的注意事项已在行内注明——这是需要如实指出的薄弱环节)。字段映射依据真实生产集成编写;薄弱环节(订单状态、交货日期、应收账款结账方式)已在行内标出,供你对照自己的安装版本核实。mikro— 适用于 MSSQL 上的 Mikro ERP(Mikro Yazılım、Fly/Jump/V16–V17):使用SIPARISLER(sip_前缀,行级)作为销售订单、CARI_HESAPLAR作为客户、STOK_HAREKETLERI作为现有库存(sth_tip表示出入库),并可选择启用应收账款账龄分析,数据来自CARI_HESAP_HAREKETLERI(cha_vade为到期日)。需要如实说明的薄弱环节——sip_tip销售筛选条件、总额是否含增值税、订单没有发货日期,以及纯滚动余额的应收账款分类账没有未结项目标记——均已在行内标出,供你对照自己的版本核实。
Logo Tiger、Netsis 和 Mikro 均使用 MSSQL,合计覆盖了土耳其中小企业 ERP 市场的大部分。要为其他 ERP(SAP B1、Odoo、自定义系统)编写配置文件,只需编写三个 SELECT 语句,输出规范列即可——可以单独编写一个 YAML 文件并通过 profile: 指定,也可以将文件放入 erp_report_engine/profiles/,随软件包一同发布。契约就这么简单——validate 会立即告诉你是否配置正确。
实现自动化运行
引擎是一条具备幂等性的命令,因此任何调度程序都能使用:
# Windows Task Scheduler — every Monday 07:00
schtasks /create /tn "erp-weekly-report" /sc weekly /d MON /st 07:00 ^
/tr "cmd /c cd /d C:\erp-report-engine && python -m erp_report_engine run -c config.yaml"
# cron — every Monday 07:00
0 7 * * 1 cd /opt/erp-report-engine && python -m erp_report_engine run -c config.yaml
每次运行都会向 state.db 追加数据,因此报告可以说出“连续第三周下滑”——跨运行记忆,无需从 ERP 重新查询历史记录。
退出代码可让调度程序根据运行失败的原因采取不同操作:0 成功 · 2 配置错误 · 3 数据库/连接错误 · 4 契约错误(配置文件架构错误或源数据计数无法核对) · 5 --strict 下的数据质量失败 · 1 其他意外情况。机器可读的结果会输出到 stdout(… run -c config.yaml | jq);日志会输出到 stderr,也可通过 --log-file run.jsonl 同时写入 JSON 行文件。在 CI 中运行 validate --strict,即可在数字无法核对时让流水线失败。
可选的 AI 摘要,绝不说谎。 run --narrate 会添加一份 LLM 执行摘要,但它的诚实性由设计保证:模型只会收到经过审计的汇总数据(KPI、发现、账龄/集中度——绝不包含原始行),报告还会打印它看到的确切载荷,任何人都能核实模型获得了什么。如果未配置密钥,该标志不会执行任何操作,报告也不会改变。任何兼容 OpenAI 的端点都可使用,包括本地、无需密钥的模型(Ollama / LM Studio)。大多数工具会在生成后才加上幻觉检查;而在这里,模型根本看不到任何可能泄露信息或导致过度断言的内容。
内置交付功能。 run --send 会通过电子邮件(SMTP)发送报告、将摘要发布到 Slack 或 Teams(Power Automate Workflows),并在成功或失败时向 healthchecks.io 发送无人值守监测信号——这样就能发现静默运行的 cron。所有密钥都来自环境变量;某个渠道失败时会记录日志,但不会导致运行失败。在 delivery: 块中进行配置(参见 config.example.yaml)。如需完全自动化的流水线,power_automate 渠道会向 Teams 发布 Adaptive Card,并且将 HTML 报告存档到 SharePoint/OneDrive——可导入的流程和分步指南见 automation/POWER-AUTOMATE.md。报告会自动生成,并且自动交付——这是大多数 BI 工具收费提供的功能。
Power BI 指挥中心
引擎还为交互式 Power BI 层提供数据——本仓库中没有 .pbix 二进制文件。整个项目是一份以代码编写的 PBIP 项目:使用 TMDL 编写的语义模型(星型架构、28 个附带文档的 DAX 度量值——包括SVG 微型图表度量值,可直接通过 DAX 绘制每位客户的迷你趋势线和每个商品的覆盖条形图,以及一个应收账款账龄事实表和相关度量值——基于无间断周序号的 Time Shift 计算组,以及一个字段参数);使用 PBIR 编写的报告(5 个页面 / 30 个视觉对象,由脚本根据精简规格生成);以及一套通过 Microsoft 官方主题架构验证的暗色未来风格主题。
# a demo export already ships in powerbi/data — just open the project:
# powerbi/ERP Command Center.pbip (Power BI Desktop)
# to feed it your own ERP (writes to gitignored powerbi/data.local by default):
erp-report-engine export-powerbi -c config.yaml
其标志性页面是信任页面:将源数据核对、数据质量门禁和完整的 SQL 审计轨迹渲染为视觉对象——仪表板会展示其依据。警报阈值与 insights.py 中的阈值相同,并在 DAX 中重新推导:一套定义,两个呈现界面。在项目加载到 Desktop 之前,pbir-cli 会依据 TMDL 模型验证字段绑定。完整指南:powerbi/README.md。
通过 Agent 查询你的 ERP — 带防护的 MCP 服务
AI Agent 连接 ERP 是一个一直没有得到妥善解决的信任问题:现有的每个“ERP MCP”都是依赖 ERP 自身权限的 REST 封装,而每个数据库 MCP 都会把原始表交给 Agent。这个引擎提供了别处尚不存在的组合 — 一个 Model Context Protocol 服务,Agent 可通过规范实体(orders,而不是 LG_001_01_ORFICHE)进行交互,并使用与报表相同的只读防护和审计跟踪;在严格模式下,防护会默认拒绝所有无法识别的函数,并将每个数据结果标记为不可信输入。
pipx install "erp-report-engine[mcp]"
erp-report-engine mcp -c config.yaml # stdio server
六种工具,全部经由受防护的路径:
| 工具 | Agent 可获取的内容 |
|---|---|
describe_model | 规范的语义层 — 每个实体的粒度,以及每列的类型和含义(例如,发货前 actual_ship_date 为 NULL,因此逾期但尚未发货的订单不会被计入准时率),还包括可运行的示例查询。提供含义,而不只是名称 — 这能确保 Agent 使用文本转 SQL 时结果准确。不包含原始 ERP 表名 |
weekly_report | 完整的 KPI 简报 — 发现、数据质量检查、对账、SQL 审计跟踪 |
reconcile | 获取的行数与每个实体独立执行的 COUNT(*) 结果对比,并给出信任判定 |
aging | 应收账款账龄 — 按逾期天数分组的未结余额、逾期百分比,以及欠下最多逾期款项的客户(仅汇总数据) |
check_query | 检查 SQL 语句是否能通过防护 — 但不执行它 |
query | 执行只读 SELECT/WITH,并限制结果数量、记录审计;返回的行作为不可信数据 |
规范模型也以机器可读契约的形式发布 — model.json,或运行 erp-report-engine schema — 这样工具无需连接即可读取实体、列类型、含义和示例查询。CI 会根据代码重新生成它,并在出现偏差时失败,因此契约不会声称引擎不具备的内容。
第一方 Agent 技能包(erp-safe-query、explain-kpi-move、write-erp-profile)会教 Agent 如何遵循这种粒度开展工作 — 查询前先试运行,使用聚合而不是倾倒行数据,引用经过审计的数字,并且绝不把 ERP 文本当作命令。
一行即可试用,无需 ERP,也无需配置 — 通过内置演示数据库提供受防护的 MCP:
erp-report-engine mcp --demo # or: docker run --rm -i erp-report-engine
然后让 Claude Desktop(或任何 MCP 客户端)连接它:
{ "mcpServers": { "erp": { "command": "erp-report-engine", "args": ["mcp", "-c", "C:\\path\\config.yaml"] } } }
或者将它安装为 Claude Code 插件 — 只需两行,即可使用受防护的 MCP 服务、安全查询技能,以及无需设置的 /trust-benchmark 和 /erp-schema 命令:
/plugin marketplace add gulmezeren2-byte/erp-report-engine
/plugin install erp-report-engine
服务通过 uvx 启动(无需手动安装),并从你的项目读取 erp-config.yaml。数据库连接配置完成前,技能和基准测试/架构命令也能立即使用。(详情:.claude-plugin/。)
Agent 无法写入数据:防护会拒绝除仅调用可识别函数的单条只读查询以外的所有内容,会话本身也是只读的;此外,依据 2025 年 MCP 数据外泄事件的教训,每个返回值都会附带说明,强调行数据是数据而非指令。据我们所知,这是第一个在 SQL 层实施防护的 ERP MCP 服务,也是第一个适用于 Logo Tiger 的此类服务。它已收录于 awesome-turkish-mcp,这是一个精选的土耳其数据与领域 MCP 服务索引。
不会做什么
坦诚胜于营销——在将它用于生产环境前,你应该了解它的边界:
- 这里的准时是轻量版 OTIF。 它按订单评分,条件是
shipped ≤ promised。真正的 OTIF 需要行级收货数据,而大多数 ERP 订单表都不包含这类数据,因此报告称其为“准时发货”,而非“OTIF”,并在页脚说明原因。 - 准时率只对已发货的订单评分——这种统计方向很危险。 延迟且仍未发货的订单没有
actual_ship_date,因此既不计入分子,也不计入分母,永远不会让指标失分。极端情况下,随着履约崩溃,准时率会不断上升:最糟糕的订单被排除在样本之外。引擎无法仅凭订单表中的数据解决这个问题,因此它会统计百分比无法体现的情况——“本周有 N 个订单承诺发货,但尚未发货”——并在卡片上显示为单独的发现。要么结合这两项一起看,要么就别看这个百分比。 - 样本太少时,不会宣称准时率有所变动。 两次交付从 1/2 变为 2/2,确实是“+50 个百分点”,但若面不改色地这样报告,报告就会失去公信力。评分交付少于 5 次时,会显示变动,但明确说明不作判断。
- 发现只是线索,不是结论。“驱动因素:Ege 地区,占变动的 111%”会告诉你先从哪里查起,但不会告诉你原因——那是分析师(也就是你)的工作。
- Logo Tiger 配置是字段映射,不是经过认证的集成。 Logo 架构会因版本和本地化设置而异;配置文件中的字段说明列出了需要核实的内容。
- 当前未完成的周永远不会绘制在图表中。 趋势图止于最近一个已完成的 ISO 周,因为把周一早上的“暴跌”误当成实际趋势,而它其实只是一个只有两天数据的周,这会让仪表板失去可信度。
- 这是每周简报,不是 BI 平台。 没有下钻分析、实时数据或用户管理。它只做一件事:自动生成每周一的报告,并附上依据。
设计决策
为什么使用基于规则的发现,而不是大模型? 为了确定性。相同的数据库状态始终会生成相同的报告;它可以在与 ERP 相邻的隔离网络环境中运行;报告中的每个数字都能追溯到审计记录中的 SQL 语句。不会生成任何无法重新推导的内容。
为什么采用自包含 HTML 文件? 无需基础设施。图表使用内嵌 SVG,样式使用内嵌 CSS;没有 CDN,也没有跟踪功能。十年后,它仍可在 Outlook 的浏览器预览中、手机上或文件共享目录中正常呈现。
为什么核对行数? 因为“DataFrame 有 494 行”和“源查询返回 494 行”是两种不同的说法。无人值守的系统必须审计自己的输入——每个实体都会使用独立的 COUNT(*) 重新计数,并在任何人信任 KPI 之前,以红色标记所有不一致之处。
测试
pip install pytest && python -m pytest tests/ -v
测试套件涵盖只读保护(大量注入尝试)、配置文件契约、日历核心(单元测试和基于属性的测试)、渲染转义、诚实性修复、CLI 退出代码、MCP 工具、SPC 信号、交付路由,以及完整的端到端运行和 Power BI PBIP 完整性检查(导出器键、无间断的周序号、视觉元素重叠检测,以及模型中是否存在每个视觉元素字段)。CI 还会运行 ruff,并在 Python 3.10–3.13 上检测 PBIR 生成器漂移,出现漂移时即判定失败。
路线图
已交付:带防护机制的 MCP 服务 + 第一方 Agent 技能包、可选的 LLM 叙述层(仅聚合数据,设计上确保如实呈现)、SPC/XmR 异常检测层、原生交付(SMTP/Slack/Teams/healthchecks)+ Power Automate 流水线、声明式配置文件契约、三个真实的土耳其 ERP 配置文件(Logo Tiger、Netsis、Mikro)——每个都可选支持应收账款账龄,营收集中度分析(前三名占比 + HHI)、DAX SVG 迷你图表,以及通过架构验证的深色 Power BI 主题。接下来:
- 在 PyPI 上运行
pip install erp-report-engine+ 发布 Docker 镜像 - 增加更多 ERP 配置文件(SAP Business One、Odoo、通用 ODBC 模板)
- 为持续余额分类账(Netsis / Mikro)实现精确 FIFO 未结项目账龄,使应收账款账龄无需配置未结项目匹配即可保持精确
测量诚信系列的一部分
如实呈现运营情况的工具,作者:Eren Gülmez:
- otif-analytics — 从“报告值为 98%”到“OTIF 为 59%”的 5 步指标阶梯
- forecast-accuracy-lab — WMAPE 与 MAPE,以及为什么丢弃零值会让预测看起来更好
- opsaudit — 带有不可移除诚信区块的运营指标 CLI
- auto-report-pipeline — 本引擎由 CSV 驱动的小型同类项目
- forecast-autoresearch — 针对密封留出集改进预测的 Agent
许可证
MIT © Eren Gülmez
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
