Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 47 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,120 +1,79 @@
[![ShopMate · 选购有灵感,经营有把握](docs/assets/cover.png)](https://chantso.github.io/shopmate/)

# ShopMate

面向单一零售品牌官方商店的应用与 Agent 工作台。CityBuddy 提供同一家店的交易与身份后端;ShopMate 负责买家与操作员的应用入口、Agent 和对话。一个商品目录、一支运营团队、多位顾客,不包含多商户入驻。商家端支持经营分析、商品与库存、订单问题和审批执行;买家端支持推荐与比较、购物车、本人订单及由用户确认的结账、模拟付款和退款申请。
[![CI](https://github.com/ChanTso/shopmate/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/ChanTso/shopmate/actions/workflows/ci.yml)

**原生购物客户端 × 经营 Agent,让选择、确认与执行成为连续的体验。**

**[浏览产品官网 ↗](https://chantso.github.io/shopmate/)** · [Android](android/README.md) · [iOS](ios/README.md) · [本地运行](docs/RUNTIME.md#本地运行) · [业务验收](evals/records/retail-v2-20260907/README.md)

面向同一零售品牌的 Android / iOS 买家 App 与 React 商家工作台。买家说出需求、比较商品、确认交易;运营人员从经营数据出发,准备方案、核对变更并批准执行。[CityBuddy](https://github.com/ChanTso/citybuddy) 提供实际交易与身份后端。

项目复用 [commerce-agents](vendor/commerce-agents/README.md) 的商家与购物核心、Messages 运行时和零售页面组件;业务工具、身份、持久会话及实际写入接入 CityBuddy。原 [Apache-2.0 许可证](vendor/commerce-agents/LICENSE)、版权声明和[图片来源](web/public/products/IMAGE-CREDITS.md)保留。
## 一间商店,两种视角

## 产品展示
| 买家 · Android / iOS | 商家 · React Web |
|---|---|
| 商品与规格、比较推荐、购物规划 | 经营趋势、库存预警、订单与售后 |
| 商品上下文对话、流式卡片、可编辑偏好 | 只读 SQL 分析、独立 Python 计算 |
| 购物车、报价确认、订单、模拟支付退款 | 商品维护、调价、补货、促销与营销草案 |
| 秒杀预约、状态查询与原操作恢复 | 差异预览、操作员批准、执行回执 |

[静态产品官网](site/README.md)位于 `site/`,与商家 React 工作台 `web/` 分开。官网使用实际客户端画面与滚动交互,不依赖在线模型或交易服务;完整业务演示在本地运行。Android 与 SwiftUI 买家客户端共享 KMP 协议与恢复核心,构建说明分别见 `android/README.md`、`ios/README.md`
官网展示真实客户端画面与交互演示;完整业务由本地服务运行

## 当前能力
## 值得深入的四个设计

- **经营分析**:主 Agent 组织查询与追问,复杂计算交给分析子 Agent;它通过受限 SQL 取数,也可在独立 Python 容器内计算完整查询结果。成交额来自成功付款的历史订单,流量和广告归因有独立的观察期间与来源;缺失数据不填零。
- **经营首页与订单**:四项核心指标、可切换日趋势和三类待办使用同一报告口径;近期订单读取当前全店标准单与秒杀单,不受历史报告截止限制。按 SKU 子单展示成交时的金额,订单、付款、退款和履约状态分别保留。
- **商品与运营**:服务端分页浏览商品系列和单品,详情展开真实 SKU、规格、当前价格、库存、内容和成本观察;库存预警及订单问题提供对应分析入口。
- **五类草案**:支持 `LISTING_UPDATE`、`PRICE_UPDATE`、`INVENTORY_ACTION`、`PROMOTION`、`CAMPAIGN`。涉及商品的操作展开后至多 25 个 SKU;卡片分别显示金额、数量、开关和文字差异。
- **操作员审批**:模型可读取、建案和取消未执行方案,不能批准。批准按钮使用登录操作员的直接身份;Java 核对快照、版本和业务条件,在同一事务内保存实际变更、草案回执及适用的商品 Outbox。冲突整批拒绝,重复批准返回原结果。
- **恢复与停止**:会话和草案引用保存在 SQLite,业务终态以 Java 为准。刷新后重新登录可恢复记录;“停止生成”中断当前请求,不撤销已保存的草案或已执行的变更。审批结果独立保存在业务回执中,模型生成不阻止普通购物与操作员审批;真实版本冲突和未知写入仍需核对。
- **原生界面,共享规则。** Android 使用 Jetpack Compose,iOS 使用 SwiftUI;KMP 共享 SSE 解帧、消息归并、报价与恢复规则。导航、网络取消、安全存储和生命周期保留平台实现。
- **流式阅读与异步状态。** 文字和商品卡片增量到达,阅读历史时保持位置,回到末尾再跟随。分页绑定已提交查询,迟到详情不能覆盖新选择;SwiftUI 以不可变消息段建立相等性边界,保留未变化卡片。
- **原操作恢复。** 写入前持久化请求 key、原参数与确认报价。响应丢失后核对原回执,按原意图恢复;生成任务、普通购物与审批独立推进,业务终态由 Java 事务决定。
- **能分析,也有执行边界。** 经营 Agent 将复杂分析交给只读 SQL 子 Agent,完整且有界的数据可交独立 Python 容器计算。Skills 按需加载,旧工具结果裁剪,记忆可改删;各类模型调用共用预算。结账、付款、退款确认及经营变更批准均由用户操作。

促销批准会立即修改商品实际售价,**经营窗口结束后不会自动恢复价格**。开始前批准返回 `promotion_not_started` 并保留待批准状态;过期未执行方案被拒绝。营销活动创建或更新的是本站计划、受众、文案和预算,不代表向外部广告平台投放,也不改写既有支出或收入观察。
## 系统边界

```mermaid
flowchart LR
Buyer[买家购物助手] --> Host[ShopMate API / 身份隔离的会话与记忆]
Merchant[商家经营工作台] --> Host
Host --> Shopping[购物 Agent]
Host --> Trading[经营 Agent]
Trading --> Analysis[只读 SQL 分析子 Agent]
Analysis --> Views[受限经营视图]
Analysis --> Sandbox[独立 Python 沙箱]
Shopping -->|买家 OBO / 工具权限| Java[CityBuddy 业务接口]
Trading -->|商家 OBO / 工具权限| Java
Host -->|用户确认 / 操作员批准| Java
Java --> Transaction[身份与版本校验 / 交易 / 回执 / Outbox]
App[Android / iOS] --> Host[ShopMate API]
Web[React 商家工作台] --> Host
Host --> Agents[买家 / 经营 Agent]
Agents --> Analysis[只读 SQL / Python 沙箱]
Host --> State[(SQLite · 对话与恢复)]
Host -->|受限工具 / 人工确认| Java[CityBuddy · Auth / Commerce]
App -->|秒杀| Java
Java --> DB[(MySQL · 业务状态)]
Analysis -->|只读经营视图| DB
```

商家入口为 React/Vite Web `/`,买家入口为 [Kotlin/Compose Android App](android/README.md);买家登录、人工确认、停止恢复与记忆管理见[买家使用说明](docs/BUYER.md)。旧 `/buyer` Web 页面已退役;旧客服入口与重复模型循环已撤下,Java 的授权、退款确认及回执机制继续复用。两个角色都可调用有来源的网页搜索;经营分析可调用独立 Python 沙箱。[完整零售验收](evals/records/retail-v1-20260907/README.md)记录真实业务任务、页面操作、记忆、并发与中断恢复;业务成绩和边界检查分别报告
ShopMate 当前为单实例宿主,SQLite 使用 WAL 保存对话、意图和偏好;MySQL 保存身份、商品、订单和交易回执。两者职责与运行约束见[工程指南](docs/RUNTIME.md#身份对话与持久状态)

## 身份、对话与持久状态
## 验证与结果

普通购物和经营接口只要求对应角色的 `Authorization: Bearer`,不要求聊天 ID。服务端按主体和角色保存内部授权绑定,再按 Java 端点交换精确 scope 的 OBO;部分 UI 购物操作也走此受限代理。模型没有付款、退款确认或操作员批准工具
原生测试覆盖流式阅读、取消、分页与详情竞态、跨页面状态和原请求恢复;业务集成测试通过真实接口与 SQL 核对交易结果

聊天通过 `POST /api/{buyer|merchant}/conversations` 创建,列表和恢复分别使用 `GET /conversations`、`GET /conversations/{id}`,流式调用为 `POST /conversations/{id}/chat`。原 `/session`、`/sessions`、`/chat` 保留为历史协议兼容接口,正式客户端不使用它们。命令与结账/退款记录按主体读取,旧操作保留原 key、请求体和授权绑定,换聊天不会变成新的业务意图
[零售验收](evals/records/retail-v2-20260907/README.md)包含 **18 个已知场景、30 次真实模型尝试:24 次通过,3 次业务失败,3 次提供者故障**。购物付款退款、促销成交与经营分析等核对实际回答和数据库状态,报告保留失败、工作负载与完整源码版本

当前为单进程、单实例 Python 服务;SQLite 存储对话、意图、恢复记录和记忆,使用 WAL,必须保存在持久目录,不能随容器重建丢弃。`state_path` 可配置,夹具重置先用 SQLite backup 保存原库;Java/MySQL 是交易权威来源。不得直接以多个 Uvicorn workers 扩容。默认最多 8 个活跃聊天任务、每用户 2 个,同一对话串行;超额返回 429,普通业务请求不占模型任务名额。这些是任务上限配置,不是容量测量
[StateEval](https://github.com/ChanTso/state-eval)单独检验授权边界;业务完成、权限正确与模型回答质量分别判定

## 本地运行

需要同级 [CityBuddy](https://github.com/ChanTso/citybuddy) 仓库、Java 21、Python 3.11+、Node.js 24、uv 和 Docker Compose。CityBuddy 至少包含 [PR #159](https://github.com/ChanTso/citybuddy/pull/159)(`2eb42634f082c0ddf93639f902db38009381d337`),提供零售/营销迁移、商家操作、全店近期订单读取和 FAQ 发布 CLI。

首次准备 Java 服务:
需要同级 CityBuddy 仓库、Java 21、Python 3.11+、Node.js 24、uv 与 Docker Compose。完成[首次后端准备](docs/RUNTIME.md#本地运行)后:

```sh
cd ../citybuddy
make init-local setup-java setup-python
./mvnw --batch-mode --no-transfer-progress -pl auth-service,commerce-service -am package

cd ../shopmate
uv sync --frozen
python3 scripts/local_runtime.py up
npm --prefix web ci
npm --prefix web run build
uv run uvicorn shopmate.app:create_app --factory --host 127.0.0.1 --port 8101
```

`up` 要求 ShopMate API 已停止;首次初始化统一零售夹具,已有该版本数据时保留当前业务变更。构建后由 Python 同源提供商家 Web,访问 `http://127.0.0.1:8101/`,不需要另起 Next/Node 服务。开发 Web 时另开终端 `npm --prefix web run dev`,3100 端口将 API 请求代理到 8101。

操作员账号为 `shopmate-fixture-operator`,本地生成密码保存在忽略的 `.run/operator_password`。Bearer 只保留在页面内存,刷新后重新登录。Android 构建与安装见 [android/README.md](android/README.md),模拟器连接 `http://10.0.2.2:8101`。

启动脚本使用独立的 `shopmate` Compose project 和数据卷,不重置 CityBuddy 默认演示库。Auth/Commerce 使用 9081/9082,ShopMate API/Web 使用 8101。停止 API 后运行 `python3 scripts/local_runtime.py stop` 停止本项目 Java 与数据服务、保留卷。对话数据库必须保留在 `.run` 或另一个持久目录中。

## 模型、预算与时间

模型代理凭证继续来自同级 `citybuddy/.env` 的 `CLIPROXY_BASE_URL` 和 `CLIPROXY_API_KEY`。默认主模型与分析模型均为 `gpt-5.6-terra`,经 Chat Completions 适配对接 Messages 循环。运行参数位于 `.run/settings.json`,也可通过 `SHOPMATE_CONFIG` 指定配置文件;凭证不传入浏览器或模型工具参数。

每回合主、分析子 Agent 共用默认 16 次模型调用和 300 秒截止;主循环最多 12 个工具轮。分析账号仅有六个经营视图的 SELECT,默认查询上限 2 秒、200 行及 16,000 字节。Python 只接收这些视图的完整、有界查询结果;截断表在执行前拒绝。缓存用量仅展示代理实际报告的字段,未知量不推断成命中率或费用收益。主、分析、记忆和搜索请求共用模型调用预算;实际 Responses 搜索用量与 Chat 用量合计一次。默认每聊天回合至多 3 次搜索和 3 次 Python 尝试,次数与回合时间限制不是硬 token 或费用上限。

网页搜索通过独立的 Responses 请求接入现有普通工具接口,返回外部摘要、实际引用和服务提供的查阅来源。来源卡将引用与查阅列表分开;没有元数据时明确提示。网页内容不作为本站商品、订单、政策或权限真相。当前代理不支持原生 Messages server tools,本部署没有启用原生 server search、code execution 或原生提前派发;搜索与 Python 能力由宿主实际执行。字段依据见 [Responses 搜索文档](https://developers.openai.com/api/docs/guides/tools-web-search)。

`local_runtime.py up` 从 `infra/analysis-sandbox/` 专用目录构建 `shopmate-analysis:1`。每次 Python 调用创建独立容器:非 root、无网络、只读根目录、不挂载宿主或项目文件,固定 Python/pandas/numpy,1 CPU / 512 MiB / 64 PID / 32 MiB 临时目录。单次执行窗口至多 20 秒(含排队),结束清理另有 10 秒限时;合计输出至多 64 KiB,宿主同时运行至多两个;查询和排队也受任务截止约束。停止生成、任务超时或正常关闭会终止对应容器;若 Docker 失联导致无法核实清理,会报告错误并拒绝后续沙箱执行,不把它当成成功。宿主被强制杀死后的遗留容器不属于该保证,可按 `shopmate.analysis=true` 标签检查。容器约束说明见 [Docker 文档](https://docs.docker.com/engine/containers/run/)。

当前演示数据为 `shopmate-retail-v1`:**87 个目录根、104 个可交易 SKU、90 个完整 Shanghai 日、CNY**。这是从 vendored 零售样例和确定性造数构成的演示数据,不是实际经营记录。报告截止固定为 `2026-09-05T00:00:00+08:00`;模型的操作时钟是每轮真实 Shanghai 时间。相对报表期间使用报告截止,促销的“今天/明天”使用真实操作日期。详情见[零售夹具与重置说明](docs/retail-fixture.md)。

## 使用工作台

登录后可依次体验以下流程;这是当前能力的操作说明,不代表一次新的模型验收结果:

1. 在商品页翻页、筛选状态和内容质量,打开商品系列并核对各 SKU、成本和报告期间销量。
2. 问“当前报告期间的成交、流量和转化,相比上一期间有什么变化?请列出依据。”继续追问贡献商品或营销计划的同期 ROAS。
3. 从库存提醒、商品详情或营销页提出方案,在草案卡片核对完整差异。仅提出方案不会立即修改商品。
4. 点击批准或取消,再从历史和业务页面读回状态。促销先核对真实允许批准窗口及到期不自动恢复的后果。
5. 流式运行时点击“停止生成”,随后刷新会话核对保存的状态。不要依据未完成的回复判断写入是否发生。

## 检查与历史记录

以下命令运行代码检查,不调用真实模型:

```sh
uv run ruff check src tests scripts integration_tests
uv run ruff format --check src tests scripts integration_tests
uv run pytest --import-mode=importlib tests \
vendor/commerce-agents/commerce-common/tests \
vendor/commerce-agents/merchant-agent/core/tests \
vendor/commerce-agents/merchant-agent/runtime-messages-api/tests \
vendor/commerce-agents/shopping-agent/core/tests \
vendor/commerce-agents/shopping-agent/runtime-messages-api/tests
npm --prefix web run typecheck
npm --prefix web test
npm --prefix web run build
```

真实 Java/数据库边界检查使用 `uv run pytest integration_tests -q`,会修改保留的演示业务数据,应与其他任务串行运行。每次完整运行前,先停止 API 和全部写入、保存所需记录,按[手工重置流程](docs/retail-fixture.md#手工重置)恢复夹具后重新启动 API;正常 `up` 会保留已批准的变更,不能代替重置。直接重复写入套件可能触发无变更草案拒绝,或继续改变测试商品的价格和库存。

[最终零售业务验收](evals/records/retail-v2-20260907/README.md)覆盖18个已知场景、按登记共30次:**24次通过、3次业务失败、3次提供者故障**,对应冻结版本 `4020ff93f4797e2ae3142e8a4123442d3d8693b7`。购物付款退款、商品维护、补货、促销成交、营销审批、搜索与SQL/Python分析均核对实际回复和数据库终态;日期表达和遗漏回答的失败保留。61个聊天回合的结束等待p50为30.54秒、p95为87.08秒,包含失败,不代表并发容量。[前一轮54次与边界验证](evals/records/retail-v1-20260907/README.md)单列,不混合版本或分母;操作仍须由用户确认并经Java事务校验。
商家工作台:**http://127.0.0.1:8101/**。买家客户端按 [Android](android/README.md) / [iOS](ios/README.md) 说明构建;模型配置、演示账号、数据重置及检查命令统一见[运行指南](docs/RUNTIME.md)。

[评测索引](evals/records/README.md)保留旧七商品/42 日 UTC 版本的 **78/90** 与定向 **21/24**;它们不描述当前零售数据或本次完整批。[历史浏览器演示、截图和 SQL](docs/demo-20260906/README.md)仍对应旧版,当前双端页面、记忆与恢复记录见新版验收。
## 工程入口

## Native buyer clients
| 目录 | 内容 |
|---|---|
| [`android/`](android/) · [`ios/`](ios/) · [`shared/`](shared/) | 原生客户端与 KMP 业务核心 |
| [`web/`](web/) · [`src/shopmate/`](src/shopmate/) | React 工作台与 Agent 宿主 |
| [`integration_tests/`](integration_tests/) · [`evals/`](evals/) | 业务边界测试与真实模型验收 |
| [`site/`](site/) | 独立构建的 GitHub Pages 产品官网 |

The [SwiftUI buyer client](ios/README.md) uses the same Kotlin Multiplatform core as Android for streaming messages, checkout contracts and recovery. Android and iOS cover the same buyer business: catalog and variants, contextual chat, cart, reviewed checkout, simulated payment, orders/refunds, seckill, profile and editable memory. Platform UI, networking and lifecycle stay native. Both use the existing ShopMate/CityBuddy services.
复用 [commerce-agents](vendor/commerce-agents/README.md) 的零售核心与 Messages 运行时,扩展原生客户端、业务工具、身份、持久状态与实际交易接入。保留上游 [Apache-2.0 许可](vendor/commerce-agents/LICENSE)及[图片来源](web/public/products/IMAGE-CREDITS.md);封面使用[官网中相同的原生演示画面](site/README.md)。
Loading
Loading