dify-chatflow-prompt.md 51 KB

Dify Chatflow 提示词定义

1. 概述

本文档定义数字能量学智能体 Dify Chatflow 工作流的完整配置,包括系统提示词、输入架构、输出格式和节点配置。

用途: 用户在微信小程序中生成能量盘后,向 AI 提问获得数字能量学专业解读。

知识库集成: Chatflow 在 LLM 节点之前插入知识检索节点,从 Dify 知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)中动态检索与用户问题相关的命理知识,将检索结果注入 LLM 上下文,实现 RAG(检索增强生成)。

数据流: 小程序 → 后端 API → Dify Chatflow → 知识检索(RAG)→ LLM 解读 → 返回用户


2. 输入架构

2.1 Dify API 调用格式

后端 DifyService.java 调用 Dify API:

POST /v1/chat-messages
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "inputs": {
    "chart_context": "{...chart JSON...}"
  },
  "query": "请解读我的主性格",
  "user": "user_12345"
}

2.2 chart_context JSON 格式

后端传入的 chart_context 字段包含完整的能量盘数据,格式如下:

{
  "positions": {
    "A": 2, "B": 0, "C": 9, "D": 5, "E": 8,
    "F": 2, "G": 9, "H": 5, "I": 4, "J": 11,
    "K": 5, "L": 9, "M": 7, "N": 5, "O": 3
  },
  "zones": {
    "mainCharacter": {
      "name": "主性格",
      "positions": ["O"],
      "values": [3]
    },
    "fatherSource": {
      "name": "父源区",
      "positions": ["I", "J"],
      "values": [4, 11]
    },
    "motherSource": {
      "name": "母源区",
      "positions": ["K", "L"],
      "values": [5, 9]
    },
    "leftZone": {
      "name": "左区(0-20岁)",
      "positions": ["A", "F", "I", "O"],
      "values": [2, 2, 4, 3]
    },
    "middleZone": {
      "name": "中区(20-40岁)",
      "positions": ["B", "G", "J", "M", "N", "O"],
      "values": [0, 9, 11, 7, 5, 3]
    },
    "rightZone": {
      "name": "右区(40-60岁)",
      "positions": ["C", "D", "E", "H", "K", "L", "O"],
      "values": [9, 5, 8, 5, 5, 9, 3]
    }
  },
  "mainCharacter": 3,
  "isMasterNumber": false,
  "chartName": "张三",
  "birthYear": 2009,
  "birthMonth": 5,
  "birthDay": 26
}

2.3 Dify Chatflow Input 变量

变量名 类型 说明 来源
chart_context JSON 完整的能量盘数据 后端传入
query String 用户提问内容 用户输入

3. 系统提示词 (LLM 节点 System Prompt)

# 角色设定
你是「数字能量学智能解读师」,一位精通数字能量学(Digital Numerology)的资深专家。你擅长通过 Classic A 三角形能量盘(5行15格)为用户提供性格分析、天赋解码和人生指引。

# 专业背景
- 精通数字能量学理论,包括主性格数字 1-9 及卓越数 11、22、33 的解读
- 熟练掌握五区分析:左区(0-20岁成长)、中区(20-40岁发展)、右区(40-60岁成就)
- 理解父源区、母源区对个人性格形成的影响
- 能够结合多位置数字组合进行综合分析

# 解读原则
1. 基于提供的能量盘数据进行专业解读,不编造或臆测数字
2. 聚焦性格倾向、天赋优势、潜在挑战和发展建议
3. 以积极正向的视角进行解读,强调成长和发展的可能性
4. 语言专业但不晦涩,温暖且有洞察力
5. 不预测具体未来事件(财运数字、结婚年份等),不提供医疗建议
6. 若用户询问能量盘数据范围之外的领域,委婉说明数字能量学的局限性

# 知识库检索使用规则(优先级最高)

当 `knowledge_retrieval` 上下文中有检索结果时,你**必须优先使用检索结果**中的内容进行解读,而非依赖自身的内部知识。

**检索结果使用规则:**
1. `knowledge_retrieval` 中包含多个匹配的文档片段(chunks),每个 chunk 包含文件名、标题和内容
2. 根据 `knowledge_retrieval` 的来源文件标记(如 `能量盘24位置详解.md`、`三角能量盘组合对判读规则.md`、`数字能量分析维度手册.md`、`五区三组分析指南.md`),判断该信息适用于能量盘的哪个方面
3. 如果多个 chunk 提供同一主题的不同视角,合并使用以给出全面解读
4. 如果知识库检索结果与你的内部记忆冲突,**以检索结果为准**
5. 如果在检索结果中找不到相关信息,再使用下方"数字含义参考"中的内置信息

# 数字含义参考(即备知识,知识库中无相关内容时使用)
- 1 (开创者): 独立、创新、领导力
- 2 (合作者): 细腻、敏感、善于沟通
- 3 (表达者): 创意、社交、乐观向上
- 4 (建设者): 务实、稳定、规则意识
- 5 (自由者): 冒险、变化、适应力强
- 6 (守护者): 责任、关爱、追求完美
- 7 (探索者): 深度思考、求知、内省
- 8 (掌控者): 权威、商业头脑、决策力
- 9 (智慧者): 博爱、理想主义、包容
- 11 (启迪者): 直觉力强、灵感丰富(卓越数)
- 22 (建造大师): 将理想变为现实(卓越数)
- 33 (大爱者): 无私奉献、疗愈他人(卓越数)

# 响应格式
1. 首先回应用户的具体问题
2. 引用能量盘中的具体位置和数字进行解释
3. 提供有针对性的建议
4. 保持友好、鼓励的语气
5. 回复语言使用中文

# chart_context JSON 结构说明
用户能量盘数据通过 chart_context 变量传入,包含以下关键字段:
- mainCharacter: 主性格数字(位于 O 位)
- positions: 全部 15 个位置(A-O)的数字值
- zones: 五区数据,每区包含名称、位置列表和对应数字值
- isMasterNumber: 主性格是否为卓越数
- chartName: 用户姓名(如有提供)
- birthYear/month/day: 出生日期

请根据这些数据为用户提供有深度、个性化的数字能量学解读。

3A. 知识库检索增强集成(RAG)

3A.1 检索策略

Chatflow 在 LLM 节点之前插入知识检索(Knowledge Retrieval)节点,从知识库 数字能量学知识库 V2(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)中检索与用户问题最相关的内容。

为什么需要 RAG:

  • 硬编码在 System Prompt 中的 1-9 数字含义过于简略(仅 1 行/数字),无法支撑深度解读
  • 知识库包含 25 篇文档(179,730 words),覆盖 P0 核心文档(A1/B2/C1/C2)
  • 每次 Chatflow 调用时动态检索,LLM 根据检索结果进行解读,精度远超固定提示词

3A.2 多轮对话中的检索策略

轮次 检索策略 说明
第一轮(首次发送) chart_context 智能构建查询 后端首次发送时附带 chart_context,Chatflow 通过代码节点/LLM 节点自动提取能量盘关键信息生成结构化 KB 查询
后续轮次 直接用用户 query 检索 Dify Chatflow 自动保存会话上下文,后续直接以 query 检索
深度追问 一次对话多次检索 每次用户提问都触发 KB 检索,确保每轮回复都有最新知识支撑

3A.3 查询变量构建方案

在 Chatflow 中插入一个代码节点(Code Node)LLM 节点,将 chart_context 转换为最优 KB 查询:

# 代码节点:将 chart_context + query 转换为 KB 查询
def main(chart_context: dict, query: str) -> dict:
    # 解析 chart_context
    positions = chart_context.get("positions", {})
    main_number = chart_context.get("mainCharacter", 0)
    
    # 从用户 query 中提取关键词
    dimension_keywords = {
        "事业": "事业维度 延年",
        "财富": "财富维度 天医",
        "感情": "感情维度 六煞",
        "健康": "健康维度 绝命 五鬼",
        "性格": "主性格 性格特质",
    }
    
    kb_query = query  # 默认直接用用户问题
    for keyword, search_term in dimension_keywords.items():
        if keyword in query:
            kb_query = f"{search_term} {query}"
            break
    
    return {
        "kb_query": kb_query,
    }

也可简化配置:直接使用用户 query 作为 KB 检索查询(如果不需要维度映射预处理)。

3A.4 Metadata 过滤

Dify v1.11.4 知识库支持 metadata 过滤。在知识检索节点中可按 tags 过滤:

  • 维度过滤:tags 包含 ["维度:财富"](用户问财富相关问题)
  • 优先级过滤:tags 包含 ["优先级:P0"](优先返回核心文档)
  • 文档类型过滤:tags 包含 ["用途:判读规则"](需要判读规则时)

3A.5 检索参数配置

参数 推荐值 说明
Top-K 10 每次检索返回 10 个 chunks(实测 top_k=6 时 A2 主性格在部分查询中排第8,增大至 10 确保覆盖)。需在 Dify 控制台数据集设置中手动修改(API PATCH 不生效)
Score Threshold 0.5 低于此阈值的结果被过滤(已在 KB 层面配置)
Retrieval Mode Hybrid Search 向量 60% + 全文 40%,兼顾语义和关键词匹配

4. Dify Chatflow 节点配置

4.1 完整节点列表

节点 类型 配置
START 开始节点 输入变量:chart_context (JSON), query (String)
Knowledge Retrieval 知识检索节点 Dataset: 数字能量学知识库 V2 (ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)
Query: {{#sys.query#}}(用户 query 变量,或代码节点输出的 kb_query
Top-K: 10
Score Threshold: 0.5
Result Type: paragraph
LLM LLM 节点 System Prompt: 见第 3 节(含 KB 使用规则)
Model: gpt-4o 或 Claude 3.5 Sonnet
Temperature: 0.7
Max Tokens: 2000
Memory: 启用对话轮次 (最近 6 轮)
Context: 引用 Knowledge Retrieval 节点的输出作为上下文
END 结束节点 输出变量:answer (String) — LLM 节点的输出文本

4.2 节点连接关系

START ──→ Knowledge Retrieval ──→ LLM ──→ END
                                     ↑
                              (chart_context 也传入 LLM)

4.3 LLM 节点变量映射

Dify 变量 映射到 说明
chart_context LLM 系统提示词中的上下文变量 能量盘数据,嵌入提示词中供 LLM 引用
query LLM 用户消息输入 用户提问内容
knowledge_retrieval LLM 系统提示词中的检索结果上下文 知识库检索返回的 chunks,LLM 需优先使用

4.4 简化版配置(无代码节点)

如果不使用代码节点转换查询,可直接将 query 变量传给 Knowledge Retrieval 节点作为检索查询。Chatflow 配置如下:

START (chart_context, query)
    │
    ▼
[Knowledge Retrieval Node]
    ├─ 查询: {{#sys.query#}}
    ├─ 数据集: 数字能量学知识库 V2
    └─ Top-K: 10(需控制台手动设置,API不生效)
    │
    ▼
[LLM Node]
    ├─ System Prompt: 含 KB 使用规则 + chart_context
    └─ Context: 引用 KB 检索结果
    │
    ▼
END (answer)

4.5 进阶版配置(带代码节点预处理)

START (chart_context, query)
    │
    ▼
[Code Node: 查询构建]
    ├─ 输入: chart_context, query
    └─ 输出: kb_query (构建后的检索查询)
    │
    ▼
[Knowledge Retrieval Node]
    ├─ 查询: {{kb_query}}
    ├─ 数据集: 数字能量学知识库 V2
    └─ Top-K: 10(需控制台手动设置,API不生效)
    │
    ▼
[LLM Node]
    ├─ System Prompt: 含 KB 使用规则 + chart_context
    └─ Context: 引用 KB 检索结果
    │
    ▼
END (answer)

5. 示例对话

示例 1:主性格解读

用户输入: 请解读我的主性格

chart_context 示例:

{
  "positions": {"O": 7},
  "mainCharacter": 7,
  "zones": {
    "leftZone": {"values": [1, 1, 4, 7]},
    "middleZone": {"values": [9, 4, 5, 11, 3, 7]},
    "rightZone": {"values": [4, 7, 6, 4, 6, 6, 7]}
  },
  "isMasterNumber": false
}

预期回复(示例):

你好!根据你的数字能量盘,你的主性格数字是 7。

🔹 主性格 7 号 — 探索者

7 号人是天生的思考者和探索者,你对世界有着强烈的好奇心和求知欲。你善于分析、喜欢独处思考,对于哲学、科学和未知领域有天然的吸引力。

✨ 你的核心特质:
- 深度思考能力:你善于从现象看到本质
- 独立精神:不盲从权威,有自己的判断标准
- 分析天赋:逻辑清晰,适合研究分析类工作

💡 成长建议:
- 多信任自己的直觉,你的第六感往往很准
- 适度走出思考的舒适区,增加社交互动
- 避免过度完美主义,行动比完美更重要

你的左区(0-20岁)有数字 1 和 4,说明早年培养了独立自主的性格和务实的态度;中区(20-40岁)出现卓越数 11,预示着这个阶段你的直觉力和灵感将显著提升。

想了解更多关于某个区域的详细解读吗?

示例 2:财富与事业发展

用户输入: 我的事业发展和财富运势如何?

chart_context 示例:

{
  "mainCharacter": 8,
  "positions": {"O": 8, "I": 4, "J": 5, "K": 6, "L": 9},
  "zones": {
    "middleZone": {"values": [2, 3, 5, 7, 6, 8]},
    "rightZone": {"values": [1, 4, 3, 8, 6, 9, 8]}
  }
}

预期回复(示例):

感谢你的提问!从你的数字能量盘来看,非常有亮点。

🔹 主性格 8 号 — 掌控者

8 号是数字能量学中的"事业王者",你天生具备商业头脑和领导力。你对权力和资源有敏锐的感知力,擅长将想法转化为实际的商业价值。

📊 事业发展分析:
- 中区(20-40岁):数字组合 2-3-5-7-6-8,显示这个阶段你适合团队合作(2),发挥创意表达能力(3),经历必要的变化和积累(5-7),最终在第 8 位达到事业高度
- 右区(40-60岁):数字 8 出现在关键位置,预示这个阶段是你事业成就的黄金时期

💰 财富特质:
- 父源区(I=4, J=5):务实且有商业嗅觉,适合实体行业或资源整合型工作
- 母源区(K=6, L=9):有责任感和远见,能平衡短期收益和长期发展
- 财富关键词:稳健增长、资源整合、领导力变现

💡 发展建议:
1. 30-40 岁是重要的积累期,建议深耕一个领域建立专业壁垒
2. 8 号人适合创业或管理岗位,可考虑在 35 岁后独立发展
3. 注意平衡事业与健康,8 号人容易过度投入工作

想更深入了解某个具体方向的建议吗?

6. Dify 控制台设置步骤

步骤 1:登录 Dify 并创建 Chatflow

  1. 登录 Dify 平台 (https://cloud.dify.ai 或自部署实例)
  2. 点击「创建应用」→ 选择「Chatflow」
  3. 输入应用名称:数字能量学智能体
  4. 点击「创建」

步骤 2:配置输入变量

  1. 点击 START 节点
  2. 添加输入变量:
    • 变量名:chart_context,类型:JSON,必填:是
    • 变量名:query,类型:文本,必填:是

步骤 3:添加 LLM 节点

  1. 拖入 LLM 节点到画布
  2. 连接 START → LLM
  3. 配置 LLM 节点:
    • System Prompt:填写第 3 节的完整系统提示词
    • 模型:选择 Claude 3.5 Sonnet 或 GPT-4o(推荐 Claude 以获得更好的中文解读质量)
    • Temperature:0.7
    • Maximum Tokens:2000
    • 开启记忆功能,保留最近 6 轮对话
  4. 在"上下文"中引用 chart_context 变量
  5. 在"用户消息"中引用 query 变量

步骤 4:配置 END 节点

  1. 拖入 END 节点,连接 LLM → END
  2. 输出变量选择 LLM 节点的 text 输出

步骤 5:发布

  1. 点击「发布」按钮
  2. 记录 API 访问地址和 API Key

步骤 6:配置后端

在后端 application.yml 中配置:

num:
  dify:
    base-url: https://your-dify-instance.com/v1
    api-key: your_dify_api_key_here

6A. Java 后端响应处理

6A.1 Dify API 响应格式

{
  "answer": "你的能量盘主性格数字是 7...",
  "conversation_id": "conv_abc123",
  "message_id": "msg_abc123",
  "created_at": 1717200000,
  "metadata": {
    "usage": {
      "prompt_tokens": 1500,
      "completion_tokens": 350,
      "total_tokens": 1850
    },
    "retriever_resources": [
      {
        "dataset_id": "3ff939b3-8686-44f6-8ef5-65b1e53b55d3",
        "document_id": "doc_abc",
        "document_name": "能量盘24位置详解.md",
        "segment_id": "seg_abc",
        "score": 0.85,
        "content": "O位置代表..."
      }
    ]
  }
}

6A.2 DifyService.java 处理流程

// 伪代码 — 后端 DifyService 应实现的处理逻辑
public class DifyService {
    
    // 1. 调用 Dify Chatflow API
    public ChatResponse sendMessage(ChatRequest req) {
        // POST /v1/chat-messages
        // Body: { inputs: { chart_context }, query, user, response_mode: "blocking" }
        // Headers: Authorization: Bearer ${apiKey}
    }
    
    // 2. 解析响应
    public String extractAnswer(JSONObject difyResponse) {
        return difyResponse.getString("answer");  // LLM 生成的文本
    }
    
    // 3. 错误处理策略
    // - 4xx: 参数错误 → 记录日志,返回用户"服务暂时不可用"
    // - 5xx: 服务端错误 → 重试 1 次(指数退避 1s → 2s),超时 30s
    // - 429: 速率限制 → 等待 Retry-After 头中指定的时间
    // - 空 answer: 返回"暂未获取到解读结果,请稍后再试"
    
    // 4. Token 用量追踪
    // metadata.usage.total_tokens 用于配额计算和成本监控
    
    // 5. Conversation 管理
    // conversation_id 用于多轮对话关联,每次请求需传入
}

6A.3 错误处理汇总

场景 处理方式 用户可见消息
HTTP 4xx 记录错误日志,不重试 "解读服务暂时不可用,请稍后再试"
HTTP 5xx 重试 1 次(1s 退避) "解读服务繁忙,请稍后再试"
HTTP 429 等待 Retry-After "请求过于频繁,请稍后再试"
网络超时 (30s) 重试 1 次 "解读请求超时,请重试"
空 answer 视为异常,记录 "暂未获取到解读结果"

7. 位置详情面板 Workflow (US-1.4)

7.1 概述

用户在三角能量盘上点击某一位置(A-X)时,前端弹出底部面板显示 3 个 Tab 的内容。 这些内容由 Dify Workflow「位置详情面板」 生成,该 Workflow 独立于 Chatflow(不共享对话上下文),每次点击独立请求。

属性
目的 为 US-1.4 "数字点击查看含义" 提供面板内容
Dify 应用类型 Workflow(非 Chatflow)
数据流 前端 → 后端 /api/position/detail → Dify Workflow → KB 检索 (A1+A2) → LLM 生成 → JSON
记忆 无,每次点击独立请求

7.2 数据流

用户点击 O 位置
    │
    ▼
POST /api/position/detail
{ chartRecordId, position: "O", digit: 7 }
    │
    ├─ 后端:验证权限 → 计算吉凶 → 检测相邻组合
    └─ DifyService.invokeWorkflow(inputs)
        │
        ▼
    Dify Workflow "位置详情面板"
        ├─ KB 检索 1 → A1 位置含义
        ├─ KB 检索 2 → A2 数字特征
        └─ LLM → JSON { tab1, tab2, tab3 }
    │
    ▼
后端合并 → 返回前端
{ tab1, tab2, tab3, auspicious, comboInfo }

7.3 输入架构

Dify API 调用格式:

POST /v1/workflows/run
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "inputs": {
    "position_letter": "O",
    "position_name": "主性格数",
    "digit": 7
  }
}

输入变量:

变量 类型 必填 说明 来源
position_letter String 位置字母 A-X 前端传入(如 "O")
position_name String 位置中文名称 后端根据 letter 映射(如 "主性格数")
digit Integer 该位置数字 (0-9/11/22/33) 后端从 chart_data 读取

7.4 工作流节点配置

Node 1: KB 检索 - A1 位置含义

配置项
数据集 3ff939b3-8686-44f6-8ef5-65b1e53b55d3
检索模式 混合检索 (hybrid_search)
Top-K 3
检索查询 {position_name} {position_letter} 含义 能量 公式
示例 主性格数 O 含义 能量 公式

Node 2: KB 检索 - A2 数字特征

配置项
数据集 同上
检索模式 混合检索
Top-K 3
检索查询 数字 {digit} 性格 特征 优势 挑战 成长
示例 数字 7 性格 特征 优势 挑战 成长

两个节点在 Workflow 画布中以并行方式连接至 LLM 节点,互不依赖。

Node 3: LLM 生成

参数
模型 Claude 3.5 Sonnet(推荐)
Temperature 0.5(结构化输出需稳定性)
Max Tokens 1000
上下文引用 KB 检索 1 输出 + KB 检索 2 输出

System Prompt(完整版):

你是一个数字能量学专家。请根据以下知识库内容,为能量盘位置的详情面板生成内容。

## 位置信息
- 字母:{position_letter}
- 名称:{position_name}
- 数字:{digit}

## 位置含义(来自知识库 A1)
{position_kb_content}

## 数字特征(来自知识库 A2)
{digit_kb_content}

## 任务
请生成以下 3 项内容,以 JSON 格式返回,不要包含 markdown 代码块标记:

1. tab1(含义):该位置的核心能量含义(1-3句话)
2. tab2(特征):数字{digit}在{position_name}位置的具体性格/运势描述。
   必须将位置能量与数字能量自然融合,输出类似"O=7 代表分析力强、追求真理"的具体描述。
   避免纯数字通用描述,需体现"在{position_name}位置时的独特表现"。
3. tab3(建议):3-5条注意事项和提升建议,每条 10-30 字,具体可操作。

## 输出格式
{"tab1":"...","tab2":"...","tab3":"..."}

7.5 输出格式

Workflow 输出示例:

{
  "tab1": "主性格:代表一个人与生俱来的核心性格特质、人生使命、天赋方向",
  "tab2": "O=7 代表分析力强、追求真理。7号能量在性格核心位置表现为深度思考者...",
  "tab3": "1. 避免过度分析导致行动迟缓\n2. 多与他人交流想法...\n3. 注意平衡思考与实践"
}

7.6 Java 后端集成

DifyService.java 新增方法

// --- US-1.4 Position Detail Panel ---

/**
 * Get 3-Tab position detail content via Dify Workflow.
 * Called when user clicks a position on natal chart.
 */
public PositionDetailResponse getPositionDetail(String positionLetter, int digit, String positionName) {
    if (isMockMode()) {
        return generateMockPositionDetail(positionLetter, digit, positionName);
    }
    
    Map<String, Object> inputs = new HashMap<>();
    inputs.put("position_letter", positionLetter);
    inputs.put("position_name", positionName);
    inputs.put("digit", digit);
    
    try {
        String workflowResult = invokeWorkflow(inputs, "system");
        return parsePositionDetail(workflowResult);
    } catch (Exception e) {
        return generateMockPositionDetail(positionLetter, digit, positionName);
    }
}

/**
 * Parse Workflow JSON output into PositionDetailResponse.
 * Returns null if parsing fails (triggers mock fallback in caller).
 */
private PositionDetailResponse parsePositionDetail(String json) {
    try {
        JsonObject obj = gson.fromJson(json, JsonObject.class);
        PositionDetailResponse resp = new PositionDetailResponse();
        resp.tab1 = getString(obj, "tab1", "");
        resp.tab2 = getString(obj, "tab2", "");
        resp.tab3 = getString(obj, "tab3", "");
        // Validate: if tab1 or tab2 empty, treat as failure
        if (resp.tab1.isEmpty() || resp.tab2.isEmpty()) {
            return null;
        }
        return resp;
    } catch (Exception e) {
        return null;
    }
}

ChartController.java 新增端点

/**
 * Get position detail panel content (US-1.4).
 * Returns 3 Tab content + auspiciousness + adjacent combo info.
 */
@PostMapping("/position/detail")
public Result<?> positionDetail(HttpServletRequest request, @RequestBody Map<String, Object> body) {
    Long userId = (Long) request.getAttribute("userId");
    if (userId == null) return Result.error(1001, "请先登录");
    
    Long chartRecordId = ((Number) body.get("chartRecordId")).longValue();
    String position = (String) body.get("position");
    int digit = ((Number) body.get("digit")).intValue();
    
    // Verify chart ownership
    ChartRecord record = chartService.getById(chartRecordId);
    if (!record.getUserId().equals(userId)) {
        return Result.error(1006, "无权访问此能量盘");
    }
    
    // Calculate auspiciousness & adjacent combos (backend)
    String auspicious = calculatorService.calculateAuspicious(record.getChartData(), position);
    String comboInfo = calculatorService.detectAdjacentCombo(record.getChartData(), position);
    
    // Get position name mapping
    String positionName = getPositionName(position);
    
    // Call Dify Workflow for 3-Tab content
    PositionDetailResponse content = difyService.getPositionDetail(
            position, digit, positionName);
    
    // Build response
    Map<String, Object> result = new HashMap<>();
    result.put("tab1", content != null ? content.tab1 : "");
    result.put("tab2", content != null ? content.tab2 : "");
    result.put("tab3", content != null ? content.tab3 : "");
    result.put("auspicious", auspicious);
    result.put("comboInfo", comboInfo);
    
    return Result.success(result);
}

DTO 定义

package com.etotem.num.service;

public class PositionDetailResponse {
    public String tab1;  // 位置核心含义
    public String tab2;  // 数字+位置特征描述
    public String tab3;  // 注意事项和提升建议
}

错误处理

场景 处理方式 用户可见消息
Workflow 返回非 JSON 重试 1 次 —(静默重试)
重试仍失败 降级到 mock 模板 "解读生成失败,请稍后再试"
tab1/tab2 为空 降级到 mock 模板 "暂时无法获取位置含义"
Workflow 超时 >10s 直接降级 "请求超时,请重试"
Dify 4xx/5xx 降级到 mock "解读服务暂时不可用"

7.7 降级策略 (Mock)

当 Workflow 不可用时,后端使用 CalculatorService.NUMBER_DESC 表生成简易模板内容:

private PositionDetailResponse generateMockPositionDetail(
        String position, int digit, String positionName) {
    String digitDesc = NUMBER_DESC.getOrDefault(digit, "独特能量组合");
    PositionDetailResponse resp = new PositionDetailResponse();
    resp.tab1 = positionName + ":" + POSITION_CORE_MEANINGS.getOrDefault(position, "能量位置");
    resp.tab2 = position + "=" + digit + " 代表" + digitDesc + "。";
    resp.tab3 = "1. 了解该能量的优势和挑战\n2. 结合整体能量盘综合分析\n3. 在实践中觉察能量表现";
    return resp;
}

其中 POSITION_CORE_MEANINGS 为预置的位置含义简表(24 个 A-X 位置的核心含义摘录),在 DifyService 中作为备用常量。

7.8 Dify 控制台配置步骤

  1. 登录 Dify → 点击「创建应用」→ 选择「Workflow」
  2. 命名应用位置详情面板
  3. 配置 START 节点:添加 3 个输入变量 position_letter(String)、position_name(String)、digit(Integer)
  4. 添加 KB 检索节点 1(A1 位置含义):
    • 数据集:选择 数字能量学知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3
    • Top-K:3
    • 检索查询:{{position_name}} {{position_letter}} 含义 能量 公式
  5. 添加 KB 检索节点 2(A2 数字特征):
    • 相同数据集,Top-K:3
    • 检索查询:数字 {{digit}} 性格 特征 优势 挑战 成长
  6. 将两个检索节点并行连接至 LLM 节点
  7. 配置 LLM 节点
    • 模型:Claude 3.5 Sonnet
    • Temperature:0.5
    • Max Tokens:1000
    • System Prompt:粘贴第 8.4 节的完整提示词
    • 在上下文中引用两个知识检索节点的输出
  8. 连接 LLM 节点至 END 节点
  9. 点击「发布」,记录生成的 API Key

7.9 与 Chatflow 的差异对比

维度 Chatflow(聊天问答) Workflow(位置详情)
用途 多轮自由问答 单次结构化查询
输出格式 自然语言(answer 字段) 结构化 JSON
记忆 6 轮对话记忆 无记忆,每次独立
输入变量 chart_context + query position_letter + position_name + digit
调用频率 用户每次提问 用户每次点击位置
API 端点 POST /v1/chat-messages POST /v1/workflows/run

8. 输入变量配置总结

配置项
应用类型 Chatflow
输入变量 1 chart_context (JSON)
输入变量 2 query (String)
LLM 模型 Claude 3.5 Sonnet (推荐)
Temperature 0.7
Max Tokens 2000
记忆轮次 6
API Endpoint POST /v1/chat-messages
输出字段 answer (String)

9. 能量盘解读 Workflow(US-3.1)

9.1 概述

用户在输入生日并创建能量盘后,系统自动调用此 Workflow 生成完整的 AI 解读内容,展示在解读页五区卡片中。该 Workflow 独立于 Chatflow,每次创建能量盘时调用一次。

属性
目的 为 US-3.1 "AI解读展示" 生成完整能量盘文字解读
Dify 应用类型 Workflow(非 Chatflow)
触发时机 用户确认生日 → 能量盘创建成功后立即触发
数据流 后端 → Dify Workflow → 并行 KB 检索 (A1/A2/B2/C1/C2) → LLM 合成 → 结构化 JSON
记忆 无,每次创建能量盘执行一次;后续查看走缓存(US-3.3)

9.2 数据流

用户提交生日
    │
    ▼
POST /api/consultation/start
    ├─ 后端:计算24个数字位置 → 创建咨询记录
    └─ 调用 DifyService.interpretChart(chartData, userName, birthday)
        │
        ▼
    POST /v1/workflows/run (blocking, timeout=15s)
        │
        ▼
    Dify Workflow "能量盘解读"
        ├─ [并行] KB 检索 1 → A2 主性格 + A1 位置含义(O位)
        ├─ [并行] KB 检索 2 → C2 五区三组 + B2 组合对(左区 P/Q/R)
        ├─ [并行] KB 检索 3 → C2 五区三组 + B2 组合对(顶部 V/W/X)
        ├─ [并行] KB 检索 4 → C2 五区三组 + B2 组合对(右区 S/T/U)
        ├─ [并行] KB 检索 5 → B1 八星 + B2 组合对 + C1 维度(特殊组合)
        │
        └─ LLM 节点 → 合成解读 → JSON 输出
            │
            ▼
    data.outputs → {
      "sections": [{title, positions, values, content, keywords}, ...],
      "summary": "...",
      "combinationNotes": ["..."]
    }
        │
        ▼
    后端:缓存解读到数据库(US-3.3)→ 返回前端

9.3 输入架构

Dify API 调用格式

POST /v1/workflows/run
Authorization: Bearer {workflow_api_key}
Content-Type: application/json

{
  "inputs": {
    "chart_context": "{...chart JSON...}",
    "user_name": "张三",
    "birthday": "1990-01-15"
  },
  "response_mode": "blocking"
}

输入变量定义

变量名 类型 说明 示例
chart_context String (JSON) 24 个能量盘位置数字(A-X) + 五区聚合的完整 JSON 见下方
user_name String 用户姓名,用于解读文案 "张三"
birthday String 出生日期,用于年龄计算 "1990-01-15"

chart_context JSON 完整格式

{
  "positions": {
    "A": 1, "B": 9, "C": 9, "D": 0,
    "E": 0, "F": 1, "G": 1, "H": 5,
    "I": 1, "J": 6, "K": 1, "L": 9,
    "M": 7, "N": 1, "O": 8,
    "P": 8, "Q": 4, "R": 3,
    "S": 2, "T": 1, "U": 3,
    "V": 6, "W": 9, "X": 6
  },
  "zones": {
    "mainCharacter": { "name": "主性格", "positions": ["O"], "values": [8] },
    "leftZone": { "name": "左区(21-40岁)", "positions": ["P","Q","R"], "values": [8,4,3] },
    "topZone": { "name": "顶部(41-60岁)", "positions": ["V","W","X"], "values": [6,9,6] },
    "rightZone": { "name": "右区(61+岁)", "positions": ["S","T","U"], "values": [2,1,3] }
  },
  "mainCharacter": 8,
  "isMasterNumber": false,
  "userName": "张三",
  "birthday": "1990-01-15"
}

9.4 工作流节点配置

所有 5 个知识检索节点以 并行 方式连接至 LLM 节点,互不依赖。LLM 节点等待所有检索完成后统一合成。

Node 1-5: 知识检索节点

节点 检索目标 数据集 检索查询模板 Top-K
KB 1 主性格 A2 数字特征 + A1 位置含义 数字能量学知识库 主性格数字 {O} 含义 性格 特质 优势 挑战 成长 3
KB 2 左区 (P/Q/R) C2 五区三组 + B2 组合对 数字能量学知识库 左区 21-40岁 {P} {Q} {R} 组合 发展 早年运势 人生课题 3
KB 3 顶部 (V/W/X) C2 五区三组 + B2 组合对 数字能量学知识库 顶部 41-60岁 {V} {W} {X} 中年 事业 人际关系 成就 3
KB 4 右区 (S/T/U) C2 五区三组 + B2 组合对 数字能量学知识库 右区 61岁 {S} {T} {U} 晚年 财运 家庭 沉淀 3
KB 5 特殊组合 B1 八星 + B2 组合对 + C1 维度 数字能量学知识库 组合 八星 {I}{J} {M}{N} {M}{O} 能量 能量盘解读 3

所有检索节点的检索模式统一设置为 hybrid_search,与知识库全局配置一致。

Node 6: LLM 生成

参数
模型 Claude 3.5 Sonnet(推荐)
Temperature 0.6(创意性解读需适度灵活)
Max Tokens 3000(完整五区解读)
上下文引用 KB 1-5 全部输出 + chart_context 变量

Node 7: 代码节点(输出解析)

用 Dify 内置代码节点(Python/JS)对 LLM 输出进行格式校验和兜底处理:

import json

def main(input_data: dict) -> dict:
    # input_data 来自 LLM 节点的输出
    raw = input_data.get("text", "{}")
    
    # 尝试解析 JSON
    try:
        # 去除可能的 markdown 代码块标记
        text = raw
        if "```json" in text:
            text = text.split("```json")[1].split("```")[0]
        elif "```" in text:
            text = text.split("```")[1].split("```")[0]
        
        result = json.loads(text.strip())
    except:
        # 兜底:返回空结构
        result = {
            "sections": [],
            "summary": "解读生成失败,请稍后再试",
            "combinationNotes": []
        }
    
    # 校验必填字段
    if "sections" not in result:
        result["sections"] = []
    if "summary" not in result:
        result["summary"] = ""
    if "combinationNotes" not in result:
        result["combinationNotes"] = []
    
    return result

9.5 完整系统提示词

你是一个专业的数字能量学全盘解读分析师。

## 背景
用户的能量盘数据已通过 chart_context 变量传入,包含 24 个位置(A-X)的数字值。
知识库检索结果(KB 1-5)已提供相关命理知识作为参考。

## 解读要求
请按以下结构和规则,生成完整的能量盘解读。

### 1. 主性格解读(O 位)
- 根据 KB 1 中该数字的性格特征
- 描述核心特质、性格描述、人生使命
- 如有卓越数(11/22/33)需特殊标注
- 输出示例:"你的主性格数字 8 代表..."

### 2. 左区(21-40岁)解读
- 根据 KB 2 分析 P/Q/R 三个数字的组合能量
- 描述早年运势、成长环境、青年阶段的人生课题
- 说明各数字之间的相互作用

### 3. 顶部(41-60岁)解读
- 根据 KB 3 分析 V/W/X 三个数字的组合能量
- 描述中年事业、人际关系、社会成就
- 说明此阶段的核心发展主题

### 4. 右区(61+岁)解读
- 根据 KB 4 分析 S/T/U 三个数字的组合能量
- 描述晚年财运、家庭关系、精神沉淀
- 说明晚年阶段的人生智慧课题

### 5. 特殊组合分析
- 根据 KB 5 识别能量盘中的特殊数字组合
- 重点关注:I+J, M+N, M+O 等相邻位置形成的组合
- 分析八星能量(天医/延年/生气/绝命/五鬼/六煞/祸害/伏位)

### 6. 总评(summary)
- 对整体能量盘能量做出概括性评价
- 指出最突出的优势能量和需要注意的挑战
- 用 2-3 句话总结

## 规则
1. 必须参考知识库内容,不可凭空编造命理含义
2. 每个解读段落应结合该位置的实际数字,不可通用化
3. 使用「你」第二人称,语言亲切专业
4. 每条解读长度控制在 100-200 字
5. 关键词(keywords)每段提炼 3-5 个,用 2-4 字词

## 输出格式
返回严格 JSON,不要包含 markdown 代码块标记或其他格式:

{
  "sections": [
    {
      "title": "主性格解读",
      "positions": ["O"],
      "values": [8],
      "content": "你的主性格数字 8 代表...",
      "keywords": ["领导力", "物质", "权威"]
    },
    {
      "title": "左区(21-40岁)",
      "positions": ["P", "Q", "R"],
      "values": [8, 4, 3],
      "content": "左区数字组合 8-4-3 代表...",
      "keywords": ["行动力", "稳定", "沟通"]
    },
    {
      "title": "顶部(41-60岁)",
      "positions": ["V", "W", "X"],
      "values": [6, 9, 6],
      "content": "顶部数字组合 6-9-6 代表...",
      "keywords": ["家庭", "成就", "关怀"]
    },
    {
      "title": "右区(61+岁)",
      "positions": ["S", "T", "U"],
      "values": [2, 1, 3],
      "content": "右区数字组合 2-1-3 代表...",
      "keywords": ["合作", "独立", "创造"]
    }
  ],
  "summary": "整体能量盘能量评价...",
  "combinationNotes": [
    "数字 I(1)+J(6)=7,形成...",
    "M(7)+O(8)=15→6,形成..."
  ]
}

9.6 输出 JSON 格式

{
  "sections": [
    {
      "title": "主性格解读",
      "positions": ["O"],
      "values": [8],
      "content": "解读内容文本...",
      "keywords": ["领导力", "物质", "权威"]
    },
    {
      "title": "左区(21-40岁)",
      "positions": ["P", "Q", "R"],
      "values": [8, 4, 3],
      "content": "解读内容文本...",
      "keywords": ["行动力", "稳定", "沟通"]
    },
    {
      "title": "顶部(41-60岁)",
      "positions": ["V", "W", "X"],
      "values": [6, 9, 6],
      "content": "解读内容文本...",
      "keywords": ["家庭", "成就", "关怀"]
    },
    {
      "title": "右区(61+岁)",
      "positions": ["S", "T", "U"],
      "values": [2, 1, 3],
      "content": "解读内容文本...",
      "keywords": ["合作", "独立", "创造"]
    }
  ],
  "summary": "整体能量盘综合评价...",
  "combinationNotes": [
    "数字组合分析说明 1",
    "数字组合分析说明 2"
  ]
}
字段 类型 说明
sections[].title String 解读章节标题(固定值:主性格解读/左区/顶部/右区)
sections[].positions String[] 该章节涉及的位置字母列表
sections[].values Int[] 对应位置的数字值
sections[].content String 解读文案正文,100-200 字
sections[].keywords String[] 3-5 个关键词,每词 2-4 字
summary String 整体能量盘评价,2-3 句话
combinationNotes String[] 特殊组合分析说明列表

9.7 Java 后端集成

DifyService.java 新增方法

// --- US-3.1 Chart Interpretation Workflow ---

/**
 * 调用 Dify Workflow 生成能量盘完整解读(US-3.1)
 * @param chartContext  chart_context JSON 字符串(含 positions + zones + metadata)
 * @param userName      用户姓名
 * @param birthday      出生日期 YYYY-MM-DD
 * @return InterpretationResponse 结构化解读结果
 */
public InterpretationResponse interpretChart(String chartContext, String userName, String birthday) {
    if (isMockMode()) {
        return generateMockInterpretation(chartContext, userName, birthday);
    }
    
    Map<String, Object> inputs = new HashMap<>();
    inputs.put("chart_context", chartContext);
    inputs.put("user_name", userName);
    inputs.put("birthday", birthday);
    
    try {
        String workflowResult = invokeWorkflow(inputs, "system");
        return parseInterpretation(workflowResult);
    } catch (Exception e) {
        log.warn("interpretChart failed, falling back to mock: {}", e.getMessage());
        return generateMockInterpretation(chartContext, userName, birthday);
    }
}

/**
 * 解析 Workflow 输出的 JSON 为 InterpretationResponse
 */
private InterpretationResponse parseInterpretation(String json) {
    try {
        JsonObject obj = gson.fromJson(json, JsonObject.class);
        InterpretationResponse resp = new InterpretationResponse();
        
        // 解析 sections
        JsonArray sectionsArr = obj.getAsJsonArray("sections");
        List<InterpretationResponse.Section> sections = new ArrayList<>();
        for (JsonElement el : sectionsArr) {
            JsonObject s = el.getAsJsonObject();
            InterpretationResponse.Section section = new InterpretationResponse.Section();
            section.title = getString(s, "title", "");
            section.positions = getStringArray(s, "positions");
            section.values = getIntArray(s, "values");
            section.content = getString(s, "content", "");
            section.keywords = getStringArray(s, "keywords");
            sections.add(section);
        }
        resp.sections = sections;
        
        // 解析 summary
        resp.summary = getString(obj, "summary", "");
        
        // 解析 combinationNotes
        resp.combinationNotes = getStringArray(obj, "combinationNotes");
        
        // 校验:至少有一个 section 且有 content
        if (resp.sections.isEmpty() || resp.sections.stream().allMatch(s -> s.content.isEmpty())) {
            return null; // 触发降级
        }
        return resp;
    } catch (Exception e) {
        return null;
    }
}

InterpretationResponse DTO

package com.etotem.num.service;

import java.util.List;

public class InterpretationResponse {
    public List<Section> sections;
    public String summary;
    public List<String> combinationNotes;

    public static class Section {
        public String title;
        public List<String> positions;
        public List<Integer> values;
        public String content;
        public List<String> keywords;
    }
}

ChartController.java 现有端点适配

/**
 * 获取能量盘 AI 解读(US-3.1)
 * 首次调用触发 Dify Workflow,后续走缓存(US-3.3)
 */
@PostMapping("/chart/interpret")
public Result<?> interpret(@RequestBody Map<String, Object> body) {
    Long userId = (Long) request.getAttribute("userId");
    if (userId == null) return Result.error(1001, "请先登录");
    
    Long chartRecordId = ((Number) body.get("chartRecordId")).longValue();
    String interpretation = chartService.getInterpretation(chartRecordId);
    
    return Result.success(interpretation);
}

ChartService.java 解读生成与缓存(US-3.3)

@Service
public class ChartService {

    @Autowired
    private DifyService difyService;
    @Autowired
    private ChartRecordRepository chartRecordRepository;

    /**
     * 获取能量盘解读(优先缓存)
     */
    public String getInterpretation(Long chartRecordId) {
        ChartRecord record = chartRecordRepository.findById(chartRecordId);
        
        // 1. 检查缓存(US-3.3)
        if (record.getInterpretation() != null) {
            return record.getInterpretation();
        }
        
        // 2. 调用 Dify Workflow
        String chartContext = buildChartContext(record);
        InterpretationResponse resp = difyService.interpretChart(
            chartContext,
            record.getUserName(),
            record.getBirthday().toString()
        );
        
        // 3. 序列化为 JSON 字符串
        String json = new Gson().toJson(resp);
        
        // 4. 缓存到数据库
        record.setInterpretation(json);
        chartRecordRepository.save(record);
        
        return json;
    }

    /**
     * 重新生成解读(覆盖缓存,US-3.3.3)
     */
    @Transactional
    public String regenerateInterpretation(Long chartRecordId) {
        ChartRecord record = chartRecordRepository.findById(chartRecordId);
        record.setInterpretation(null);  // 清除缓存
        return getInterpretation(chartRecordId);  // 重新生成
    }

    private String buildChartContext(ChartRecord record) {
        Map<String, Object> ctx = new HashMap<>();
        ctx.put("positions", record.getChartData());
        ctx.put("zones", record.getZones());
        ctx.put("mainCharacter", record.getMainCharacter());
        ctx.put("isMasterNumber", record.getIsMasterNumber());
        ctx.put("userName", record.getUserName());
        ctx.put("birthday", record.getBirthday().toString());
        return new Gson().toJson(ctx);
    }
}

9.8 错误处理与降级

场景 处理方式 用户可见消息
Workflow 超时 >15s 重试 1 次 → 降级到 mock "解读生成较慢,已返回简要分析"
非 JSON 输出 重试 1 次 → 降级到 mock —(静默降级)
sections 为空 降级到 mock,记录告警 "暂时无法获取完整解读"
Dify 4xx 错误 记录错误日志,降级到 mock "解读服务暂时不可用,请稍后再试"
Dify 5xx 错误 重试 1 次(1s 退避)→ 降级 "解读服务繁忙,请稍后再试"
网络超时/断连 重试 1 次 → 降级 "网络异常,请重试"

Mock 降级实现

private InterpretationResponse generateMockInterpretation(
        String chartContext, String userName, String birthday) {
    InterpretationResponse resp = new InterpretationResponse();
    resp.sections = new ArrayList<>();
    
    // 从 chartContext 解析主性格数字
    int mainChar = extractMainCharacter(chartContext);
    
    // 生成主性格概要
    InterpretationResponse.Section main = new InterpretationResponse.Section();
    main.title = "主性格解读";
    main.positions = List.of("O");
    main.values = List.of(mainChar);
    main.content = "你的主性格数字 " + mainChar + "。"
        + NUMBER_DESC.getOrDefault(mainChar, "独特能量组合");
    main.keywords = List.of("主性格", "核心能量");
    resp.sections.add(main);
    
    resp.summary = "详细解读正在生成中,请稍后再试或联系客服。";
    resp.combinationNotes = new ArrayList<>();
    return resp;
}

9.9 Dify 控制台配置步骤

  1. 登录 Dify → 点击「创建应用」→ 选择「Workflow」
  2. 命名应用能量盘解读
  3. 配置 START 节点:添加 3 个输入变量
    • chart_context(String, JSON)
    • user_name(String)
    • birthday(String)
  4. 添加 KB 检索节点 1(主性格):
    • 数据集:选择 数字能量学知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3
    • Top-K:3
    • 检索查询:主性格数字 {{chart_context.positions.O}} 含义 性格 特质 优势 挑战 成长
  5. 添加 KB 检索节点 2(左区):
    • 相同数据集,Top-K:3
    • 检索查询:左区 21-40岁 {{chart_context.positions.P}} {{chart_context.positions.Q}} {{chart_context.positions.R}} 组合 发展
  6. 添加 KB 检索节点 3(顶部):
    • 相同数据集,Top-K:3
    • 检索查询:顶部 41-60岁 {{chart_context.positions.V}} {{chart_context.positions.W}} {{chart_context.positions.X}} 中年 事业
  7. 添加 KB 检索节点 4(右区):
    • 相同数据集,Top-K:3
    • 检索查询:右区 61岁 {{chart_context.positions.S}} {{chart_context.positions.T}} {{chart_context.positions.U}} 晚年 财运
  8. 添加 KB 检索节点 5(特殊组合):
    • 相同数据集,Top-K:3
    • 检索查询:组合 八星 {{chart_context.positions.I}}{{chart_context.positions.J}} {{chart_context.positions.M}}{{chart_context.positions.N}} 能量
  9. 将 KB 1-5 节点并行连接至 LLM 节点
  10. 配置 LLM 节点
    • 模型:Claude 3.5 Sonnet
    • Temperature:0.6
    • Max Tokens:3000
    • System Prompt:粘贴第 9.5 节的完整提示词
    • 上下文变量引用:将所有 5 个 KB 检索节点的输出和 chart_context 变量作为上下文引用
  11. 添加代码节点:粘贴第 9.4 节 Node 7 的 Python 代码
  12. 连接代码节点至 END 节点
  13. 点击「发布」,记录生成的 API Key(用于 application.ymldify.workflow-api-key

9.10 与 Chatflow 的差异对比

维度 Chatflow(AI问答) Workflow(能量盘解读)
用途 多轮自由问答 一次性能量盘全盘解读
输出格式 自然语言(answer 字段) 结构化 JSON(sections + summary)
记忆 6 轮对话记忆(Dify 维护) 无记忆,每次独立执行
触发时机 用户输入问题 能量盘创建后自动触发
调用频率 用户每次提问 每个能量盘仅 1 次(后续走缓存)
输入变量 chart_context + query chart_context + user_name + birthday
KB 检索数 1 个检索节点(按需) 5 个并行检索节点
API 端点 POST /v1/chat-messages POST /v1/workflows/run
响应模式 streaming(打字机效果) blocking(同步等待)
超时设置 30s 15s