本文档定义数字能量学智能体 Dify Chatflow 工作流的完整配置,包括系统提示词、输入架构、输出格式和节点配置。
用途: 用户在微信小程序中生成能量盘后,向 AI 提问获得数字能量学专业解读。
知识库集成: Chatflow 在 LLM 节点之前插入知识检索节点,从 Dify 知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)中动态检索与用户问题相关的命理知识,将检索结果注入 LLM 上下文,实现 RAG(检索增强生成)。
数据流: 小程序 → 后端 API → Dify Chatflow → 知识检索(RAG)→ LLM 解读 → 返回用户
后端 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"
}
后端传入的 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
}
| 变量名 | 类型 | 说明 | 来源 |
|---|---|---|---|
chart_context |
JSON | 完整的能量盘数据 | 后端传入 |
query |
String | 用户提问内容 | 用户输入 |
# 角色设定
你是「数字能量学智能解读师」,一位精通数字能量学(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: 出生日期
请根据这些数据为用户提供有深度、个性化的数字能量学解读。
Chatflow 在 LLM 节点之前插入知识检索(Knowledge Retrieval)节点,从知识库 数字能量学知识库 V2(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)中检索与用户问题最相关的内容。
为什么需要 RAG:
| 轮次 | 检索策略 | 说明 |
|---|---|---|
| 第一轮(首次发送) | chart_context 智能构建查询 | 后端首次发送时附带 chart_context,Chatflow 通过代码节点/LLM 节点自动提取能量盘关键信息生成结构化 KB 查询 |
| 后续轮次 | 直接用用户 query 检索 | Dify Chatflow 自动保存会话上下文,后续直接以 query 检索 |
| 深度追问 | 一次对话多次检索 | 每次用户提问都触发 KB 检索,确保每轮回复都有最新知识支撑 |
在 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 检索查询(如果不需要维度映射预处理)。
Dify v1.11.4 知识库支持 metadata 过滤。在知识检索节点中可按 tags 过滤:
tags 包含 ["维度:财富"](用户问财富相关问题)tags 包含 ["优先级:P0"](优先返回核心文档)tags 包含 ["用途:判读规则"](需要判读规则时)| 参数 | 推荐值 | 说明 |
|---|---|---|
| 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%,兼顾语义和关键词匹配 |
| 节点 | 类型 | 配置 |
|---|---|---|
| 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 节点的输出文本 |
START ──→ Knowledge Retrieval ──→ LLM ──→ END
↑
(chart_context 也传入 LLM)
| Dify 变量 | 映射到 | 说明 |
|---|---|---|
chart_context |
LLM 系统提示词中的上下文变量 | 能量盘数据,嵌入提示词中供 LLM 引用 |
query |
LLM 用户消息输入 | 用户提问内容 |
knowledge_retrieval |
LLM 系统提示词中的检索结果上下文 | 知识库检索返回的 chunks,LLM 需优先使用 |
如果不使用代码节点转换查询,可直接将 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)
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)
用户输入: 请解读我的主性格
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,预示着这个阶段你的直觉力和灵感将显著提升。
想了解更多关于某个区域的详细解读吗?
用户输入: 我的事业发展和财富运势如何?
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 号人容易过度投入工作
想更深入了解某个具体方向的建议吗?
数字能量学智能体chart_context,类型:JSON,必填:是query,类型:文本,必填:是chart_context 变量query 变量text 输出在后端 application.yml 中配置:
num:
dify:
base-url: https://your-dify-instance.com/v1
api-key: your_dify_api_key_here
{
"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位置代表..."
}
]
}
}
// 伪代码 — 后端 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 用于多轮对话关联,每次请求需传入
}
| 场景 | 处理方式 | 用户可见消息 |
|---|---|---|
| HTTP 4xx | 记录错误日志,不重试 | "解读服务暂时不可用,请稍后再试" |
| HTTP 5xx | 重试 1 次(1s 退避) | "解读服务繁忙,请稍后再试" |
| HTTP 429 | 等待 Retry-After | "请求过于频繁,请稍后再试" |
| 网络超时 (30s) | 重试 1 次 | "解读请求超时,请重试" |
| 空 answer | 视为异常,记录 | "暂未获取到解读结果" |
用户在三角能量盘上点击某一位置(A-X)时,前端弹出底部面板显示 3 个 Tab 的内容。 这些内容由 Dify Workflow「位置详情面板」 生成,该 Workflow 独立于 Chatflow(不共享对话上下文),每次点击独立请求。
| 属性 | 值 |
|---|---|
| 目的 | 为 US-1.4 "数字点击查看含义" 提供面板内容 |
| Dify 应用类型 | Workflow(非 Chatflow) |
| 数据流 | 前端 → 后端 /api/position/detail → Dify Workflow → KB 检索 (A1+A2) → LLM 生成 → JSON |
| 记忆 | 无,每次点击独立请求 |
用户点击 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 }
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 读取 |
| 配置项 | 值 |
|---|---|
| 数据集 | 3ff939b3-8686-44f6-8ef5-65b1e53b55d3 |
| 检索模式 | 混合检索 (hybrid_search) |
| Top-K | 3 |
| 检索查询 | {position_name} {position_letter} 含义 能量 公式 |
| 示例 | 主性格数 O 含义 能量 公式 |
| 配置项 | 值 |
|---|---|
| 数据集 | 同上 |
| 检索模式 | 混合检索 |
| Top-K | 3 |
| 检索查询 | 数字 {digit} 性格 特征 优势 挑战 成长 |
| 示例 | 数字 7 性格 特征 优势 挑战 成长 |
两个节点在 Workflow 画布中以并行方式连接至 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":"..."}
Workflow 输出示例:
{
"tab1": "主性格:代表一个人与生俱来的核心性格特质、人生使命、天赋方向",
"tab2": "O=7 代表分析力强、追求真理。7号能量在性格核心位置表现为深度思考者...",
"tab3": "1. 避免过度分析导致行动迟缓\n2. 多与他人交流想法...\n3. 注意平衡思考与实践"
}
// --- 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;
}
}
/**
* 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);
}
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 | "解读服务暂时不可用" |
当 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 中作为备用常量。
位置详情面板position_letter(String)、position_name(String)、digit(Integer)数字能量学知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)3{{position_name}} {{position_letter}} 含义 能量 公式3数字 {{digit}} 性格 特征 优势 挑战 成长0.51000| 维度 | Chatflow(聊天问答) | Workflow(位置详情) |
|---|---|---|
| 用途 | 多轮自由问答 | 单次结构化查询 |
| 输出格式 | 自然语言(answer 字段) |
结构化 JSON |
| 记忆 | 6 轮对话记忆 | 无记忆,每次独立 |
| 输入变量 | chart_context + query |
position_letter + position_name + digit |
| 调用频率 | 用户每次提问 | 用户每次点击位置 |
| API 端点 | POST /v1/chat-messages |
POST /v1/workflows/run |
| 配置项 | 值 |
|---|---|
| 应用类型 | 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) |
用户在输入生日并创建能量盘后,系统自动调用此 Workflow 生成完整的 AI 解读内容,展示在解读页五区卡片中。该 Workflow 独立于 Chatflow,每次创建能量盘时调用一次。
| 属性 | 值 |
|---|---|
| 目的 | 为 US-3.1 "AI解读展示" 生成完整能量盘文字解读 |
| Dify 应用类型 | Workflow(非 Chatflow) |
| 触发时机 | 用户确认生日 → 能量盘创建成功后立即触发 |
| 数据流 | 后端 → Dify Workflow → 并行 KB 检索 (A1/A2/B2/C1/C2) → LLM 合成 → 结构化 JSON |
| 记忆 | 无,每次创建能量盘执行一次;后续查看走缓存(US-3.3) |
用户提交生日
│
▼
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)→ 返回前端
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" |
{
"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"
}
所有 5 个知识检索节点以 并行 方式连接至 LLM 节点,互不依赖。LLM 节点等待所有检索完成后统一合成。
| 节点 | 检索目标 | 数据集 | 检索查询模板 | 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,与知识库全局配置一致。
| 参数 | 值 |
|---|---|
| 模型 | Claude 3.5 Sonnet(推荐) |
| Temperature | 0.6(创意性解读需适度灵活) |
| Max Tokens | 3000(完整五区解读) |
| 上下文引用 | KB 1-5 全部输出 + chart_context 变量 |
用 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
你是一个专业的数字能量学全盘解读分析师。
## 背景
用户的能量盘数据已通过 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,形成..."
]
}
{
"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[] | 特殊组合分析说明列表 |
// --- 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;
}
}
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;
}
}
/**
* 获取能量盘 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);
}
@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);
}
}
| 场景 | 处理方式 | 用户可见消息 |
|---|---|---|
| Workflow 超时 >15s | 重试 1 次 → 降级到 mock | "解读生成较慢,已返回简要分析" |
| 非 JSON 输出 | 重试 1 次 → 降级到 mock | —(静默降级) |
| sections 为空 | 降级到 mock,记录告警 | "暂时无法获取完整解读" |
| Dify 4xx 错误 | 记录错误日志,降级到 mock | "解读服务暂时不可用,请稍后再试" |
| Dify 5xx 错误 | 重试 1 次(1s 退避)→ 降级 | "解读服务繁忙,请稍后再试" |
| 网络超时/断连 | 重试 1 次 → 降级 | "网络异常,请重试" |
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;
}
能量盘解读chart_context(String, JSON)user_name(String)birthday(String)数字能量学知识库(ID: 3ff939b3-8686-44f6-8ef5-65b1e53b55d3)3主性格数字 {{chart_context.positions.O}} 含义 性格 特质 优势 挑战 成长3左区 21-40岁 {{chart_context.positions.P}} {{chart_context.positions.Q}} {{chart_context.positions.R}} 组合 发展3顶部 41-60岁 {{chart_context.positions.V}} {{chart_context.positions.W}} {{chart_context.positions.X}} 中年 事业3右区 61岁 {{chart_context.positions.S}} {{chart_context.positions.T}} {{chart_context.positions.U}} 晚年 财运3组合 八星 {{chart_context.positions.I}}{{chart_context.positions.J}} {{chart_context.positions.M}}{{chart_context.positions.N}} 能量0.63000chart_context 变量作为上下文引用application.yml 的 dify.workflow-api-key)| 维度 | 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 |