大神经的花园 · 系统架构

对外站点 https://jobrag.com.cn。本页只展开花卉站:检索、花神经客服、花友图鉴、花园建档。 同机还有票据管家与商务标书 Agent,不画进主图;两边只共用匿名反馈库。

1. 整体架构

浏览器只打花卉站。Nginx 终结 HTTPS,Gunicorn 跑 Flask。查询与客服共用 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"]

  subgraph pages["页面"]
    home["首页"]
    searchPage["结果页 search"]
    community["花友图鉴 community"]
    garden["花迹花园 garden"]
    feedback["意见反馈 feedback"]
  end

  subgraph apis["核心接口"]
    searchApi["POST api-search"]
    chatApi["POST api-flower-chat"]
    uploadApi["POST community"]
    fbApi["POST api-feedback"]
  end

  qp["query_pipeline 清洗 · 认植物 · 意图 · 改写 · 分库"]
  local["local_plants L1养护卡 L2秋海棠名录"]
  contrib["user_contributions 花友库 + CLIP"]
  bloom["bloomtrace 花园档案 + 识图"]
  mem["bt_chat_sessions 会话记忆"]
  fbstore["feedback_store 匿名意见"]

  subgraph ext["外部与知识层"]
    dash["DashScope qwen-vl-plus"]
    weather["和风天气 QWeather"]
    clip["本地 CLIP ViT-B-32"]
    dify["Dify 1.16.1"]
    weaviate["Weaviate 1.27.0"]
    pg["PostgreSQL 15 Dify库"]
  end

  user --> nginx --> gunicorn --> flask
  flask --> pages
  flask --> apis
  searchApi --> qp
  chatApi --> qp
  chatApi --> mem
  qp --> local
  qp --> contrib
  qp --> dify
  searchApi --> clip
  chatApi --> clip
  searchApi --> dash
  chatApi --> dash
  uploadApi --> contrib
  contrib --> clip
  contrib --> dify
  garden --> bloom
  bloom --> mem
  bloom --> dash
  bloom --> weather
  fbApi --> fbstore
  dify --> weaviate
  dify --> pg
        

箭头是调用方向。Dify 的 Postgres / Weaviate 属于知识平台,不是花卉站自己的业务库。

2. 组件与版本

以下为 2026-08-15 在云主机 Ubuntu 22.04.5 上核对的运行版本。

层级组件版本作用
系统Ubuntu22.04.5 LTS云主机操作系统
接入Nginx1.18.0HTTPS 终结,反代 127.0.0.1:5000
运行时Python3.10.12花卉站解释器
运行时Gunicorn26.0.02 worker,超时 180 秒,systemd job-rag.service
应用Flask / Werkzeug / Jinja2Flask 3.1.3页面与 API
HTTPrequests2.34.2调用 Dify、审核接口
图像Pillow12.3.0上传校验、感知哈希、CLIP 预处理
模型运行PyTorch2.13.0+cu130本地 CLIP 推理
模型运行torchvision0.28.0+cu130CLIP 依赖
模型运行transformers5.14.1加载 CLIPModel / CLIPProcessor
模型运行safetensors0.8.0本地权重格式
安全cryptography49.0.0上传元数据可选加密、JWT 相关
知识平台Dify1.16.1知识库 retrieve、花神经 Chat
知识平台dify-plugin-daemon0.6.10-localDify 插件进程
知识平台dify-sandbox0.2.15Dify 沙箱
缓存Redis6-alpineDify 队列与缓存,花卉站不直连
天气和风天气 QWeatherGeo 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 花园档案、bt_logs 养护日志、bt_users、bt_weather_cache、bt_conversations 客服日志、bt_chat_sessions 会话记忆
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
花友上传入库后的向量化
以图搜图 search_similar_uploads
上传图片 NSFW 审核
路径 /opt/job-rag-system/models/openai-clip-vit-base-patch32。相似度阈值 0.78。不经过 Dify。
qwen-vl-plus 阿里云 DashScope 兼容模式 花园建档识图 identify_plant_from_image
检索/客服有图时把植物名补进问句
唯一视觉模型。用于「这是什么植物」的命名,不直接生成养护长文。不再回退到本机 Ollama。
text-embedding-v4 通义 Tongyi / langgenius/tongyi Dify 三个花卉知识库的向量化与 hybrid retrieve L4 检索节点。text_model,不用来做图片 embedding。
花神经 Chat 应用 Dify 1.16.1 应用「花神经」mode=chat /api/flower-chat 在本地卡片未命中之后 绑定三库,对话内检索 + 生成。检索页不调用此应用。
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。先做冷却、字数和敏感词;有图先比花友相似图,没中再识图得名。检索只返回资料,不调用花神经。

点框看说明。 感知规划执行反思
flowchart TD
  qStart["首页输入"] --> qApi["提交检索接口"]
  qApi --> qPace["冷却与频次"]
  qPace --> qKw["关键词审核"]
  qKw --> qKwOk{"通过?"}
  qKwOk -->|否| qBlock["拒绝查询"]
  qKwOk -->|是| qShort{"字太少且无图?"}
  qShort -->|是| qAsk["请补充花名或照片"]
  qShort -->|否| qHas{"有图片?"}
  qHas -->|是| qSim["CLIP 花友图"]
  qSim --> qSimOk{"命中?"}
  qSimOk -->|是| qHit["返回花友图"]
  qSimOk -->|否| qVl["通义视觉识名"]
  qVl --> qMerge["拼进问句"]
  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 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 qHit act
  class qLocal act
  class qDocs act
  class qBlock refl
  class qAsk refl
  class qMiss refl
  class qSimOk refl
            

7. 花卉智能客服流程

浮窗「花神经」走 POST /api/flower-chat。先做冷却、最短二字和敏感词;装载会话与花园档案后,本地卡片或花友图能答就直接答。需要生成时才把档案拼进问句,交给 Dify。

点框看说明。 感知规划执行反思
flowchart TD
  cWid["花神经浮窗"] --> cApi["提交客服接口"]
  cApi --> cPace["冷却两秒"]
  cPace --> cKw["关键词审核"]
  cKw --> cKwOk{"通过?"}
  cKwOk -->|否| cBlock["拒绝"]
  cKwOk -->|是| cShort{"过短且无图?"}
  cShort -->|是| cAsk["请补充"]
  cShort -->|否| cValid["校验图片格式"]
  cValid --> cMem["装载会话与档案"]
  cMem --> cImg{"有图片?"}
  cImg -->|是| 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 cSim perc
  class cApi plan
  class cPace plan
  class cKw plan
  class cKwOk plan
  class cShort plan
  class cValid plan
  class cMem plan
  class cRoute plan
  class cOut plan
  class cBind plan
  class cVl act
  class cRet act
  class cCard act
  class cDify act
  class cSave act
  class cBlock refl
  class cAsk refl
  class cClarify 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. 用户上传流程

花友图鉴先审核再入库。文本关键词始终拦;语义审核改走阿里云 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
            

10. 输入预处理流程

搜索和客服共用 query_guardquery_pipeline.preprocess。先截断、拦敏感词和过短输入,再认植物、认意图、改写分库。

点框看说明。 感知规划执行反思
flowchart TD
  pRaw["原始问句"] --> pClip["截到 500 字"]
  pClip --> pPace["冷却与频次"]
  pPace --> pKw["关键词审核"]
  pKw --> pKwOk{"通过?"}
  pKwOk -->|否| pBlock["拒绝"]
  pKwOk -->|是| pShort{"有效字不足 2?"}
  pShort -->|是| pAsk["请补充"]
  pShort -->|否| pZw["去零宽字符和 HTML"]
  pZw --> pInj["去掉注入句"]
  pInj --> pPlant["认植物名"]
  pPlant --> pIntent["认意图"]
  pIntent --> pRew["改写问句"]
  pRew --> pRoute["按层分库"]
  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 pRaw perc
  class pClip plan
  class pPace plan
  class pKw plan
  class pKwOk plan
  class pShort plan
  class pZw plan
  class pInj plan
  class pPlant plan
  class pIntent plan
  class pRew plan
  class pRoute act
  class pBlock refl
  class pAsk refl
            
步骤函数做什么
G1 截断query_guard.clip_query最多 500 字;输入框 maxlength 同步限制
G2 节奏enforce_query_pace搜索 1 秒、客服 2 秒冷却;每 IP 每分钟 20 次
G3 敏感词moderate_text与花友上传同一套关键词;不把命中词回显给用户
G4 最短字effective_length无图时至少 2 个汉字或字母
N1 清洗preprocess去零宽字符、HTML、注入句
N2 认植物extract_plant常见名/别名/学名,秋海棠中文名;最长匹配优先
N3 认意图classify_intentcare / pest / identify / chitchat / unclear / general
N4 改写rewrite_query补标准中文名和意图关键词
N5 分库route_querysearch 只检索;chat 本地未中则花神经

11. RAG 路由矩阵

植物库 × 意图/api/search 检索页/api/flower-chat 花神经
常见花卉 × 养护/鉴定L1 养护卡同一张养护卡
常见花卉 × 病虫害L4 花卉知识库片段改写句 → 花神经
秋海棠 × 鉴定L2 名录卡片同一张名录卡片
秋海棠 × 养护/病虫害L4 秋海棠库 + 用户图鉴改写句 → 花神经(不用 L2 空字段当养护)
未识别植物L1 → L4 → 花友补充 → miss同左;仍无则花神经生成
意图不清 / 一字乱敲澄清或请补充澄清或请补充,不调 Dify
闲聊短自我介绍花神经对话
敏感词拒绝查询拒绝发送
有图花友相似图;否则识图名进入同一矩阵同左;需生成时把图一并交给花神经
数据模型/查法可以回答禁止
L1常见花卉卡约 250 种规则匹配,无向量模型光照、浇水、养护要点拿它鉴定秋海棠名录
L2iBegonias 秋海棠名录规则匹配,无向量模型品种名、形态字段空 description 当养护百科
L3花友图鉴 SQLite关键词;CLIP 相似图补充实例和照片覆盖 L1/L2 权威答案
L4Dify 三个数据集text-embedding-v4 hybrid retrieve,阈值 0.35病虫害、深度养护把空记录整库向量化
L5花神经 ChatDify 对话内检索 + 生成;登录时注入花园档案摘要多轮、闲聊、按用户在养花给建议无依据时编造品种或药方;把档案当全站知识

12. 安全基线

搜索和客服现在与花友上传共用关键词审核。语义审核和识图都走阿里云,不再依赖你本机上的 Ollama / qwen2.5。

现网做法
长度输入框与后端都限制 500 字
最短有效输入无图时至少 2 个汉字或字母;客服一个字一个字回车会被拦住
冷却搜索 1 秒、客服 2 秒;每 IP 每通道每分钟 20 次
敏感词色情 / 政治 / 暴恐关键词;命中只提示换题,不回显词表
上传语义审核阿里云 qwen-plus;未配密钥则只走关键词
识图仅 DashScope qwen-vl-plus,失败即报错,不再回退 Ollama
客服图片仅 JPG/PNG,单张 5MB,一次最多 5 张
用户记忆会话 ID 按登录用户或匿名浏览器隔离;花园档案不含手机邮箱;注入前缀只给 Dify,不写入 bt_conversations