跳转至

07 - 实战项目

Dify实战项目任务分解图

完整的Dify应用(智能客服系统)

📖 项目概述

本项目将构建一个完整的智能客服系统,综合运用前面学到的Dify知识。该系统将具备智能问答、多轮对话、知识库检索、情感分析等功能,能够为用户提供高质量的客服服务。

🎯 项目目标

完成本项目后,你将能够:

  • 构建完整的Dify应用
  • 集成多种数据源
  • 优化模型配置
  • 部署和管理应用
  • 实现复杂的业务逻辑
  • 掌握实际项目的开发流程

1. 项目需求

1.1 功能需求

  • 智能问答:基于知识库的智能问答
  • 多轮对话:维护对话上下文
  • 知识库检索:从多个知识库检索信息
  • 情感分析:分析用户情绪
  • 意图识别:识别用户意图
  • 人工转接:必要时转接人工客服
  • 数据统计:统计对话数据

1.2 技术需求

  • Dify平台
  • 大语言模型(GPT-4o / GPT-4o-mini 等)
  • 多个知识库
  • API服务
  • 数据库(存储对话历史)

2. 项目架构

2.1 系统架构

Text Only
智能客服系统
├── 前端界面
│   ├── 聊天窗口
│   ├── 历史记录
│   └── 用户设置
├── Dify工作流
│   ├── 开始节点
│   ├── 意图识别节点
│   ├── 知识库检索节点
│   ├── LLM生成节点
│   ├── 情感分析节点
│   ├── 条件判断节点
│   └── 结束节点
├── 数据源
│   ├── FAQ知识库
│   ├── 产品手册知识库
│   ├── 政策文档知识库
│   └── 历史对话数据库
├── API服务
    ├── REST API
    ├── SSE(Server-Sent Events)流式响应
    └── Webhook

2.2 数据流

Text Only
用户输入
意图识别
知识库检索
LLM生成回复
情感分析
条件判断
返回回复

3. 实施步骤

3.1 创建工作流

代码示例 - 完整工作流配置

Python
import requests
import json
from typing import Dict, List

class CustomerServiceWorkflow:
    """智能客服工作流"""

    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        self.base_url = "https://api.dify.ai/v1"

    def create_app(self) -> Dict:
        """
        创建应用

        注意:应用需在Dify Web界面创建,Dify不提供通过API创建应用的接口
        创建步骤:
        1. 登录Dify控制台 -> 创建应用 -> 选择“工作流”类型
        2. 在可视化编辑器中设计工作流
        3. 发布并获取API Key
        """
        app_config = {
            "name": "智能客服系统",
            "description": "基于Dify的智能客服系统",
            "mode": "workflow",
            "icon": "🤖",
            "icon_background": "#FFEAD5"
        }
        print(f"请在Dify Web界面创建应用: {app_config['name']}")
        return {"message": "请在Dify Web界面创建应用", "config": app_config}

    def build_workflow(self) -> Dict:
        """构建完整工作流"""
        return {
            "graph": {
                "nodes": [
                    # 开始节点
                    {
                        "id": "start",
                        "type": "start",
                        "data": {
                            "title": "开始",
                            "variables": [
                                {
                                    "variable": "user_input",
                                    "label": "用户输入",
                                    "type": "paragraph",
                                    "max_length": 1000,
                                    "required": True
                                },
                                {
                                    "variable": "user_id",
                                    "label": "用户ID",
                                    "type": "text-input",
                                    "required": True
                                },
                                {
                                    "variable": "conversation_id",
                                    "label": "会话ID",
                                    "type": "text-input",
                                    "required": False
                                }
                            ]
                        }
                    },
                    # 意图识别节点
                    {
                        "id": "intent_recognition",
                        "type": "llm",
                        "data": {
                            "title": "意图识别",
                            "model": {
                                "provider": "openai",
                                "name": "gpt-4o-mini",
                                "mode": "chat"
                            },
                            "prompt_template": [
                                {
                                    "role": "system",
                                    "text": """你是一个意图识别专家。请识别用户问题的意图。

可能的意图包括:
1. product_inquiry - 产品咨询
2. order_inquiry - 订单查询
3. technical_support - 技术支持
4. refund_request - 退款申请
5. complaint - 投诉
6. greeting - 问候
7. other - 其他

只返回意图类型,不要添加其他内容。"""
                                },
                                {
                                    "role": "user",
                                    "text": "{{#start.user_input#}}"
                                }
                            ]
                        }
                    },
                    # 知识库检索节点
                    {
                        "id": "kb_retrieval",
                        "type": "knowledge-retrieval",
                        "data": {
                            "title": "知识库检索",
                            "dataset_ids": [
                                "faq_dataset_id",
                                "product_manual_dataset_id",
                                "policy_dataset_id"
                            ],
                            "retrieval_mode": "multiple",
                            "multiple_retrieval_config": {
                                "top_k": 5,
                                "score_threshold": 0.5,
                                "reranking_enable": True
                            }
                        }
                    },
                    # LLM生成回复节点
                    {
                        "id": "llm_response",
                        "type": "llm",
                        "data": {
                            "title": "生成回复",
                            "model": {
                                "provider": "openai",
                                "name": "gpt-4o",
                                "mode": "chat"
                            },
                            "prompt_template": [
                                {
                                    "role": "system",
                                    "text": """你是一个专业的客服代表,需要:
1. 礼貌友好地回答用户问题
2. 基于检索到的知识库信息回答
3. 如果知识库中没有相关信息,诚实地说明
4. 保持专业和耐心
5. 适当使用表情符号增加亲和力"""
                                },
                                {
                                    "role": "user",
                                    "text": """用户问题:{{#start.user_input#}}

用户意图:{{#intent_recognition.text#}}

检索到的相关信息:
{{#kb_retrieval.context#}}

请基于以上信息回答用户问题。"""
                                }
                            ]
                        }
                    },
                    # 情感分析节点
                    {
                        "id": "sentiment_analysis",
                        "type": "llm",
                        "data": {
                            "title": "情感分析",
                            "model": {
                                "provider": "openai",
                                "name": "gpt-4o-mini",
                                "mode": "chat"
                            },
                            "prompt_template": [
                                {
                                    "role": "system",
                                    "text": "你是一个情感分析专家。请分析用户输入的情感倾向。"
                                },
                                {
                                    "role": "user",
                                    "text": """用户输入:{{#start.user_input#}}

请分析用户的情感倾向(positive/negative/neutral)和情绪强度(1-10)。

以JSON格式返回:
{
    "sentiment": "情感倾向",
    "intensity": 情绪强度
}"""
                                }
                            ]
                        }
                    },
                    # 条件判断节点
                    {
                        "id": "check_human_needed",
                        "type": "if-else",
                        "data": {
                            "title": "是否需要人工",
                            "cases": [
                                {
                                    "case_id": "need_human",
                                    "conditions": [
                                        {
                                            "variable_selector": ["sentiment_analysis", "text"],
                                            "comparison_operator": "contains",
                                            "value": "negative"
                                        },
                                        {
                                            "variable_selector": ["intent_recognition", "text"],
                                            "comparison_operator": "is",
                                            "value": "complaint"
                                        }
                                    ],
                                    "logical_operator": "or"
                                },
                                {
                                    "case_id": "auto_reply",
                                    "conditions": [],
                                    "logical_operator": "and"
                                }
                            ]
                        }
                    },
                    # 人工转接节点
                    {
                        "id": "human_handoff",
                        "type": "code",
                        "data": {
                            "title": "人工转接",
                            "code": """
import json

def main(user_id: str, user_input: str, sentiment: str) -> dict:
    # 生成转接消息
    message = {
        "type": "human_handoff",
        "user_id": user_id,
        "user_input": user_input,
        "sentiment": sentiment,
        "timestamp": "2024-01-01T00:00:00Z",
        "priority": "high" if sentiment == "negative" else "normal"
    }

    # 这里可以发送通知给人工客服
    # 例如:发送到队列、发送邮件等

    return {
        "handoff": True,
        "message": "已为您转接人工客服,请稍候...",
        "data": message
    }
""",
                            "outputs": [
                                {"variable": "handoff", "type": "boolean"},
                                {"variable": "message", "type": "string"},
                                {"variable": "data", "type": "object"}
                            ]
                        }
                    },
                    # 数据记录节点
                    {
                        "id": "log_data",
                        "type": "code",
                        "data": {
                            "title": "记录数据",
                            "code": """
import json
from datetime import datetime

def main(user_id: str, user_input: str, response: str,
         intent: str, sentiment: str) -> dict:
    # 创建日志记录
    log_entry = {
        "user_id": user_id,
        "user_input": user_input,
        "bot_response": response,
        "intent": intent,
        "sentiment": sentiment,
        "timestamp": datetime.now().isoformat()
    }

    # 这里可以保存到数据库
    # 例如:insert into conversation_logs values (...)

    return {
        "logged": True,
        "log_entry": log_entry
    }
""",
                            "outputs": [
                                {"variable": "logged", "type": "boolean"},
                                {"variable": "log_entry", "type": "object"}
                            ]
                        }
                    },
                    # 结束节点
                    {
                        "id": "end",
                        "type": "end",
                        "data": {
                            "title": "结束",
                            "outputs": [
                                {
                                    "value_selector": ["llm_response", "text"],
                                    "variable": "auto_response"
                                },
                                {
                                    "value_selector": ["human_handoff", "message"],
                                    "variable": "human_response"
                                },
                                {
                                    "value_selector": ["log_data", "log_entry"],
                                    "variable": "log_data"
                                },
                                {
                                    "value_selector": ["intent_recognition", "text"],
                                    "variable": "intent"
                                },
                                {
                                    "value_selector": ["sentiment_analysis", "text"],
                                    "variable": "sentiment"
                                }
                            ]
                        }
                    }
                ],
                "edges": [
                    {"id": "start-intent", "source": "start", "target": "intent_recognition"},
                    {"id": "intent-kb", "source": "intent_recognition", "target": "kb_retrieval"},
                    {"id": "kb-llm", "source": "kb_retrieval", "target": "llm_response"},
                    {"id": "llm-sentiment", "source": "llm_response", "target": "sentiment_analysis"},
                    {"id": "sentiment-check", "source": "sentiment_analysis", "target": "check_human_needed"},
                    {"id": "check-human", "source": "check_human_needed", "target": "human_handoff", "sourceHandle": "need_human"},
                    {"id": "check-log", "source": "check_human_needed", "target": "log_data", "sourceHandle": "auto_reply"},
                    {"id": "human-end", "source": "human_handoff", "target": "end"},
                    {"id": "log-end", "source": "log_data", "target": "end"}
                ]
            }
        }

    def deploy_workflow(self, app_id: str, workflow: Dict) -> Dict:
        """
        部署工作流

        注意:工作流的设计和发布需在Dify Web界面完成
        发布后可通过 POST /v1/workflows/run 调用工作流
        以下展示的是工作流的数据结构,供在Web界面设计时参考
        """
        print(f"请在Dify Web界面设计并发布工作流")
        print(f"工作流包含 {len(workflow.get('graph', {}).get('nodes', []))} 个节点")
        return {"message": "请在Dify Web界面发布工作流"}

# 使用示例
if __name__ == "__main__":
    workflow = CustomerServiceWorkflow(api_key="your_api_key_here")

    # 注意:应用需在Dify Web界面创建
    app = workflow.create_app()
    print(f"应用配置: {app}")

    # 构建工作流配置(在Web界面设计时参考)
    workflow_config = workflow.build_workflow()

    # 工作流需在Web界面发布
    result = workflow.deploy_workflow("your_app_id", workflow_config)
    print(f"工作流发布提示: {result}")

3.2 集成数据源

代码示例 - 知识库管理

Python
class KnowledgeBaseManager:
    """知识库管理器"""

    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        self.base_url = "https://api.dify.ai/v1"

    def create_faq_dataset(self) -> Dict:
        """创建FAQ知识库"""
        url = f"{self.base_url}/datasets"

        payload = {
            "name": "FAQ知识库",
            "description": "常见问题解答",
            "permission": "only_me",
            "data_source_type": "upload_file"
        }

        try:
            response = requests.post(url, headers=self.headers, json=payload)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            return {"error": str(e)}

    def create_product_manual_dataset(self) -> Dict:
        """创建产品手册知识库"""
        url = f"{self.base_url}/datasets"

        payload = {
            "name": "产品手册知识库",
            "description": "产品使用手册",
            "permission": "only_me",
            "data_source_type": "upload_file"
        }

        try:
            response = requests.post(url, headers=self.headers, json=payload)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            return {"error": str(e)}

    def create_policy_dataset(self) -> Dict:
        """创建政策文档知识库"""
        url = f"{self.base_url}/datasets"

        payload = {
            "name": "政策文档知识库",
            "description": "公司政策和规定",
            "permission": "only_me",
            "data_source_type": "upload_file"
        }

        try:
            response = requests.post(url, headers=self.headers, json=payload)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            return {"error": str(e)}

    def upload_document(self, dataset_id: str, file_path: str) -> Dict:
        """上传文档到知识库"""
        # 构建文件上传API地址,使用Dify的create-by-file端点
        url = f"{self.base_url}/datasets/{dataset_id}/document/create-by-file"

        try:
            with open(file_path, 'rb') as f:  # with自动管理资源,确保文件正确关闭
                # 以二进制模式读取文件,准备multipart/form-data上传
                files = {'file': f}
                # 设置索引技术为高质量模式(使用Embedding向量化)
                # process_rule设为automatic让Dify自动处理分段和清洗
                data = {
                    'indexing_technique': 'high_quality',
                    'process_rule': json.dumps({"mode": "automatic"})  # json.dumps将Python对象转为JSON字符串
                }

                # 注意:文件上传不能使用Content-Type: application/json
                # 只需传Authorization头,requests会自动设置multipart头
                response = requests.post(
                    url,
                    headers={"Authorization": f"Bearer {self.api_key}"},
                    files=files,
                    data=data
                )
                response.raise_for_status()
                return response.json()
        except Exception as e:
            return {"error": str(e)}

    def batch_upload(self, dataset_id: str, file_paths: List[str]) -> List[Dict]:
        """批量上传文档到指定知识库"""
        results = []

        # 遍历所有文件路径,逐个上传(Dify暂不支持批量上传API)
        for file_path in file_paths:
            result = self.upload_document(dataset_id, file_path)
            # 记录每个文件的上传结果,便于后续检查失败情况
            results.append({
                "file": file_path,
                "result": result
            })

        return results

# 使用示例
if __name__ == "__main__":
    manager = KnowledgeBaseManager(api_key="your_api_key_here")

    # 创建知识库
    faq_dataset = manager.create_faq_dataset()
    print(f"FAQ知识库创建成功!ID: {faq_dataset.get('id')}")

    product_dataset = manager.create_product_manual_dataset()
    print(f"产品手册知识库创建成功!ID: {product_dataset.get('id')}")

    policy_dataset = manager.create_policy_dataset()
    print(f"政策文档知识库创建成功!ID: {policy_dataset.get('id')}")

    # 上传文档
    results = manager.batch_upload(
        dataset_id=faq_dataset["id"],
        file_paths=[
            "docs/faq1.pdf",
            "docs/faq2.pdf",
            "docs/faq3.pdf"
        ]
    )

    print(f"批量上传完成,共处理 {len(results)} 个文件")

3.3 优化模型

代码示例 - 模型配置优化

Python
class ModelOptimizer:
    """模型优化器"""

    def __init__(self, api_key: str, app_id: str):
        self.api_key = api_key
        self.app_id = app_id
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        self.base_url = "https://api.dify.ai/v1"

    def optimize_for_customer_service(self) -> Dict:
        """
        为客服场景优化模型

        注意:模型配置需在Dify Web界面完成
        以下展示推荐的配置参数
        """
        recommended_config = {
            "provider": "openai",
            "model_name": "gpt-4o",
            "temperature": 0.7,
            "top_p": 0.9,
            "max_tokens": 2048,
            "presence_penalty": 0.1,
            "frequency_penalty": 0.1,
            "system_prompt": """你是一个专业的客服代表,需要遵循以下原则:

1. 礼貌友好:使用礼貌用语,保持友好态度
2. 专业准确:基于事实回答,确保准确性
3. 简洁明了:回答简洁,避免冗长
4. 同理心:理解用户需求,表达同理心
5. 主动性:主动提供相关信息和解决方案

请用中文回答,适当使用表情符号增加亲和力。"""
        }
        print(f"请在Dify Web界面配置模型参数: {recommended_config}")
        return recommended_config

    def test_model_performance(self, test_queries: List[str]) -> List[Dict]:
        """测试模型性能——对一组测试问题逐一调用工作流,记录响应质量和延迟"""
        # 使用工作流运行接口(blocking模式等待完整响应)
        url = f"{self.base_url}/workflows/run"

        results = []

        for query in test_queries:
            # 构建工作流输入参数,与工作流开始节点的变量定义对应
            payload = {
                "inputs": {
                    "user_input": query,
                    "user_id": "test_user",
                    "conversation_id": ""  # 空字符串表示新会话
                },
                "response_mode": "blocking",  # 同步等待,适合测试场景
                "user": "test_user"
            }

            try:
                import time
                # 记录请求开始时间,用于计算端到端响应延迟
                start_time = time.time()

                response = requests.post(url, headers=self.headers, json=payload)
                response.raise_for_status()

                end_time = time.time()
                response_time = end_time - start_time

                result = response.json()

                # 收集关键性能指标:响应内容、延迟时间、Token消耗
                results.append({
                    "query": query,
                    "response": result.get("data", {}).get("outputs", {}),
                    "response_time": response_time,
                    "tokens": result.get("metadata", {}).get("usage", {}).get("total_tokens", 0)
                })

            except requests.exceptions.RequestException as e:
                results.append({
                    "query": query,
                    "error": str(e)
                })

        return results

# 使用示例
if __name__ == "__main__":
    optimizer = ModelOptimizer(
        api_key="your_api_key_here",
        app_id="app_id_here"
    )

    # 优化模型
    result = optimizer.optimize_for_customer_service()
    print(f"模型优化结果: {result}")

    # 测试性能
    test_queries = [
        "你们的产品有哪些?",
        "如何查询订单?",
        "我想退款",
        "产品坏了怎么办?"
    ]

    results = optimizer.test_model_performance(test_queries)

    print("\n性能测试结果:")
    for result in results:
        print(f"\n问题: {result['query']}")
        if "error" in result:
            print(f"错误: {result['error']}")
        else:
            print(f"响应时间: {result['response_time']:.2f}秒")
            print(f"Token数: {result['tokens']}")

3.4 部署应用

代码示例 - 完整部署流程

Python
class CustomerServiceDeployer:
    """客服系统部署器"""

    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        self.base_url = "https://api.dify.ai/v1"

    def deploy_complete_system(self, app_id: str) -> Dict:
        """
        部署完整系统——按顺序执行五步部署流程

        注意:应用需先在Dify Web界面创建,获取app_id和API Key后再调用此方法

        Args:
            app_id: 在Dify Web界面创建应用后获取的应用ID
        """
        # ===== 步骤1:获取应用配置建议 =====
        # 初始化工作流管理器,打印推荐的应用配置参数
        workflow = CustomerServiceWorkflow(self.api_key)
        app_config = workflow.create_app()
        print(f"应用配置建议: {app_config['config']}")

        # ===== 步骤2:创建三个业务知识库 =====
        # 分别创建FAQ、产品手册、政策文档知识库,实现多源知识检索
        kb_manager = KnowledgeBaseManager(self.api_key)
        faq_dataset = kb_manager.create_faq_dataset()
        product_dataset = kb_manager.create_product_manual_dataset()
        policy_dataset = kb_manager.create_policy_dataset()

        # ===== 步骤3:生成工作流配置 =====
        # 构建包含意图识别→知识库检索→LLM生成→情感分析的完整工作流
        workflow_config = workflow.build_workflow()
        # 注意:工作流需在Dify Web界面的可视化编辑器中配置
        print(f"工作流配置已生成,请在Web界面中按此配置搭建工作流")

        # ===== 步骤4:优化模型参数 =====
        # 针对客服场景调整temperature、max_tokens等参数
        optimizer = ModelOptimizer(self.api_key, app_id)
        optimizer.optimize_for_customer_service()

        # ===== 步骤5:生成API访问密钥 =====
        api_keys = self.generate_api_keys(app_id)

        return {
            "app_id": app_id,
            "faq_dataset_id": faq_dataset.get("id"),
            "product_dataset_id": product_dataset.get("id"),
            "policy_dataset_id": policy_dataset.get("id"),
            "workflow_config": workflow_config,
            "api_keys": api_keys,
            "status": "configured"
        }

    def generate_api_keys(self, app_id: str) -> List[Dict]:
        """
        生成API密钥

        注意:API密钥需在Dify Web界面管理
        进入应用 -> 访问API -> 创建/管理API密钥
        Dify不提供通过API创建密钥的接口
        """
        print(f"请在Dify Web界面为应用 {app_id} 创建API密钥")
        print("步骤:应用 -> 访问API -> API密钥 -> 创建")
        return [{"message": "请在Web界面创建API密钥"}]

    def get_deployment_info(self, app_id: str) -> Dict:
        """
        获取部署信息

        通过 /info 和 /parameters 接口获取应用基本信息
        """
        info = {}

        try:  # try/except捕获异常
            # 获取应用基本信息
            resp = requests.get(
                f"{self.base_url}/info",
                headers=self.headers
            )
            resp.raise_for_status()
            info["app_info"] = resp.json()

            # 获取应用参数
            resp = requests.get(
                f"{self.base_url}/parameters",
                headers=self.headers
            )
            resp.raise_for_status()
            info["parameters"] = resp.json()

            info["app_id"] = app_id
            info["status"] = "running"
            return info

        except requests.exceptions.RequestException as e:
            return {"error": str(e)}

# 使用示例
if __name__ == "__main__":
    deployer = CustomerServiceDeployer(api_key="your_api_key_here")

    # 注意:需先在Dify Web界面创建应用,获取app_id
    app_id = "your_app_id_here"  # 在Dify Web控制台创建后获取

    # 部署完整系统
    deployment_info = deployer.deploy_complete_system(app_id)

    print("配置完成!")
    print(f"应用ID: {deployment_info['app_id']}")
    print(f"FAQ知识库ID: {deployment_info['faq_dataset_id']}")
    print(f"产品手册知识库ID: {deployment_info['product_dataset_id']}")
    print(f"政策文档知识库ID: {deployment_info['policy_dataset_id']}")
    print(f"API密钥: {deployment_info['api_keys']}")

    # 获取部署信息
    info = deployer.get_deployment_info(deployment_info['app_id'])
    print(f"\n部署信息: {info}")

4. 前端集成

4.1 聊天界面

代码示例 - React聊天组件

JSX
// ChatWindow.jsx
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';

const ChatWindow = ({ appId, apiKey, userId }) => {  // 箭头函数:简洁的函数语法
    // ===== 状态管理 =====
    const [messages, setMessages] = useState([]);       // 消息历史列表
    const [input, setInput] = useState('');               // 输入框内容
    const [isLoading, setIsLoading] = useState(false);    // 加载状态(防止重复发送)
    const [conversationId, setConversationId] = useState(''); // Dify会话ID(多轮对话)
    const messagesEndRef = useRef(null);                  // 用于自动滚动到最新消息

    // 自动滚动到底部,确保用户看到最新消息
    const scrollToBottom = () => {  // const不可重新赋值;let块级作用域变量
        messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });  // ?.可选链:对象为null/undefined时安全返回undefined
    };

    // 每次消息列表更新时触发自动滚动
    useEffect(() => {
        scrollToBottom();
    }, [messages]);

    // ===== 发送消息核心逻辑 =====
    const sendMessage = async () => {  // async定义异步函数;await等待Promise完成
        // 防止发送空消息或重复发送
        if (!input.trim() || isLoading) return;

        // 先将用户消息添加到界面(乐观更新,提升用户体验)
        const userMessage = { role: 'user', content: input };
        setMessages(prev => [...prev, userMessage]);  // ...展开运算符:展开数组/对象
        setInput('');        // 清空输入框
        setIsLoading(true);  // 显示加载动画

        try {  // try/catch捕获异常
            // 调用Dify对话型应用的chat-messages接口
            const response = await axios.post(  // await等待异步操作完成
                'https://api.dify.ai/v1/chat-messages',
                {
                    inputs: {},              // 工作流变量(此处无额外变量)
                    query: input,             // 用户输入的问题
                    response_mode: 'blocking', // 同步模式,等待完整回复
                    conversation_id: conversationId, // 传入会话ID实现多轮对话
                    user: userId              // 用户标识,用于对话隔离
                },
                {
                    headers: {
                        'Authorization': `Bearer ${apiKey}`,
                        'Content-Type': 'application/json'
                    }
                }
            );

            // 将AI回复添加到消息列表
            const botMessage = {
                role: 'assistant',
                content: response.data.answer
            };

            setMessages(prev => [...prev, botMessage]);

            // 保存会话ID,后续请求携带此ID以维持上下文连续性
            if (response.data.conversation_id) {
                setConversationId(response.data.conversation_id);
            }
        } catch (error) {
            console.error('发送消息失败:', error);
            const errorMessage = {
                role: 'assistant',
                content: '抱歉,我遇到了一些问题。请稍后再试。'
            };
            setMessages(prev => [...prev, errorMessage]);
        } finally {
            setIsLoading(false);
        }
    };

    // 键盘事件:Enter发送,Shift+Enter换行
    const handleKeyPress = (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();  // 阻止默认换行行为
            sendMessage();
        }
    };

    return (
        <div className="chat-window">
            <div className="messages-container">
                {messages.map((message, index) => (  // map转换每个元素;filter筛选;reduce累积
                    <div
                        key={index}
                        className={`message ${message.role}`}
                    >
                        <div className="message-content">
                            {message.content}
                        </div>
                    </div>
                ))}
                {isLoading && (
                    <div className="message assistant">
                        <div className="message-content loading">
                            <span className="typing-indicator">
                                <span></span>
                                <span></span>
                                <span></span>
                            </span>
                        </div>
                    </div>
                )}
                <div ref={messagesEndRef} />
            </div>

            <div className="input-container">
                <textarea
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                    onKeyDown={handleKeyPress}
                    placeholder="输入您的问题..."
                    rows={1}
                    disabled={isLoading}
                />
                <button
                    onClick={sendMessage}
                    disabled={!input.trim() || isLoading}
                    className="send-button"
                >
                    发送
                </button>
            </div>
        </div>
    );
};

export default ChatWindow;

4.2 样式文件

代码示例 - CSS样式

CSS
/* ChatWindow.css — 智能客服聊天界面样式 */

/* ===== 聊天窗口容器:使用Flex纵向布局,撑满视口高度 ===== */
.chat-window {
    display: flex;
    flex-direction: column;
    height: 100vh;
    max-width: 800px;
    margin: 0 auto;            /* 水平居中 */
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    overflow: hidden;
    background: #ffffff;
}

/* ===== 消息列表区域:弹性填充剩余空间,支持纵向滚动 ===== */
.messages-container {
    flex: 1;
    overflow-y: auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;   /* 消息气泡间距 */
}

/* ===== 消息气泡通用样式 ===== */
.message {
    display: flex;
    max-width: 80%;  /* 气泡最大宽度,避免过长 */
}

/* 用户消息靠右对齐 */
.message.user {
    align-self: flex-end;
}

/* AI回复靠左对齐 */
.message.assistant {
    align-self: flex-start;
}

/* 消息内容区域:圆角卡片效果 */
.message-content {
    padding: 12px 16px;
    border-radius: 12px;
    line-height: 1.5;
    word-wrap: break-word;
}

/* 用户消息:蓝色背景 + 右下角尖角效果 */
.message.user .message-content {
    background: #007bff;
    color: white;
    border-bottom-right-radius: 2px;
}

/* AI回复:浅灰色背景 + 左下角尖角效果 */
.message.assistant .message-content {
    background: #f0f0f0;
    color: #333;
    border-bottom-left-radius: 2px;
}

.message-content.loading {
    display: flex;
    align-items: center;
    gap: 4px;
}

.typing-indicator {
    display: flex;
    gap: 4px;
}

.typing-indicator span {
    width: 8px;
    height: 8px;
    background: #999;
    border-radius: 50%;
    animation: typing 1.4s infinite ease-in-out both;
}

.typing-indicator span:nth-child(1) {
    animation-delay: -0.32s;
}

.typing-indicator span:nth-child(2) {
    animation-delay: -0.16s;
}

/* 打字动画关键帧:三个圆点依次缩放,模拟思考中效果 */
@keyframes typing {
    0%, 80%, 100% {
        transform: scale(0);
    }
    40% {
        transform: scale(1);
    }
}

/* ===== 底部输入区域:输入框 + 发送按钮水平排列 ===== */
.input-container {
    display: flex;
    gap: 12px;
    padding: 16px;
    border-top: 1px solid #e0e0e0;
    background: #f9f9f9;
}

.input-container textarea {
    flex: 1;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 8px;
    resize: none;
    font-family: inherit;
    font-size: 14px;
    outline: none;
}

.input-container textarea:focus {
    border-color: #007bff;
}

/* 发送按钮:主色调蓝色,带过渡动画 */
.send-button {
    padding: 12px 24px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-weight: 500;
    transition: background 0.2s;  /* 悬停颜色过渡 */
}

/* 悬停时加深颜色(未禁用状态下) */
.send-button:hover:not(:disabled) {
    background: #0056b3;
}

/* 禁用状态:灰色背景 + 禁止光标,防止重复点击 */
.send-button:disabled {
    background: #ccc;
    cursor: not-allowed;
}

5. 项目总结

5.1 完成的功能

  • ✅ 智能问答
  • ✅ 多轮对话
  • ✅ 知识库检索
  • ✅ 情感分析
  • ✅ 意图识别
  • ✅ 人工转接
  • ✅ 数据记录

5.2 技术亮点

  • 工作流设计
  • 多知识库集成
  • 模型优化
  • API部署
  • 前端集成

6. 扩展方向

6.1 功能扩展

  • 多语言支持
  • 语音识别
  • 语音合成
  • 视频客服
  • 社交媒体集成

6.2 性能优化

  • 缓存优化
  • 负载均衡
  • CDN加速
  • 数据库优化

7. 练习题

基础练习

  1. 创建简单客服系统
  2. 创建工作流
  3. 集成知识库
  4. 测试功能

进阶练习

  1. 完善客服系统
  2. 添加情感分析
  3. 实现人工转接
  4. 优化用户体验

8. 最佳实践

✅ 推荐做法

  1. 充分测试
  2. 测试所有功能
  3. 测试边界情况
  4. 进行压力测试

  5. 监控告警

  6. 设置监控指标
  7. 配置告警规则
  8. 及时响应问题

❌ 避免做法

  1. 忽视安全
  2. 实施认证授权
  3. 加密敏感数据
  4. 定期安全审计

9. 常见问题

Q1: 如何提高客服系统的响应速度?

A: 优化方法: - 使用缓存减少重复查询 - 优化知识库检索 - 选择合适的模型 - 使用流式响应

Q2: 如何处理复杂的用户问题?

A: 处理方法: - 意图识别 - 多轮对话 - 知识库检索 - 必要时转接人工

10. 总结

本项目构建了一个完整的智能客服系统,综合运用了Dify的各种功能。通过这个项目,你应该能够:

  • 设计复杂的工作流
  • 集成多种数据源
  • 优化模型配置
  • 部署和管理应用

继续探索Dify的更多功能,构建更强大的AI应用!


最后更新日期:2026-02-12 适用版本:Dify实战教程 v2026