Trae Assistant
Initial commit with enhancements
0f22fe9
import os
import json
import logging
import sqlite3
import requests
import datetime
from flask import Flask, render_template, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename
# Configuration
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
INSTANCE_PATH = os.path.join(BASE_DIR, "instance")
DB_PATH = os.path.join(INSTANCE_PATH, "permit_flow.db")
os.makedirs(INSTANCE_PATH, exist_ok=True)
app = Flask(__name__, instance_path=INSTANCE_PATH)
app.config['SECRET_KEY'] = 'dev-secret-key-permit-flow'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload
# Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# SiliconFlow API Configuration
SILICONFLOW_API_KEY = "sk-vimuseiptfbomzegyuvmebjzooncsqbyjtlddrfodzcdskgi"
SILICONFLOW_API_URL = "https://api.siliconflow.cn/v1/chat/completions"
MODEL_NAME = "Qwen/Qwen2.5-7B-Instruct"
# Database Setup
def get_db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
conn = get_db()
c = conn.cursor()
# Projects table (e.g., "Open a Cafe")
c.execute('''CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
industry TEXT,
location TEXT,
status TEXT DEFAULT 'planning',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
risk_score INTEGER DEFAULT 0
)''')
# Permits/Licenses table
c.execute('''CREATE TABLE IF NOT EXISTS permits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER,
name TEXT NOT NULL,
authority TEXT,
status TEXT DEFAULT 'pending', -- pending, preparing, submitted, approved
priority TEXT DEFAULT 'medium',
requirements TEXT, -- JSON list of requirements
estimated_time TEXT,
FOREIGN KEY (project_id) REFERENCES projects (id)
)''')
# Assets table (generated docs, uploaded files)
c.execute('''CREATE TABLE IF NOT EXISTS assets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id INTEGER,
permit_id INTEGER,
name TEXT NOT NULL,
type TEXT, -- document, form, guide
content TEXT, -- Markdown content or file path
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects (id)
)''')
conn.commit()
conn.close()
# Initialize DB on start
init_db()
def populate_default_data():
conn = get_db()
cursor = conn.cursor()
# Check if projects exist
cursor.execute('SELECT count(*) FROM projects')
if cursor.fetchone()[0] == 0:
logger.info("Populating default data...")
# Create a default project
default_project = {
"name": "示例:成都市高新区精品咖啡馆",
"description": "计划在成都市高新区天府三街开设一家主要经营精品手冲咖啡和少量西式甜点的咖啡馆,面积约80平方米。",
"industry": "餐饮/食品",
"location": "成都市高新区",
"risk_score": 45
}
cursor.execute('INSERT INTO projects (name, description, industry, location, risk_score) VALUES (?, ?, ?, ?, ?)',
(default_project['name'], default_project['description'], default_project['industry'], default_project['location'], default_project['risk_score']))
project_id = cursor.lastrowid
# Default permits
permits = [
{"name": "营业执照", "authority": "市场监督管理局", "priority": "high", "estimated_time": "3个工作日", "requirements": ["身份证原件", "经营场所证明", "名称预先核准通知书"]},
{"name": "食品经营许可证", "authority": "市场监督管理局", "priority": "high", "estimated_time": "15个工作日", "requirements": ["营业执照", "健康证", "食品安全规章制度", "平面布局图"]},
{"name": "招牌设置许可", "authority": "城市管理行政执法局", "priority": "medium", "estimated_time": "5个工作日", "requirements": ["效果图", "租赁合同", "营业执照复印件"]}
]
for p in permits:
reqs_json = json.dumps(p['requirements'], ensure_ascii=False)
cursor.execute('INSERT INTO permits (project_id, name, authority, priority, estimated_time, requirements) VALUES (?, ?, ?, ?, ?, ?)',
(project_id, p['name'], p['authority'], p['priority'], p['estimated_time'], reqs_json))
permit_id = cursor.lastrowid
# Add a sample asset for the first permit
if p['name'] == "食品经营许可证":
content = "# 食品安全管理制度\n\n## 第一章 总则\n\n第一条 为保障食品安全,根据《食品安全法》制定本制度。\n\n## 第二章 从业人员健康管理\n\n第二条 从业人员必须持有效健康证上岗..."
cursor.execute('INSERT INTO assets (project_id, permit_id, name, type, content) VALUES (?, ?, ?, ?, ?)',
(project_id, permit_id, "食品安全管理制度范本", "document", content))
conn.commit()
conn.close()
populate_default_data()
# --- AI Service ---
def call_ai_service(system_prompt, user_prompt, temperature=0.7):
headers = {
"Authorization": f"Bearer {SILICONFLOW_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": MODEL_NAME,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
"temperature": temperature,
"max_tokens": 2048
}
try:
response = requests.post(SILICONFLOW_API_URL, headers=headers, json=payload, timeout=30)
response.raise_for_status()
result = response.json()
content = result['choices'][0]['message']['content']
return content
except Exception as e:
logger.error(f"AI Service Error: {str(e)}")
# Mock Fallback
return None
def analyze_business_scenario(name, description, location):
system_prompt = """
你是一个专业的商业合规与行政审批专家智能体 (Permit Flow Agent)。
你的任务是根据用户的商业计划,分析需要办理的行政许可证照。
请以 JSON 格式输出分析结果,不要包含 markdown 代码块标记,直接返回 JSON 对象。
JSON 结构如下:
{
"analysis": "对商业计划的简短合规性分析",
"risk_score": 0-100 (风险评分,越高越难),
"permits": [
{
"name": "许可证名称 (如:食品经营许可证)",
"authority": "审批部门 (如:市场监督管理局)",
"priority": "high/medium/low",
"estimated_time": "预计耗时 (如:15个工作日)",
"requirements": ["要求1", "要求2"]
}
],
"suggested_assets": [
{
"name": "资产名称 (如:食品安全管理制度范本)",
"type": "document",
"content_brief": "文档的大致内容描述"
}
]
}
"""
user_prompt = f"我的商业计划是:{name}。\n详细描述:{description}。\n所在地:{location}。\n请帮我分析需要办理哪些证照。"
ai_response = call_ai_service(system_prompt, user_prompt)
if ai_response:
try:
# Clean up markdown code blocks if present
cleaned_response = ai_response.replace("```json", "").replace("```", "").strip()
return json.loads(cleaned_response)
except json.JSONDecodeError:
logger.error("Failed to parse AI JSON response")
pass # Fallthrough to mock
# Mock Data Fallback
logger.warning("Using Mock Data for Analysis")
return {
"analysis": "模拟模式:基于您的描述,这是一个标准的商业实体注册流程。系统已为您生成预设的合规路径。",
"risk_score": 35,
"permits": [
{
"name": "营业执照",
"authority": "市场监督管理局",
"priority": "high",
"estimated_time": "3个工作日",
"requirements": ["身份证原件", "场地证明", "核名通知书"]
},
{
"name": "行业综合许可证",
"authority": "行政审批局",
"priority": "medium",
"estimated_time": "10个工作日",
"requirements": ["申请表", "平面图", "承诺书"]
}
],
"suggested_assets": [
{
"name": "企业设立登记申请书",
"type": "form",
"content_brief": "标准申请表格模板"
}
]
}
def generate_asset_content(asset_name, context):
system_prompt = """
你是一个专业的商业文书撰写助手。请根据上下文生成详细的文档内容。
输出格式为 Markdown。
"""
user_prompt = f"请为项目撰写一份'{asset_name}'。\n背景信息:{context}"
content = call_ai_service(system_prompt, user_prompt)
if not content:
return f"# {asset_name}\n\n*(模拟模式生成内容)*\n\n这是系统自动生成的{asset_name}模板。\n\n1. 请填写相关信息...\n2. 签字盖章..."
return content
# --- Routes ---
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/projects', methods=['GET'])
def list_projects():
conn = get_db()
projects = conn.execute('SELECT * FROM projects ORDER BY created_at DESC').fetchall()
conn.close()
return jsonify([dict(p) for p in projects])
@app.route('/api/projects', methods=['POST'])
def create_project():
data = request.json
name = data.get('name')
description = data.get('description')
location = data.get('location', 'Unknown')
industry = data.get('industry', 'General')
# 1. AI Analysis
analysis = analyze_business_scenario(name, description, location)
conn = get_db()
cursor = conn.cursor()
# 2. Save Project
cursor.execute('INSERT INTO projects (name, description, industry, location, risk_score) VALUES (?, ?, ?, ?, ?)',
(name, analysis['analysis'], industry, location, analysis['risk_score']))
project_id = cursor.lastrowid
# 3. Save Permits
for permit in analysis['permits']:
reqs_json = json.dumps(permit['requirements'], ensure_ascii=False)
cursor.execute('INSERT INTO permits (project_id, name, authority, priority, estimated_time, requirements) VALUES (?, ?, ?, ?, ?, ?)',
(project_id, permit['name'], permit['authority'], permit['priority'], permit['estimated_time'], reqs_json))
permit_id = cursor.lastrowid
# Create initial asset for this permit if available
# (Simplified: just create one general asset for now based on suggestions)
# 4. Create Suggested Assets
for asset in analysis.get('suggested_assets', []):
# Generate content immediately or later? Let's generate a placeholder first to be fast
content = f"# {asset['name']}\n\n等待生成..."
cursor.execute('INSERT INTO assets (project_id, permit_id, name, type, content) VALUES (?, ?, ?, ?, ?)',
(project_id, 0, asset['name'], asset['type'], content))
conn.commit()
conn.close()
return jsonify({"success": True, "project_id": project_id})
@app.route('/api/projects/<int:project_id>', methods=['GET'])
def get_project(project_id):
conn = get_db()
project = conn.execute('SELECT * FROM projects WHERE id = ?', (project_id,)).fetchone()
permits = conn.execute('SELECT * FROM permits WHERE project_id = ?', (project_id,)).fetchall()
assets = conn.execute('SELECT * FROM assets WHERE project_id = ?', (project_id,)).fetchall()
conn.close()
if not project:
return jsonify({"error": "Not found"}), 404
return jsonify({
"project": dict(project),
"permits": [dict(p) for p in permits],
"assets": [dict(a) for a in assets]
})
@app.route('/api/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files['file']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
if file:
filename = secure_filename(file.filename)
# In a real app, save to disk or S3. Here we just return success and mock a DB entry
# save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
# file.save(save_path)
# Add to assets table as a record
project_id = request.form.get('project_id')
if project_id:
conn = get_db()
cursor = conn.cursor()
cursor.execute('INSERT INTO assets (project_id, permit_id, name, type, content) VALUES (?, ?, ?, ?, ?)',
(project_id, 0, filename, 'file', f"已上传文件: {filename} (存储于服务器)"))
conn.commit()
conn.close()
return jsonify({"success": True, "filename": filename})
return jsonify({"error": "Upload failed"}), 500
@app.errorhandler(404)
def page_not_found(e):
return render_template('index.html'), 200 # Support SPA routing if needed, or just return index
@app.errorhandler(500)
def internal_server_error(e):
return jsonify(error=str(e)), 500
@app.route('/api/assets/generate', methods=['POST'])
def generate_asset():
data = request.json
asset_id = data.get('asset_id')
project_context = data.get('context', '')
conn = get_db()
asset = conn.execute('SELECT * FROM assets WHERE id = ?', (asset_id,)).fetchone()
if not asset:
conn.close()
return jsonify({"error": "Asset not found"}), 404
content = generate_asset_content(asset['name'], project_context)
conn.execute('UPDATE assets SET content = ? WHERE id = ?', (content, asset_id))
conn.commit()
conn.close()
return jsonify({"success": True, "content": content})
@app.route('/api/permits/<int:permit_id>/status', methods=['POST'])
def update_permit_status(permit_id):
data = request.json
status = data.get('status')
conn = get_db()
conn.execute('UPDATE permits SET status = ? WHERE id = ?', (status, permit_id))
conn.commit()
conn.close()
return jsonify({"success": True})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=7860, debug=True)