使用 Flask 构建基于 Dify 的企业资金投向与客户分类评估系统
前言
在金融、银行等对公业务中,准确判断企业的经营范围与其资金投向、客户分类是否匹配至关重要。本文将介绍如何使用 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)}), 400
except 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)
}), 400
except 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>