花迹网 · 系统架构
对外站点 https://jobrag.com.cn。本页展开花卉站:检索、站内以图搜图、花神经客服、花友图鉴、花园档案(含配土与问这盆花)、养护小贴士、技能扩展(拍照识病/养护打卡/浇水提醒)。
同机还有票据管家与商务标书 Agent,不画进主图;两边只共用匿名反馈库。
1. 整体架构
浏览器只打花卉站。Nginx 终结 HTTPS,Gunicorn 跑 Flask(Blueprint 架构)。查询与客服共用 query_pipeline,再按层访问本地卡片、花友库或 Dify。
flowchart TB
user["用户浏览器"]
nginx["Nginx 1.18.0 HTTPS :443"]
gunicorn["Gunicorn 26.0.0 2 workers :5000"]
flask["Flask 3.1.3 · Python 3.10.12 · Blueprint 架构"]
subgraph pages["页面"]
home["首页"]
searchPage["结果页 search"]
community["花友图鉴 community"]
garden["花迹花园 garden"]
gardenDetail["花卉档案 garden_detail"]
feedback["意见反馈 feedback"]
end
subgraph apis["核心接口"]
searchApi["POST api-search"]
chatApi["POST api-flower-chat"]
chatHistory["POST api-flower-chat/history/clear"]
uploadApi["POST community"]
fbApi["POST api-feedback"]
end
qp["query_pipeline 清洗 · 认植物 · 意图 · 改写 · 分库"]
local["local_plants L1 养护卡 L2 秋海棠名录"]
catalog["catalog_image_search 站内图库 CLIP"]
contrib["user_contributions 花友库 + CLIP"]
bloom["bloomtrace 档案 · 简报 · 配土 · 识图"]
soil["soil_mix 品种配土建议"]
mem["bt_chat_sessions 会话记忆(含 plant 维度)"]
fbstore["feedback_store 匿名意见"]
knowledge["养护小贴士(前端硬编码)"]
subgraph ext["外部与知识层"]
dash["DashScope qwen-vl-plus"]
weather["和风天气 QWeather"]
clip["本地 CLIP ViT-B-32"]
disease["病害分类模型(规划)"]
dify["Dify 1.16.1"]
weaviate["Weaviate 1.27.0"]
pg["PostgreSQL 15 Dify 库"]
imgs["plants_kb/images 站内带图花卉"]
end
subgraph skills["技能扩展(规划)"]
photoDiag["拍照识病"]
careCheckin["养护打卡"]
waterRemind["浇水提醒"]
growthCompare["生长对比"]
varietyRec["品种推荐"]
pestWarn["病虫害预警"]
end
user --> nginx --> gunicorn --> flask
flask --> pages
flask --> apis
searchApi --> qp
chatApi --> qp
chatApi --> mem
chatHistory --> mem
qp --> local
qp --> contrib
qp --> dify
searchApi --> catalog
chatApi --> catalog
catalog --> clip
catalog --> imgs
searchApi --> dash
chatApi --> dash
uploadApi --> contrib
contrib --> clip
contrib --> dify
garden --> bloom
gardenDetail --> bloom
gardenDetail --> knowledge
bloom --> mem
bloom --> dash
bloom --> weather
bloom --> soil
fbApi --> fbstore
dify --> weaviate
dify --> pg
chatApi -.-> photoDiag
chatApi -.-> careCheckin
careCheckin -.-> waterRemind
waterRemind -.-> weather
箭头是调用方向。Dify 的 Postgres / Weaviate 属于知识平台,不是花卉站自己的业务库。虚线是规划中的技能扩展。
2. 组件与版本
以下为 2026-08-25 在云主机 Ubuntu 22.04.5 上核对的运行版本。
| 层级 | 组件 | 版本 | 作用 |
|---|---|---|---|
| 系统 | Ubuntu | 22.04.5 LTS | 云主机操作系统 |
| 接入 | Nginx | 1.18.0 | HTTPS 终结,反代 127.0.0.1:5000 |
| 运行时 | Python | 3.10.12 | 花卉站解释器 |
| 运行时 | Gunicorn | 26.0.0 | 2 worker,超时 180 秒,systemd job-rag.service,Blueprint 架构 |
| 应用 | Flask / Werkzeug / Jinja2 | Flask 3.1.3 | 页面与 API |
| HTTP | requests | 2.34.2 | 调用 Dify、审核接口 |
| 图像 | Pillow | 12.3.0 | 上传校验、感知哈希、CLIP 预处理 |
| 模型运行 | PyTorch | 2.13.0+cu130 | 本地 CLIP 推理 |
| 模型运行 | torchvision | 0.28.0+cu130 | CLIP 依赖 |
| 模型运行 | transformers | 5.14.1 | 加载 CLIPModel / CLIPProcessor |
| 模型运行 | safetensors | 0.8.0 | 本地权重格式 |
| 安全 | cryptography | 49.0.0 | 上传元数据可选加密、JWT 相关 |
| 知识平台 | Dify | 1.16.1 | 知识库 retrieve、花神经 Chat |
| 知识平台 | dify-plugin-daemon | 0.6.10-local | Dify 插件进程 |
| 知识平台 | dify-sandbox | 0.2.15 | Dify 沙箱 |
| 缓存 | Redis | 6-alpine | Dify 队列与缓存,花卉站不直连 |
| 天气 | 和风天气 QWeather | Geo v2 + 3 日预报 | 花园日志天气缓存 |
3. 数据库与版本
花卉站业务数据全部是 SQLite 3.37.2。Dify 自己另有 PostgreSQL 15 和 Weaviate 向量库。
| 库 | 引擎 / 版本 | 路径 | 主要内容 |
|---|---|---|---|
| bloomtrace.db | SQLite 3.37.2 | /opt/job-rag-system/app/bloomtrace.db | bt_plants 花园档案(含 soil_type / soil_ratio / last_repot_at / weekly_brief)、bt_logs 养护日志(换盆可记配土)、bt_users、bt_weather_cache、bt_conversations 客服日志、bt_chat_sessions 会话记忆(含 owner::plant::plant_id 维度) |
| catalog_image_index.db | SQLite 3.37.2 | /opt/job-rag-system/data/plants_kb/catalog_image_index.db | 站内带图花卉的 CLIP 向量索引。覆盖 plants_kb/images 下全部照片;首页以图搜图优先查此库 |
| community_uploads.db | SQLite 3.37.2 | /opt/job-rag-system/app/community_uploads.db | 花友图鉴:标题、说明、图片路径、感知哈希、CLIP 向量、审核状态、同步 Dify 文档 ID |
| feedback.db | SQLite 3.37.2 | /opt/job-rag-system/data/feedback.db | 匿名意见。花卉站与票据管家共用。字段:应用、类型、正文、页面路径、时间、已读状态。无姓名/手机/邮箱 |
| jobs.db | SQLite 3.37.2 | /opt/job-rag-system/app/jobs.db | 历史职位爬取表,当前花卉站主流程不再使用 |
| Dify 业务库 | PostgreSQL 15-alpine | docker-db_postgres-1 | Dify 应用、知识库元数据、对话。花卉站通过 HTTPS API 访问,不直连 |
| Dify 向量库 | Weaviate 1.27.0 | docker-weaviate-1 | 三个花卉知识库的 text-embedding-v4 向量 |
三个 Dify 数据集:花卉知识库、秋海棠中文知识库、用户上传花卉图鉴。索引方式 high_quality,doc_form=text_model,不是多模态向量库。
4. 模型与使用节点
本地视觉检索用 CLIP(站内图库 + 花友图);识图用通义视觉;知识库向量用通义文本嵌入;配土用本地规则表;客服生成走 Dify 应用「花神经」。
| 模型 | 提供方 / 规格 | 用在哪个节点 | 说明 |
|---|---|---|---|
| CLIP ViT-B/32 | 本地 openai/clip-vit-base-patch32 transformers CLIPModel |
站内图库以图搜图 catalog_image_search 花友图 search_similar_uploads 上传图片 NSFW 审核 |
路径 /opt/job-rag-system/models/openai-clip-vit-base-patch32。站内图库阈值约 0.68;花友图 0.78。不经过 Dify。启动不预热,首次搜图或后台补索引时加载。 |
| qwen-vl-plus | 阿里云 DashScope 兼容模式 | 花园建档识图 identify_plant_from_image 站内图库未命中时补植物名进问句 |
唯一视觉命名模型。图库 CLIP 优先;识名失败且纯图查询会明确说没认出来,不再默默跑偏到错误养护卡。 |
| soil_mix 规则表 | 本地 soil_mix.py | 档案页配土建议、换盆打卡、问这盆花「该配什么土」 | 按品种优先、类别兜底给出配土名称与体积比。不是大模型生成。 |
| text-embedding-v4 | 通义 Tongyi / langgenius/tongyi | Dify 三个花卉知识库的向量化与 hybrid retrieve | L4 检索节点。text_model,不用来做图片 embedding。 |
| 花神经 Chat 应用 | Dify 1.16.1 应用「花神经」mode=chat | /api/flower-chat;档案页「问这盆花」带 plant_id | 绑定三库,对话内检索 + 生成。浇水/一周/黄叶/配土可先走档案规则答,再进 Dify。 |
| qwen-plus | 阿里云 DashScope 兼容模式 | 花友上传文本 Layer 2 语义审核 | 关键词层始终启用。LLM 层走阿里云;未配密钥则跳过语义层,仍拦关键词。 |
| 感知哈希 aHash 8x8 | Pillow 本地 | CLIP 不可用时的相似图回退 上传去重辅助 |
不是神经网络,列在这里便于对照节点。 |
5. 工作模式
花卉站不是每一步都让模型自己选工具。首页查询、花神经、花友上传都是固定流水线:先拦异常输入,再认植物和意图,最后按层取资料。颜色:绿=感知,蓝=规划,金=执行,红=反思。点图上的框看右侧说明,也可自动演示。
flowchart LR perc["1 感知
读字 · 看图 · CLIP"] --> plan["2 规划
清洗、认植物、意图"] plan --> act["3 执行
养护卡 · 知识库 · 花神经"] act --> refl["4 反思
澄清、未命中、敏感词拒绝"] refl -->|字太少或意图不清| perc classDef cPerc fill:#e7efe4,stroke:#315f43,color:#1c2a1e classDef cPlan fill:#e8eef8,stroke:#3d4f7c,color:#1c2a1e classDef cAct fill:#fff6df,stroke:#c4a15a,color:#1c2a1e classDef cRefl fill:#fdecec,stroke:#b45353,color:#1c2a1e class perc cPerc class plan cPlan class act cAct class refl cRefl
检索页只返回资料,不调用花神经生成。花神经在本地卡片未命中后才生成。本机 Ollama / qwen2.5 已停用,视觉和语义审核走阿里云。
6. 用户查询流程
首页搜索框 POST /api/search。先做冷却、字数和敏感词;有图先对站内带图花卉做 CLIP 匹配,再比花友图;都没命中才用通义识名。检索只返回资料,不调用花神经。
flowchart TD
qStart["首页输入"] --> qApi["提交检索接口"]
qApi --> qPace["冷却与频次"]
qPace --> qKw["关键词审核"]
qKw --> qKwOk{"通过?"}
qKwOk -->|否| qBlock["拒绝查询"]
qKwOk -->|是| qShort{"字太少且无图?"}
qShort -->|是| qAsk["请补充花名或照片"]
qShort -->|否| qHas{"有图片?"}
qHas -->|是| qCat["CLIP 站内图库"]
qCat --> qCatOk{"命中?"}
qCatOk -->|是| qCatHit["返回品种+养护"]
qCatOk -->|否| qSim["CLIP 花友图"]
qSim --> qSimOk{"命中?"}
qSimOk -->|是| qHit["返回花友图"]
qSimOk -->|否| qVl["通义视觉识名"]
qVl --> qNameOk{"认出名字?"}
qNameOk -->|是| qMerge["拼进问句"]
qNameOk -->|否| qImgMiss["明确说没认出来"]
qMerge --> qRoute["检索路由"]
qHas -->|否| qRoute
qRoute --> qCard{"本地卡?"}
qCard -->|是| qLocal["返回养护卡或名录"]
qCard -->|否| qL4["知识库 retrieve"]
qL4 --> qScore{"分数大于 0.35?"}
qScore -->|是| qDocs["返回片段"]
qScore -->|否| qMiss["未命中"]
classDef perc fill:#e7efe4,stroke:#315f43,color:#1c2a1e
classDef plan fill:#e8eef8,stroke:#3d4f7c,color:#1c2a1e
classDef act fill:#fff6df,stroke:#c4a15a,color:#1c2a1e
classDef refl fill:#fdecec,stroke:#b45353,color:#1c2a1e
class qStart perc
class qHas perc
class qCat perc
class qSim perc
class qApi plan
class qPace plan
class qKw plan
class qKwOk plan
class qShort plan
class qRoute plan
class qCard plan
class qL4 plan
class qScore plan
class qVl act
class qMerge act
class qCatHit act
class qHit act
class qLocal act
class qDocs act
class qBlock refl
class qAsk refl
class qMiss refl
class qImgMiss refl
class qCatOk refl
class qSimOk refl
class qNameOk refl
7. 花卉智能客服流程
浮窗「花神经」与档案页「问这盆花」都走 POST /api/flower-chat。档案页带 plant_id 时,浇水 / 一周 / 黄叶 / 配土可先用档案规则答;有图先对站内图库再比花友图。需要生成时才把这一盆的档案拼进问句,交给 Dify。
flowchart TD
cWid["花神经或问这盆花"] --> cApi["提交客服接口"]
cApi --> cPace["冷却两秒"]
cPace --> cKw["关键词审核"]
cKw --> cKwOk{"通过?"}
cKwOk -->|否| cBlock["拒绝"]
cKwOk -->|是| cShort{"过短且无图?"}
cShort -->|是| cAsk["请补充"]
cShort -->|否| cValid["校验图片格式"]
cValid --> cMem["装载会话与档案"]
cMem --> cFocus{"带 plant_id?"}
cFocus -->|是| cRule{"浇水一周黄叶配土?"}
cRule -->|是| cArch["档案规则答"]
cRule -->|否| cImg{"有图片?"}
cFocus -->|否| cImg
cImg -->|是| cCat["CLIP 站内图库"]
cCat --> cCatOk{"命中?"}
cCatOk -->|是| cCatHit["返回图库匹配"]
cCatOk -->|否| cSim["CLIP 花友图"]
cSim --> cSimOk{"命中?"}
cSimOk -->|是| cRet["返回花友图"]
cSimOk -->|否| cVl["通义视觉识名"]
cVl --> cRoute["客服路由"]
cImg -->|否| cRoute
cRoute --> cOut{"结果"}
cOut -->|澄清| cClarify["追问不调模型"]
cOut -->|本地卡| cCard["返回养护卡"]
cOut -->|需生成| cBind["拼入这一盆档案"]
cBind --> cDify["交给花神经"]
cDify --> cSave["回写会话ID"]
classDef perc fill:#e7efe4,stroke:#315f43,color:#1c2a1e
classDef plan fill:#e8eef8,stroke:#3d4f7c,color:#1c2a1e
classDef act fill:#fff6df,stroke:#c4a15a,color:#1c2a1e
classDef refl fill:#fdecec,stroke:#b45353,color:#1c2a1e
class cWid perc
class cImg perc
class cCat perc
class cSim perc
class cApi plan
class cPace plan
class cKw plan
class cKwOk plan
class cShort plan
class cValid plan
class cMem plan
class cFocus plan
class cRule plan
class cRoute plan
class cOut plan
class cBind plan
class cVl act
class cArch act
class cCatHit act
class cRet act
class cCard act
class cDify act
class cSave act
class cBlock refl
class cAsk refl
class cClarify refl
class cCatOk refl
class cSimOk refl
闲聊在检索页只给短自我介绍;在花神经里会进入 Dify 多轮对话。登录用户会带上花园档案;匿名用户只有 30 分钟内的会话记忆。发送按钮在上一轮未完成时禁用,答完后仍有 2 秒冷却。
8. 用户记忆
记忆分三层,互不混用。首页检索仍然一轮一问,不读花园档案。客服只在需要花神经生成时注入档案,本地养护卡保持通用。
| 层 | 存什么 | 谁能用 | 多久失效 | 不存什么 |
|---|---|---|---|---|
| 短期会话 | Dify conversation_id,写在 bt_chat_sessions | 同一浏览器会话;登录用户换页仍能续上 | 空闲 30 分钟后新开一轮 | 不把别人的会话 ID 接到当前用户 |
| 花园档案 | 昵称、默认城市、最多 8 盆花的位置·朝向·生长阶段·最近一条日志 | 已登录且调用花神经生成时 | 以当前花园数据为准,每次现查 | 手机、邮箱、密码、IP、他人植株 |
| 对话日志 | bt_conversations 原文问答,给后台统计 | 管理员查看 | 长期保存 | 不会再喂回模型当记忆;注入前缀也不写入此表 |
flowchart TD
mWho["识别登录态"] --> mKind{"花园用户?"}
mKind -->|是| mProf["读取花园档案"]
mProf --> mSum["压缩成档案摘要"]
mKind -->|否| mAnon["匿名浏览器"]
mAnon --> mSid["分配匿名会话号"]
mSum --> mIdle{"空闲超过 30 分钟?"}
mSid --> mIdle
mIdle -->|是| mNew["新开 Dify 会话"]
mIdle -->|否| mKeep["沿用 conversation_id"]
mNew --> mAsk["用户提问"]
mKeep --> mAsk
mAsk --> mNeed{"需要花神经?"}
mNeed -->|否| mLocal["本地卡或花友图"]
mNeed -->|是| mJoin["档案加用户问题"]
mJoin --> mDify["Dify 多轮生成"]
mDify --> mWrite["回写会话ID"]
classDef perc fill:#e7efe4,stroke:#315f43,color:#1c2a1e
classDef plan fill:#e8eef8,stroke:#3d4f7c,color:#1c2a1e
classDef act fill:#fff6df,stroke:#c4a15a,color:#1c2a1e
classDef refl fill:#fdecec,stroke:#b45353,color:#1c2a1e
class mWho perc
class mKind perc
class mAsk perc
class mProf plan
class mSum plan
class mAnon plan
class mSid plan
class mIdle plan
class mNeed plan
class mJoin plan
class mKeep act
class mNew act
class mDify act
class mWrite act
class mLocal refl
匿名用户各自有独立的 Dify user,不再共用 flower-chat-anonymous。刷新页面会向 GET api-flower-chat-session 取回未过期的会话 ID。
9. 技能扩展(规划)
花卉档案页 Agent 技能扩展路线图,按投入产出比分三层:必做(高价值低投入)、值得做(中价值中投入)、长期(高价值高投入)。
| 优先级 | 技能 | 触发方式 | 实现方案 | 预期效果 | 工期 |
|---|---|---|---|---|---|
| P0 | 拍照识病 | 档案页聊天框旁按钮或对话中发图自动触发 | 前端图片上传 + 后端病害分类模型/Dify 视觉节点 + 结合档案品种拼接诊断 | 准确率>75%,用户痛点最强易传播 | 1.5 周 |
| P0 | 养护打卡 | 对话中说"浇水了/施肥了"自动识别,或 Agent 主动问 | Dify 输出结构化 JSON → 后端解析写入养护日志表 | 养护日志填写率提升 3 倍 | 1 周 |
| P1 | 智能浇水提醒 | 后端定时任务 + 天气 API,满足条件时推送 | 每日读取花园数据 → 和风天气 API → 品种需水频率 + 上次浇水时间 → 计算提醒 | 日活提升,打开率>40% | 1 周 |
| P1 | 生长对比 | 档案页新增"生长时间线"Tab,上传多张照片后 Agent 对比分析 | 照片时间线组件 + Dify 视觉分析节点对比不同时期状态 | 增强成就感,促进分享 | 1.5 周 |
| P1 | 品种推荐 | 首页/档案页"不知道养什么?"入口,3 步问答 | 问卷式交互 → 用户条件注入 Dify → RAG 检索匹配品种 | 新用户转化率提升 | 1.5 周 |
| P1 | 病虫害预警 | 后端定时任务,结合天气 + 季节 + 用户品种主动推送 | 规则引擎(湿度>80%+ 月季→白粉病预警)+ Dify 生成个性化文案 | 增强粘性和专业感 | 1.5 周 |
| P2 | 联网搜索 | Agent 能搜索最新花友经验/论文/论坛 | Dify 工具节点调用 Bing/Google API + 摘要 + 可信度过滤 | 搜索结果质量需控制 | 3 周 |
| P2 | 语音交互 | 语音提问 + 语音回复,适合园艺场景(手脏时) | ASR/TTS 集成(讯飞/阿里云)+ 方言支持 + 延迟优化 | 响应延迟需优化 | 3 周 |
| P2 | AR 识花 | 摄像头实时识别 + 叠加养护信息 | 方案 A:H5 拍照识别(2 周);方案 B:AR.js 或原生 AR SDK(6 周) | 模型推理速度需优化 | 2-6 周 |
| P2 | 多模态诊断 | 结合照片 + 文字描述 + 环境数据综合诊断 | 多模态模型调优(GPT-4V 或本地)+ 环境数据注入 | 准确率保障 | 4 周 |
技术架构推荐:Dify 做大脑(意图识别 + 结果组织),本地函数做手脚(数据读写 + 确定性计算),前端做脸面(交互体验 + 可视化)。如果只做 3 个,优先做拍照识病、养护打卡、浇水提醒。
10. 用户上传流程
花友图鉴先审核再入库。文本关键词始终拦;语义审核改走阿里云 qwen-plus,不再调用本机 Ollama。
flowchart TD
uForm["填写标题说明和照片"] --> uPost["提交图鉴接口"]
uPost --> uRate["IP 频次限制"]
uRate --> uKw["关键词审核"]
uKw --> uKwOk{"通过?"}
uKwOk -->|否| uRej["拒绝 不入库"]
uKwOk -->|是| uLlm["qwen-plus 语义审核"]
uLlm --> uImg["CLIP 图片审核"]
uImg --> uImgOk{"通过?"}
uImgOk -->|否| uRej
uImgOk -->|是| uStore["校验并落盘"]
uStore --> uDup{"疑似重复?"}
uDup -->|是| uRej
uDup -->|否| uSql["写入 SQLite"]
uSql --> uDify["同步 Dify 花卉图鉴"]
uDify --> uClip["异步 CLIP 向量"]
uClip --> uDone["前台展示"]
classDef perc fill:#e7efe4,stroke:#315f43,color:#1c2a1e
classDef plan fill:#e8eef8,stroke:#3d4f7c,color:#1c2a1e
classDef act fill:#fff6df,stroke:#c4a15a,color:#1c2a1e
classDef refl fill:#fdecec,stroke:#b45353,color:#1c2a1e
class uForm perc
class uPost plan
class uRate plan
class uKw plan
class uKwOk plan
class uLlm plan
class uImg plan
class uStore act
class uSql act
class uDify act
class uClip act
class uDone act
class uRej refl
class uImgOk refl
class uDup refl