【1902】0121-1 Dify工作流节点详细配置(方案B最终版)

Dify工作流节点详细配置(方案B最终版)

本文档详细描述了Dify工作流中方案B的14个节点配置。工作流从初始化会话开始,进入主学习循环,包含获取幻灯片内容、显示题目、用户作答、评分反馈等环节。系统根据用户得分(0-3题正确)自动切换学习路径(A速通/B正常/C刷题),循环处理每个幻灯片直到课程结束,最后生成学习报告。文档采用Mermaid流程图可视化展示节点关系,并详细说明了每个节点的输入输出变量、代码逻辑和条件判断规则,实现了完整的自适应学习流程。
3题全对
答对2题
0-1题
未结束
已结束
继续循环
开始节点
代码执行: 初始化会话

调用API获取第一个slide
循环节点开始
代码执行: 获取当前slide

调用GET /api/slides/slide_id
LLM: 显示Slide内容

包含标题、内容、图片
代码执行: 获取测试题

从slide数据中提取quiz
LLM: 显示3道测试题

要求用户作答
LLM: 等待用户输入答案

格式: A,B,C
代码执行: 评分

计算正确题数
代码执行: 提交分数

POST /api/quiz/submit

获取下一个slide_id和路径
条件分支: 判断分数
变量赋值: 路径=A

message=切换到速通
变量赋值: 路径=B

message=继续正常
变量赋值: 路径=C

message=切换到刷题
LLM: 显示反馈

告知用户分数和路径
代码执行: 更新变量

current_slide_id=next_slide_id
条件分支: 是否结束

next_slide_id是否为null
循环终止条件检查
代码执行: 获取学习报告

GET /api/session/user_id/report
LLM: 显示学习报告

路径、成绩、建议
结束节点

节点总数:14个

  • 1个开始节点
  • 5个代码执行节点
  • 5个LLM节点
  • 1个条件分支节点
  • 1个循环节点
  • 1个结束节点

节点1: 开始节点

节点类型: 开始

输入变量:

yaml 复制代码
course_id:
  类型: String
  描述: 课程ID
  必填: 是
  示例: "1"

user_id:
  类型: String
  描述: 用户ID
  必填: 是
  示例: "user_123"

节点2: 代码执行 - 初始化会话

节点名称: 初始化会话

输入变量 : course_id, user_id

代码:

python 复制代码
def main(course_id, user_id):
    import requests
    import json
    
    # 调用外部API开始课程
    api_url = "http://localhost:8000/api/courses/start"
    
    try:
        response = requests.post(
            api_url,
            json={
                "course_id": int(course_id),
                "user_id": user_id
            },
            headers={"Content-Type": "application/json"},
            timeout=10
        )
        
        if response.status_code != 200:
            return {
                "session_id": None,
                "current_slide_id": None,
                "current_path": "B",
                "error": f"API error: {response.status_code}"
            }
        
        data = response.json()
        
        return {
            "session_id": data.get("session_id"),
            "current_slide_id": data.get("first_slide_id"),
            "current_path": "B",
            "error": None
        }
    
    except Exception as e:
        return {
            "session_id": None,
            "current_slide_id": None,
            "current_path": "B",
            "error": str(e)
        }

输出变量:

yaml 复制代码
session_id:
  类型: String
  描述: 会话ID
  
current_slide_id:
  类型: String
  描述: 第一个slide的ID
  
current_path:
  类型: String
  描述: 初始路径(默认B)
  
error:
  类型: String
  描述: 错误信息(如果有)

节点3: 循环节点

节点名称: 学习循环

循环变量:

yaml 复制代码
current_slide_id:
  类型: String
  初始值: {{初始化会话.current_slide_id}}
  说明: 当前正在学习的slide ID

session_id:
  类型: String
  初始值: {{初始化会话.session_id}}
  说明: 用户会话ID(在整个循环中保持不变)

current_path:
  类型: String
  初始值: {{初始化会话.current_path}}
  说明: 当前学习路径(A/B/C)

循环终止条件:

yaml 复制代码
添加条件:
  变量: 循环 {{x}} current_slide_id
  运算符: 为空
  值: (留空)
  
说明: 当current_slide_id为None/null时,表示课程结束

最大循环次数: 50

循环变量更新规则:

yaml 复制代码
每次循环结束后,将以下节点的输出更新到循环变量:
  current_slide_id: {{提交并获取下一步.next_slide_id}}
  current_path: {{提交并获取下一步.new_path}}
  session_id: {{初始化会话.session_id}}  (保持不变)

节点4: 代码执行 - 获取当前Slide

节点名称: 获取Slide数据

输入变量:

  • current_slide_id (来自循环变量)
  • session_id (来自循环变量)

代码:

python 复制代码
def main(current_slide_id, session_id):
    import requests
    import json
    
    if not current_slide_id or current_slide_id == "None":
        return {
            "slide_id": None,
            "slide_title": "",
            "content_text": "",
            "images_text": "",
            "quiz_json": "{}",
            "slide_type": "",
            "error": "No slide_id"
        }
    
    api_url = f"http://localhost:8000/api/slides/{current_slide_id}"
    
    try:
        response = requests.get(
            api_url,
            params={"session_id": session_id},
            headers={"Content-Type": "application/json"},
            timeout=10
        )
        
        if response.status_code != 200:
            return {
                "slide_id": current_slide_id,
                "slide_title": "Error",
                "content_text": f"Failed to load slide: {response.status_code}",
                "images_text": "",
                "quiz_json": "{}",
                "slide_type": "",
                "error": f"API error: {response.status_code}"
            }
        
        data = response.json()
        
        # 格式化slide内容
        content = data.get("content", {})
        headings = content.get("headings", [])
        paragraphs = content.get("paragraphs", [])
        images = content.get("images", [])
        
        # 组装内容文本
        content_text = ""
        for heading in headings:
            content_text += f"## {heading}\n\n"
        for para in paragraphs:
            content_text += f"{para}\n\n"
        
        # 组装图片文本
        images_text = ""
        for img in images:
            images_text += f"\n[图片: {img.get('caption', '无标题')}]"
        
        # 将quiz转为JSON字符串
        quiz_json = json.dumps(data.get("quiz", {}), ensure_ascii=False)
        
        return {
            "slide_id": data.get("id"),
            "slide_title": data.get("title", ""),
            "content_text": content_text.strip(),
            "images_text": images_text,
            "quiz_json": quiz_json,
            "slide_type": data.get("type", "X"),
            "error": None
        }
    
    except Exception as e:
        return {
            "slide_id": current_slide_id,
            "slide_title": "Error",
            "content_text": f"Exception: {str(e)}",
            "images_text": "",
            "quiz_json": "{}",
            "slide_type": "",
            "error": str(e)
        }

输出变量:

yaml 复制代码
slide_id:
  类型: String

slide_title:
  类型: String

content_text:
  类型: String

images_text:
  类型: String

quiz_json:
  类型: String

slide_type:
  类型: String

error:
  类型: String

节点5: LLM - 显示Slide内容

节点名称: 展示Slide

模型: deepseek-ai/DeepSeek-V3 (或其他可用模型)

SYSTEM:

复制代码
你是一个专业的课程讲解助手。你的任务是清晰地展示课程内容,帮助学生理解知识点。

请用友好、鼓励的语气展示内容,让学生感到学习是愉快的。

USER:

复制代码
【Slide类型: {{获取Slide数据.slide_type}}】

# {{获取Slide数据.slide_title}}

{{获取Slide数据.content_text}}

{{获取Slide数据.images_text}}

---

请仔细阅读以上内容。理解后,输入"继续"或"下一步"开始测试。

输出设置:

yaml 复制代码
结构化输出: 关闭

输出变量:

yaml 复制代码
text:
  类型: String
  描述: LLM生成的展示内容

usage:
  类型: Object
  描述: Token使用情况

节点6: 代码执行 - 解析测试题

节点名称: 准备测试题

输入变量 : quiz_json (来自节点4)

代码:

python 复制代码
def main(quiz_json):
    import json
    
    try:
        quiz = json.loads(quiz_json)
    except:
        return {
            "quiz_text": "本slide没有测试题,请继续下一个slide。",
            "question1": "",
            "question2": "",
            "question3": "",
            "correct1": "",
            "correct2": "",
            "correct3": "",
            "has_quiz": False
        }
    
    questions = quiz.get("questions", [])
    
    if len(questions) < 3:
        return {
            "quiz_text": "测试题数量不足,请联系管理员。",
            "question1": "",
            "question2": "",
            "question3": "",
            "correct1": "",
            "correct2": "",
            "correct3": "",
            "has_quiz": False
        }
    
    # 格式化测试题文本
    quiz_text = "请回答以下3道测试题:\n\n"
    
    for i, q in enumerate(questions[:3], 1):
        quiz_text += f"**题目 {i}**: {q.get('question')}\n"
        for key, value in sorted(q.get("options", {}).items()):
            quiz_text += f"  {key}. {value}\n"
        quiz_text += "\n"
    
    quiz_text += "\n请按照格式输入答案,例如: A,B,C 或 A B C"
    
    return {
        "quiz_text": quiz_text,
        "question1": questions[0].get("question", ""),
        "question2": questions[1].get("question", ""),
        "question3": questions[2].get("question", ""),
        "correct1": questions[0].get("correct", ""),
        "correct2": questions[1].get("correct", ""),
        "correct3": questions[2].get("correct", ""),
        "has_quiz": True
    }

输出变量:

yaml 复制代码
quiz_text:
  类型: String
  描述: 格式化后的测试题文本

question1:
  类型: String
  描述: 第1题题目

question2:
  类型: String
  描述: 第2题题目

question3:
  类型: String
  描述: 第3题题目

correct1:
  类型: String
  描述: 第1题正确答案

correct2:
  类型: String
  描述: 第2题正确答案

correct3:
  类型: String
  描述: 第3题正确答案

has_quiz:
  类型: Boolean
  描述: 是否有有效的测试题

节点7: LLM - 显示测试题并获取答案

节点名称: 测试交互

模型: deepseek-ai/DeepSeek-V3

SYSTEM:

复制代码
你是一个测试助手。

你的任务:
1. 展示测试题目
2. 等待用户输入答案
3. 识别用户的3个答案选项(只提取A、B、C、D这些字母)
4. 按照"答案1,答案2,答案3"的格式返回

用户可能的输入格式:
- A,B,C
- A B C  
- ABC
- "我选A第二题B第三题C"

你需要提取出3个字母,然后只返回格式化后的答案,例如"A,B,C"

重要:只返回格式化后的答案,不要有任何其他文字。

USER:

复制代码
{{准备测试题.quiz_text}}

请输入你的答案:

输出变量:

yaml 复制代码
text:
  类型: String
  描述: 格式化后的答案,如"A,B,C"

usage:
  类型: Object

节点8: 代码执行 - 评分

节点名称: 计算分数

输入变量:

  • text (来自节点7,用户答案)
  • correct1, correct2, correct3 (来自节点6)

代码:

python 复制代码
def main(text, correct1, correct2, correct3):
    import re
    
    # 提取答案中的字母
    user_answers_raw = text.strip().upper()
    answers = re.findall(r'[A-D]', user_answers_raw)
    
    if len(answers) != 3:
        return {
            "correct_count": 0,
            "answer1": "",
            "answer2": "",
            "answer3": "",
            "is_correct1": False,
            "is_correct2": False,
            "is_correct3": False,
            "error": f"答案格式错误,提取到{len(answers)}个答案,需要3个"
        }
    
    correct_answers = [correct1, correct2, correct3]
    correct_count = 0
    is_correct = [False, False, False]
    
    for i in range(3):
        if answers[i] == correct_answers[i]:
            correct_count += 1
            is_correct[i] = True
    
    return {
        "correct_count": correct_count,
        "answer1": answers[0],
        "answer2": answers[1],
        "answer3": answers[2],
        "is_correct1": is_correct[0],
        "is_correct2": is_correct[1],
        "is_correct3": is_correct[2],
        "error": None
    }

输出变量:

yaml 复制代码
correct_count:
  类型: Number
  描述: 答对的题数

answer1:
  类型: String

answer2:
  类型: String

answer3:
  类型: String

is_correct1:
  类型: Boolean

is_correct2:
  类型: Boolean

is_correct3:
  类型: Boolean

error:
  类型: String

节点9: 代码执行 - 提交分数

节点名称: 提交并获取下一步

输入变量:

  • current_slide_id (循环变量)
  • correct_count (来自节点8)
  • session_id (循环变量)

代码:

python 复制代码
def main(current_slide_id, correct_count, session_id):
    import requests
    import json
    
    api_url = "http://localhost:8000/api/quiz/submit"
    
    try:
        response = requests.post(
            api_url,
            json={
                "session_id": session_id,
                "slide_id": current_slide_id,
                "correct_count": int(correct_count)
            },
            headers={"Content-Type": "application/json"},
            timeout=10
        )
        
        if response.status_code != 200:
            return {
                "next_slide_id": None,
                "new_path": "B",
                "path_changed": False,
                "message": f"提交失败: {response.status_code}",
                "error": f"API error: {response.status_code}"
            }
        
        data = response.json()
        
        return {
            "next_slide_id": data.get("next_slide_id"),
            "new_path": data.get("new_path", "B"),
            "path_changed": data.get("path_changed", False),
            "message": data.get("message", ""),
            "error": None
        }
    
    except Exception as e:
        return {
            "next_slide_id": None,
            "new_path": "B",
            "path_changed": False,
            "message": f"提交失败: {str(e)}",
            "error": str(e)
        }

输出变量:

yaml 复制代码
next_slide_id:
  类型: String
  描述: 下一个slide的ID(如果为None表示课程结束)

new_path:
  类型: String
  描述: 新的学习路径(A/B/C)

path_changed:
  类型: Boolean
  描述: 路径是否发生了变化

message:
  类型: String
  描述: 反馈信息

error:
  类型: String
  描述: 错误信息(如果有)

节点10: LLM - 显示反馈

节点名称: 展示反馈

模型: deepseek-ai/DeepSeek-V3

SYSTEM:

复制代码
你是一个学习反馈助手。向学生展示他们的测试结果和下一步学习路径。

请用鼓励的语气给予反馈,即使学生答错了,也要给予积极的建议。

USER:

复制代码
测试完成!

你答对了 {{计算分数.correct_count}} 道题(共3题)。

详细结果:
- 题目1: 你的答案 {{计算分数.answer1}} | 正确答案 {{准备测试题.correct1}} | {{计算分数.is_correct1}}
- 题目2: 你的答案 {{计算分数.answer2}} | 正确答案 {{准备测试题.correct2}} | {{计算分数.is_correct2}}
- 题目3: 你的答案 {{计算分数.answer3}} | 正确答案 {{准备测试题.correct3}} | {{计算分数.is_correct3}}

---

路径更新: {{提交并获取下一步.message}}

当前学习路径: {{提交并获取下一步.new_path}}路径

准备进入下一个Slide...

输出变量:

yaml 复制代码
text:
  类型: String

usage:
  类型: Object

节点11: 条件分支 - 检查是否结束

节点名称: 判断课程结束

条件配置:

yaml 复制代码
IF条件:
  变量: {{提交并获取下一步.next_slide_id}}
  运算符: 不为空
  值: (留空)

下一步:
  IF分支: 返回到循环节点(继续学习)
  ELSE分支: 进入"生成报告"节点

说明:

  • 如果 next_slide_id 不为空,说明还有下一个slide,继续循环
  • 如果 next_slide_id 为空/None,说明课程结束,生成报告

节点12: 代码执行 - 获取学习报告

节点名称: 生成报告

输入变量:

  • session_id (循环变量)
  • user_id (开始节点)

代码:

python 复制代码
def main(session_id, user_id):
    import requests
    
    api_url = f"http://localhost:8000/api/session/{user_id}/report"
    
    try:
        response = requests.get(
            api_url,
            params={"session_id": session_id},
            headers={"Content-Type": "application/json"},
            timeout=10
        )
        
        if response.status_code != 200:
            return {
                "report_text": f"生成报告失败: {response.status_code}",
                "total_slides": 0,
                "accuracy": 0,
                "path_a_count": 0,
                "path_b_count": 0,
                "path_c_count": 0,
                "error": f"API error: {response.status_code}"
            }
        
        data = response.json()
        
        # 格式化报告
        path_history = " → ".join(data.get("path_history", []))
        
        report_text = f"""
# 学习报告

## 学习路径
{path_history}

## 统计数据
- 完成slides数: {data.get('total_slides_completed', 0)}
- 总答题数: {data.get('total_questions', 0)}
- 正确率: {data.get('accuracy', 0)}%
- 学习时长: {data.get('duration', 0)}分钟

## 路径分布
- A路径(速通): {data.get('path_distribution', {}).get('A', 0)} 次
- B路径(正常): {data.get('path_distribution', {}).get('B', 0)} 次
- C路径(刷题): {data.get('path_distribution', {}).get('C', 0)} 次

## 知识点掌握情况
{data.get('knowledge_summary', '无数据')}

## 学习建议
{data.get('recommendations', '继续保持!')}
"""
        
        return {
            "report_text": report_text,
            "total_slides": data.get('total_slides_completed', 0),
            "accuracy": data.get('accuracy', 0),
            "path_a_count": data.get('path_distribution', {}).get('A', 0),
            "path_b_count": data.get('path_distribution', {}).get('B', 0),
            "path_c_count": data.get('path_distribution', {}).get('C', 0),
            "error": None
        }
    
    except Exception as e:
        return {
            "report_text": f"生成报告时出错: {str(e)}",
            "total_slides": 0,
            "accuracy": 0,
            "path_a_count": 0,
            "path_b_count": 0,
            "path_c_count": 0,
            "error": str(e)
        }

输出变量:

yaml 复制代码
report_text:
  类型: String
  描述: 格式化的报告文本

total_slides:
  类型: Number
  描述: 完成的slide总数

accuracy:
  类型: Number
  描述: 正确率(百分比)

path_a_count:
  类型: Number

path_b_count:
  类型: Number

path_c_count:
  类型: Number

error:
  类型: String

节点13: LLM - 显示学习报告

节点名称: 展示报告

模型: deepseek-ai/DeepSeek-V3

SYSTEM:

复制代码
你是一个学习总结助手。向学生展示他们的学习成果和建议。

请用鼓励和积极的语气总结学生的学习情况,给予具体的改进建议。

USER:

复制代码
恭喜你完成本次学习!

{{生成报告.report_text}}

---

感谢使用自适应学习系统!

希望这次学习对你有所帮助。继续保持学习热情!

输出变量:

yaml 复制代码
text:
  类型: String

usage:
  类型: Object

节点14: 结束节点

节点类型: 结束

输出变量:

yaml 复制代码
将以下变量作为最终输出:
  final_report:
    类型: String
    值: {{展示报告.text}}
  
  total_slides:
    类型: Number
    值: {{生成报告.total_slides}}
  
  accuracy:
    类型: Number
    值: {{生成报告.accuracy}}

关键配置注意事项

1. 循环节点的变量更新

循环变量更新规则(重要):

在循环节点配置界面中:

yaml 复制代码
循环开始时的初始值:
  current_slide_id: {{初始化会话.current_slide_id}}
  session_id: {{初始化会话.session_id}}
  current_path: {{初始化会话.current_path}}

每次循环后更新为:
  current_slide_id: {{提交并获取下一步.next_slide_id}}
  current_path: {{提交并获取下一步.new_path}}
  session_id: {{初始化会话.session_id}}  (保持初始值不变)

2. API地址配置

所有代码节点中的API地址需要根据实际情况修改:

python 复制代码
# 开发环境(FastAPI在本地运行)
api_url = "http://localhost:8000/..."

# 生产环境(FastAPI部署在服务器)
api_url = "http://your-server-ip:8000/..."

# 使用域名(推荐)
api_url = "https://your-domain.com/..."

3. 循环终止条件

正确的配置:

yaml 复制代码
变量: 循环 {{x}} current_slide_id
运算符: 为空
值: (留空,不填任何内容)

说明 : 当FastAPI返回 "next_slide_id": null 时,这个条件会触发,循环终止。

4. 错误处理

每个代码节点都包含了 try-except 错误处理,返回的数据中都有 error 字段。

可选:添加条件分支来处理错误情况,例如在节点4后面添加:

yaml 复制代码
IF {{获取Slide数据.error}} 不为空:
  → 显示错误信息,终止流程
ELSE:
  → 继续正常流程

5. 测试建议

逐步测试:

  1. 先测试节点2(初始化会话),确保能成功调用FastAPI
  2. 手动设置循环变量,测试节点4(获取Slide)
  3. 测试节点6-9(完整的答题流程)
  4. 最后测试完整的循环

测试数据:

yaml 复制代码
course_id: "1"
user_id: "test_user"

6. 常见问题

问题1: 循环变量无法更新

  • 检查循环节点的"变量更新规则"配置
  • 确保引用的节点名称和变量名完全正确

问题2: API调用失败

  • 检查FastAPI服务是否正在运行(http://localhost:8000/docs
  • 检查API地址是否正确
  • 查看FastAPI终端的日志

问题3: 循环不终止

  • 检查FastAPI的 submit_quiz 函数是否正确返回 None
  • 检查循环终止条件的配置

完整的节点连接顺序

复制代码
开始
  ↓
初始化会话
  ↓
循环开始 ←─────────────┐
  ↓                     │
获取Slide数据           │
  ↓                     │
展示Slide              │
  ↓                     │
准备测试题              │
  ↓                     │
测试交互               │
  ↓                     │
计算分数               │
  ↓                     │
提交并获取下一步        │
  ↓                     │
展示反馈               │
  ↓                     │
判断课程结束            │
  ↓                     │
IF 未结束 ──────────────┘
  │
  │ ELSE (已结束)
  ↓
生成报告
  ↓
展示报告
  ↓
结束

这就是完整的14个节点配置。按照这个配置搭建,配合FastAPI后端,就能实现完整的自适应学习系统。

相关推荐
lbb 小魔仙2 小时前
【Java】Java JVM 调优实战:GC 调优参数 + 内存泄漏排查,线上性能提升实战
java·开发语言·jvm
第7个前端2 小时前
elementplus相同ElMessage只显示一个
前端
大柏怎么被偷了2 小时前
【Linux】线程的概念
java·linux·jvm
IT 行者2 小时前
基于Servlet的纯原生Java Web工程之工程搭建:去除依赖的繁琐,返璞归真
java·前端·servlet
wenjianhai2 小时前
若依(RuoYi-Vue-Plus)框架使用WebSocket(2)
java·若依·websocke4t
霍理迪2 小时前
js数据类型与运算符
开发语言·前端·javascript
Hi_kenyon2 小时前
小白理解main.js
前端·javascript·vue.js
ID_180079054732 小时前
淘宝平台商品详情API(item_get)深度解析
java·服务器·前端
梦想的旅途22 小时前
基于RPA的多线程企微外部群异步推送架构
java·开发语言·jvm