[控场AI]
· 10 分钟阅读· 5,209 字

StockSense AI:先计算后解释的开源零售分析助手

StockSense AI:先计算后解释的开源零售分析助手

StockSense AI 用确定性 Python 计算证据、可选本地模型仅作解释,为小零售商提供可审计的分析工具。

StockSense AI 是一个开源零售分析 MVP,核心主张是把商业算术锁在确定性 Python 代码里,语言模型只负责基于有界结构化上下文解释既定事实。它明确区分核心分析、Gemma 模型和核心回退三种答案来源,并在界面展示支撑证据。指标定义严格:净销售额排除税费,毛利润仅在成本完整时返回,缺失数据如实显示为未知而非零。库存采用只追加的变动账本,记录盘点、到货、损坏和修正,销售导入不会静默覆盖库存。AI 路径通过 Ollama 调用 gemma2:2b,完全可选,离线时其余功能照常运行。项目定位为 MVP 基础而非成品 SaaS,README 坦承缺少行级安全、工作区角色、CSRF 防护、历史成本快照和季节性需求等能力。

当聊天机器人算不清库存时

打烊时分,店主面对一张几百行的表格:销售记录齐全,但库存状态介于"大概还行"和"昨天就该补货"之间。仪表盘能显示总数,却说不清某个商品为何排名第一、利润数字是否可信,或哪些信息还缺失。

这正是 StockSense AI 试图回答的问题:小型零售商能否在不把算术交给聊天机器人的前提下,从销售和库存数据中获得有用答案?

开发者 sleepyaishik69 在 GitHub 上开源了这个零售分析 MVP(仓库地址)。它的核心思路是:传统分析后端负责生成证据,可选本地语言模型只负责解释证据。如果模型不可用,核心仪表盘照常工作。

StockSense AI 项目截图

设计原则:先计算,后解释

自然语言答案可以听起来很漂亮,却完全错误。StockSense 把商业算术牢牢锁在确定性 Python 代码里。后端根据选定的工作区、门店和日期范围,计算总额、对比、商品排名和库存信号。"哪个商品卖出最多?""净销售额是多少?"这类问题直接从确定性事实中得出答案。

对于更宽泛的问题,StockSense 会构建一个有界的结构化分析上下文,包含当前周期与等长上一周期的对比:销售和利润趋势、商品与品类排名、件数变化、库存信号、购物篮模式以及已知数据缺口。如果本地 Ollama 和配置的 Gemma 模型可用,模型会基于这些上下文撰写解释。模型没有 SQL 工具权限,无法修改库存或下单。

界面会标注答案是来自核心分析、Gemma 模型还是核心分析回退,同时展示日期范围和支撑的指标、商品、库存证据。当所选周期没有相关数据时,应用会明确说明,而不是编造一个畅销品。

这种分离至关重要:模型可以辅助解读结果,但不应悄悄成为结果的来源。早期版本曾出现尴尬场景——用户问"哪个商品卖得最多",答案却说无法判断,而证据面板明明显示着销售总额;问"如何卖得更多",得到的却是宽泛的营销建议,与商品和数据毫无关联。

确定性计算指同一组输入在任意时刻、任意次运行都产生完全相同输出的计算过程。StockSense 用 Python 的 Decimal 类型而非浮点数做金额运算,正是因为二进制浮点数无法精确表示 0.1 这类十进制小数,累加几百笔销售后会出现分位偏差。确定性后端还带来可复现性:当用户质疑某个净销售额数字时,可以沿记录逐条回溯到原始导入行,而语言模型的生成过程不具备这种可审计性。这也是把算术与解释分离的根本原因——模型输出的自然语言本身无法被验证,但被喂给模型的事实上下文可以。

应用当前包含哪些能力

工作区从空白开始

新注册会创建工作区和第一个门店,但不预置任何商品或销售数据。商品、门店、销售和导入都带有工作区作用域,API 将认证工作区应用于查询。这意味着演示数据集不会冒充用户的真实业务数据——附带的 CSV 只是显式的合成测试夹具,开发者想探索产品时可以手动导入。

导入现有销售数据而非重新录入

导入流程接受 CSV 和 Excel 文件。预览会检测常见列别名、显示样本,并建议源列到 StockSense 字段的映射方式,用户确认后才写入记录。

日期、数量和单价为必填;交易 ID、SKU、商品名、品类、折扣、税费、单位成本和退货标识为可选。后端验证日期和数字,拒绝非正数量与重复行,可以只导入有效行,同时把被拒行及原因记入导入历史。遇到新 SKU 时,导入流程可以自动创建商品记录。这让数据质量问题变得可见:部分成功的导入不会被悄悄当成完美导入。

指标有明确定义

分析代码使用 Python Decimal 值,金额按四舍五入到分处理。核心定义包括:

  • 净销售额 = 总销售额 − 折扣 − 退款,税费虽被存储但不计入净销售额
  • 毛利润 = 净销售额 − 销货成本,仅当每条相关销售记录都有单位成本时才返回
  • 毛利率 = 毛利润 ÷ 净销售额,成本不完整或净销售额为零时不可用
  • 交易数按门店范围内去重后的交易 ID 计数
  • 客单价 = 净销售额 ÷ 交易数
  • 库存价值使用当前商品成本和当前在库数量,是当下估算而非历史估值

这些规则防止仪表盘在诚实答案是"未知"的地方显示"0"。成本数据缺失尤其关键:商品当前目录成本无法追溯证明某条销售记录在当时实际发生的成本。

销售与商品分析

概览包含所选周期快照与等长上一周期对比,按周或月粒度追踪销售和利润趋势,按件数、销售额和毛利润排名商品,对比商品件数的环比变化,并按品类分组。

仪表盘拆分为概览、销售分析、商品、库存、财务、营销、Ask StockSense、报表、导入和设置等实用视图,让店主能从指标钻取到背后的记录和运营细节。报表可导出为 CSV。目标不是用图表淹没小企业,而是让人回答日常问题、看清假设,然后回去经营店铺。

库存作为一系列变动

StockSense 采用只追加的库存变动账本,记录期初盘点、实物盘点、到货、损坏和修正。每条记录包含商品、门店、工作区、变动类型、数量变化、变动后数量、备注、操作人和时间。商品库存编辑也会生成变动记录。销售导入不会悄悄覆盖今天的库存。

这是对"把库存当成一个可变整数"的刻意转变。变动历史为操作者提供了计数变化时可追溯的线索。不过账本只记录用户开始录入之后的变动,无法重建从未被捕获的库存历史。

概览中的每日行动中心会标记未知库存、低库存或断货风险、建议补货和缺失的商品成本。补货估算使用所选周期的日均件数、提前期和配置的补货阈值。这些是复核提示:它们不建模季节性、已确认的进货订单、供应商包装规格或需求不确定性,也永远不会自动生成采购订单。

只追加账本(append-only ledger)是一种数据建模方式:记录只新增、不修改、不删除,当前状态由全部历史记录累加推导得出。它与直接更新某个 stock_quantity 字段形成鲜明对比。账本模式的优势在于可追溯——当实物盘点与系统数字对不上时,可以翻阅每一次到货、损坏和修正记录定位差异来源。代价是查询当前库存需要聚合历史,通常要靠索引或物化视图优化。会计系统的复式记账、版本控制系统的提交历史都采用类似思路。StockSense 明确承认账本的边界:它只能记录用户开始录入之后的变动,此前的库存历史无法凭空重建。

预算与购物篮信号

采购预算规划器根据当前记录的成本和库存,把指定预算分配到支持的补货建议中。它只包含有足够信息估算成本的产品,不联系供应商也不下单。

营销视图可以呈现同一交易购物篮中一起出现的商品,使用支持度、置信度和提升度指标,且仅在存在可用交易 ID 时生效。这些是描述性的共购模式,不能证明一个商品导致另一个商品销售,也无法衡量促销效果。促销提升、价格弹性和客户分群不在当前数据模型的能力范围内。

支持度、置信度和提升度是关联规则挖掘的三个标准指标。设 A、B 为两件商品:支持度 = 同时含 A 和 B 的交易数 ÷ 总交易数,衡量这个组合有多常见;置信度 = 同时含 A 和 B 的交易数 ÷ 含 A 的交易数,即在买了 A 的人中有多少也买了 B;提升度 = 置信度 ÷ B 的总体购买率,大于 1 说明 A 和 B 同现的频率高于随机巧合。提升度是关键,因为热销商品之间的高置信度可能只是因为它本身就卖得多。这三个指标都是描述性的,无法排除混杂因素,因此 StockSense 强调它们不能证明因果关系,也不能衡量促销效果。

通过 Ollama 的可选本地 Gemma

AI 路径是可选的。StockSense 可以调用本地运行的 Ollama 服务,配置为 gemma2:2b。仪表盘会显示模型状态;模型驱动的洞察生成有超时和有界并发限制。如果 Ollama 离线,确定性分析、报表、导入和其他仪表盘功能仍然可用,自然语言聊天会回退到已标注的计算事实摘要。

这对于本地实验很有用,也让模型依赖与数据管道保持分离。但这不意味着整个应用已经生产加固:本地运行模型仍需要合适的硬件和操作者管理的服务。

Ollama 是一个本地大模型运行时,负责下载、加载模型权重并通过 HTTP 接口提供推理服务,默认监听 11434 端口。与调用云端 API 不同,Ollama 让模型完全运行在开发者自己的机器上,数据不出本地,适合处理销售明细这类敏感商业记录。代价是推理速度取决于本地硬件:gemma2:2b 这类约 20 亿参数的模型在无独立显卡的笔记本上也能运行,但响应延迟明显高于云端服务。StockSense 因此设置了超时和有界并发,避免模型卡顿时拖垮整个仪表盘。更换模型只需修改 OLLAMA_MODEL 配置并重新拉取,应用代码无需改动。

技术栈与架构

  • 前端: Next.js 16、React 19、TypeScript、Tailwind CSS、Recharts
  • API: Python、FastAPI、SQLAlchemy、Pydantic
  • 持久化: 本地开发用 SQLite,Docker Compose 路径用 PostgreSQL
  • 模式变更: Alembic 迁移,初始模式和库存变动迁移都在仓库中
  • 推理: 可选通过 Ollama HTTP 调用 Gemma;核心指标不依赖任何模型

请求路径大致为:CSV/Excel 导入 → 预览 → 映射列 → 验证 → 确认 → 工作区作用域的关系记录 → Python Decimal 分析与库存信号 → 仪表盘和 CSV 报表,或 → 有界事实上下文 → 可选的 Ollama/Gemma 解释。

SQLite 模式下 API 在启动时创建空模式;PostgreSQL 部署应用 Alembic 迁移链。仓库中的 schema.sql 是生成的 SQLite 快照,迁移才是模式演进的真实来源。

运行方式与已知限制

克隆仓库后,先在 backend 目录创建虚拟环境、安装依赖、执行 alembic upgrade head,再用 uvicorn app.main:app --reload --port 8000 启动 API;在另一个终端进入 frontend 执行 npm install 和 npm run dev。打开 http://localhost:3000,创建工作区后即可导入自己的销售文件或合成的测试 CSV。API 文档位于 http://localhost:8000/docs。

想试用可选的本地模型,安装 Ollama 后执行 ollama pull gemma2:2b 和 ollama serve 即可。API 从配置中读取 OLLAMA_BASE_URL、OLLAMA_MODEL 和 OLLAMA_TIMEOUT_SECONDS。

StockSense 是 MVP 基础,不是成品 SaaS。README 列出的重要缺口包括:PostgreSQL 行级安全、工作区角色、密码重置、CSRF 防护、速率限制、供应商记录、已确认采购订单与部分收货、历史成本快照、季节性需求、促销分析和浏览器端到端测试均未实现。会话 cookie 目前只支持本地 HTTP 开发;真实部署需要 HTTPS、生产级 cookie 设置、密钥管理、数据库凭证保护、备份、监控和安全审查。财务输出是运营分析,不构成会计、税务或专业财务建议。

把限制写进产品故事本身就是一种优势。零售商应该能够分辨哪些数字是被观测到的、哪些是被估算的,以及哪些问题当前数据无法回答。

值得借鉴的工程决策

最有用的工程决策是在引入自然语言之前先把数据路径显式化。这迫使开发者定义净销售额、退货、缺失成本、交易标识、库存历史,以及实测事实与合理解释之间的边界。

这也改变了对"AI 分析"的理解。令人印象深刻的不是让模型总结一张表格,而是准备正确的证据、保留指标语义、展示来源,并让系统敢于说"数据不够"。对库存而言,这意味着记录变动而非假装当前计数能解释过去;对成本而言,这意味着让缺失可见,而不是用猜测填补空白。

分享:

相关推荐