386 lines
12 KiB
Python
386 lines
12 KiB
Python
"""
|
||
智慧监督AI文书写作服务 - 主应用
|
||
"""
|
||
from flask import Flask, request, jsonify, send_from_directory
|
||
from flask_cors import CORS
|
||
from flasgger import Swagger
|
||
import os
|
||
from datetime import datetime
|
||
from dotenv import load_dotenv
|
||
|
||
from services.ai_service import AIService
|
||
from services.field_service import FieldService
|
||
from utils.response import success_response, error_response
|
||
|
||
# 加载环境变量
|
||
load_dotenv()
|
||
|
||
app = Flask(__name__)
|
||
CORS(app) # 允许跨域请求
|
||
|
||
# 配置Swagger
|
||
swagger_config = {
|
||
"headers": [],
|
||
"specs": [
|
||
{
|
||
"endpoint": "apispec",
|
||
"route": "/apispec.json",
|
||
"rule_filter": lambda rule: True,
|
||
"model_filter": lambda tag: True,
|
||
}
|
||
],
|
||
"static_url_path": "/flasgger_static",
|
||
"swagger_ui": True,
|
||
"specs_route": "/api-docs"
|
||
}
|
||
|
||
swagger_template = {
|
||
"swagger": "2.0",
|
||
"info": {
|
||
"title": "智慧监督AI文书写作服务 API",
|
||
"description": "基于大模型的智能文书生成服务,支持从非结构化文本中提取结构化字段数据",
|
||
"version": "1.0.0",
|
||
"contact": {
|
||
"name": "API支持"
|
||
}
|
||
},
|
||
"basePath": "/",
|
||
"schemes": ["http", "https"],
|
||
"tags": [
|
||
{
|
||
"name": "AI解析",
|
||
"description": "AI字段提取相关接口"
|
||
},
|
||
{
|
||
"name": "字段配置",
|
||
"description": "字段配置查询接口"
|
||
}
|
||
]
|
||
}
|
||
|
||
swagger = Swagger(app, config=swagger_config, template=swagger_template)
|
||
|
||
# 初始化服务
|
||
ai_service = AIService()
|
||
field_service = FieldService()
|
||
|
||
|
||
@app.route('/')
|
||
def index():
|
||
"""返回测试页面"""
|
||
return send_from_directory('static', 'index.html')
|
||
|
||
|
||
@app.route('/api/ai/extract', methods=['POST'])
|
||
def extract():
|
||
"""
|
||
AI字段提取接口
|
||
从输入的非结构化文本中提取结构化字段数据
|
||
|
||
---
|
||
tags:
|
||
- AI解析
|
||
summary: 从输入数据中提取结构化字段
|
||
description: 使用AI大模型从输入文本中提取结构化字段,支持多种业务类型
|
||
consumes:
|
||
- application/json
|
||
produces:
|
||
- application/json
|
||
parameters:
|
||
- in: body
|
||
name: body
|
||
description: 请求参数
|
||
required: true
|
||
schema:
|
||
type: object
|
||
required:
|
||
- businessType
|
||
- inputData
|
||
properties:
|
||
businessType:
|
||
type: string
|
||
description: 业务类型
|
||
example: INVESTIGATION
|
||
inputData:
|
||
type: array
|
||
description: 输入数据列表
|
||
items:
|
||
type: object
|
||
properties:
|
||
fieldCode:
|
||
type: string
|
||
description: 字段编码
|
||
example: clue_info
|
||
fieldValue:
|
||
type: string
|
||
description: 字段值(原始文本)
|
||
example: 被举报用户名称是张三,年龄30岁,某公司总经理
|
||
responses:
|
||
200:
|
||
description: 解析成功
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
description: 响应码,0表示成功
|
||
example: 0
|
||
data:
|
||
type: object
|
||
properties:
|
||
outData:
|
||
type: array
|
||
description: 提取的字段列表
|
||
items:
|
||
type: object
|
||
properties:
|
||
fieldCode:
|
||
type: string
|
||
description: 字段编码
|
||
example: target_name
|
||
fieldValue:
|
||
type: string
|
||
description: 提取的字段值
|
||
example: 张三
|
||
msg:
|
||
type: string
|
||
description: 响应消息
|
||
example: ok
|
||
isSuccess:
|
||
type: boolean
|
||
description: 是否成功
|
||
example: true
|
||
timestamp:
|
||
type: string
|
||
description: 时间戳
|
||
errorMsg:
|
||
type: string
|
||
description: 错误信息(成功时为空)
|
||
400:
|
||
description: 请求参数错误
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
example: 400
|
||
errorMsg:
|
||
type: string
|
||
example: 请求参数不能为空
|
||
isSuccess:
|
||
type: boolean
|
||
example: false
|
||
1001:
|
||
description: 业务类型不存在
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
example: 1001
|
||
errorMsg:
|
||
type: string
|
||
example: 未找到业务类型 INVESTIGATION 对应的字段配置
|
||
isSuccess:
|
||
type: boolean
|
||
example: false
|
||
2001:
|
||
description: AI解析超时或发生错误
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
example: 2001
|
||
errorMsg:
|
||
type: string
|
||
example: AI解析超时或发生错误
|
||
isSuccess:
|
||
type: boolean
|
||
example: false
|
||
2002:
|
||
description: AI解析失败
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
example: 2002
|
||
errorMsg:
|
||
type: string
|
||
example: AI解析失败,请检查输入文本质量
|
||
isSuccess:
|
||
type: boolean
|
||
example: false
|
||
"""
|
||
try:
|
||
data = request.get_json()
|
||
|
||
# 验证请求参数
|
||
if not data:
|
||
return error_response(400, "请求参数不能为空")
|
||
|
||
business_type = data.get('businessType')
|
||
input_data = data.get('inputData', [])
|
||
|
||
if not business_type:
|
||
return error_response(400, "businessType参数不能为空")
|
||
|
||
if not input_data or not isinstance(input_data, list):
|
||
return error_response(400, "inputData参数必须是非空数组")
|
||
|
||
# 获取业务类型对应的输出字段
|
||
output_fields = field_service.get_output_fields_by_business_type(business_type)
|
||
|
||
if not output_fields:
|
||
return error_response(1001, f"未找到业务类型 {business_type} 对应的字段配置")
|
||
|
||
# 构建AI提示词
|
||
prompt = field_service.build_extract_prompt(input_data, output_fields, business_type)
|
||
|
||
# 调用AI服务进行解析
|
||
ai_result = ai_service.extract_fields(prompt, output_fields)
|
||
|
||
if not ai_result:
|
||
return error_response(2002, "AI解析失败,请检查输入文本质量")
|
||
|
||
# 构建返回数据
|
||
out_data = []
|
||
for field in output_fields:
|
||
field_code = field['field_code']
|
||
field_value = ai_result.get(field_code, '')
|
||
out_data.append({
|
||
'fieldCode': field_code,
|
||
'fieldValue': field_value
|
||
})
|
||
|
||
return success_response({'outData': out_data})
|
||
|
||
except Exception as e:
|
||
return error_response(2001, f"AI解析超时或发生错误: {str(e)}")
|
||
|
||
|
||
@app.route('/api/fields', methods=['GET'])
|
||
def get_fields():
|
||
"""
|
||
获取字段配置接口
|
||
获取指定业务类型的输入和输出字段配置
|
||
|
||
---
|
||
tags:
|
||
- 字段配置
|
||
summary: 获取字段配置
|
||
description: 获取指定业务类型的输入字段和输出字段配置,用于测试页面展示
|
||
produces:
|
||
- application/json
|
||
parameters:
|
||
- in: query
|
||
name: businessType
|
||
type: string
|
||
required: false
|
||
default: INVESTIGATION
|
||
description: 业务类型
|
||
example: INVESTIGATION
|
||
responses:
|
||
200:
|
||
description: 获取成功
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
description: 响应码,0表示成功
|
||
example: 0
|
||
data:
|
||
type: object
|
||
properties:
|
||
fields:
|
||
type: object
|
||
properties:
|
||
input_fields:
|
||
type: array
|
||
description: 输入字段列表
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
description: 字段ID
|
||
name:
|
||
type: string
|
||
description: 字段名称
|
||
example: 线索信息
|
||
field_code:
|
||
type: string
|
||
description: 字段编码
|
||
example: clue_info
|
||
field_type:
|
||
type: integer
|
||
description: 字段类型(1=输入字段,2=输出字段)
|
||
example: 1
|
||
output_fields:
|
||
type: array
|
||
description: 输出字段列表
|
||
items:
|
||
type: object
|
||
properties:
|
||
id:
|
||
type: integer
|
||
description: 字段ID
|
||
name:
|
||
type: string
|
||
description: 字段名称
|
||
example: 被核查人姓名
|
||
field_code:
|
||
type: string
|
||
description: 字段编码
|
||
example: target_name
|
||
field_type:
|
||
type: integer
|
||
description: 字段类型(1=输入字段,2=输出字段)
|
||
example: 2
|
||
msg:
|
||
type: string
|
||
description: 响应消息
|
||
example: ok
|
||
isSuccess:
|
||
type: boolean
|
||
description: 是否成功
|
||
example: true
|
||
500:
|
||
description: 服务器错误
|
||
schema:
|
||
type: object
|
||
properties:
|
||
code:
|
||
type: integer
|
||
example: 500
|
||
errorMsg:
|
||
type: string
|
||
example: 获取字段配置失败
|
||
isSuccess:
|
||
type: boolean
|
||
example: false
|
||
"""
|
||
try:
|
||
business_type = request.args.get('businessType', 'INVESTIGATION')
|
||
fields = field_service.get_fields_by_business_type(business_type)
|
||
return success_response({'fields': fields})
|
||
except Exception as e:
|
||
return error_response(500, f"获取字段配置失败: {str(e)}")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
# 确保static目录存在
|
||
os.makedirs('static', exist_ok=True)
|
||
|
||
port = int(os.getenv('PORT', 7500))
|
||
debug = os.getenv('DEBUG', 'False').lower() == 'true'
|
||
|
||
print(f"服务启动在 http://localhost:{port}")
|
||
print(f"测试页面: http://localhost:{port}/")
|
||
print(f"Swagger API文档: http://localhost:{port}/api-docs")
|
||
|
||
app.run(host='0.0.0.0', port=port, debug=debug)
|
||
|