使用 Flask 构建基于 Dify 的企业资金投向与客户分类评估系统
使用 Flask 构建基于 Dify 的企业资金投向与客户分类评估系统
- 前言
- 一、🧩 技术栈
- 二、📦 项目结构概览
- 三、 🔧 核心功能模块说明
- 1 配置参数
- 2 请求封装函数
- ✅ 功能说明:
- 3 Prompt 构造函数
- 4 Flask 路由定义
- 🏠 首页路由 `/`
- 📥 评估接口 `/evaluate`
- 🧪 健康检查 `/health`
- 🛠️ 运行方式
- 🧪 示例请求体(JSON)
- 💡 可扩展性建议
- 📝 总结
前言
在金融、银行等对公业务中,准确判断企业的经营范围与其资金投向、客户分类是否匹配至关重要。本文将介绍如何使用 Python 和 Dify(一个强大的 LLM 应用开发平台)构建一个自动化的评估服务。
我们将通过 Flask 搭建一个 Web 接口,接收用户的输入信息,调用 Dify 提供的 API 来分析企业的资金投向和客户分类是否合理,并返回结构化结果。
一、🧩 技术栈
- Python 3.10+
- Flask:用于构建 Web 后端服务
- Requests:用于调用 Dify API
- JSON:处理数据解析和格式化输出
- Dify AI 平台:提供大模型推理能力
- 前端模板引擎 Jinja2:渲染评估表单页面
二、📦 项目结构概览
main_by_dify_v2.py # 主程序文件
templates/
└── evaluation_form.html # 表单页面模板
三、 🔧 核心功能模块说明
1 配置参数
API_URL = "http://localhost/v1/chat-messages"
API_KEY = "app-***" # 替换为你的API密钥
[API_URL]是 Dify 提供的接口地址。
[API_KEY]是你在 Dify 平台上创建应用时获取的访问令牌。
2 请求封装函数
[ask_dify]该函数负责向 Dify 发送请求并处理响应:
def ask_dify(question: str, user_id: str = "user_123") -> Dict[str, Any]:headers = {"Authorization": f"Bearer {API_KEY}","Content-Type": "application/json"}payload = {"inputs": {},"query": question,"response_mode": "blocking","user": user_id}response = requests.post(API_URL, json=payload, headers=headers, timeout=30)response.raise_for_status()data = response.json()if 'answer' not in data:raise ValueError("API response missing 'answer' field")data_dict = json.loads(data['answer'])required_fields = ["funding_direction", "customer_category"]for field in required_fields:if field not in data_dict:raise KeyError(f"API response missing required field: {field}")return {'status': 'success','message': '评估完成','funding_evaluation': data_dict["funding_direction"],'category_evaluation': data_dict["customer_category"]}
✅ 功能说明:
- 使用
requests
发起 POST 请求 - 设置超时时间防止挂起
- 对响应进行 JSON 解析
- 校验关键字段是否存在
3 Prompt 构造函数
generate_prompt
构造符合要求的提示词内容,确保大模型能返回结构化结果:
def generate_prompt(customer_info: str, funding_direction: str, customer_category: str) -> str:return f"""#角色你是一名对公业务专家#核心任务基于经营范围,分析客户的资金投向与行内类别是否正确。不用输出think过程经营范围为: {json.dumps(customer_info)}资金投向为: {json.dumps(funding_direction)}行内类别为: {json.dumps(customer_category)}#输出规范{{"funding_direction": "分析资金投向是否正确,如果不正确,请给出分析原因", "customer_category": "分析行内类别是否正确,如果不正确,请给出分析原因"}}"""
4 Flask 路由定义
🏠 首页路由 /
@app.route('/')
def index() -> str:return render_template('evaluation_form.html')
渲染 HTML 页面,提供用户输入表单。
📥 评估接口 /evaluate
@app.route('/evaluate', methods=['POST'])
def evaluate_route() -> Any:try:data = request.get_json()prompt = generate_prompt(data['customer_info'],data['funding_direction'],data['customer_category'])result = ask_dify(prompt)return jsonify(result)except ValueError as e:return jsonify({'status': 'error', 'message': str(e)}), 400except Exception as e:return jsonify({'status': 'error', 'message': f"评估失败: {str(e)}"}), 500
处理用户提交的数据,生成提示词并调用 Dify 接口,返回结构化评估结果。
🧪 健康检查 /health
@app.route('/health', methods=['GET'])
def health_check() -> Dict[str, str]:return {"status": "healthy"}
用于监控服务运行状态。
🛠️ 运行方式
python main_by_dify_v2.py --port 5000 --host 0.0.0.0 --debug
启动后访问 http://localhost:5000
即可打开评估表单页面。
🧪 示例请求体(JSON)
{"customer_info": "计算机软件开发与销售","funding_direction": "投资人工智能研发","customer_category": "科技型企业"
}
💡 可扩展性建议
- 将代码拆分为多个模块(如
routes.py
,services.py
,utils.py
) - 添加单元测试(推荐使用
pytest
) - 支持异步响应模式(Dify 支持 streaming 和 async 模式)
- 使用 gunicorn + nginx 部署生产环境
- 引入 Redis 缓存历史评估结果
📝 总结
通过本文我们实现了一个基于 Flask 和 Dify 的企业资金投向与客户分类评估系统。它具备以下特点:
- 接口清晰、响应结构化
- 支持 JSON 输入输出
- 易于扩展和维护
- 利用大模型能力自动化评估复杂业务逻辑
未来你可以根据业务需求进一步扩展此系统,例如支持多语言、增强安全机制、引入审计日志等。
📌 完整代码
main_by_dify_v2
import requests
from flask import Flask, render_template, request, jsonify
import logging
import json
from typing import Dict, Any, Optional
import argparse# 配置常量
API_URL = "http://0.0.0.0/v1/chat-messages"
API_KEY = "app-***" # 替换为你的API密钥# 初始化 Flask 应用
app = Flask(__name__)
app.logger.setLevel(logging.INFO)def ask_dify(question: str, user_id: str = "user_123") -> Dict[str, Any]:"""向Dify API发送查询并处理响应Args:question: 要查询的问题user_id: 用户标识符Returns:包含评估结果的字典Raises:Exception: 当API请求失败或响应格式不正确时"""headers = {"Authorization": f"Bearer {API_KEY}","Content-Type": "application/json"}payload = {"inputs": {},"query": question,"response_mode": "blocking","user": user_id}try:response = requests.post(API_URL, json=payload, headers=headers, timeout=1000)response.raise_for_status() # 检查HTTP错误data = response.json()app.logger.debug("Received API response: %s", data)if 'answer' not in data:raise ValueError("API response missing 'answer' field")data_dict = json.loads(data['answer'])required_fields = ["funding_direction", "customer_category"]for field in required_fields:if field not in data_dict:raise KeyError(f"API response missing required field: {field}")return {'status': 'success','message': '评估完成','funding_evaluation': data_dict["funding_direction"],'category_evaluation': data_dict["customer_category"]}except requests.exceptions.RequestException as e:app.logger.error("API request failed: %s", str(e))raise Exception(f"API请求失败: {str(e)}")except json.JSONDecodeError as e:app.logger.error("Failed to parse API response: %s", str(e))raise Exception("API响应解析失败")def generate_prompt(customer_info: str, funding_direction: str, customer_category: str) -> str:"""生成用于评估的提示文本Args:customer_info: 客户经营范围信息funding_direction: 资金投向customer_category: 客户分类Returns:格式化后的提示文本"""return f"""#角色你是一名对公业务专家#核心任务基于经营范围,分析客户的资金投向与行内类别是否正确。不用输出think过程经营范围为: {customer_info}资金投向为: {funding_direction}行内类别为: {customer_category}#输出规范{{"funding_direction": "分析资金投向是否正确,如果不正确,请给出分析原因", "customer_category": "分析行内类别是否正确,如果不正确,请给出分析原因"}}"""@app.route('/')
def index() -> str:"""显示评估表单页面"""return render_template('evaluation_form.html')@app.route('/evaluate', methods=['POST'])
def evaluate_route() -> Any:"""处理评估请求的后端接口Returns:JSON响应: 包含评估结果或错误信息"""try:data = request.get_json()if not data:raise ValueError("请求体为空或不是有效的JSON")required_fields = ['customer_info', 'funding_direction', 'customer_category']for field in required_fields:if field not in data:raise ValueError(f"缺少必要字段: {field}")app.logger.info("Processing evaluation request for customer: %s",data.get('customer_info', 'unknown'))prompt = generate_prompt(data['customer_info'],data['funding_direction'],data['customer_category'])result = ask_dify(prompt)return jsonify(result)except ValueError as e:app.logger.warning("Invalid request: %s", str(e))return jsonify({'status': 'error','message': str(e)}), 400except Exception as e:app.logger.error("Evaluation failed: %s", str(e), exc_info=True)return jsonify({'status': 'error','message': f"评估失败: {str(e)}"}), 500@app.route('/health', methods=['GET'])
def health_check() -> Dict[str, str]:"""健康检查接口"""return {"status": "healthy"}def parse_args() -> argparse.Namespace:"""解析命令行参数"""parser = argparse.ArgumentParser(description="Run the evaluation service")parser.add_argument('--port', type=int, default=5006, help="Port to run the server on")parser.add_argument('--host', default='0.0.0.0', help="Host to bind the server to")parser.add_argument('--debug', action='store_true', help="Run in debug mode")return parser.parse_args()if __name__ == '__main__':args = parse_args()app.run(host=args.host, port=args.port, debug=args.debug)
evaluation_form.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>用户价值评估</title><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><style>* {box-sizing: border-box;font-family: "Microsoft YaHei", "PingFang SC", sans-serif;}body {background: linear-gradient(135deg, #f5f7fa 0%, #e4edf9 100%);min-height: 100vh;margin: 0;padding: 20px;display: flex;justify-content: center;align-items: center;}.evaluation-card {width: 100%;max-width: 900px;background: white;border-radius: 16px;box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);overflow: hidden;}.card-header {background: linear-gradient(to right, #3498db, #2c3e50);color: white;padding: 25px 40px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);}.card-title {margin: 0;font-size: 28px;font-weight: 600;letter-spacing: 0.5px;}.card-subtitle {margin: 8px 0 0;opacity: 0.9;font-weight: 400;font-size: 16px;}.card-body {padding: 40px;}.form-grid {display: grid;grid-template-columns: 150px 1fr;gap: 25px;align-items: center;}.form-label {font-weight: 600;font-size: 16px;color: #2c3e50;text-align: right;padding-right: 10px;}.form-control-group {display: flex;flex-direction: column;gap: 5px;}.text-input, .textarea-input {width: 100%;padding: 14px;border: 1px solid #dce4ec;border-radius: 8px;font-size: 15px;transition: all 0.3s ease;}.text-input:focus, .textarea-input:focus {border-color: #3498db;outline: none;box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);}.text-input::placeholder, .textarea-input::placeholder {color: #a0aec0;}.textarea-input {height: 120px;resize: vertical;line-height: 1.5;}.evaluation-result {margin-top: 35px;display: none;}.result-container {background: #f8f9fa;border-left: 4px solid #3498db;border-radius: 0 6px 6px 0;padding: 20px;margin-top: 10px;}.result-title {display: block;font-weight: 600;margin-bottom: 8px;color: #2c3e50;}/* 添加CSS样式 */
.result-container {width: 100%; /* 宽度自适应容器 */max-width: 600px; /* 建议在银行系统设置固定宽度 */max-height: 300px; /* 控制最大高度 */overflow-y: auto; /* 垂直滚动条 */border: 1px solid #e0e0e0; /* 银行系统常见的浅灰色边框 */border-radius: 4px;background-color: #f8f9fa; /* 银行常用的浅灰背景 */margin-top: 8px;padding: 12px;box-shadow: inset 0 1px 2px rgba(0,0,0,0.05); /* 内阴影增强可读性 */
}.result-content {white-space: pre-wrap; /* 保留空白同时自动换行 */word-wrap: break-word; /* 强制长单词换行 */word-break: break-word; /* 支持中文换行 */font-family: 'SimSun', 'Microsoft YaHei', sans-serif; /* 银行常用字体 */font-size: 14px;line-height: 1.6;color: #333; /* 银行系统常用深灰文字 */
}.card-footer {padding: 0 40px 30px;display: flex;justify-content: center;}.evaluate-button {background: linear-gradient(to right, #3498db, #2c3e50);color: white;border: none;padding: 14px 50px;font-size: 18px;font-weight: 500;border-radius: 8px;cursor: pointer;box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);transition: all 0.3s ease;display: flex;align-items: center;gap: 10px;}.evaluate-button:hover {transform: translateY(-2px);box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);}.evaluate-button:active {transform: translateY(1px);}.evaluate-button i {font-size: 20px;}.feedback-message {text-align: center;margin-top: 20px;padding: 15px;border-radius: 8px;font-size: 16px;}.success { background-color: #dff0d8; color: #3c763d; border: 1px solid #d6e9c6; }.error { background-color: #f2dede; color: #a94442; border: 1px solid #ebccd1; }.processing { background-color: #d9edf7; color: #31708f; border: 1px solid #bce8f1; }</style>
</head>
<body><div class="evaluation-card"><div class="card-header"><h1 class="card-title">客户评估助手</h1><p class="card-subtitle">基于大模型的智能分析与风险评估</p></div><div class="card-body"><form class="form-grid" id="evaluationForm"><!-- 第1行:客户名称 --><label class="form-label" for="customerName">客户名称</label><div class="form-control-group"><input type="text" class="text-input" id="customerName" placeholder="请输入客户名称"></div><!-- 第2行:客户资料 --><label class="form-label" for="customerInfo">客户资料</label><div class="form-control-group"><textarea class="textarea-input" id="customerInfo" placeholder="请输入客户资料"></textarea></div><!-- 第3行:资金投向 --><label class="form-label" for="fundingDirection">资金投向</label><div class="form-control-group"><input type="text" class="text-input" id="fundingDirection" placeholder="请输入资金投向"><!-- 资金投向评估结果 --><div class="evaluation-result" id="fundingResult"><span class="result-title">大模型评估结果:</span><div class="result-container"><!-- <input type="textarea" class="text-input" id="fundingEvaluation" readonly>--><div class="result-content" id="fundingEvaluation"></div></div></div></div><!-- 第4行:客户类别 --><label class="form-label" for="customerCategory">客户类别</label><div class="form-control-group"><input type="text" class="text-input" id="customerCategory" placeholder="请输入客户类别"><!-- 客户类别评估结果 --><div class="evaluation-result" id="categoryResult"><span class="result-title">大模型评估结果:</span><div class="result-container"><!-- <input type="text" class="text-input" id="categoryEvaluation" readonly>--><div class="result-content" id="categoryEvaluation"></div></div></div></div><!-- 反馈消息 --><div class="feedback-message" id="formFeedback"></div></form></div><div class="card-footer"><button class="evaluate-button" id="evaluateBtn"><i>📊</i> 开始评估</button></div></div><script>$(document).ready(function() {$('#evaluateBtn').click(function() {// 收集表单数据const formData = {customer_name: $('#customerName').val().trim(),customer_info: $('#customerInfo').val().trim(),funding_direction: $('#fundingDirection').val().trim(),customer_category: $('#customerCategory').val().trim()};// 表单验证const feedback = $('#formFeedback');let isValid = true;feedback.removeClass().text('');// 清除之前的错误标记$('.text-input, .textarea-input').css('border-color', '#dce4ec');// 检查客户名称if (!formData.customer_name) {$('#customerName').css('border-color', '#e74c3c');showFeedback('请填写客户名称', 'error');isValid = false;}// 检查客户资料if (!formData.customer_info) {$('#customerInfo').css('border-color', '#e74c3c');if (!isValid) showFeedback('请填写所有必填项', 'error');else showFeedback('请填写客户资料', 'error');isValid = false;}// 检查资金投向if (!formData.funding_direction) {$('#fundingDirection').css('border-color', '#e74c3c');if (!isValid) showFeedback('请填写所有必填项', 'error');isValid = false;}// 检查客户类别if (!formData.customer_category) {$('#customerCategory').css('border-color', '#e74c3c');if (!isValid) showFeedback('请填写所有必填项', 'error');isValid = false;}if (!isValid) return;// 显示处理中状态showFeedback('正在使用大模型分析中,请稍候...', 'processing');// 发送评估请求$.ajax({type: "POST",url: "/evaluate",contentType: "application/json",data: JSON.stringify(formData),success: function(response) {// 显示评估结果$('#fundingEvaluation').val(response.funding_evaluation);document.getElementById('fundingEvaluation').innerText = response.funding_evaluation;$('#categoryEvaluation').val(response.category_evaluation);document.getElementById('categoryEvaluation').innerText = response.category_evaluation;// 显示结果区域$('#fundingResult').show();$('#categoryResult').show();// 成功反馈showFeedback(response.message + ' ✅', 'success');},error: function(xhr) {const errorMsg = xhr.responseJSON?.error || '评估请求失败,请重试';showFeedback('错误: ' + errorMsg, 'error');}});});// 输入框改变时移除错误标记$('.text-input, .textarea-input').on('input', function() {$(this).css('border-color', '#dce4ec');});function showFeedback(message, type) {const feedback = $('#formFeedback');feedback.text(message).removeClass().addClass('feedback-message ' + type);}});</script>
</body>
</html>