🌟 标题:10分钟搭建智能旅行助手!Dify+高德地图API实现国庆行程一键规划

在这里插入图片描述

📌 核心功能

  • 实时天气查询:接入高德天气API,避开雨天景点
  • 智能路径规划:支持驾车/步行/公交多种方案
  • 个性化行程生成:根据预算、天数自动推荐景点和美食

📋 目录

  1. 环境准备(3步上手)
  2. 核心代码实现
  3. Dify高级配置与性能优化
  4. Dify插件开发实战
  5. 高德地图API高级应用
  6. 复杂工作流设计
  7. 性能优化与监控
  8. 实战案例:多城市旅行规划
  9. 附录:资源与工具
  10. 前端界面定制开发
  11. 多模态功能实现
  12. 企业级部署方案
  13. 高级工作流案例:旅行预算计算器
  14. 扩展工具集成:火车票API
  15. 用户体验优化

一、环境准备(3步上手)

1. 部署Dify服务

# 克隆代码库
git clone https://github.com/langgenius/dify.git  # 获取Dify开源代码
cd dify/docker
cp .env.example .env  # 复制环境变量模板
docker compose up -d  # 启动Docker容器集群

访问 http://localhost:3000,注册管理员账号并配置模型(推荐DeepSeek-V3)。

2. 获取高德API Key

⚠️ 警告:API Key需妥善保管,避免公开泄露

  1. 登录高德开放平台,创建应用并添加「Web服务」Key
  2. 配置MCP服务器URL:
{
  "mcpServers": {
    "amap-maps": {
      "url": "https://mcp.amap.com/sse?key=你的API_KEY",  // 替换为实际Key
      "timeout": 60  // 连接超时时间(秒)
    }
  }
}

3. 安装Dify插件

  • 在「插件市场」搜索并安装 MCP SSE 插件
  • 粘贴上述JSON配置,完成高德地图服务授权

二、核心代码实现

1. 天气查询工具(Python)

import requests  # 导入HTTP请求库

def get_weather(city):
    """
    查询城市天气信息
    :param city: 城市名称(如"北京")
    :return: 包含日期、天气状况和温度的字典
    """
    url = f"https://restapi.amap.com/v3/weather/weatherInfo?key=你的API_KEY&city={city}&extensions=all"
    response = requests.get(url)  # 发送GET请求
    data = response.json()  # 解析JSON响应
    return {
        "date": data["forecasts"][0]["date"],  # 日期
        "weather": data["forecasts"][0]["casts"][0]["dayweather"],  # 日间天气
        "temp": f"{data['forecasts'][0]['casts'][0]['daytemp']}°C"  # 日间温度
    }

2. Dify工作流设计

  1. 创建Agent应用:选择「ReAct策略」,添加MCP工具
  2. 设置提示词
你是国庆旅行规划师,需调用高德地图工具完成:
1. 获取用户输入:目的地、天数、预算
2. 调用maps_weather查询天气
3. 调用maps_direction_driving规划路线
4. 生成markdown格式行程表
  1. 配置输入表单
    • 目的地(文本框)
    • 旅行天数(数字)
    • 预算范围(下拉选择:经济型/舒适型)

三、Dify高级配置与性能优化

1. 多操作系统部署详解

Windows系统(WSL2配置)
# 安装WSL2
wsl --install Ubuntu-22.04  # 安装Ubuntu子系统
# 设置默认WSL版本
wsl --set-default-version 2
# 启动Ubuntu并设置用户
ubuntu config --default-user your_username  # 替换为实际用户名

解决WSL网络问题:修改/etc/resolv.conf,替换DNS为nameserver 8.8.8.8,避免Docker容器网络不通。

macOS配置(M芯片适配)
# 安装Docker Desktop
brew install --cask docker  # 通过Homebrew安装Docker
# 启动后配置资源:Preferences→Resources→分配4GB内存+2CPU
# 克隆代码并启动
git clone https://github.com/langgenius/dify.git
cd dify/docker
docker compose up -d  # 启动服务

2. 数据库与Redis性能调优

PostgreSQL配置(修改.env文件):

# 连接池大小(默认5,高并发建议10-20)
SQLALCHEMY_POOL_SIZE=15
# 连接超时(秒)
SQLALCHEMY_POOL_RECYCLE=300  # 避免长连接失效

Redis缓存策略

# 启动Redis容器时配置
docker run -d --name redis -p 6379:6379 redis:alpine \
  --maxmemory 2gb \  # 最大内存限制
  --maxmemory-policy allkeys-lru  # 内存满时淘汰最近最少使用的键

三、Dify插件开发实战

1. 自定义天气工具开发

步骤1:创建插件项目
# 安装插件脚手架
pip install dify-plugin-cli  # Dify插件开发工具
# 初始化项目
dify plugin init --type tool --name weather-tool  # 创建天气工具项目
cd weather-tool
步骤2:配置manifest.yaml
identity:
  name: weather-tool
  label:
    zh_Hans: 天气查询
  description:
    zh_Hans: 调用高德天气API获取实时天气
  icon: icon.svg
credentials:
  api_key:
    type: secret-input
    required: true
    label:
      zh_Hans: 高德API Key  # 插件认证所需参数
步骤3:实现工具逻辑(main.py)
import requests
from dify_plugin import ToolProvider

class WeatherTool(ToolProvider):
    def invoke(self, user_input, credentials, parameters):
        """
        工具调用入口方法
        :param user_input: 用户输入文本
        :param credentials: 包含api_key的认证信息
        :param parameters: 工具调用参数(城市名)
        """
        api_key = credentials['api_key']  # 从配置中获取API Key
        city = parameters['city']  # 获取目标城市
        url = f"https://restapi.amap.com/v3/weather/weatherInfo?key={api_key}&city={city}"
        response = requests.get(url)  # 调用高德天气API
        data = response.json()
        
        if data['status'] == '1':  # API调用成功(status=1)
            return {
                "date": data['forecasts'][0]['date'],
                "weather": data['forecasts'][0]['casts'][0]['dayweather'],
                "temp": f"{data['forecasts'][0]['casts'][0]['daytemp']}°C"
            }
        else:
            raise Exception(f"天气查询失败: {data['info']}")  # 抛出错误信息
步骤4:签名与发布
# 生成签名密钥
dify signature generate -f weather_tool  # 创建插件签名密钥
# 打包插件
dify plugin pack --name weather-tool --version 1.0.0  # 生成插件安装包
# 上传至Dify插件市场

四、高德地图API高级应用

1. 周边景点搜索实现

def search_nearby_attractions(location, radius=2000, keywords="景点"):
    """
    搜索指定坐标周边景点
    :param location: 经纬度(格式"经度,纬度")
    :param radius: 搜索半径(米),默认2000米
    :param keywords: 搜索关键词,默认"景点"
    :return: 包含名称、地址、距离和评分的景点列表
    """
    url = f"https://restapi.amap.com/v3/place/around?key={api_key}&location={location}&radius={radius}&keywords={keywords}"
    response = requests.get(url)
    data = response.json()
    
    if data['status'] == '1':  # API调用成功
        return [
            {
                "name": poi['name'],
                "address": poi['address'],
                "distance": poi['distance'] + "米",
                "rating": poi.get('biz_ext', {}).get('rating', '暂无评分')  # 处理评分可能不存在的情况
            } for poi in data['pois'][:5]  # 取前5个结果
        ]
    return []

2. 多路径规划对比

def compare_routes(origin, destination):
    """对比驾车和公交路线"""
    # 驾车路线API
    driving_url = f"https://restapi.amap.com/v3/direction/driving?origin={origin}&destination={destination}&key={api_key}"
    # 公交路线API
    transit_url = f"https://restapi.amap.com/v3/direction/transit/integrated?origin={origin}&destination={destination}&key={api_key}"
    
    driving_data = requests.get(driving_url).json()
    transit_data = requests.get(transit_url).json()
    
    return {
        "driving": {
            "distance": driving_data['route']['paths'][0]['distance'] + "米",  # 驾车距离
            "duration": driving_data['route']['paths'][0]['duration'] + "秒"   # 驾车时间
        },
        "transit": {
            "distance": transit_data['route']['paths'][0]['distance'] + "米",  # 公交距离
            "duration": transit_data['route']['paths'][0]['duration'] + "秒",  # 公交时间
            "bus_lines": len(transit_data['route']['paths'][0]['segments'])  # 公交换乘次数
        }
    }

五、复杂工作流设计

1. 条件分支与迭代节点

场景:根据用户输入的旅行类型(亲子游/自驾游)生成不同行程

  • 条件分支配置

    • travel_type == "亲子游":调用儿童友好型景点API,优先推荐乐园、博物馆
    • travel_type == "自驾游":调用沿途加油站、停车场POI
  • 迭代节点应用
    使用循环处理多日行程生成,每个迭代生成一天的详细安排,包含景点、交通、餐饮。

2. 并行处理优化

配置:同时调用天气、景点、酒店API,减少总耗时

graph TD
    A[用户输入] --> B{并行调用}
    B --> C[天气API]  # 获取天气数据
    B --> D[景点API]  # 获取景点数据
    B --> E[酒店API]  # 获取酒店数据
    C & D & E --> F[变量聚合器]  # 合并多接口结果
    F --> G[生成行程]

3. 错误处理机制

import time  # 导入时间模块用于延迟重试

# API调用重试装饰器
def retry(max_retries=3, delay=1):
    """
    函数重试装饰器
    :param max_retries: 最大重试次数
    :param delay: 初始延迟时间(秒),指数退避
    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(max_retries):
                try:
                    return func(*args, **kwargs)  # 执行原函数
                except Exception as e:
                    if i == max_retries - 1:  # 最后一次重试失败则抛出异常
                        raise e
                    time.sleep(delay * (i + 1))  # 指数退避等待
        return wrapper
    return decorator

@retry()  # 应用重试装饰器
def call_amap_api(url):
    return requests.get(url, timeout=10)  # 设置10秒超时

六、性能优化与监控

1. Redis缓存实现

import redis
import json  # 导入JSON模块用于数据序列化
r = redis.Redis(host='localhost', port=6379, db=0)  # 连接Redis服务器

def get_cached_weather(city):
    """带缓存的天气查询"""
    cache_key = f"weather:{city}"  # 缓存键格式:weather:城市名
    cached = r.get(cache_key)  # 尝试获取缓存
    
    if cached:
        return json.loads(cached)  # 缓存命中,返回解析后的数据
    
    # 缓存未命中,调用API
    weather = get_weather(city)
    r.setex(cache_key, 7200, json.dumps(weather))  # 设置2小时过期缓存
    return weather

2. 监控指标配置(Prometheus)

# prometheus.yml
scrape_configs:
  - job_name: 'dify'  # 任务名称
    static_configs:
      - targets: ['dify-api:5000']  # Dify API服务地址
    metrics_path: '/metrics'  # 指标暴露路径

3. 负载均衡(Nginx配置)

http {
    upstream dify_servers {
        server dify-instance-1:3000;  # 实例1
        server dify-instance-2:3000;  # 实例2
    }
    server {
        listen 80;
        location / {
            proxy_pass http://dify_servers;  # 反向代理到实例集群
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

七、实战案例:多城市旅行规划

行程生成代码

def generate_multi_city_itinerary(cities, days_per_city):
    """
    生成多城市行程计划
    :param cities: 城市列表(如["北京", "西安", "成都"])
    :param days_per_city: 每个城市停留天数(如[3,2,4])
    :return: 结构化行程数据
    """
    itinerary = []
    for city, days in zip(cities, days_per_city):
        # 获取城市坐标
        location = geocode(city)  # 需实现地理编码函数
        # 获取天气(带缓存)
        weather = get_cached_weather(city)
        # 获取景点
        attractions = search_nearby_attractions(location)
        # 生成每日行程
        daily_plan = []
        for day in range(1, days + 1):
            daily_plan.append({
                "day": day,
                "weather": weather,
                "attractions": attractions[(day-1)*3 : day*3],  # 每天3个景点
                "dining": search_nearby_attractions(location, radius=1000, keywords="美食")[:2]  # 每天2家餐厅
            })
        itinerary.append({
            "city": city,
            "days": daily_plan
        })
    return itinerary

# 使用示例
itinerary = generate_multi_city_itinerary(
    cities=["北京", "西安", "成都"],
    days_per_city=[3, 2, 4]
)

结果输出(Markdown格式)

## 🗺️ 多城市旅行计划(北京-西安-成都)  
### 📅 北京(3天)  
#### Day 1  
- **天气**:晴,22°C  
- **景点**:  
  1. 天安门广场(距离1.2公里)  
  2. 故宫博物院(评分4.8)  
  3. 景山公园(距离故宫500米)  
- **餐饮**:四季民福烤鸭(距离故宫800米)  

八、附录:资源与工具

1. 开发工具清单

  • Docker Compose:一键部署Dify服务
  • Postman:测试API接口(含Dify和高德API集合)
  • Grafana:监控面板模板(Dify性能指标)
  • VSCode插件:Dify插件开发脚手架

2. 代码仓库

3. 参考文档

九、前端界面定制开发

1. React组件深度集成

行程卡片组件(TripCard.jsx)
import React, { useState } from 'react';
import { Card, Button, Spin, Tag } from 'antd';  // 导入Ant Design组件
import { EditOutlined, MapOutlined } from '@ant-design/icons';  // 导入图标组件
import './TripCard.css';  // 导入自定义样式

const TripCard = ({ itinerary, onEdit }) => {
  const [loading, setLoading] = useState(false);  // 地图加载状态
  
  const handleViewMap = () => {
    setLoading(true);
    // 调用地图组件显示行程路线
    setTimeout(() => setLoading(false), 800);  // 模拟加载延迟
  };
  
  return (
    <Card 
      className="trip-card"
      title={`${itinerary.city} (${itinerary.days.length}天)`}  // 卡片标题
      extra={[
        <Button icon={<EditOutlined />} onClick={onEdit}>编辑</Button>,
        <Button icon={<MapOutlined />} loading={loading} onClick={handleViewMap}>
          查看地图
        </Button>
      ]}
    >
      {itinerary.days.map(day => (  // 遍历每日行程
        <div key={day.day} className="day-section">
          <Tag color="blue">Day {day.day}</Tag>  // 日期标签
          <div className="weather-info">
            🌤️ {day.weather.weather} {day.weather.temp}  // 天气信息
          </div>
          <div className="attractions-list">
            {day.attractions.map((attraction, idx) => (  // 遍历景点
              <div key={idx} className="attraction-item">
                {idx+1}. {attraction.name} 
                <span className="distance">({attraction.distance})</span>  // 距离信息
              </div>
            ))}
          </div>
        </div>
      ))}
    </Card>
  );
};

export default TripCard;
地图可视化组件(使用高德地图JS API)
import React, { useEffect, useRef } from 'react';

const TripMap = ({ itinerary }) => {
  const mapRef = useRef(null);  // DOM引用
  
  useEffect(() => {
    // 动态加载高德地图JS API
    const script = document.createElement('script');
    script.src = `https://webapi.amap.com/maps?v=2.0&key=${process.env.REACT_APP_AMAP_KEY}`;  // 从环境变量获取Key
    script.onload = initMap;  // 脚本加载完成后初始化地图
    document.body.appendChild(script);
    
    return () => document.body.removeChild(script);  // 组件卸载时清理
  }, []);
  
  const initMap = () => {
    const map = new AMap.Map(mapRef.current, {
      zoom: 10,
      center: [116.39748, 39.90882]  // 默认北京坐标
    });
    
    // 添加行程途经点标记
    itinerary.forEach(cityPlan => {
      AMap.plugin('AMap.Geocoder', () => {  // 加载地理编码插件
        const geocoder = new AMap.Geocoder();
        geocoder.getLocation(cityPlan.city, (status, result) => {
          if (status === 'complete') {
            const center = result.geocodes[0].location;
            new AMap.Marker({  // 创建地图标记
              position: center,
              title: cityPlan.city,
              map: map
            });
          }
        });
      });
    });
  };
  
  return <div ref={mapRef} style={{ width: '100%', height: '400px' }} />;  // 地图容器
};

export default TripMap;

2. 状态管理实现(Redux)

// store/tripSlice.js
import { createSlice } from '@reduxjs/toolkit';  // 导入Redux Toolkit

const initialState = {
  currentItinerary: null,  // 当前行程
  savedItineraries: [],    // 保存的行程列表
  isEditing: false         // 编辑状态
};

export const tripSlice = createSlice({
  name: 'trip',
  initialState,
  reducers: {
    setCurrentItinerary: (state, action) => {
      state.currentItinerary = action.payload;  // 设置当前行程
    },
    saveItinerary: (state) => {
      if (state.currentItinerary) {
        const existingIndex = state.savedItineraries.findIndex(
          item => item.id === state.currentItinerary.id
        );
        if (existingIndex >= 0) {
          state.savedItineraries[existingIndex] = state.currentItinerary;  // 更新现有行程
        } else {
          state.savedItineraries.push({  // 添加新行程
            ...state.currentItinerary,
            id: Date.now().toString()  // 使用时间戳作为唯一ID
          });
        }
      }
    },
    deleteItinerary: (state, action) => {
      // 删除指定ID的行程
      state.savedItineraries = state.savedItineraries.filter(
        item => item.id !== action.payload
      );
    }
  }
});

export const { setCurrentItinerary, saveItinerary, deleteItinerary } = tripSlice.actions;
export default tripSlice.reducer;

十、多模态功能实现

1. PDF行程解析

import PyPDF2
from dify_plugin import ToolProvider

class PDFTripParser(ToolProvider):
    def invoke(self, user_input, credentials, parameters):
        """解析PDF格式行程文件"""
        file_path = parameters['file_path']  # 获取文件路径
        text = ""
        with open(file_path, 'rb') as file:
            reader = PyPDF2.PdfReader(file)  # 创建PDF阅读器
            for page in reader.pages:
                text += page.extract_text()  # 提取页面文本
        
        # 使用正则表达式提取关键信息
        import re
        cities = re.findall(r'城市:([^,,]+)', text)  # 提取城市名
        days = re.findall(r'共(\d+)天', text)  # 提取总天数
        attractions = re.findall(r'景点:([^,,]+)', text)  # 提取景点名
        
        return {
            "parsed_result": {
                "cities": cities,
                "total_days": days[0] if days else "未知",
                "key_attractions": attractions[:5]  # 取前5个景点
            },
            "raw_text": text[:500] + "..."  # 返回文本预览(前500字符)
        }

2. 语音交互集成(Whisper API)

import openai
from dify_plugin import ToolProvider

class VoiceAssistantTool(ToolProvider):
    def invoke(self, user_input, credentials, parameters):
        """语音转文字/文字转语音工具"""
        openai.api_key = credentials['openai_key']  # 设置OpenAI API密钥
        
        if parameters['action'] == 'transcribe':
            # 语音转文字
            audio_file = open(parameters['audio_path'], 'rb')
            transcript = openai.Audio.transcribe(
                "whisper-1",  # 使用Whisper模型
                audio_file,
                language="zh"  # 指定中文
            )
            return {"text": transcript['text']}
            
        elif parameters['action'] == 'tts':
            # 文字转语音
            response = openai.Audio.create(
                model="tts-1",  # 使用TTS模型
                voice="alloy",  # 语音风格
                input=parameters['text']
            )
            return {"audio_url": response['data'][0]['url']}  # 返回音频URL

3. 旅行照片分类

from transformers import CLIPProcessor, CLIPModel
import torch  # 导入PyTorch深度学习框架

class PhotoClassifier:
    def __init__(self):
        """初始化CLIP图像分类模型"""
        self.model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")  # 加载预训练模型
        self.processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")  # 加载处理器
        self.labels = ["风景", "人物", "美食", "建筑", "动物", "植物"]  # 分类标签
        
    def classify(self, image_path):
        """
        分类旅行照片
        :param image_path: 图片路径
        :return: 分类结果及置信度
        """
        from PIL import Image
        image = Image.open(image_path).convert("RGB")  # 打开并转换为RGB格式
        
        inputs = self.processor(
            text=[f"a photo of {label}" for label in self.labels],  # 生成文本提示
            images=image,
            return_tensors="pt",  # 返回PyTorch张量
            padding=True
        )
        
        with torch.no_grad():  # 禁用梯度计算
            outputs = self.model(**inputs)
            
        logits_per_image = outputs.logits_per_image  # 图像-文本相似度分数
        probs = logits_per_image.softmax(dim=1)  # 转换为概率
        top_probs, top_indices = probs.topk(2)  # 获取前2个结果
        
        return [
            {"label": self.labels[i], "score": float(p)} 
            for i, p in zip(top_indices[0], top_probs[0])
        ]

# 使用示例
classifier = PhotoClassifier()
print(classifier.classify("travel_photo.jpg"))
# 输出: [{"label": "风景", "score": 0.87}, {"label": "建筑", "score": 0.12}]

十一、企业级部署方案

1. Kubernetes部署清单

dify-deployment.yaml

apiVersion: apps/v1
kind: StatefulSet  # 使用StatefulSet确保稳定的网络标识
metadata:
  name: dify
spec:
  serviceName: dify
  replicas: 3  # 部署3个副本实现高可用
  selector:
    matchLabels:
      app: dify
  template:
    metadata:
      labels:
        app: dify
    spec:
      containers:
      - name: dify-api
        image: langgenius/dify-api:latest  # 使用官方镜像
        ports:
        - containerPort: 5000  # API服务端口
        envFrom:
        - configMapRef:
            name: dify-config  # 引用配置文件
        - secretRef:
            name: dify-secrets  # 引用密钥
        resources:
          requests:
            memory: "1Gi"  # 请求资源
            cpu: "500m"
          limits:
            memory: "2Gi"  # 限制资源
            cpu: "1000m"
        livenessProbe:  # 存活探针
          httpGet:
            path: /health
            port: 5000
          initialDelaySeconds: 30  # 初始化延迟
          periodSeconds: 10  # 检查周期
        volumeMounts:
        - name: dify-data
          mountPath: /app/data  # 数据持久化
  volumeClaimTemplates:
  - metadata:
      name: dify-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 10Gi  # 请求10GB存储

hpa.yaml(自动扩缩容)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: dify-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: dify
  minReplicas: 2  # 最小副本数
  maxReplicas: 10  # 最大副本数
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70  # CPU使用率阈值70%
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80  # 内存使用率阈值80%

2. 备份策略(CronJob)

apiVersion: batch/v1
kind: CronJob
metadata:
  name: dify-backup
spec:
  schedule: "0 3 * * *"  # 每天凌晨3点执行
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:14  # 使用PostgreSQL镜像(含pg_dump工具)
            command: ["/bin/sh", "-c"]
            args:
            - pg_dump -h postgres -U $POSTGRES_USER $POSTGRES_DB > /backup/dify_$(date +%Y%m%d).sql;
              gzip /backup/dify_$(date +%Y%m%d).sql;  # 压缩备份文件
            env:
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secrets
                  key: username  # 从密钥获取数据库用户名
            - name: POSTGRES_DB
              value: dify  # 数据库名称
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secrets
                  key: password  # 从密钥获取密码
            volumeMounts:
            - name: backup-volume
              mountPath: /backup  # 挂载备份存储卷
          volumes:
          - name: backup-volume
            persistentVolumeClaim:
              claimName: backup-pvc  # 使用PVC存储备份
          restartPolicy: OnFailure  # 失败时重启

3. 安全加固(Nginx配置)

server {
    listen 443 ssl;  # 启用HTTPS
    server_name assistant.yourcompany.com;  # 替换为实际域名
    
    # SSL配置
    ssl_certificate /etc/nginx/certs/fullchain.pem;  # 证书链
    ssl_certificate_key /etc/nginx/certs/privkey.pem;  # 私钥
    ssl_protocols TLSv1.2 TLSv1.3;  # 支持的TLS版本
    ssl_ciphers HIGH:!aNULL:!MD5;  # 加密套件
    
    # API限流
    limit_req_zone $binary_remote_addr zone=dify_api:10m rate=10r/s;  # 限制每秒10请求
    
    location /api/ {
        limit_req zone=dify_api burst=20 nodelay;  # 突发20请求无延迟
        proxy_pass http://dify-api:5000;  # 代理到API服务
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # CORS设置
        add_header Access-Control-Allow-Origin "https://yourfrontend.com";  # 允许前端域名
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";  # 允许方法
        add_header Access-Control-Allow-Headers "Content-Type, Authorization";  # 允许头信息
    }
    
    # 静态资源缓存
    location /static/ {
        alias /var/www/dify/static/;  # 静态文件路径
        expires 1d;  # 缓存1天
        add_header Cache-Control "public, max-age=86400";  # 缓存控制头
    }
}

十二、高级工作流案例:旅行预算计算器

1. 预算计算工具实现

def calculate_trip_budget(itinerary, user_preferences):
    """
    计算旅行预算
    :param itinerary: 行程计划
    :param user_preferences: 用户偏好 {transport_level, hotel_level, dining_level}
                             level: 1-经济, 2-舒适, 3-豪华
    """
    # 基础价格系数(不同级别)
    transport_factors = {1: 1.0, 2: 1.8, 3: 3.0}
    hotel_factors = {1: 1.0, 2: 2.5, 3: 5.0}
    dining_factors = {1: 1.0, 2: 2.0, 3: 4.0}
    
    budget = {
        "transport": 0,       # 交通费用
        "accommodation": 0,   # 住宿费用
        "dining": 0,          # 餐饮费用
        "attractions": 0,     # 景点门票
        "shopping": 0,        # 购物预算
        "total": 0            # 总预算
    }
    
    # 城市间交通费用(高铁二等座均价:0.4元/公里)
    city_distances = {
        ("北京", "西安"): 1200,
        ("西安", "成都"): 700,
        # 其他城市间距离...
    }
    
    # 计算城市间交通费用
    for i in range(len(itinerary)-1):
        from_city = itinerary[i]['city']
        to_city = itinerary[i+1]['city']
        distance = city_distances.get((from_city, to_city), 500)  # 默认500公里
        base_cost = distance * 0.4  # 基础费用
        budget['transport'] += base_cost * transport_factors[user_preferences['transport_level']]
    
    # 计算住宿费用(日均基础价格:经济150元/晚)
    for city_plan in itinerary:
        nights = len(city_plan['days'])  # 住宿天数 = 城市停留天数
        base_hotel_cost = 150 * nights
        budget['accommodation'] += base_hotel_cost * hotel_factors[user_preferences['hotel_level']]
    
    # 计算餐饮费用(日均基础价格:经济80元/天)
    total_days = sum(len(plan['days']) for plan in itinerary)  # 总天数
    base_dining_cost = 80 * total_days
    budget['dining'] += base_dining_cost * dining_factors[user_preferences['dining_level']]
    
    # 景点门票(平均200元/天)
    budget['attractions'] = total_days * 200
    
    # 购物预算(餐饮费用的50%)
    budget['shopping'] = budget['dining'] * 0.5
    
    # 总计(加10%应急费用)
    budget['total'] = sum(budget.values()) * 1.1
    
    # 格式化金额(保留两位小数)
    for key in budget:
        budget[key] = round(budget[key], 2)
    
    return budget

# 使用示例
budget = calculate_trip_budget(
    itinerary=itinerary,  # 前面生成的行程计划
    user_preferences={
        "transport_level": 2,  # 舒适交通
        "hotel_level": 2,      # 舒适酒店
        "dining_level": 2      # 舒适餐饮
    }
)

2. 预算可视化(Markdown表格)

## 💰 旅行预算明细  
| 项目         | 金额(元) | 占比 |  
|--------------|------------|------|  
| 交通         | {transport} | {transport/total*100}% |  
| 住宿         | {accommodation} | {accommodation/total*100}% |  
| 餐饮         | {dining} | {dining/total*100}% |  
| 景点门票     | {attractions} | {attractions/total*100}% |  
| 购物         | {shopping} | {shopping/total*100}% |  
| **总计**     | **{total}** | **100%** |  

> 📌 注:已包含10%应急费用  
> 🔄 可通过调整偏好设置重新计算预算

十三、扩展工具集成:火车票API

12306 API封装(使用第三方API服务)

import requests

class TrainTicketAPI:
    def __init__(self, api_key):
        self.base_url = "https://api.juheapi.com/train/ticket_price"  # 聚合数据API
        self.api_key = api_key  # 第三方API密钥
    
    def get_ticket_price(self, from_station, to_station, date):
        """
        获取火车票价格
        :param from_station: 出发站
        :param to_station: 到达站
        :param date: 日期(YYYY-MM-DD)
        :return: 不同席别价格字典
        """
        params = {
            "key": self.api_key,
            "from": from_station,
            "to": to_station,
            "date": date
        }
        
        response = requests.get(self.base_url, params=params)
        data = response.json()
        
        if data['error_code'] == 0:  # API调用成功
            # 提取主要席别价格
            tickets = {}
            for item in data['result']['list']:
                tickets[item['seat']] = {
                    "price": item['price'],
                    "remaining": item['remain']  # 余票情况
                }
            return tickets
        else:
            raise Exception(f"获取火车票价格失败: {data['reason']}")

# 使用示例
train_api = TrainTicketAPI(api_key="your_juhe_api_key")  # 替换为实际Key
prices = train_api.get_ticket_price("北京", "西安", "2023-10-01")
print(prices)
# 输出: {
#   "二等座": {"price": "515.0", "remaining": "有"},
#   "一等座": {"price": "820.0", "remaining": "少量"},
#   "商务座": {"price": "1600.0", "remaining": "充足"}
# }

十四、用户体验优化

1. 加载状态组件(Skeleton屏)

import React from 'react';
import { Skeleton, Card } from 'antd';

const TripCardSkeleton = () => (
  <Card className="trip-card-skeleton">
    <Skeleton.Title level={4} />  {/* 标题占位 */}
    <div className="day-skeleton">
      <Skeleton.Button style={{ width: 60 }} />  {/* 日期标签占位 */}
      <Skeleton.Input style={{ width: 120, margin: '0 10px' }} />  {/* 天气信息占位 */}
    </div>
    <div className="attractions-skeleton">
      {[1, 2, 3].map(i => (  {/* 生成3个景点占位 */}
        <div key={i} className="attraction-item-skeleton">
          <Skeleton.Input style={{ width: '80%' }} />  {/* 景点名称占位 */}
        </div>
      ))}
    </div>
  </Card>
);

export default TripCardSkeleton;

2. 错误处理UI

import React from 'react';
import { Alert, Button, Space } from 'antd';
import { ReloadOutlined, QuestionCircleOutlined } from '@ant-design/icons';

const ErrorBoundary = ({ error, onRetry, children }) => {
  if (error) {
    return (
      <div className="error-boundary">
        <Alert
          message="操作失败"
          description={
            <div>
              <p>{error.message || "发生未知错误"}</p>  {/* 显示错误信息 */}
              <Space style={{ marginTop: 16 }}>
                <Button 
                  icon={<ReloadOutlined />} 
                  onClick={onRetry}  {/* 重试按钮 */}
                >
                  重试
                </Button>
                <Button 
                  icon={<QuestionCircleOutlined />} 
                  type="primary"
                  onClick={() => window.open("/help/trip-planner", "_blank")}  {/* 打开帮助文档 */}
                >
                  查看帮助
                </Button>
              </Space>
            </div>
          }
          type="error"
          showIcon
        />
      </div>
    );
  }
  
  return children;  {/* 无错误时渲染子组件 */}
};

export default ErrorBoundary;

3. 行程热力图(ECharts实现)

# 使用pyecharts生成行程热力图
from pyecharts import options as opts
from pyecharts.charts import HeatMap
from pyecharts.commons.utils import JsCode

def generate_trip_heatmap(itinerary):
    """生成行程热力图(按景点访问频率)"""
    # 模拟数据:景点访问热度(1-10)
    heat_data = []
    for city_plan in itinerary:
        for day in city_plan['days']:
            for idx, attraction in enumerate(day['attractions']):
                # 模拟经纬度(实际应从高德API获取)
                lng = 116.3 + idx * 0.01
                lat = 39.9 + idx * 0.01
                heat_data.append([lng, lat, 10 - idx])  # 按顺序热度递减
    
    heatmap = (
        HeatMap()
        .add_coordinate_json(
            # 高德地图JSON数据(需提前下载)
            json_file="amap_cities.json",
            name_field="name",
            lng_field="center_lng",
            lat_field="center_lat",
        )
        .add(
            series_name="景点热度",
            data=heat_data,
            label_opts=opts.LabelOpts(is_show=False),
            emphasis_itemstyle_opts=opts.ItemStyleOpts(
                color=JsCode("""new echarts.graphic.RadialGradient(0.4, 0.3, 1, [{
                    offset: 0,
                    color: 'rgba(255, 255, 255, 0.8)'
                }, {
                    offset: 1,
                    color: 'rgba(255, 150, 0, 0)'
                }])""")
            ),
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="行程热力图"),
            visualmap_opts=opts.VisualMapOpts(
                min_=0,
                max_=10,
                range_text=["热度高", "热度低"],
                orient="horizontal",
                pos_right="center",
                pos_bottom="10%",
            ),
            tooltip_opts=opts.TooltipOpts(formatter="{b}: {c}"),
        )
    )
    
    return heatmap.render("trip_heatmap.html")  # 生成HTML文件
```]]
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐