从一个家庭药柜到公网 AI 问诊:AI token 深解析与宝塔部署全流程实录

周店瑟谷 5 阅读 建站与数字基建

项目地址:zyf.seguzhixue.cn(家庭中药柜索引 + AI 倪师问诊,Flask + SQLite + 原生 JS,单容器 Docker 部署)。这篇文章把从「一张截图里的药柜表」到「公网可访问的 AI 问诊站」全过程里,值得复用的关键动作沉淀下来——AI 上下文里每一类 token 从哪来、怎么算、怎么裁、参数怎么配,55 张图怎么不花一分钱跑通,宝塔 Docker 从零到 https 的每一步、反向代理哪里最容易踩坑,以及更新与回滚的完整闭环。

一、项目长什么样

两个标签页,全部功能围绕「家里真实的药柜」:

  • 药柜索引——187 味药材按七个柜层(望岳 / 含章 / 厚德 / 知味 / 藏珍 / 承露 / 拾遗)登记,每味带实拍图、性味归经、功效、位置、注意事项;另含食补食材、18 首经方、55 首自制调养方、安全协议。

  • AI 倪师问诊——四个内置技能(症状辨析 / 配方建议 / 经方与专栏方速查 / 药材知识),把整柜药材清单压进 system prompt,问「昨晚淋雨受凉鼻塞怕风」它回答用的每味药都标着「第 X 层」。

技术栈刻意保守:Flask 3 + SQLite + 无构建步骤的原生 JS,一个 252KB 的数据库文件 + 62MB 图片目录就是全部状态,Docker 单容器可跑。选这套不是为了怀旧,而是家庭工具的第一需求是十年后还能跑。

支撑 AI 问诊的知识供给是两级结构:全量清单常驻 system 前缀(便宜、稳定、可缓存),重量级详情按需注入(贵、易变、必须门控):

两级知识供给架构
图 1 · 两级知识供给:SQLite 里的 187 味药 → 全量清单进 system 前缀,详情按相关度门控后动态注入

二、AI token 深解析:从计算方式到省钱配置

这一章把 AI 问诊的 token 账本彻底摊开:每一类 token 从哪来、怎么统计、怎么裁剪、参数怎么配、省了多少、代价是什么。所有数字都来自项目里的实测脚本 verify_ai_context.py 和真实 API 响应,不是估算。

2.1 token 是怎么算的:usage 字段透传成「账单行」

DeepSeek 的 OpenAI 兼容接口在每次响应里都带 usage 对象。后端原样透传给前端:

res = call_api(ai_cfg, messages)
return {"content": res["content"], "usage": res.get("usage"),
        "context_chars": ..., "detail_count": ...}

前端在每条回复气泡下方渲染一行用量小字:

// templates/index.html
const u = m.usage && (m.usage.prompt_tokens || m.usage.total_tokens);
if(u) bits.push(`输入 <b>${m.usage.prompt_tokens||0}</b> / 输出 <b>${m.usage.completion_tokens||0}</b> tokens · 缓存命中 <b>${m.usage.prompt_cache_hit_tokens}</b>`);

这行小字就是「账单」:

  • prompt_tokens——本次输入总量(system + 全部历史 + 本轮提问 + 动态详情块);

  • completion_tokens——本次输出总量(推理模型的思考部分也算在这里);

  • prompt_cache_hit_tokens——输入中命中前缀缓存、按 1/10 价计费的部分。

每天看一眼这行字,比月底看账单能早发现十倍的浪费。它也是后面所有优化的「对照组」——没有计量就没有优化。

2.2 一次提问的上下文结构:谁在缓存里,谁每轮都变

每次发给模型的消息分三段,缓存属性完全不同:

一次提问的消息结构
图 2 · 一次提问的消息结构——system 前缀逐字节一致吃缓存,变化量永远追加在消息最末尾

此处内容已隐藏,「密码可见」