微信小程序:基于「酒柜拥有度」的个性化鸡尾酒推荐、采购清单与配方管理工具。 零后端 · 纯源码 · 零构建(无 npm 依赖、无打包链)。
「调酒灵感」帮助用户从自己实际拥有的酒出发,发现能调的鸡尾酒、生成缺料采购清单、管理配方与调制记录。产品以"酒柜"为核心主轴组织全流程:声明拥有 → 推荐能调 / 差一味 / 缺料 → 采购清单 → 调制记录。
当前形态为个人主体的微信小程序,零后端(所有状态存于本地 Storage),推荐与分析在端侧纯算法完成。后端能力(云同步、自创内容、AI 推荐、公开社区)已在路线图中规划,需引入微信云开发后解锁。
- 酒柜驱动推荐:以你实际拥有的酒为信号,先按「可制作性」硬性分层再打分,天然避免推荐重复感。
- 「换一杯」会话去重:本次会话内绝不重复,50 杯目录整轮耗尽才回绕重洗(ADR-012)。
- 关系图谱组合推荐:基酒家族 × 风味向量两轴邻接表,同分按邻接度 tie-break,并按当前卡注入同族进阶 / 跨界相似邻居,破信息茧房(ADR-011)。
- 酒柜口味画像卡:收藏按图谱分类打标 → 占比聚合 → 从 45 条文案库选出「微醺的调酒师」口吻的画像(如「你总爱金酒那一抹明亮,像吧台灯打亮的一杯」),17 杯收藏不再显示「形成中」(ADR-014)。
- 越用越懂你(行为反哺):浏览 / 停留 / 调成埋点端侧聚合为属性级偏好,对匹配酒封顶 +12 加成;停留时长(每 10s 计 1 单位)纳入学习权重(ADR-010 / ADR-012)。
- 筛选偏好跨会话保留:发现页选的心情 / 酒感持久化存储,重启仍在,并联动到酒柜分析语(ADR-013)。
- 采购清单聚合:跨收藏酒汇总缺料、去重并按使用杯数排序,一眼看清「最该先买哪一味」。
- 卖点 / 买点导出:单杯结构化导出(卖点 + 要买的清单 + 做法 + 风味),便于分享与自制。
- 端侧纯算法推荐:多信号加权(心情 / 口感 / 基酒 / 场景 / 酸甜苦 / 易得 / 拥有度)+ 冷启动轮转,零后端零联网。
- 深色主题 + 零位图:程序化 Hero 与杯型,规避包体与版权风险;贴合酒饮品类氛围。
- 点对点分享(规划中):网易云式单酒 / 酒单卡片,个人主体即可(公开社区需企业主体)。
![]() 发现页 |
![]() 详情页 |
![]() 筛选页 |
![]() 酒库 |
![]() 酒柜 |
![]() 采购清单 |
截图为深色主题下的页面预览。实际使用中首启 Overlay 会展示「微醺的调酒师」人设。
下图从用户入口出发,逐层向下梳理到组件层、算法/工具层、数据层,并标出模块间的依赖与数据流向。在 GitHub 上会自动渲染;本地不支持 Mermaid 的预览器会显示为代码块。
IA 现状:4 个主 tab 收敛进 hub 单页容器(
<swiper>左右滑切换 + 自定义底栏),detail / shopping 为独立页;每个 tab 内容由原生<scroll-view>承载滚动(ADR-015)。
flowchart TB
subgraph Entry["hub 单页容器(swiper 4 tab + custom-tab-bar)"]
direction LR
T1["tab-discover 发现/推荐"]
T2["tab-filter 筛选"]
T3["tab-library 酒库"]
T4["tab-favorites 酒柜"]
end
subgraph Pages["独立页"]
P4["detail 详情"]
P6["shopping 采购清单"]
end
subgraph UI["组件层 Components"]
C1["page-shell 页面骨架"]
C2["chip 筛选芯片"]
C3["prep-sheet 做法卡"]
C4["welcome-overlay 人设首启"]
C6["mini-drink-visual 杯型可视化"]
end
subgraph Core["算法/工具层 utils"]
U1["recommend<br/>多信号加权 + 会话去重 + 图谱组合层"]
U1b["flavor-graph 关系图谱<br/>(家族×风味 邻接表)"]
U1c["taste-profile 口味画像<br/>打标/聚合/区间"]
U1d["taste-copy 画像文案库(45条)"]
U1e["rec-prefs 显式筛选持久化"]
U2["buying-analysis 采购聚合"]
U3["export-cocktail 卖点/买点导出"]
U4["adapter 目录适配/守卫"]
U5["taxonomy 基酒分类法(7)"]
U6["behavior 行为埋点<br/>(浏览/停留/调成)"]
end
subgraph Data["数据层"]
D1["data/catalog 50 杯酒"]
D2["本地 Storage<br/>收藏/酒柜/recPrefs/behaviorStore"]
D3["设计 token 色板(深色)"]
end
T1 --> U1
T1 --> U1e
T2 --> C2
T3 --> D1
T4 --> U1c
T4 --> U1e
T4 --> U6
P4 --> U3
P4 --> U6
P6 --> U2
U1 --> U1b
U1 --> U6
U1c --> U1b
U1c --> U1d
U1 --> U5
U1 --> U4
U4 --> D1
U1e --> D2
U6 --> D2
U2 --> D2
C1 --> D3
C4 --> D3
| 维度 | 选型 | 说明 |
|---|---|---|
| 渲染 | 微信原生 WebView(非 Skyline,暂定) | lazyCodeLoading: "requiredComponents" 已启用 |
| 语言 | WXML / WXSS / JS | TS 仅作为 .ts 文档源,无构建链;DevTools 只跑 .js,故关键导出写双文件(.ts+.js) |
| 状态 | 本地 Storage | 收藏、酒柜、偏好、埋点均在端侧 |
| 后端 | 当前零后端 | 路线图规划微信云开发(见 DECISION_LOG ADR-001) |
| 设计系统 | 自研 token 色板 + 自定义组件 | 默认深色主题(Layer 0);组件:page-shell / chip / prep-sheet / welcome-overlay |
| 资源 | 程序化 Hero(线性渐变 + 杯型),零位图 | 满足 Zero Asset 约束 |
依赖方向:页面 → 组件(组合呈现)→
utils(算法/适配)→data/catalog与本地Storage;组件视觉受设计 token 约束。零后端下推荐与分析不联网,所有状态留在端侧。
端侧纯算法如何实现"从信号到推荐",以及为何天然避免重复感:
flowchart TB
S0["用户显式信号<br/>mood / feel / base / scene<br/>acid / bitter / body / easy<br/>(rec-prefs 持久化, 重启保留)"] --> S1["买点信号 owned<br/>(酒柜拥有度, 显式输入)"]
S2["埋点行为信号 learned<br/>(浏览+停留+调成 → 属性级偏好<br/>停留每10s计1单位, 半衰期21天)"] -->|"封顶 +12 加成"| S3
S0 --> S3
S1 --> S3
S3["scoreDrink 显式加权 0-100<br/>+ 行为封顶加成(≤12)"]
S3 --> S4["rankDrinks 排序<br/>(excludeIds 会话去重)"]
S4 --> GX["rankWithGraph 图谱组合层<br/>同分按邻接度 tie-break<br/>+ 锚点邻居注入(同族进阶/跨界相似)"]
GX --> S5{"带型分配"}
S5 --> B1["高分带 topBand"]
S5 --> B2["冷启动轮转 coldStartBand<br/>(跨7基酒 按天旋转)"]
S5 --> B3["探索带 buildExplorationBand"]
B1 --> R["动态理由 (好喝记录式两段)"]
B2 --> R
B3 --> R
R --> OUT["discover 渲染: 高分带 + 探索带<br/>(sessionSeen 会话内绝不重复)"]
行为反哺(埋点 → 推荐):
utils/behavior在端侧记录make_complete/detail_view/detail_dwell/recommendation_dwell等事件;recommend.learnFromBehavior()跨你调成/查看/停留的多杯酒按互动量加权聚合(权重made*2 + view + dwellUnits,近期行为权重更高),产出各基酒/心情/场景的亲和度向量(0–1),对匹配的酒封顶 +12 加分,并支持"金酒+威士忌都爱"这类多重喜好。护栏:① 活跃度地板(累计加权互动不足不生效,防误点噪声);② 不反超显式偏好(仅作加成,非归一化主信号);③ 忽略负反馈(recommendation_swap划走仅记录、不进减分);④ 探索机制(buildExplorationBand在偏好明显时强制掺入未探索基酒,防信息茧房)。详见DECISION_LOG.mdADR-010 / ADR-012。
会话去重(换一杯绝不重复):组件维护
sessionSeen(本次会话已展示集)作excludeIds传入引擎,候选池过滤 + 带内二次去重;50 杯目录整轮耗尽才回绕重洗(ADR-012)。
图谱组合层(分数优先,图谱做组合):
rankWithGraph对同分酒按graphAffinity(同族 0.6 + 跨界相似 0..0.4)tie-break,并以当前卡为锚点注入其同族进阶 / 跨界相似邻居 1–2 张——图谱只做组合、不改写主排序,图谱无锚点时优雅降级(ADR-011)。
推荐不是单一函数,而是「信号 → 算法 → 文案」三层各司其职。三者相互独立:文案不改变排序,信号只做加成,算法只负责打分与多样性。
| 层 | 职责 | 实现 | 对结果的影响 |
|---|---|---|---|
| 信号层(埋点 → 推荐) | 把"你实际调成 / 查看过什么"转成属性级偏好,作为打分的加成信号 | behavior 记录事件;learnFromBehavior() 跨多杯聚合 + 时间衰减,产出基酒 / 心情 / 场景亲和度向量(0–1),支持多重喜好 |
对匹配酒封顶 +12 加分,让推荐"越用越懂你"并泛化到相似酒;不参与归一化分母,故无历史的新用户不被惩罚、也不反超显式选择;口味维度(酸/苦/酒体)由显式偏好路径处理,不计入行为加成以免淹没个人信号 |
| 算法层(推荐算法) | 把显式偏好 + 行为加成算成 0–100 匹配分,排序并分配推荐带 | scoreDrink(多信号加权 + 封顶加成)/ rankDrinks(降序)/ topBand·coldStartBand·buildExplorationBand(高分带 / 冷启动多样带 / 探索轮转) |
产出有序、多样、不重复的候选集:高分带保证"换一杯"仍是好酒,冷启动带跨基酒按天旋转保证首屏多样性 |
| 文案层(推荐文案) | 给每杯酒合成 ≤3 条人话理由,解释"为什么推荐这杯" | scoreDrink 命中信号优先生成动态理由(如「正合你放松的心情」),不足用酒品自带卖点补足;行为命中追加「你常喝这类,应该合你口味」 |
只呈现、不改排序:分数是"匹配度",文案是"给用户看的理由",二者解耦。文案永远不反向影响算法层的分数与顺序 |
三层边界刻意收紧:① 信号层封顶 +12 且为加成(不进分母),避免行为数据喧宾夺主;② 文案层纯展示,杜绝"为凑理由而改分";③ 算法层对显式偏好零后端本地计算,确保离线可用、隐私不出端;已预留全局模型接入点(未来云聚合),当前为纯本地学习、不影响零后端架构。
utils/flavor-graph 是一张静态邻接表,两条轴:
- 纵向 sameFamily:相同基酒家族(朗姆 → 朗姆里更复杂的款),支撑"同族进阶";
- 横向 flavorMirror:风味向量(酸/甜/苦/烈/清爽,弱=1/中=2/强=3)欧氏距离相近、但基酒不同(威士忌酸 ↔ 金酸酒),支撑"跨界相似"。
三种消费方式(同一张图,三处各取所需):
| 消费方 | 用什么 | 干什么 |
|---|---|---|
推荐组合层(recommend.rankWithGraph) |
graphAffinity / collectAnchors |
同分 tie-break + 锚点邻居注入(ADR-011) |
酒柜画像(taste-profile) |
仅 FLAVOR_NUM 数值化 |
单杯主导风味结构打标(不调距离函数,ADR-014) |
洞察卡建议(suggestFromFavorites) |
mirrorNeighbors / familyNeighbors |
"不如试试「X」"的具体推荐(跨界优先,退同族) |
纪律:
flavorDistance是酒与酒的距离,单杯打标只取自身 5 维向量的最大维——两者语义不同,混用会让打标结果不可解释。
把"收藏了 17 杯却只显示形成中"的洞察卡,升级为可解释的分类画像:
flowchart LR
A["收藏 ids<br/>(favoritesStore)"] --> B["tagFavorites 逐杯打标<br/>family=baseSpirit(7类)<br/>flavorSig=5维最大维<br/>(并列 酸>甜>苦>烈>清爽)"]
B --> C["aggregate 占比聚合<br/>(精确浮点, sum===1)"]
C --> D{"门槛<br/>≥5杯 且 topFamilyPct≥0.20"}
D -->|不满足| E["降级: 形成中/刚开始<br/>(不硬造画像)"]
D -->|满足| F["pickProfileCard 区间桶<br/>high≥0.40 / mid≥0.20 / low"]
F --> G["taste-copy 45条文案库<br/>byCombo命中优先, 否则家族句+风味句"]
G --> H["buildInsight 返回<br/>+ filterNote 筛选联动句"]
- 门槛与降级:少于 5 杯、或无明显家族倾向(6 杯六不同家族)→ 回落既有分支,绝不硬给画像;
- 文案库分层:
byFamily(7 家族 × 3 桶)+byFlavorSig(5 结构 × 3 桶)+byCombo(9 条家族×风味的凝练联合句); - 文案铁律(详见
plans/taste-copy-guidelines.md):无数字、无报告腔、末句无句号、拼接前 4 字不撞头,口吻 = 微醺的调酒师; - 验证:
node scripts/verify-taste-profile.js(10 组断言含降级路径与文案规约校验)。
下表按"页面 / 模块 → 职责 → 消费的核心 utils → 关键数据"映射,替代扁平清单,便于快速定位代码归属:
| 页面 / 模块 | 职责 | 消费的核心 utils | 关键数据 |
|---|---|---|---|
hub |
单页容器:swiper 承载 4 个 tab,左右滑切换 | —(组合 tab-* 组件) | tabIndex |
tab-discover |
推荐首页:高分带 + 探索带,可切酒柜模式;筛选持久化水合/落盘 | recommend, rec-prefs |
catalog, Storage(偏好/recPrefs/行为) |
tab-filter |
单维度横向互斥分区筛选 | adapter, taxonomy |
taxonomy 枚举 |
tab-library |
全量酒库浏览 | adapter |
catalog |
tab-favorites |
酒柜 / 收藏:口味画像卡 + 网格 + 采购入口 | taste-profile, taste-copy, recommend, rec-prefs |
Storage(收藏/recPrefs/行为) |
detail |
单杯详情:卖点 + 做法 + 导出入口 + 停留埋点 | export-cocktail, adapter, behavior |
catalog 单杯 |
shopping |
跨收藏汇总缺料采购清单 | buying-analysis |
Storage(收藏配方) |
recommend |
多信号加权 + 行为反哺(封顶+12) + 会话去重 + 图谱组合层 | taxonomy, adapter, behavior, flavor-graph |
catalog, Storage(偏好/行为) |
flavor-graph |
关系图谱:家族×风味邻接表(同族/跨界/距离) | taxonomy |
catalog(DRINKS) |
taste-profile |
口味画像:单杯打标 + 占比聚合 + 区间选卡 | flavor-graph(仅 FLAVOR_NUM) |
catalog, Storage(收藏) |
taste-copy |
画像文案库 45 条 + 查表(微醺调酒师口吻) | —(纯数据) | — |
rec-prefs |
显式筛选持久化(键 recPrefs) | —(Storage 读写) | Storage |
behavior |
行为埋点:浏览/点击/停留/调成/划走 | —(Storage 读写) | Storage(behaviorStore) |
buying-analysis |
采购聚合 + 买点信号 | —(读 catalog 配料) | Storage |
export-cocktail |
卖点 / 买点结构化导出文本 | —(读 catalog 单杯) | — |
adapter |
目录适配 + token 守卫回退 | taxonomy |
catalog |
taxonomy |
基酒 7 种 canonical 契约 | — | 枚举定义 |
以酒柜为中枢串联全流程(Mermaid 渲染):
flowchart LR
A["声明酒柜 favorites<br/>(勾选拥有酒)"] --> B["发现推荐 discover"]
B -->|开 按我的酒柜推荐| C{"可制作?"}
C -->|能调| D["查看详情 detail"]
C -->|差一味/缺料| E["采购清单 shopping"]
D --> F["导出卖点/买点 export<br/>(酒卡文本)"]
D --> FD["导出埋点数据 export<br/>(行为记录 JSON)"]
E --> G["买齐配料"]
G --> H["调制记录 (本地)"]
F --> H
FD --> H
D -.->|收藏/取消| A
E -.->|更新酒柜| A
流程里的「导出」包含两种互不替代的独立动作:
- 导出卖点/买点:详情页把酒卡(卖点、要买什么、做法)导出为文本/分享;
- 导出埋点数据:把本地行为记录(
behaviorStore:查看/调成/划走等事件)导出为 JSON,用于离线分析或备份。
- 以酒柜为核心主轴:不是把酒柜当附加 toggle,而是用"拥有度"驱动推荐分层与采购闭环。
- 消费级工艺基线 + 调酒师人设:首启 Overlay 设定人设「微醺的调酒师」(去 AI 措辞以规避审核口径);Voice 专业克制有温度、第二人称、建议非判定。
- Zero Asset:全程序化 Hero 与杯型,零位图,规避包体与版权风险。
- 深色主题默认:掩盖 UI / 动效短板,且贴合酒饮品类氛围。
完整记录见 DECISION_LOG.md(ADR-001 ~ ADR-015)。摘要:
- 零后端是约束不是红线(ADR-001):当前因"不会做后端"而零后端,引入云开发后解除。
- 深色主题默认(ADR-002):掩盖短板 + 贴合品类。
- 基酒 7 种 canonical(ADR-003):gin/whiskey/rum/vodka/tequila/sake/brandy,改枚举须同步 taxonomy。
- 不引入 TDesign npm(ADR-004):项目零构建零 npm,TDesign 需 Build npm 且稀释品牌 → 改手搓品牌组件。
- Skyline 推迟(ADR-005):性能非视觉问题,迁移成本高,提审后可选。
- 多信号推荐 + 冷启动轮转 + 行为反哺(ADR-006 / ADR-010):端侧多信号加权(心情/酒感/基酒/场景/酸甜/苦/酒体/易得/拥有度)归一化到 0–100,无信号时跨基酒按天轮转保证首屏多样性;行为埋点(调成/查看)反推属性级偏好并对匹配酒封顶 +12 加成,设活跃度地板(互动<3 不生效)、不反超显式偏好、忽略负反馈(划走仅记录不降分)三道护栏。
- 网易云式点对点分享(ADR-007):个人主体即可,公开社区才需企业主体。
- IA 以酒柜为主轴(ADR-008):重构方向。
- 仓库策略:公开(AGPL-3.0) + 精简集(ADR-009):排除构建 / 记忆目录,见
.gitignore。 - 关系图谱 = 主流程组合层(ADR-011):分数优先,图谱只做同分 tie-break + 锚点邻居注入;
rankWithGraph+ 注入截断修复(满带不再切掉注入卡)。 - 推荐会话去重 + 停留埋点(ADR-012):
sessionSeen/excludeIds整目录耗尽才回绕;停留(每 10s 计 1 单位)进入行为学习权重。 - RecPrefs 显式筛选持久化(ADR-013):独立键
recPrefs双轨于隐式行为学习,发现页水合/落盘并联动分析语。 - 酒柜口味画像 = 图谱分类打标 + 区间文案库(ADR-014):门槛(≥5 杯且有明确倾向)不满足即降级;文案规约禁数字/禁报告腔/末句无句号/拼接撞头防护。
- swiper 内滚动用原生 scroll-view(ADR-015):CSS overflow 在 swiper-item 嵌套下不可靠;组件 json 必须声明
component: true(模拟器宽松、真机严格)。
调酒灵感小程序/
├── miniprogram/ # 小程序源码(pages / components / utils / app.*)
├── data/ # 目录数据(catalog 等)
├── docs/ # 设计 / 评审文档
├── plans/ # 路线图与专项计划(含 github-setup/)
├── research/ # 调研
├── audit/ # 审计
├── pm/ # 产品管理
├── catalog-batches/ # 目录分批数据
├── scripts/ # 校验 / 构建脚本
├── project.config.json # 微信项目配置(含 appid)
├── package.json # 零运行时依赖(仅脚本工具)
├── README.md
├── DECISION_LOG.md
└── CONTRIBUTING.md
已排除:
node_modules/、automation/、画布归档/、.workbuddy/、.codebuddy/(详见.gitignore)。
- 本地开发:用微信开发者工具打开本仓库(识别
project.config.json),即可预览 / 调试。 - 验证脚本(Node 实跑,非编译检查):
脚本内置
node scripts/verify-taste-profile.js # 口味画像:打标/聚合/门槛/文案规约(10 组) node scripts/verify-insight-correspondence.js # 分析语卡与推荐卡同源联动(8 组) node scripts/verify-rec-prefs-persist.js # RecPrefs 持久化与筛选联动(9 组) node scripts/verify-recommend-dedup.js # 换一杯去重 + 图谱组合层(8 组)
wxstorage mock,无需开发者工具即可运行;改动utils/后建议全量跑一遍作回归。 - 双文件纪律:
utils/**.ts为文档源,DevTools 实际运行.js——改导出/require 必须.ts+.js同步,并各跑node --check。 - 上线(提审):属微信平台动作,与 GitHub 无关。分步指引见
plans/github-setup/LAUNCH-GUIDE.md。 - 路线图:后续迭代入口见
plans/ROADMAP.md。
Copyright © 2026 Conrad. 本仓库以 GNU Affero General Public License v3.0(AGPL-3.0) 开源。
- 任何人复用、改写或派生本仓库代码,必须同样以 AGPL-3.0 开源;
- 若将修改后的代码以网络服务形式运行(SaaS),必须向用户提供对应源码;
- 使用 / 派生须保留版权与许可声明(署名)。
完整条款见仓库根目录 LICENSE。





