From 726086eac6073442db42707f89d07f6956723e0d Mon Sep 17 00:00:00 2001 From: karuo Date: Tue, 24 Feb 2026 19:59:14 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84=20=E5=8D=A1=E8=8B=A5AI=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=202026-02-24=2019:59=20|=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=9A=E6=80=BB=E7=B4=A2=E5=BC=95=E4=B8=8E=E5=85=A5=E5=8F=A3?= =?UTF-8?q?=E3=80=81=E6=B0=B4=E6=BA=AA=E6=95=B4=E7=90=86=E5=BD=92=E6=A1=A3?= =?UTF-8?q?=E3=80=81=E5=8D=A1=E6=9C=A8=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD?= =?UTF-8?q?=E6=9E=A2=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD=E6=9E=A2=E5=8F=82?= =?UTF-8?q?=E8=80=83=E8=B5=84=E6=96=99=E3=80=81=E8=BF=90=E8=90=A5=E4=B8=AD?= =?UTF-8?q?=E6=9E=A2=E5=B7=A5=E4=BD=9C=E5=8F=B0=20|=20=E6=8E=92=E9=99=A4?= =?UTF-8?q?=20>20MB:=2012=20=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 + .../记忆系统/structured/last_chat_collect_date.txt | 2 +- .../PPT制作/脚本/公司财务分析PPT_毛玻璃.html | 622 ++++++++++++++++++ .../PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html | 449 +++++++++++++ .../PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html | 423 ++++++++++++ .../木果_项目模板/PPT制作/脚本/毛玻璃截图转PPT.py | 17 +- 运营中枢/scripts/karuo_ai_gateway/README.md | 57 ++ .../karuo_ai_gateway/config/gateway.example.yaml | 40 ++ 运营中枢/scripts/karuo_ai_gateway/main.py | 266 +++++++- .../scripts/karuo_ai_gateway/requirements.txt | 1 + .../karuo_ai_gateway/tools/generate_dept_key.py | 64 ++ .../参考资料/卡若AI外网化与外部调用方案.md | 36 + 运营中枢/工作台/gitea_push_log.md | 1 + 运营中枢/工作台/代码管理.md | 1 + 14 files changed, 1963 insertions(+), 22 deletions(-) create mode 100644 03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html create mode 100644 03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html create mode 100644 03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html create mode 100644 运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml create mode 100644 运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py diff --git a/.gitignore b/.gitignore index 37932fb4..a6438d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,10 @@ sync_tokens.env **/智能纪要/脚本/feishu_user_token.txt **/智能纪要/脚本/cookie_minutes.txt +# 卡若AI 网关:多租户配置与访问日志(不入库) +运营中枢/scripts/karuo_ai_gateway/config/gateway.yaml +运营中枢/工作台/karuo_ai_gateway_access.jsonl + # Node / 前端 node_modules/ .next/ @@ -90,6 +94,7 @@ _大文件外置/财务管理_data/chat.snapshot_收集.db # === 自动排除:超过20MB的文件(脚本自动管理,勿手动修改)=== .venv/lib/python3.14/site-packages/playwright/driver/node .venv_mem0/lib/python3.14/site-packages/grpc/_cython/cygrpc.cpython-314-darwin.so +.venv_ppt/lib/python3.14/site-packages/playwright/driver/node 01_卡资(金)/金仓_存储备份/大文件外置/消息中枢_dist/windows控制包.zip 01_卡资(金)/金仓_存储备份/大文件外置/视频切片_models/ggml-small.bin 01_卡资(金)/金仓_存储备份/大文件外置/财务管理_data/chat.snapshot_data.db @@ -98,4 +103,5 @@ _大文件外置/财务管理_data/chat.snapshot_收集.db 03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/.cache/webpack/remotion-production-4.0.427/a233e9cccba253c3b0157f54cad843b8/0.pack 03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/.remotion/chrome-headless-shell/mac-x64/chrome-headless-shell-mac-x64/chrome-headless-shell 03_卡木(木)/木叶_视频内容/视频切片/切片动效包装/10秒视频/node_modules/@rspack/binding-darwin-x64/rspack.darwin-x64.node +03_卡木(木)/木果_项目模板/PPT制作/.venv/lib/python3.14/site-packages/playwright/driver/node # === 自动排除结束 === diff --git a/02_卡人(水)/水溪_整理归档/记忆系统/structured/last_chat_collect_date.txt b/02_卡人(水)/水溪_整理归档/记忆系统/structured/last_chat_collect_date.txt index 0ec317cc..e1186354 100644 --- a/02_卡人(水)/水溪_整理归档/记忆系统/structured/last_chat_collect_date.txt +++ b/02_卡人(水)/水溪_整理归档/记忆系统/structured/last_chat_collect_date.txt @@ -1 +1 @@ -2026-02-22 \ No newline at end of file +2026-02-24 \ No newline at end of file diff --git a/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html b/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html new file mode 100644 index 00000000..59860be4 --- /dev/null +++ b/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务分析PPT_毛玻璃.html @@ -0,0 +1,622 @@ + + + + + + + + + + +
+
+
CFO Plain Talk · Multi-Dimensional
+

公司财务盘点与现金流体检

+

大白话版本:把钱从哪来、花到哪去、哪里不对劲,一次说清楚

+
+ 🧾 统一口径 + 💳 银行-卡号 + 🧹 同卡号去重 + 📈 多维报表 + 🧠 CFO 结论 +
+
数据口径:统一流水去重后(支付宝 + 短信银行 + 公司收支:卡卡猫→中信0405)
+
+
+ + +
+

Executive Summary

+
+
+
+
+ 🧠 + 一句话 +
+
+

这份口径里:净现金流是 -214 万

+

但最大问题不是“真亏这么多”,而是:短信里混进了额度/提醒,被当成支出

+

我的结论:先把口径变干净,再谈控制成本和增长

+
+
+
+ ✅ 你真正要盯的:中信银行-0405(卡卡猫)回款节奏 + 固定支出节奏 + 现金余粮。 +
+
+
+ + + + + + + + + + + + + + + + 输入:三路数据 + 支付宝 / 短信 / 公司收支 + + 清洗 + 去重 + 合并 + + + 输出:统一报表 + 年度 / 月度 / 账户 / Top 对方 + + +
+
+
+ + +
+

Data Rules

+
+
+
+
+ 🧾 + 口径说人话 +
+
+

① 银行:按“银行-卡号”分开(比如 中信银行-0405)。

+

② 合并:卡卡猫 = 中信银行-0405(短信和公司收支算同一户)。

+

③ 去重:同日期 + 同金额 + 同方向 + 同对方 → 直接去掉重复。

+

④ 时间:每一笔都强制带日期+时间节点

+
+
+
+
你现在的“统一流水去重后”总笔数:6,446 笔
+
+
+
+ + + + + + + + + + + + 短信银行 + 银行-卡号 + + + 公司收支 + 卡卡猫→0405 + + + 支付宝 + 明细交易 + + + + + + + 清洗 + 合并 + 按卡号 / 抽对方名 + 同卡号去重 + + + 多维报表 + 年/季/月/账户/Top + + +
+
+
+ + +
+

Numbers

+
+
+
+
+ 📈 + 先看四个数 +
+
+
+
2,198,237.84
+
总收入(元)
+
+
+
4,342,873.62
+
总支出(元)
+
+
+
-2,144,635.78
+
净现金流(元)
+
+
+
50.6%
+
收支比
+
+
+
大白话:进来的钱 ≈ 220 万,出去的钱 ≈ 434 万,所以净流出 ≈ 214 万
+
+
+ ⚠️ 重要提醒:支出里有大量“短信噪音”(额度/提醒/营销),这会把支出夸大。 +
+
+
+ + + + + + + + + + + + + + + 收入 vs 支出 + 收支比 50.6% + + + + 蓝:收入 220 万 + 红:支出 434 万 + + +
+
+
+ + +
+

Trend

+
+
+
+
+ 📉 + 年度怎么走 +
+
+

2017~2024:整体平稳(收支接近)

+

2025:波动大(收入 110.8 万,支出 145.6 万)

+

2026:支出异常大(184.9 万)优先核对短信噪音

+
+
+
+ CFO 大白话:趋势里只要出现“突然暴涨/暴跌”,第一件事不是惊慌,是先问:数据口径是不是变了? +
+
+
+ + + + + 净额(越低越危险) + + + + + + 2024 + -0.01万 + + + + 2025 + -33.8万 + + + + 2026 + -182.9万 + + 注:2026 异常值需先做短信噪音剔除 + + +
+
+
+ + +
+

Accounts

+
+
+
+
+ 💳 + 钱主要在哪两条线 +
+
+

收入结构:中信银行-0405 占 50.6%,支付宝占 49.1%。

+

大白话:公司收入基本就是两条腿走路

+

支出结构里:“其他”非常大 → 先当“待核对”。

+
+
+
+ ✅ 财务总监盯盘顺序:先盯“现金进出最大的账户”,也就是中信0405;其次才是零碎账户。 +
+
+
+ + + + + 收入占比(两条腿) + + + + ≈ 50 / 50 + 中信0405 / 支付宝 + + + 中信银行-0405 + 收入 111.3 万 + + + 支付宝 + 收入 108.0 万 + + 支出里“其他”先当待核对项 + + +
+
+
+ + +
+

CITIC 0405

+
+
+
+
+ 🏦 + 卡卡猫 = 中信银行-0405 +
+
+

收入:1,112,568.09(约 111.3 万)

+

支出:1,013,829.59(约 101.4 万)

+

净额:+98,738.50(约 +9.9 万)

+
+
+
+ CFO 大白话:这户总体是“能自己养活自己”的,但月度上会出现回款空窗期,需要现金阈值和预算上限。 +
+
+
+ + + + + 月度现金流(公司收支表) + + 2025-12:净 +18.08 万 + 回款 30.05 万 / 支出 11.97 万 + + + 2026-01:净 -17.99 万 + 无收入 / 支出 17.99 万(现金压力月) + + + 建议:设“现金红线” + 例如:余额低于 2 个月固定成本 → 自动降本 + 推回款 + + +
+
+
+ + +
+

Alipay

+
+
+
+
+ 📲 + 支付宝这条线 +
+
+

收入:1,080,038.40

+

支出:1,070,871.17

+

净额:+9,167.23

+

手续费:约 2,154 元(属于“交易成本”)

+
+
+
+ ✅ 大白话:支付宝整体“基本打平”,它更像一个收款/分发通道,不是亏损黑洞。 +
+
+
+ + + + + + 交易 + 收入 ≈ 108.0 万 + 支出 ≈ 107.1 万 + + + 手续费 + ≈ 2,154 元 + + 能接受的成本 ≠ 异常支出 + + +
+
+
+ + +
+

Expense Red Flags

+
+
+
+
+ 🚨 + 支出 Top 的“真假判断” +
+
+

看到这些就先别慌:它们很可能不是“真支出”

+

例子:“授予额度 398000”“剩余额度 0.17 元”营销提醒

+

我的建议:把“短信提醒类”单独一栏,别和真实流水混在一起

+
+
+
+ 🧹 下一次迭代:给短信解析加“过滤规则”(额度/授信/提醒/预测/账单非交易),让报表更接近真实。 +
+
+
+ + + + + 容易误判的短信关键词(建议过滤) + + + 授予 / 额度 / 预授信 / 可用额度 / 剩余可用额度 + + + + 提醒 / 通知 / 预测 / 将于到期 / 即将到期 + + + + 账单:应还 / 最低还款 / 本期无需还款(非交易) + + + + 最终目标:把“真实交易”与“提醒噪音”分两张表 + 这样你才能拿报表做决策(降本/预算/回款/投资) + + + +
+
+
+ + +
+

CFO Playbook

+
+
+
+
+ 🧩 + 先保命,再增长 +
+
+

第 1 件:现金能撑多久(按“固定成本/月”算)。

+

第 2 件:支出有没有上限(预算 + 审批)。

+

第 3 件:回款有没有节奏(每周盯应收)。

+
+
+
+ ✅ 大白话:财务总监最怕的不是“利润低”,是“现金断”。现金断了,业务再好也没用。 +
+
+
+ + + + + 每周必看的 3 个仪表 + + 现金余粮 + 余额 / 月固定成本 = 还能活几个月 + + + 预算执行 + 本月已花 / 预算上限(超了立刻刹车) + + + 应收回款 + 本周应收 / 已收 / 逾期(谁没付) + + +
+
+
+ + +
+

30-Day Plan

+
+
+
+
+ 🗓️ + 30 天迭代计划 +
+
+

第 1 周:把短信噪音剔除(额度/提醒/营销)→ 报表不再“虚胖”。

+

第 2 周:建立预算科目(工资/税/云/房租/外包/营销)。

+

第 3 周:做 13 周现金预测(每周滚动)。

+

第 4 周:固定仪表盘(每周 10 分钟看一次)。

+
+
+
+ ✅ 你最终要的是“自动化财务雷达”:一眼发现异常,一键追溯明细。 +
+
+
+ + + + + 从“报表”升级到“财务系统” + + + + + + W1 + W2 + W3 + W4 + + + W1 清口径 + 过滤短信噪音,真实交易单独统计 + W2 建预算 + W3 做预测 + W4 固仪表 + + +
+
+
+ + +
+
+ +

最后一句大白话

+

+ 报表不是用来“看热闹”的,
+ 是用来“提前发现风险、提前踩刹车、提前安排回款”的。 +

+
+ 🧾 口径干净 + 💳 账户清楚 + 🧠 结论能落地 + 📈 每周滚动预测 +
+
下一步:我可以继续把“短信噪音过滤规则”做成自动更新,让 2026 年异常支出回归真实。
+
+
+ + + diff --git a/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html b/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html new file mode 100644 index 00000000..80fc600c --- /dev/null +++ b/03_卡木(木)/木果_项目模板/PPT制作/脚本/公司财务月报_2026年1月PPT_毛玻璃.html @@ -0,0 +1,449 @@ + + + + + + + + + + +
+
+
Monthly CFO Report · Plain Talk
+

2026年1月 · 公司财务月报

+

十页左右|把“钱从哪来、花到哪去、下月怎么管”讲明白

+
+ 🏦 卡卡猫·中信0405 + 🧾 支付宝·企业 + 🌊 芸归喜·网商9532 + 📩 短信口径 +
+
数据来源:`2026年1月财务报表_完整表格.md`(生成:2026-02-08)
+
+
+ + +
+

Executive Summary

+
+
+
+
+ 🧠 + 一句话结论 +
+
+

1月:公司账户核心结论就是一句话:卡卡猫(中信0405)单月净流出 17.99 万

+

支付宝企业本月是小幅净流入 +240.71;芸归喜只有收入 5.94

+
+
+
+ ✅ CFO 大白话:这月不是“赚不赚钱”的问题,是“现金流怎么扛”的问题。 +
+
+
+ + + + 本月三条线(净额) + + 卡卡猫(中信0405) + 净 -179,853.23 + + + 支付宝(企业) + 净 +240.71 + + + 短信口径(实时) + 净 +3,715 + + +
+
+
+ + +
+

Scope

+
+
+
+
+ 🧾 + 这月报表怎么用 +
+
+

我们优先把“可确认”的钱算清楚:卡卡猫中信0405支付宝企业芸归喜网商9532

+

短信口径是“实时提醒”:更像雷达,不是最终账。

+

腾讯云消费:本月待补(通常次月出账单)。

+
+
+
+ ⚠️ 待补项(本月):云消费(腾讯云)、飞书报销/付款明细。 +
+
+
+ + + + 本月数据来源 + + 重点机构交易明细 + 卡卡猫 / 芸归喜 + + + 支付宝 API + 企业号 signcustomer + + + iPhone 短信 + 实时提醒(需过滤噪音) + + +
+
+
+ + +
+

Overview

+
+
+
+
+ 📋 + 总览表(大白话) +
+ + + + + + + +
短信口径(实时)净 +3,715
卡卡猫(中信0405)净 -179,853.23
芸归喜(网商9532)收入 5.94
支付宝(企业)净 +240.71
飞书工资(参考)65,500
云消费(腾讯云)待补
+
+ 大白话:这月现金压力主要来自卡卡猫;支付宝这条线是“小打小闹”;短信口径是“雷达提示”。 +
+
+
+
+ + + + 现金流“重心” + + 重心:卡卡猫中信0405 + 本月净流出 17.99 万(需要预算上限 + 回款节奏) + + + 小项:支付宝 / 芸归喜 / 短信 + 这些更适合做“监控与预警”,不是现金流主引擎 + + +
+
+
+ + +
+

CITIC 0405

+
+
+
+
+ 🏦 + 卡卡猫(中信0405) +
+
+

收入:0(本月无转入)

+

支出:179,853.23

+

净现金流:-179,853.23

+
+
+
+ CFO 大白话:没有收入的月份,支出再“合理”也会把现金吃掉。这个月就是典型“现金压力月”。 +
+
+
+ + + + 支出构成(节选) + + 厦门存客宝:60,000 + + 苏培玉:26,660.74 + + 好帖云:16,100 + + 缴税(TIPS)+ 银行费 + 多笔打款 + 典型:税费、工资/打款、服务费、供应商付款 + + +
+
+
+ + +
+

Alipay (Enterprise)

+
+
+
+
+ 📲 + 支付宝企业号 +
+
+

收入:1,301.90(5 笔)

+

支出:1,061.19(10 笔)

+

净额:+240.71

+
+
+
+ ✅ 大白话:支付宝这条线本月是“小幅净流入”,它更像收款/分发通道。 +
+
+
+ + + + 一月特点 + + 收入:暗黑4 / Cursor / 充值 + 小额多笔,偏零售型 + + + 支出:服务费/转账/平台消费 + 更适合做“费用科目化” + + 建议:把“平台消费”单独列一科目 + + +
+
+
+ + +
+

Small Lines

+
+
+
+
+ 🌊 + 芸归喜 & 飞书 +
+
+

芸归喜(网商9532):收入 5.94(体量很小)。

+

飞书工资(参考):65,500(若属公司刚性成本,需纳入预算)。

+
+
+
+ CFO 大白话:小账户别花太多精力,重点是把刚性成本“预算化”,避免现金月爆雷。 +
+
+
+ + + + 刚性成本提醒 + + 工资/税/云/房租/外包 + 这些最好在月初就“先占用预算” + 剩下的钱才是你能自由支配的 + + + 建议:设“现金红线” + 比如:现金余额 < 2个月固定成本 → 自动降本/停非必要支出 + + +
+
+
+ + +
+

SMS Radar

+
+
+
+
+ 📩 + 短信口径(实时) +
+
+

本月短信口径:收入 4,885 / 支出 1,170 / 净 3,715

+

大白话:短信更像“预警雷达”,帮你发现“突然发生了什么”。

+

但它不适合当最终账:可能混入提醒/营销/额度

+
+
+
+ ✅ 用法建议:短信用来“抓异常”;最终对账以重点机构交易明细 & 银行对账单为准。 +
+
+
+ + + + 短信的正确姿势 + + ✅ 看“有没有发生” + 比如:转入/转出/缴税/大额打款 + + + ❌ 别当“最终账” + 提醒/营销/额度会把数字带偏 + + 一句话:短信抓异常,明细做结账 + + +
+
+
+ + +
+

Risks & Actions

+
+
+
+
+ 🚨 + 本月风险点 +
+
+

风险 1:中信0405 无收入但支出 17.99 万 → 现金压力月。

+

风险 2:云消费待补 → 月末可能再来一刀。

+

风险 3:工资/税/固定服务费 → 刚性成本必须预算化。

+
+
+
+ ✅ CFO 行动:下月把“预算上限 + 回款节奏 + 现金红线”三件事落到制度里。 +
+
+
+ + + + 下月三条硬动作 + + 1) 预算上限 + 工资/税/云/外包/营销:每科目一个上限 + + + 2) 回款节奏 + 每周盯“应收/已收/逾期”,减少空窗期 + + + 3) 现金红线 + 现金 < 2个月固定成本 → 自动降本/暂停非必要支出 + + +
+
+
+ + +
+
+ +

本月最后一句大白话

+

+ 1月最关键不是“算得多细”,
+ 是先把卡卡猫中信0405的现金流稳住:
+ 预算有上限、回款有节奏、现金有红线。 +

+
+ 🧾 云消费补齐 + 📊 费用科目化 + 🧠 每周滚动预测 + 🚨 异常预警 +
+
下一步迭代:把“云消费(腾讯云)”与“飞书报销/付款”补齐,形成完整月结。
+
+
+ + + diff --git a/03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html b/03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html new file mode 100644 index 00000000..a38381c6 --- /dev/null +++ b/03_卡木(木)/木果_项目模板/PPT制作/脚本/家里NAS对话描述PPT_毛玻璃.html @@ -0,0 +1,423 @@ + + + + + + + + + + +
+
+
Conversation → SOP → Skill
+

家里 NAS 对话描述

+

DiskStation · Time Machine · 外网挂载 · 自动沉淀

+
+ 💧 诊断 + 🪙 运维 + 🌱 产出 + 🔥 复盘 +
+
+
+ + +
+

What Happened

+
+
+
+
+ ⏱️ + 对话触发 +
+
+

① 时间机器:红点 + 等待首次备份

+

② 弹窗:未识别备份磁盘(DiskStation.local)

+

③ 诉求:外网挂载 1TB 共享到 Finder「位置」

+
+
+
+
关键目标:能自动就自动;做不了就落材料;最后沉淀到 Skill,可复用。
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + DiskStation ↔ 外网 + + +
+
+
+ + +
+

Assets Map

+
+
+
+ 🗺️ + 公司 / 家里 两套 NAS +
+
+
+
CKB NAS
+
192.168.1.201
+
外网:open.quwanzhi.com
+
Gitea::3000(已穿透)
+
+
+
家里 DiskStation
+
192.168.110.29
+
外网:opennas2.quwanzhi.com
+
Time Machine:共享
+
+
+
+
对话中所有“定位/修复/挂载”,都先区分:110 网段=家里;1 网段=公司。
+
+
+
+ + + + + + + + + + + + + + + CKB NAS + 家里 NAS + open.quwanzhi.com + opennas2.quwanzhi.com + + + + + 对外入口 / 穿透 + + + Gitea / 同步 + + + + Time Machine + + +
+
+
+ + +
+

Flowchart

+
+
+
+
+ 🔁 + 从对话 → SOP → Skill +
+
+ 这页是“对话行动链”:诊断 → 修复 → 验证 → 沉淀。后续同类问题直接复用。 +
+
+
+
产出物:检测脚本、排查文档、Skill 小节、外网挂载脚本、复盘规则。
+
+
+
+ + + + + + + + + + + + + + + + 识别问题(对话触发) + + + 自动检测(脚本) + + + Time Machine 修复 + + + 外网挂载(SMB+frp) + + + 验证(再跑脚本) + + + 沉淀:文档+Skill + + + + + + + + + + + + + 查IP/端口/状态 + 错误→按材料 + 移除→重加 + 确认红点消失 + 写入 SOP/Skill + + +
+
+
+ + +
+

Deliverables

+
+
+
+ 🧰 + 可复用资产 +
+
+

① 检测脚本:time_machine_diskstation_auto.sh

+

② 排查文档:Time_Machine_DiskStation_错误排查.md

+

③ Skill:群晖NAS管理(含 Time Machine 小节)

+

④ 外网挂载:mount_diskstation_1tb.sh + 参考资料

+

⑤ 复盘规则:目标每句≤30字+必须含%

+
+
+
+ + + + + + + + + + + + + + + + + Scripts · Docs · Skills + + +
+
+
+ + +
+

Home NAS Snapshot

+
+
+
+ 🪙 + 资产摘要 +
+
+
DS213j
MODEL
+
armv7l
ARCH
+
497MB
RAM
+
5.4TB
DISK
+
+
+
开放端口:22 / 80 / 443 / 139 / 445 / 5000 / 5001;对外域名:opennas2.quwanzhi.com。
+
+
+
+ + + + + + + + + + + + Home NAS + + + + + + Time Machine / SMB / FRP + + +
+
+
+ + +
+

Next Actions

+
+
+
+ ▶️ + 按顺序执行 +
+
+

① Time Machine:移除“共享”→重新添加

+

② 外网挂载:frpc 加 SMB 4452 → Finder ⌘K

+

③ 自动化:遇到同类问题直接跑检测脚本

+

④ 复盘:对话结尾统一按复盘块输出

+
+
+
提示:Finder 复制文件会显示速率;侧栏固定:挂载后拖到「位置」。
+
+
+
+ + + + + + + + + + + + + + + + + Finder 位置 + + DiskStation-1TB + + + +
+
+
+ + +
+
+
+ 🏁 + 结尾 +
+

把一次对话变成可复用系统

+

诊断 → 修复 → 验证 → 文档/Skill 沉淀

+
+
PPT 用途:对外汇报、团队交接、下次直接照流程做,不再重复沟通成本。
+
+
+
+ + diff --git a/03_卡木(木)/木果_项目模板/PPT制作/脚本/毛玻璃截图转PPT.py b/03_卡木(木)/木果_项目模板/PPT制作/脚本/毛玻璃截图转PPT.py index f29d0c20..4e8fe283 100644 --- a/03_卡木(木)/木果_项目模板/PPT制作/脚本/毛玻璃截图转PPT.py +++ b/03_卡木(木)/木果_项目模板/PPT制作/脚本/毛玻璃截图转PPT.py @@ -63,19 +63,34 @@ def build_ppt(imgs, out_ppt): def main(): import argparse ap = argparse.ArgumentParser() - ap.add_argument("--html", default="复盘", choices=["复盘", "卡若人设", "纳瓦尔访谈", "天恩乖乖", "今日日志总结"]) + ap.add_argument("--html", default="复盘", choices=["复盘", "卡若人设", "纳瓦尔访谈", "天恩乖乖", "今日日志总结", "家里NAS对话描述", "公司财务分析", "公司财务月报_2026-01"]) args = ap.parse_args() if args.html == "卡若人设": html = BASE / "卡若人设PPT_毛玻璃.html" out_slides = OUT_ROOT / "卡若人设_毛玻璃_slides" out_ppt = OUT_ROOT / "卡若人设介绍_毛玻璃.pptx" max_slides = 5 + elif args.html == "家里NAS对话描述": + html = BASE / "家里NAS对话描述PPT_毛玻璃.html" + out_slides = OUT_ROOT / "家里NAS对话描述_毛玻璃_slides" + out_ppt = OUT_ROOT / "家里NAS_对话描述_毛玻璃.pptx" + max_slides = 8 elif args.html == "纳瓦尔访谈": html = BASE / "纳瓦尔访谈PPT_毛玻璃.html" # v2:扩展页数(含方法/问答/流程图/行动清单),避免覆盖旧版 out_slides = OUT_ROOT / "纳瓦尔访谈_毛玻璃_slides_v2" out_ppt = OUT_ROOT / "纳瓦尔访谈_读书笔记_毛玻璃_v2.pptx" max_slides = 15 + elif args.html == "公司财务分析": + html = BASE / "公司财务分析PPT_毛玻璃.html" + out_slides = OUT_ROOT / "公司财务分析_毛玻璃_slides" + out_ppt = OUT_ROOT / "公司财务_多维分析_CFO大白话_毛玻璃.pptx" + max_slides = 12 + elif args.html == "公司财务月报_2026-01": + html = BASE / "公司财务月报_2026年1月PPT_毛玻璃.html" + out_slides = OUT_ROOT / "公司财务月报_2026-01_毛玻璃_slides" + out_ppt = OUT_ROOT / "公司财务月报_2026年1月_CFO大白话_毛玻璃.pptx" + max_slides = 10 elif args.html == "天恩乖乖": html = BASE / "天恩乖乖PPT_毛玻璃.html" out_slides = TIANEN_DIR / "乖乖_毛玻璃_slides" diff --git a/运营中枢/scripts/karuo_ai_gateway/README.md b/运营中枢/scripts/karuo_ai_gateway/README.md index 7e48584b..b8332a32 100644 --- a/运营中枢/scripts/karuo_ai_gateway/README.md +++ b/运营中枢/scripts/karuo_ai_gateway/README.md @@ -15,6 +15,62 @@ uvicorn main:app --host 0.0.0.0 --port 8000 - `OPENAI_API_KEY`:OpenAI 或兼容 API 的密钥,配置后使用真实 LLM 生成回复。 - `OPENAI_API_BASE`:兼容接口地址,默认 `https://api.openai.com/v1`。 - `OPENAI_MODEL`:模型名,默认 `gpt-4o-mini`。 +- `KARUO_GATEWAY_CONFIG`:网关配置路径(默认 `config/gateway.yaml`)。 +- `KARUO_GATEWAY_SALT`:部门 Key 的 salt(用于 sha256 校验;不写入仓库)。 + +## 部门/科室鉴权与白名单(推荐启用) + +网关支持“每部门一个 Key + 技能白名单”,用于: + +- 科室/部门直接调用接口,不互相影响 +- 外网暴露时避免“全能力裸奔” +- 能按部门做限流/审计日志 + +### 1) 准备配置文件 + +从示例复制一份(`gateway.yaml` 建议不要提交到仓库): + +- `config/gateway.example.yaml` → `config/gateway.yaml` + +### 2) 准备 salt(只在环境变量) + +在运行环境里设置: + +```bash +export KARUO_GATEWAY_SALT="一个足够长的随机字符串" +``` + +### 3) 生成部门 Key 与 hash + +```bash +python tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科" +``` + +把输出里的 `api_key_sha256` 写入 `config/gateway.yaml` 对应 tenant;明文 `dept_key` 只出现一次,保存到部门系统的安全配置里。 + +### 4) 调用方式 + +#### 4.1 /v1/chat + +```bash +curl -s -X POST "http://127.0.0.1:8000/v1/chat" \ + -H "Content-Type: application/json" \ + -H "X-Karuo-Api-Key: " \ + -d '{"prompt":"你的问题"}' +``` + +#### 4.2 /v1/skills(部门自查) + +```bash +curl -s "http://127.0.0.1:8000/v1/skills" \ + -H "X-Karuo-Api-Key: " +``` + +#### 4.3 /v1/health + +```bash +curl -s "http://127.0.0.1:8000/v1/health" +``` ## 外网暴露 @@ -28,6 +84,7 @@ uvicorn main:app --host 0.0.0.0 --port 8000 ```bash curl -s -X POST "https://YOUR_DOMAIN/v1/chat" \ -H "Content-Type: application/json" \ + -H "X-Karuo-Api-Key: " \ -d '{"prompt":"你的问题"}' | jq -r '.reply' ``` diff --git a/运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml b/运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml new file mode 100644 index 00000000..d007260e --- /dev/null +++ b/运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml @@ -0,0 +1,40 @@ +version: 1 + +auth: + # 外部调用请求头名:每个部门一个 Key + header_name: X-Karuo-Api-Key + # 只存 hash,不存明文 key;salt 从环境变量读取 + salt_env: KARUO_GATEWAY_SALT + +tenants: + - id: finance + name: 财务科 + # sha256(dept_key + salt) 的 hex;用 tools/generate_dept_key.py 生成 + api_key_sha256: "REPLACE_ME" + # 允许调用的技能:支持填技能ID(如 E05a)或 SKILL 路径(如 05_卡土(土)/.../SKILL.md) + allowed_skills: + - E05a + - M07 + limits: + rpm: 60 + max_prompt_chars: 12000 + +skills: + registry_path: SKILL_REGISTRY.md + match_strategy: trigger_contains + # 未匹配到技能时的策略:deny | allow_general + on_no_match: deny + +llm: + provider: openai_compatible + api_key_env: OPENAI_API_KEY + api_base_env: OPENAI_API_BASE + model_env: OPENAI_MODEL + timeout_seconds: 60 + max_tokens: 2000 + +logging: + enabled: true + # 既可填绝对路径,也可填相对仓库根目录的路径 + path: 运营中枢/工作台/karuo_ai_gateway_access.jsonl + log_request_body: false diff --git a/运营中枢/scripts/karuo_ai_gateway/main.py b/运营中枢/scripts/karuo_ai_gateway/main.py index e5c35a51..b4c8ecad 100644 --- a/运营中枢/scripts/karuo_ai_gateway/main.py +++ b/运营中枢/scripts/karuo_ai_gateway/main.py @@ -5,8 +5,14 @@ from pathlib import Path import os import re -from typing import Tuple -from fastapi import FastAPI +import time +import json +import hashlib +import hmac +from typing import Any, Dict, List, Optional, Tuple + +import yaml +from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse from pydantic import BaseModel @@ -15,6 +21,103 @@ REPO_ROOT = Path(__file__).resolve().parents[3] app = FastAPI(title="卡若AI 网关", version="1.0") +DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent / "config" / "gateway.yaml" + + +def _is_abs_path(p: str) -> bool: + try: + return Path(p).is_absolute() + except Exception: + return False + + +def _resolve_path(p: str) -> Path: + if _is_abs_path(p): + return Path(p) + return REPO_ROOT / p + + +def _read_yaml(path: Path) -> Dict[str, Any]: + return yaml.safe_load(path.read_text(encoding="utf-8")) or {} + + +def load_config() -> Dict[str, Any]: + """ + 读取网关配置(多租户/鉴权/白名单等)。 + - 默认路径:config/gateway.yaml(不入库;你可以从 gateway.example.yaml 复制一份) + - 也支持通过环境变量 KARUO_GATEWAY_CONFIG 指定绝对/相对路径 + """ + p = os.environ.get("KARUO_GATEWAY_CONFIG", "").strip() + cfg_path = _resolve_path(p) if p else DEFAULT_CONFIG_PATH + if not cfg_path.exists(): + return {} + try: + return _read_yaml(cfg_path) + except Exception: + # 配置读失败时不要“悄悄放行”,避免外网误用 + raise + + +def _sha256_hex(s: str) -> str: + return hashlib.sha256(s.encode("utf-8")).hexdigest() + + +def _get_salt(cfg: Dict[str, Any]) -> str: + auth = (cfg or {}).get("auth") or {} + salt_env = auth.get("salt_env", "KARUO_GATEWAY_SALT") + return os.environ.get(salt_env, "") + + +def _auth_header_name(cfg: Dict[str, Any]) -> str: + auth = (cfg or {}).get("auth") or {} + return auth.get("header_name", "X-Karuo-Api-Key") + + +def _tenant_by_key(cfg: Dict[str, Any], api_key_plain: str) -> Optional[Dict[str, Any]]: + tenants = (cfg or {}).get("tenants") or [] + if not api_key_plain: + return None + salt = _get_salt(cfg) + if not salt: + return None + key_hash = _sha256_hex(api_key_plain + salt) + for t in tenants: + if not isinstance(t, dict): + continue + stored = str(t.get("api_key_sha256", "")).strip() + if stored and hmac.compare_digest(stored, key_hash): + return t + return None + + +def _rpm_allow(tenant_id: str, rpm: int) -> bool: + """ + 极简内存限流(单进程);够用就行。 + - 生产建议用 Nginx/网关层限流 + """ + if rpm <= 0: + return True + now = time.time() + window = int(now // 60) + key = f"{tenant_id}:{window}" + bucket = app.state.__dict__.setdefault("_rpm_bucket", {}) + cnt = bucket.get(key, 0) + 1 + bucket[key] = cnt + return cnt <= rpm + + +def _log_access(cfg: Dict[str, Any], record: Dict[str, Any]) -> None: + logging_cfg = (cfg or {}).get("logging") or {} + if not logging_cfg.get("enabled", False): + return + path_raw = str(logging_cfg.get("path", "")).strip() + if not path_raw: + return + p = _resolve_path(path_raw) + p.parent.mkdir(parents=True, exist_ok=True) + with p.open("a", encoding="utf-8") as f: + f.write(json.dumps(record, ensure_ascii=False) + "\n") + def load_bootstrap() -> str: p = REPO_ROOT / "BOOTSTRAP.md" @@ -24,14 +127,31 @@ def load_bootstrap() -> str: def load_registry() -> str: - p = REPO_ROOT / "SKILL_REGISTRY.md" + cfg = load_config() + skills_cfg = (cfg or {}).get("skills") or {} + reg_path = skills_cfg.get("registry_path", "SKILL_REGISTRY.md") + p = _resolve_path(reg_path) if p.exists(): return p.read_text(encoding="utf-8") return "技能注册表未找到。" -def match_skill(prompt: str) -> Tuple[str, str]: - """根据 prompt 在 SKILL_REGISTRY 中匹配技能,返回 (技能名, 路径)。""" +def _normalize_trigger_token(t: str) -> str: + t = t.strip() + t = t.replace("**", "").replace("*", "") + t = t.replace("`", "") + return t.strip() + + +def _split_triggers(triggers: str) -> List[str]: + s = triggers or "" + for ch in ["、", ",", ",", ";", ";", "|", "/"]: + s = s.replace(ch, " ") + return [tok for tok in (_normalize_trigger_token(x) for x in s.split()) if tok] + + +def match_skill(prompt: str, cfg: Optional[Dict[str, Any]] = None) -> Tuple[str, str, str]: + """根据 prompt 在 SKILL_REGISTRY 中匹配技能,返回 (技能ID, 技能名, 路径)。""" text = load_registry() lines = text.split("\n") for line in lines: @@ -39,13 +159,21 @@ def match_skill(prompt: str) -> Tuple[str, str]: continue parts = [p.strip() for p in line.split("|")] # 表列:| # | 技能 | 成员 | 触发词 | SKILL 路径 | 一句话 | - if len(parts) < 6: + if len(parts) < 7: continue - skill_name, triggers, path = parts[2], parts[4], parts[5].strip("`") - for t in triggers.replace("、", " ").split(): + skill_id = parts[1] + skill_name = parts[2] + triggers = parts[4] + path = parts[5].strip("`") + for t in _split_triggers(triggers): if t and t in prompt: - return skill_name, path - return "通用", "总索引.md" + return skill_id, skill_name, path + + skills_cfg = (cfg or load_config() or {}).get("skills") or {} + on_no_match = skills_cfg.get("on_no_match", "allow_general") + if on_no_match == "deny": + return "", "", "" + return "GENERAL", "通用", "总索引.md" class ChatRequest(BaseModel): @@ -54,11 +182,18 @@ class ChatRequest(BaseModel): class ChatResponse(BaseModel): reply: str + tenant_id: str = "" + tenant_name: str = "" + skill_id: str = "" matched_skill: str skill_path: str -def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> str: +def _llm_settings(cfg: Dict[str, Any]) -> Dict[str, Any]: + return (cfg or {}).get("llm") or {} + + +def build_reply_with_llm(prompt: str, cfg: Dict[str, Any], matched_skill: str, skill_path: str) -> str: """调用 LLM 生成回复(OpenAI 兼容)。未配置则返回模板回复。""" bootstrap = load_bootstrap() system = ( @@ -66,8 +201,9 @@ def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> st f"当前匹配技能:{matched_skill},路径:{skill_path}。" "先简短思考并输出,再给执行要点,最后必须带「[卡若复盘]」块(含目标·结果·达成率、过程 1 2 3、反思、总结、下一步)。" ) - api_key = os.environ.get("OPENAI_API_KEY") - base_url = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1") + llm_cfg = _llm_settings(cfg) + api_key = os.environ.get(llm_cfg.get("api_key_env", "OPENAI_API_KEY")) + base_url = os.environ.get(llm_cfg.get("api_base_env", "OPENAI_API_BASE"), "https://api.openai.com/v1") if api_key: try: import httpx @@ -75,11 +211,11 @@ def build_reply_with_llm(prompt: str, matched_skill: str, skill_path: str) -> st f"{base_url.rstrip('/')}/chat/completions", headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, json={ - "model": os.environ.get("OPENAI_MODEL", "gpt-4o-mini"), + "model": os.environ.get(llm_cfg.get("model_env", "OPENAI_MODEL"), "gpt-4o-mini"), "messages": [{"role": "system", "content": system}, {"role": "user", "content": prompt}], - "max_tokens": 2000, + "max_tokens": int(llm_cfg.get("max_tokens", 2000)), }, - timeout=60.0, + timeout=float(llm_cfg.get("timeout_seconds", 60)), ) if r.status_code == 200: data = r.json() @@ -131,11 +267,101 @@ def index(): @app.post("/v1/chat", response_model=ChatResponse) -def chat(req: ChatRequest): +async def chat(req: ChatRequest, request: Request): """外部调用入口:传入 prompt,返回按卡若AI 流程生成的 reply。""" - matched_skill, skill_path = match_skill(req.prompt) - reply = build_reply_with_llm(req.prompt, matched_skill, skill_path) - return ChatResponse(reply=reply, matched_skill=matched_skill, skill_path=skill_path) + cfg = load_config() + + # 1) 鉴权(如果有配置文件就强制开启) + tenant: Optional[Dict[str, Any]] = None + if cfg: + header_name = _auth_header_name(cfg) + api_key = request.headers.get(header_name, "") + tenant = _tenant_by_key(cfg, api_key) + if not tenant: + raise HTTPException(status_code=401, detail="invalid api key") + + tenant_id = str((tenant or {}).get("id", "")).strip() + tenant_name = str((tenant or {}).get("name", "")).strip() + + # 2) 限流/输入限制(可选) + limits = (tenant or {}).get("limits") or {} + max_prompt_chars = int(limits.get("max_prompt_chars", 0) or 0) + if max_prompt_chars and len(req.prompt) > max_prompt_chars: + raise HTTPException(status_code=413, detail="prompt too large") + + rpm = int(limits.get("rpm", 0) or 0) + if tenant_id and rpm and not _rpm_allow(tenant_id, rpm): + raise HTTPException(status_code=429, detail="rate limit exceeded") + + # 3) 技能匹配 + 白名单校验 + skill_id, matched_skill, skill_path = match_skill(req.prompt, cfg=cfg) + if cfg and not (skill_id and matched_skill and skill_path): + raise HTTPException(status_code=404, detail="no skill matched") + + if tenant: + allowed = (tenant.get("allowed_skills") or []) if isinstance(tenant, dict) else [] + allowed = [str(x).strip() for x in allowed if str(x).strip()] + if allowed: + # 同时支持“技能ID白名单”和“SKILL路径白名单” + if (skill_id not in allowed) and (skill_path not in allowed): + raise HTTPException(status_code=403, detail="skill not allowed for tenant") + + reply = build_reply_with_llm(req.prompt, cfg, matched_skill, skill_path) + + # 4) 访问日志(默认不落 prompt 内容) + logging_cfg = (cfg or {}).get("logging") or {} + record: Dict[str, Any] = { + "ts": int(time.time()), + "tenant_id": tenant_id, + "tenant_name": tenant_name, + "skill_id": skill_id, + "matched_skill": matched_skill, + "skill_path": skill_path, + "client": request.client.host if request.client else "", + "ua": request.headers.get("user-agent", ""), + } + if bool(logging_cfg.get("log_request_body", False)): + record["prompt"] = req.prompt + _log_access(cfg, record) + + return ChatResponse( + reply=reply, + tenant_id=tenant_id, + tenant_name=tenant_name, + skill_id=skill_id, + matched_skill=matched_skill, + skill_path=skill_path, + ) + + +@app.get("/v1/health") +def health(): + return {"ok": True} + + +@app.get("/v1/skills") +def allowed_skills(request: Request): + """ + 返回该 tenant 允许的技能清单(需要 key)。 + 用途:部门侧自查权限/联调。 + """ + cfg = load_config() + if not cfg: + return {"tenants_enabled": False, "allowed_skills": []} + header_name = _auth_header_name(cfg) + api_key = request.headers.get(header_name, "") + tenant = _tenant_by_key(cfg, api_key) + if not tenant: + raise HTTPException(status_code=401, detail="invalid api key") + allowed = tenant.get("allowed_skills") or [] + allowed = [str(x).strip() for x in allowed if str(x).strip()] + return { + "tenants_enabled": True, + "tenant_id": str(tenant.get("id", "")).strip(), + "tenant_name": str(tenant.get("name", "")).strip(), + "allowed_skills": allowed, + "header_name": header_name, + } if __name__ == "__main__": diff --git a/运营中枢/scripts/karuo_ai_gateway/requirements.txt b/运营中枢/scripts/karuo_ai_gateway/requirements.txt index 66da2380..c314dea7 100644 --- a/运营中枢/scripts/karuo_ai_gateway/requirements.txt +++ b/运营中枢/scripts/karuo_ai_gateway/requirements.txt @@ -2,3 +2,4 @@ fastapi>=0.100.0 uvicorn>=0.22.0 httpx>=0.24.0 pydantic>=2.0 +PyYAML>=6.0 diff --git a/运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py b/运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py new file mode 100644 index 00000000..d863907c --- /dev/null +++ b/运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +生成“部门/科室”的 API Key(明文只输出一次)并给出写入 gateway.yaml 的 sha256。 + +用法: + export KARUO_GATEWAY_SALT="your-long-random-salt" + python tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科" +""" + +from __future__ import annotations + +import argparse +import hashlib +import os +import secrets +import sys + + +def sha256_hex(s: str) -> str: + return hashlib.sha256(s.encode("utf-8")).hexdigest() + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--tenant-id", required=True, help="tenant id, e.g. finance") + parser.add_argument("--tenant-name", default="", help='tenant name, e.g. "财务科"') + parser.add_argument( + "--key", + default="", + help="可选:自定义明文 key(不建议)。为空则自动生成。", + ) + parser.add_argument( + "--salt-env", + default="KARUO_GATEWAY_SALT", + help="salt 的环境变量名,默认 KARUO_GATEWAY_SALT", + ) + args = parser.parse_args() + + salt = os.environ.get(args.salt_env, "") + if not salt: + print(f"[ERROR] 未读取到 salt 环境变量:{args.salt_env}", file=sys.stderr) + print("建议:export KARUO_GATEWAY_SALT=\"一个足够长的随机字符串\"", file=sys.stderr) + return 2 + + dept_key = args.key or secrets.token_urlsafe(32) + key_hash = sha256_hex(dept_key + salt) + + # 明文 key 只输出一次;请在生成后立即给到部门系统的安全配置里。 + print("tenant:") + print(f" id: {args.tenant_id}") + if args.tenant_name: + print(f" name: {args.tenant_name}") + print("") + print("dept_key (plaintext, show once):") + print(dept_key) + print("") + print("api_key_sha256 (write into gateway.yaml):") + print(key_hash) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/运营中枢/参考资料/卡若AI外网化与外部调用方案.md b/运营中枢/参考资料/卡若AI外网化与外部调用方案.md index fb4f9d54..d988fb68 100644 --- a/运营中枢/参考资料/卡若AI外网化与外部调用方案.md +++ b/运营中枢/参考资料/卡若AI外网化与外部调用方案.md @@ -82,6 +82,42 @@ uvicorn main:app --host 0.0.0.0 --port 8000 --- +## 四点五、接口配置化(科室/部门可复制) + +> 目标:让以后任何科室/部门/合作方都能“拿到一套配置 + 一个 key”,直接调用卡若AI 网关,不需要改代码。 + +### 你需要提前准备什么(一次性) + +1. **一个 salt**(只放环境变量,不写入仓库):`KARUO_GATEWAY_SALT` +2. (可选)如果要真实 LLM 输出:`OPENAI_API_KEY`(以及 `OPENAI_API_BASE`、`OPENAI_MODEL`) +3. 外网场景:域名/反代已就绪(宝塔/Nginx)或 ngrok 临时暴露 + +### 配置文件在哪里 + +- 示例:`运营中枢/scripts/karuo_ai_gateway/config/gateway.example.yaml` +- 实际:`运营中枢/scripts/karuo_ai_gateway/config/gateway.yaml`(建议不提交到仓库) +- 也可用环境变量指定:`KARUO_GATEWAY_CONFIG=/path/to/gateway.yaml` + +### 新增一个科室/部门(标准步骤) + +1. 设置 salt(运行环境): + - `export KARUO_GATEWAY_SALT="一个足够长的随机字符串"` +2. 生成部门 key(明文只输出一次)与 hash: + - `python 运营中枢/scripts/karuo_ai_gateway/tools/generate_dept_key.py --tenant-id finance --tenant-name "财务科"` +3. 将输出的 `api_key_sha256` 写入 `config/gateway.yaml` 的对应 tenant +4. 配置该 tenant 的 `allowed_skills`(技能白名单:支持技能ID如 `E05a`,或 SKILL 路径) +5. 重启网关服务 + +### 调用方式(必须带部门 key) + +- `POST /v1/chat`: + - Header:`X-Karuo-Api-Key: ` + - Body:`{"prompt":"你的问题"}` +- `GET /v1/skills`:部门自查当前允许技能(同样需要 key) +- `GET /v1/health`:健康检查(无需 key) + +--- + ## 五、最终:执行命令与链接(给 Cursor / 其他 AI 用) **固定域名**:`https://kr-ai.quwanzhi.com`(部署与配置见「内网穿透与域名配置_卡若AI标准方案.md」)。 diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 7c58d36e..15a54225 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -125,3 +125,4 @@ | 2026-02-24 05:49:20 | 🔄 卡若AI 同步 2026-02-24 05:49 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 10 个 | | 2026-02-24 11:42:10 | 🔄 卡若AI 同步 2026-02-24 11:42 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 10 个 | | 2026-02-24 16:28:06 | 🔄 卡若AI 同步 2026-02-24 16:28 | 更新:水桥平台对接、卡木、卡土、运营中枢工作台 | 排除 >20MB: 10 个 | +| 2026-02-24 16:49:15 | 🔄 卡若AI 同步 2026-02-24 16:49 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 4856b237..99c6d4b3 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -128,3 +128,4 @@ | 2026-02-24 05:49:20 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 05:49 | 更新:卡木、总索引与入口、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-24 11:42:10 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 11:42 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-24 16:28:06 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 16:28 | 更新:水桥平台对接、卡木、卡土、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-24 16:49:15 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-24 16:49 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 10 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |