氛寸

August 12, 2026 · View on GitHub

氛寸 · Fēn Cùn

别人帮你香水,氛寸帮你用好香水

一个基于实时情境的个人 「用香决策」Agent
从你已有的香柜里,告诉你此刻——喷哪瓶、喷多少、喷在哪、能留多久、要注意什么,以及为什么。


Live Demo

Next.js React TypeScript Tailwind CSS DeepSeek Deploy License


明韵主题下的今日推荐页:推荐卡、分寸建议与情境栏
今日之选 · 明韵
暗香主题下的今夜推荐页:同一套推荐卡的深色版本
今夜之选 · 暗香

它解决什么

站在香柜前,你从不缺香水——缺的是「今天到底用哪瓶、怎么用得恰到好处」的那个判断。

  • 「今天喷哪瓶」是入口:你的香柜 × 此刻情境 → 最合适的一瓶,不认同可一键换成任意一瓶,用法即时重算。
  • 「这瓶怎么用」是灵魂:喷量档位 / 喷洒位置 / 社交距离 / 留香区间 / 风险提示。
传统香水 App氛寸
解决的问题买之前——挑哪瓶买之后——今天用哪瓶、怎么用
输入喜好、预算、评论你已有的香柜 × 实时天气 × 场合
输出种草、购买链接可执行的用香建议 + 明确裁决 + 为什么

核心能力

  • 🎯 今日之选 + 怎么用 — 自动感知实时天气与此刻时段,从你的香柜打分推一瓶,附完整分寸建议。
  • ⚖️ 不迁就的裁决good / caution / avoid 三档。真不合适就先说「今天不建议这瓶」,再告诉你坚持要用时怎么补救。
  • 💬 自然语言场景 — 输入「去前任婚礼」「第一次见投资人」,DeepSeek 解析出场合、正式度、关系张力、是否饭局,喂进打分与用法。
  • 🔔 发现型钩子 — 不等你问:常喷的那瓶今天会翻车(急性天气 / 反季 / 场合预警),搁置已久的那瓶今天正合适。
  • 🔁 反馈闭环 — 答一句「今天,刚好吗」,个人偏移按瓶收敛:嫌冲就少喷,答「刚好」就记住这套配置;高温天答「淡了」归因给天气,不冤枉香水。
  • 🔂 轮换有度 — 昨天刚喷的今天自然让位,久置的自然浮起,兑现「今天喷哪瓶每天不一样」。
  • 📖 香历 — 采纳或反馈的每一瓶自动落进月历(色点 = 当日主香调),点开任一天是当日快照,可补一句话手记。无香的日子留白。
  • 🪞 演示香柜 — 第一次打开就是满配:六瓶示例香水与近一个月的穿香记录,推荐、预警、香历、画像全部有内容可看;加进你自己的第一瓶,它就整体退场。
  • 🌗 昼夜双主题 — 「明韵 / 暗香」两套设计语言,默认明韵,右上角随时切,不跟系统深浅色走。
香柜页:香水列表与搜索添加入口
香柜 · 搜名秒加 / 吃灰标记
香历页:月历色点与当日快照
香历 · 穿香日历 / 一句话手记
我的分寸页:偏好画像与用香记录
我的分寸 · 偏好画像 / 用香记录

它怎么想

决策权在规则引擎,表达权在 LLM。 匹配打分、喷量与留香判定全部由确定性规则计算,可解释、可复现、有单测;DeepSeek 只做两件事——听懂自然语言场景、把规则算好的事实翻成人话。天气永远来自和风天气 API,由服务端代理按坐标网格缓存 30 分钟。LLM 的输出还要过一道数字白名单:事实里没给过的数字(比如编造的「留香 6.2 小时」)整段拦下、退回规则模板。DeepSeek 超时,规则引擎照样出推荐。

香柜、反馈与香历都存在浏览器 localStorage,服务端不保存用户数据;目前没有账号与云同步,「我的」页可导出 / 导入 JSON 备份。

flowchart TD
    Lib["🗄️ 你的香柜<br/>localStorage 持久化"]
    Weather["🌤️ 和风天气 API<br/>实时温 / 湿 / 风"]
    Scene["💬 自然语言场景<br/>DeepSeek 解析意图"]
    Ctx["📍 此刻情境<br/>季节 · 体感 · 时段 · 场合"]
    Engine["⚙️ 规则引擎<br/>确定性打分 · 可解释"]
    Pick["🎯 今日之选<br/>喷量 · 位置 · 距离 · 留香 · 裁决"]
    LLM["✍️ DeepSeek<br/>把事实翻成人话"]
    Out["💡 有温度的解读"]
    Tpl["📄 模板兜底<br/>LLM 挂了也不白屏"]

    Weather --> Ctx
    Scene --> Ctx
    Lib --> Engine
    Ctx --> Engine
    Engine --> Pick
    Pick --> LLM --> Out
    Pick -. 降级 .-> Tpl

打分公式

src/lib/scoring.ts

score =  ( 0.38·季节匹配 + 0.19·时段匹配 + 0.43·场合贴合 )   ← 线性主项,权重归一
       ×  天气乘子 W   ∈ [0.7, 1.3]                          ← 闷热压厚重、寒冷奖暖香
       ×  质量微调 Q   ∈ [0.96, 1.04]                        ← 社区口碑只作轻推,不替你挑瓶
       ×  个人偏移(按瓶偏好 · 场合差评)                     ← 你的反馈收敛而来,正负双向、按月衰减
       ×  场景压制(规避项 · 张力与正式度)                   ← 「别太甜 / 别太冲」硬降权,高张力先压存在感

rank  =  score × 轮换新鲜度 F(d) × 换瓶隐式差评                ← 只动排序,不动裁决与展示

每一项的取值范围、成因与领域依据见领域规则手册 §6.4


四条戒律

  1. 不伪精确 — 留香 / 喷量 / 社交距离只给区间与档位,绝不给「6.2 小时」这类无法验证的假数字;证据不足就明说降级。
  2. 不过度设计 — 不上向量库、不引重后端;规则引擎在浏览器本地毫秒出结果。同一个概念只留一处判据。
  3. 轻冷启动 — 搜名秒加建香柜,不逼用户先填问卷;搜索、扩展目录、手动记一瓶与示例香柜共同保证进门就能用。
  4. 有反馈闭环 — 每次推荐都能被评价、被修正。每个反馈入口都要有明确、可测试的消费路径;兑现不了的入口直接删掉。

技术栈

选型说明
框架Next.js 16.3(App Router)+ React 19.2 + TypeScript 6一仓库承载前端与轻后端
后端Route Handlers代理和风 / DeepSeek,保护 key + 缓存 + 限流 + 降级
样式Tailwind v4(CSS-first @theme token,无 UI 库)昼夜双主题、自持字体
决策确定性规则引擎(纯 TS)前端本地打分,可解释可单测
检索MiniSearch 7(自定义中英文分词)搜名 / 品牌 / 香调秒加
状态Zustand 5 + localStorage香柜与反馈持久化,storage 适配层可替换
语义DeepSeekdeepseek-v4-flash场景解析(json_object + zod 校验)+ 自然语言解读
天气和风天气 QWeather服务端调用 + 30 分钟网格缓存
校验 / 部署zod · Vercel入参校验、GitHub 自动部署

数据

香水数据来自 ledecanteur(Fragrantica 社区数据),每一款都带真实社区投票:扩散、留香、四季与日夜分布、带强度的香调、前中后调。

原始 13.2 万款按投票数 ≥ 50 筛得 3.67 万款,再分两层:主目录取热度 Top 1500 做全中文精选(香名 89.0% 有中文名),随首屏加载;其余进扩展集,搜索索引懒加载、详情按分片取。两层合并成一张榜单,再搜不到还有手动记一瓶兜底。没有可靠中文名的 165 款保留英文——错的中文名比英文更糟。

完整管线、分层账目与中文化口径见数据工程


本地运行

需要 Node 24。

npm install
cp .env.example .env.local   # 各项 key 的用途与申请入口见文件内注释
npm run dev                  # http://localhost:3000

没有 key 也能跑:天气走「季节 + 时段」降级,解读走规则模板。提交前跑一遍:

npm run lint && npm test && npm run build

数据管线(可选,仓库已含构建产物,需自备 ledecanteur/perfumes.jsonl):

npm run extract:terms  # 流式抽取精选子集与词表
npm run build:data     # 应用中文映射 + 预计算 → public/data/perfumes.min.json
npm run build:ext      # 全量扩展集:搜索索引 + 64 详情分片 → public/data/ext*

门面素材需先起生产服务器,next dev 的调试悬浮球会入镜:

npm run build && npx next start -p 3100
SHOT_BASE=http://localhost:3100 npm run shot   # README 截图 → .scratch/shots/
SHOT_BASE=http://localhost:3100 npm run og     # 分享卡片与 PWA 图标 → public/

两个脚本用无头浏览器拍站点自己的页面,目前只从 Windows 的固定路径找 Chrome 或 Edge(scripts/shot.mjsCHROME_CANDIDATES),换平台要先改这份清单。


目录结构

src/
  app/                  今日 / 香柜(library) / 香历(journal) / 我的(profile) 四页 + API 路由
    api/context/        和风天气代理(保护 key + 网格缓存 + 降级)
    api/explain/        DeepSeek 解读(只翻译规则事实,失败降级模板)
    api/parse-intent/   DeepSeek 场景解析(zod 校验,降级关键词启发式)
  components/           AppProvider / 推荐卡 / 情境栏 / 发现型钩子 / 搜索添加 / 手动记一瓶 …
  lib/                  types · scoring(打分) · usage(用法) · recommend(编排)
                        · occasion-priors(场合先验) · format(档位话术单一出处)
                        · journal(香历) · perfumes(统一搜索 + 扩展目录) · catalog(目录加载)
                        · numguard(数字白名单) · nudges(发现型钩子) · demo(演示香柜)
                        · season · hooks · store · ratelimit
scripts/                零依赖数据构建管线 + 截图 / 分享素材 / 依赖审计门禁
data/zh-map/            英文→中文映射(accords / notes / brands / names)
docs/                   产品方案 · 领域规则手册 · 数据工程 · 声音与文案 · 迭代实录 · 截图

文档

  • 产品方案 — 定位、用户、产品模型、关键决策、指标与路线图。
  • 领域规则手册 — 每条打分与用法规则的领域依据,逐条标了证据等级。
  • 数据工程 — 13.2 万 → 3.67 万 → 中文映射的完整管线。
  • 声音与文案 — 术语、语气与表达边界。改用户可见的字之前先读它。
  • 迭代实录 — 上线后每一轮迭代的动因、取舍与沉淀下来的纪律。

参与 · 许可

  • 贡献:欢迎 Issue 与 PR;动手前请读贡献指南
  • 获取帮助SUPPORT 有分流表。
  • 行为准则:本项目遵循 Contributor Covenant
  • 安全:发现漏洞请按安全政策私下报告,勿开公开 Issue。
  • 治理:谁说了算、什么不进主线见贡献指南 · 治理
  • 许可:Copyright © 2026 MrBaoboer。源代码 AGPL-3.0-only,附 §7 商标条款——「氛寸」的名称与标识不在授权范围内(见 LICENSE)。部署修改版请依 §13 向使用者提供对应源码,并换成你自己的名称与标识。
  • 数据与第三方public/data/ 派生自 ledecanteur / Fragrantica 社区数据,不随代码按 AGPL 授权,条款单列(LicenseRef-fragrance-data);随产物分发的字体与依赖见 THIRD-PARTY-NOTICES