feat: add Expert 5 (three-stage) and Expert 6 (two-stage) prompt generators

- Expert 5: disambiguation → optimized prompt → sample execution (3 API calls)
- Expert 6: disambiguation → optimized prompt only (2 API calls, faster)
- Chinese disambiguation rules table for correct intent classification
- Domain-specific meta-prompt templates (技术/创意/分析/咨询)
- Simplified homepage to use Expert 6 flow directly, removed template cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 00:06:10 +08:00
parent 8ddd7ddfc7
commit 83c541bb8e
6 changed files with 1709 additions and 4599 deletions

View File

@@ -99,7 +99,19 @@ def create_app(config_class=None):
# 注册智能提示词优化3号专家独立实现含历史记录
from src.flask_prompt_master.routes.expert_generate_3 import expert_generate_3_bp
app.register_blueprint(expert_generate_3_bp)
# 注册智能提示词优化4号专家消歧增强阶段1 + 领域专属阶段2
from src.flask_prompt_master.routes.expert_generate_4 import expert_generate_4_bp
app.register_blueprint(expert_generate_4_bp)
# 注册智能提示词优化5号专家三阶段流水线消歧分析 → 生成优化提示词 → 立即执行样例)
from src.flask_prompt_master.routes.expert_generate_5 import expert_generate_5_bp
app.register_blueprint(expert_generate_5_bp)
# 注册智能提示词优化6号专家两阶段消歧分析 → 生成优化提示词,裁掉执行样例)
from src.flask_prompt_master.routes.expert_generate_6 import expert_generate_6_bp
app.register_blueprint(expert_generate_6_bp)
# 注册 Android 工程师专区Crash 解读、依赖冲突分析等)
from src.flask_prompt_master.routes.android_tools import android_tools_bp
app.register_blueprint(android_tools_bp)

View File

@@ -0,0 +1,319 @@
# -*- coding: utf-8 -*-
"""
智能提示词优化5号专家 — 三阶段流水线
阶段1消歧增强意图分析复用4号
阶段2生成优化提示词核心产出用户可复制复用
阶段3用优化提示词立即调模型产出样例证明提示词质量
"""
from flask import Blueprint, render_template, request, jsonify
from openai import OpenAI
import hashlib
import json
import logging
import os
import time
from datetime import datetime
from src.flask_prompt_master import db
from src.flask_prompt_master.models.models import User, Prompt
from src.flask_prompt_master.models.history_models import PromptHistory, UserStatistics
logger = logging.getLogger(__name__)
expert_generate_5_bp = Blueprint('expert_generate_5', __name__)
_dedup_cache = {}
client = OpenAI(
api_key=os.environ.get('LLM_API_KEY') or 'sk-fdf7cc1c73504e628ec0119b7e11b8cc',
base_url=os.environ.get('LLM_API_URL') or 'https://api.deepseek.com/v1'
)
# 阶段1消歧增强意图分析与4号完全相同
INTENT_PROMPT_V4 = """你是一位资深的意图分析专家。你的任务是精确理解用户需求,尤其要处理中文多义词的歧义。
## 消歧规则(优先级从高到低)
### 规则1"设计"的多义性
| 上下文 | 判定 |
|--------|------|
| 系统设计、架构设计、数据库设计、API设计、模块设计、技术方案设计 | **技术** |
| UI设计、海报设计、品牌设计、视觉设计、创意设计、艺术设计 | **创意** |
| 流程设计、组织设计、商业模式设计、制度设计 | **咨询** |
### 规则2"方案"的多义性
| 上下文 | 判定 |
|--------|------|
| 技术方案、架构方案、实施方案、部署方案 | **技术** |
| 营销方案、活动方案、传播方案、内容方案 | **创意** |
| 管理方案、策略方案、优化方案、治理方案 | **咨询** |
### 规则3"分析"的多义性
| 上下文 | 判定 |
|--------|------|
| 数据分析、日志分析、性能分析、安全分析 | **分析** |
| 需求分析、竞品分析、市场分析 | **咨询** |
| 色彩分析、构图分析、文案分析 | **创意** |
### 规则4技术关键词强信号
看到以下词时,优先判为技术:
Agent、API、SDK、架构、微服务、数据库、后端、前端、部署、DevOps、容器化、分布式、协议、接口、中间件、算法、模型训练、推理、向量、RAG、Pipeline、CI/CD、Kubernetes、Docker、Git
## 判定流程
1. 提取核心名词+动词组合
2. 查询上述消歧表
3. 判断用户最终要产出什么
4. 给出判定 + 置信度
## 输出格式
严格返回以下JSON不要任何额外内容
{
"core_intent": "技术",
"sub_category": "多Agent协作系统设计",
"domain": "二级精确领域",
"confidence": 0.92,
"disambiguation_note": "判定依据简述",
"alternative_intent": "咨询",
"key_requirements": ["需求1", "需求2", "需求3"],
"expected_output": "精确的预期产出描述",
"constraints": ["约束1", "约束2"],
"keywords": ["关键词1", "关键词2", "关键词3"]
}
"""
# 阶段2生成优化提示词的 meta-prompt每种领域不同结构
PROMPT_GENERATOR_TEMPLATES = {
"技术": """你是一位资深提示词工程师专精于技术领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的技术方案。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么技术专家角色架构师/开发者/技术顾问),给出具体的专业背景
2. **任务描述**清晰陈述要完成的技术任务拆解为2-4个可执行步骤
3. **输出结构**:定义输出的章节和每章要包含的具体内容(不要只列标题,要说明每章怎么写)
4. **质量标准**:给出具体的质量要求(如"每个API必须给出字段级规范"而非"详细描述"
5. **约束条件**:明确禁止什么、必须包含什么
关键原则:
- 你生成的是提示词,不是最终答案
- 提示词必须自包含——用户复制后可以直接在任何LLM中使用
- 使用具体的技术术语和可验证的标准
- 每个章节指引都应该是"如何写"而非"写什么"
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"创意": """你是一位资深提示词工程师专精于创意领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的创意方案。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么创意角色创意总监/文案策划/品牌顾问),给出具体的风格偏好
2. **任务描述**:清晰陈述创意任务,包括目标受众、品牌调性、传播目标
3. **输出结构**:定义输出的章节和每章要包含的具体内容——给出框架但不能限制创意发挥
4. **质量标准**:给出具体的创意质量要求(如"每个视觉描述必须包含色彩/形状/情绪三个维度"
5. **灵感框架**提供1-2个创作维度的引导但不预设具体答案
关键原则:
- 你生成的是提示词,不是最终文案/设计稿
- 提示词必须引导LLM发挥创意同时给出足够结构约束避免空泛
- 拒绝"高端大气上档次"这类空话——给出可触摸的质量标准
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"分析": """你是一位资深提示词工程师专精于数据分析领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的数据分析报告。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么分析角色数据分析师/商业分析师/研究员),给出分析方法论倾向
2. **任务描述**:清晰陈述分析目标、数据范围、要回答的核心问题
3. **输出结构**:定义输出的章节——包括分析框架、指标定义、可视化建议、洞察和局限性
4. **质量标准**:给出具体的分析质量标准(如"所有结论必须附数据支撑""指标必须给出计算口径"
5. **方法论指引**建议使用的分析思维MECE/假设驱动/探索式),但不强制
关键原则:
- 你生成的是提示词,不是最终分析报告
- 提示词必须要求LLM展示推理过程而非只给结论
- 要求LLM诚实标注数据局限性和置信度
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"咨询": """你是一位资深提示词工程师专精于管理咨询领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的咨询建议。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么咨询角色管理顾问/策略顾问/组织顾问),给出咨询方法论倾向
2. **任务描述**:清晰陈述咨询问题、客户背景、决策情境
3. **输出结构**定义输出的章节——诊断→方案矩阵→推荐→路线图→风险→ROI
4. **质量标准**:给出具体的咨询质量标准(如"方案必须可比较""风险必须有概率×影响评估"
5. **决策导向**提示词应引导LLM最终输出可执行的决策建议而非泛泛分析
关键原则:
- 你生成的是提示词,不是最终咨询报告
- 提示词必须要求LLM给出带权重的选项而非单一建议
- 要求LLM明确标注假设前提
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。"""
}
def _get_user_id():
try:
from flask_login import current_user
if current_user.is_authenticated:
return getattr(current_user, 'id', None) or getattr(current_user, 'uid', None)
except Exception:
pass
from flask import session
uid = session.get('user_id')
if uid is not None:
return uid
try:
u = User.query.filter_by(login_name='admin').first()
return u.uid if u else 1
except Exception as e:
logger.warning("5号专家 获取默认用户失败: %s", e)
return 1
@expert_generate_5_bp.route('/expert-generate-5', methods=['GET'])
def expert_generate_5_page():
return render_template('expert_generate_5.html')
@expert_generate_5_bp.route('/api/expert-generate-5/generate', methods=['POST'])
def expert_generate_5_api():
"""三阶段流水线:消歧分析 → 生成提示词 → 执行样例"""
try:
if not request.is_json:
return jsonify({'code': 400, 'message': '请求必须是JSON格式', 'data': None})
payload = request.get_json() or {}
raw_input = (payload.get('input_text') or '').strip()
if not raw_input:
return jsonify({'code': 400, 'message': '请输入您的需求', 'data': None})
uid = _get_user_id()
req_key = (uid, hashlib.md5(raw_input.encode()).hexdigest())
now_ts = time.time()
if req_key in _dedup_cache and (now_ts - _dedup_cache[req_key]) < 8:
return jsonify({'code': 429, 'message': '请勿重复提交', 'data': None})
_dedup_cache[req_key] = now_ts
if len(_dedup_cache) > 500:
_dedup_cache.clear()
# ===== 阶段1消歧意图分析 =====
logger.info("5号专家 阶段1 开始")
resp1 = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": INTENT_PROMPT_V4},
{"role": "user", "content": raw_input}
],
temperature=0.05,
timeout=60
)
intent_raw = (resp1.choices[0].message.content or "").strip()
intent_raw = intent_raw.replace('```json', '').replace('```', '').strip()
try:
intent_data = json.loads(intent_raw)
for f in ['core_intent', 'domain', 'key_requirements', 'expected_output', 'constraints', 'keywords']:
if f not in intent_data:
raise ValueError(f"缺少字段: {f}")
if intent_data['core_intent'] not in ('技术', '创意', '分析', '咨询'):
intent_data['core_intent'] = '技术'
for arr_f in ['key_requirements', 'constraints', 'keywords']:
v = intent_data.get(arr_f)
if not isinstance(v, list) or len(v) == 0:
intent_data[arr_f] = ['未指定']
intent_data.setdefault('sub_category', intent_data['domain'])
intent_data.setdefault('confidence', 0.75)
intent_data.setdefault('disambiguation_note', '')
intent_data.setdefault('alternative_intent', '')
except (json.JSONDecodeError, ValueError) as e:
logger.error("5号专家 JSON解析失败: %s", e)
return jsonify({'code': 500, 'message': '意图分析格式有误,请重试', 'data': None})
logger.info("5号专家 阶段1完成 | intent=%s | confidence=%.2f",
intent_data['core_intent'], intent_data.get('confidence', 0))
# ===== 阶段2生成优化提示词 =====
logger.info("5号专家 阶段2 开始")
core_intent = intent_data['core_intent']
meta_prompt = PROMPT_GENERATOR_TEMPLATES[core_intent]
analysis_str = json.dumps(intent_data, ensure_ascii=False, indent=2)
resp2 = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": meta_prompt.format(analysis=analysis_str)},
{"role": "user", "content": f"用户需求:{raw_input}\n\n请基于上述意图分析,生成一个高质量的提示词。"}
],
temperature=0.5,
max_tokens=1500,
timeout=90
)
optimized_prompt = (resp2.choices[0].message.content or "").strip()
if not optimized_prompt:
return jsonify({'code': 500, 'message': '提示词生成失败', 'data': None})
logger.info("5号专家 阶段2完成 | prompt_len=%d", len(optimized_prompt))
# ===== 阶段3执行样例 =====
logger.info("5号专家 阶段3 开始")
resp3 = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": optimized_prompt},
{"role": "user", "content": raw_input}
],
temperature=0.7,
max_tokens=1200,
timeout=90
)
sample_output = (resp3.choices[0].message.content or "").strip()
if not sample_output:
sample_output = "(模型未返回样例,请直接用上方提示词调用)"
logger.info("5号专家 阶段3完成 | sample_len=%d", len(sample_output))
# 保存
try:
db.session.add(Prompt(
input_text=raw_input,
generated_text=optimized_prompt,
user_id=uid,
created_at=datetime.utcnow()
))
db.session.commit()
except Exception as e:
db.session.rollback()
logger.warning("5号专家 保存失败: %s", e)
try:
PromptHistory.add_history(
user_id=uid,
original_input=raw_input,
generated_prompt=optimized_prompt,
template_name=f'智能提示词优化5号专家三阶段-{core_intent}'
)
UserStatistics.update_statistics(uid)
except Exception as e:
logger.warning("5号专家 历史保存失败: %s", e)
return jsonify({
'code': 200,
'message': 'success',
'data': {
'method': 'three-stage-pipeline',
'api_calls': 3,
'intent_analysis': intent_data,
'optimized_prompt': optimized_prompt,
'sample_output': sample_output
}
})
except Exception as e:
logger.exception("5号专家 生成失败")
return jsonify({'code': 500, 'message': str(e) or '生成失败,请重试', 'data': None})

View File

@@ -0,0 +1,300 @@
# -*- coding: utf-8 -*-
"""
智能提示词优化6号专家 — 两阶段流水线
阶段1消歧增强意图分析复用4号
阶段2生成优化提示词核心产出用户可复制复用
无阶段3裁掉执行样例只输出优化提示词
"""
from flask import Blueprint, render_template, request, jsonify
from openai import OpenAI
import hashlib
import json
import logging
import os
import time
from datetime import datetime
from src.flask_prompt_master import db
from src.flask_prompt_master.models.models import User, Prompt
from src.flask_prompt_master.models.history_models import PromptHistory, UserStatistics
logger = logging.getLogger(__name__)
expert_generate_6_bp = Blueprint('expert_generate_6', __name__)
_dedup_cache = {}
client = OpenAI(
api_key=os.environ.get('LLM_API_KEY') or 'sk-fdf7cc1c73504e628ec0119b7e11b8cc',
base_url=os.environ.get('LLM_API_URL') or 'https://api.deepseek.com/v1'
)
# 阶段1消歧增强意图分析与4号完全相同
INTENT_PROMPT_V4 = """你是一位资深的意图分析专家。你的任务是精确理解用户需求,尤其要处理中文多义词的歧义。
## 消歧规则(优先级从高到低)
### 规则1"设计"的多义性
| 上下文 | 判定 |
|--------|------|
| 系统设计、架构设计、数据库设计、API设计、模块设计、技术方案设计 | **技术** |
| UI设计、海报设计、品牌设计、视觉设计、创意设计、艺术设计 | **创意** |
| 流程设计、组织设计、商业模式设计、制度设计 | **咨询** |
### 规则2"方案"的多义性
| 上下文 | 判定 |
|--------|------|
| 技术方案、架构方案、实施方案、部署方案 | **技术** |
| 营销方案、活动方案、传播方案、内容方案 | **创意** |
| 管理方案、策略方案、优化方案、治理方案 | **咨询** |
### 规则3"分析"的多义性
| 上下文 | 判定 |
|--------|------|
| 数据分析、日志分析、性能分析、安全分析 | **分析** |
| 需求分析、竞品分析、市场分析 | **咨询** |
| 色彩分析、构图分析、文案分析 | **创意** |
### 规则4技术关键词强信号
看到以下词时,优先判为技术:
Agent、API、SDK、架构、微服务、数据库、后端、前端、部署、DevOps、容器化、分布式、协议、接口、中间件、算法、模型训练、推理、向量、RAG、Pipeline、CI/CD、Kubernetes、Docker、Git
## 判定流程
1. 提取核心名词+动词组合
2. 查询上述消歧表
3. 判断用户最终要产出什么
4. 给出判定 + 置信度
## 输出格式
严格返回以下JSON不要任何额外内容
{
"core_intent": "技术",
"sub_category": "多Agent协作系统设计",
"domain": "二级精确领域",
"confidence": 0.92,
"disambiguation_note": "判定依据简述",
"alternative_intent": "咨询",
"key_requirements": ["需求1", "需求2", "需求3"],
"expected_output": "精确的预期产出描述",
"constraints": ["约束1", "约束2"],
"keywords": ["关键词1", "关键词2", "关键词3"]
}
"""
# 阶段2生成优化提示词的 meta-prompt每种领域不同结构
PROMPT_GENERATOR_TEMPLATES = {
"技术": """你是一位资深提示词工程师专精于技术领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的技术方案。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么技术专家角色架构师/开发者/技术顾问),给出具体的专业背景
2. **任务描述**清晰陈述要完成的技术任务拆解为2-4个可执行步骤
3. **输出结构**:定义输出的章节和每章要包含的具体内容(不要只列标题,要说明每章怎么写)
4. **质量标准**:给出具体的质量要求(如"每个API必须给出字段级规范"而非"详细描述"
5. **约束条件**:明确禁止什么、必须包含什么
关键原则:
- 你生成的是提示词,不是最终答案
- 提示词必须自包含——用户复制后可以直接在任何LLM中使用
- 使用具体的技术术语和可验证的标准
- 每个章节指引都应该是"如何写"而非"写什么"
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"创意": """你是一位资深提示词工程师专精于创意领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的创意方案。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么创意角色创意总监/文案策划/品牌顾问),给出具体的风格偏好
2. **任务描述**:清晰陈述创意任务,包括目标受众、品牌调性、传播目标
3. **输出结构**:定义输出的章节和每章要包含的具体内容——给出框架但不能限制创意发挥
4. **质量标准**:给出具体的创意质量要求(如"每个视觉描述必须包含色彩/形状/情绪三个维度"
5. **灵感框架**提供1-2个创作维度的引导但不预设具体答案
关键原则:
- 你生成的是提示词,不是最终文案/设计稿
- 提示词必须引导LLM发挥创意同时给出足够结构约束避免空泛
- 拒绝"高端大气上档次"这类空话——给出可触摸的质量标准
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"分析": """你是一位资深提示词工程师专精于数据分析领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的数据分析报告。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么分析角色数据分析师/商业分析师/研究员),给出分析方法论倾向
2. **任务描述**:清晰陈述分析目标、数据范围、要回答的核心问题
3. **输出结构**:定义输出的章节——包括分析框架、指标定义、可视化建议、洞察和局限性
4. **质量标准**:给出具体的分析质量标准(如"所有结论必须附数据支撑""指标必须给出计算口径"
5. **方法论指引**建议使用的分析思维MECE/假设驱动/探索式),但不强制
关键原则:
- 你生成的是提示词,不是最终分析报告
- 提示词必须要求LLM展示推理过程而非只给结论
- 要求LLM诚实标注数据局限性和置信度
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。""",
"咨询": """你是一位资深提示词工程师专精于管理咨询领域。你的任务是生成一个高质量的提示词让下游LLM能够输出专业的咨询建议。
意图分析:
{analysis}
请生成一个完整的提示词,必须包含以下要素:
1. **角色设定**明确LLM扮演什么咨询角色管理顾问/策略顾问/组织顾问),给出咨询方法论倾向
2. **任务描述**:清晰陈述咨询问题、客户背景、决策情境
3. **输出结构**定义输出的章节——诊断→方案矩阵→推荐→路线图→风险→ROI
4. **质量标准**:给出具体的咨询质量标准(如"方案必须可比较""风险必须有概率×影响评估"
5. **决策导向**提示词应引导LLM最终输出可执行的决策建议而非泛泛分析
关键原则:
- 你生成的是提示词,不是最终咨询报告
- 提示词必须要求LLM给出带权重的选项而非单一建议
- 要求LLM明确标注假设前提
请在末尾附上一句给用户的简短说明(用「」括起来),解释这个提示词适合什么场景使用。"""
}
def _get_user_id():
try:
from flask_login import current_user
if current_user.is_authenticated:
return getattr(current_user, 'id', None) or getattr(current_user, 'uid', None)
except Exception:
pass
from flask import session
uid = session.get('user_id')
if uid is not None:
return uid
try:
u = User.query.filter_by(login_name='admin').first()
return u.uid if u else 1
except Exception as e:
logger.warning("6号专家 获取默认用户失败: %s", e)
return 1
@expert_generate_6_bp.route('/expert-generate-6', methods=['GET'])
def expert_generate_6_page():
return render_template('expert_generate_6.html')
@expert_generate_6_bp.route('/api/expert-generate-6/generate', methods=['POST'])
def expert_generate_6_api():
"""两阶段流水线:消歧分析 → 生成提示词(无执行样例)"""
try:
if not request.is_json:
return jsonify({'code': 400, 'message': '请求必须是JSON格式', 'data': None})
payload = request.get_json() or {}
raw_input = (payload.get('input_text') or '').strip()
if not raw_input:
return jsonify({'code': 400, 'message': '请输入您的需求', 'data': None})
uid = _get_user_id()
req_key = (uid, hashlib.md5(raw_input.encode()).hexdigest())
now_ts = time.time()
if req_key in _dedup_cache and (now_ts - _dedup_cache[req_key]) < 8:
return jsonify({'code': 429, 'message': '请勿重复提交', 'data': None})
_dedup_cache[req_key] = now_ts
if len(_dedup_cache) > 500:
_dedup_cache.clear()
# ===== 阶段1消歧意图分析 =====
logger.info("6号专家 阶段1 开始")
resp1 = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": INTENT_PROMPT_V4},
{"role": "user", "content": raw_input}
],
temperature=0.05,
timeout=60
)
intent_raw = (resp1.choices[0].message.content or "").strip()
intent_raw = intent_raw.replace('```json', '').replace('```', '').strip()
try:
intent_data = json.loads(intent_raw)
for f in ['core_intent', 'domain', 'key_requirements', 'expected_output', 'constraints', 'keywords']:
if f not in intent_data:
raise ValueError(f"缺少字段: {f}")
if intent_data['core_intent'] not in ('技术', '创意', '分析', '咨询'):
intent_data['core_intent'] = '技术'
for arr_f in ['key_requirements', 'constraints', 'keywords']:
v = intent_data.get(arr_f)
if not isinstance(v, list) or len(v) == 0:
intent_data[arr_f] = ['未指定']
intent_data.setdefault('sub_category', intent_data['domain'])
intent_data.setdefault('confidence', 0.75)
intent_data.setdefault('disambiguation_note', '')
intent_data.setdefault('alternative_intent', '')
except (json.JSONDecodeError, ValueError) as e:
logger.error("6号专家 JSON解析失败: %s", e)
return jsonify({'code': 500, 'message': '意图分析格式有误,请重试', 'data': None})
logger.info("6号专家 阶段1完成 | intent=%s | confidence=%.2f",
intent_data['core_intent'], intent_data.get('confidence', 0))
# ===== 阶段2生成优化提示词 =====
logger.info("6号专家 阶段2 开始")
core_intent = intent_data['core_intent']
meta_prompt = PROMPT_GENERATOR_TEMPLATES[core_intent]
analysis_str = json.dumps(intent_data, ensure_ascii=False, indent=2)
resp2 = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": meta_prompt.format(analysis=analysis_str)},
{"role": "user", "content": f"用户需求:{raw_input}\n\n请基于上述意图分析,生成一个高质量的提示词。"}
],
temperature=0.5,
max_tokens=1500,
timeout=90
)
optimized_prompt = (resp2.choices[0].message.content or "").strip()
if not optimized_prompt:
return jsonify({'code': 500, 'message': '提示词生成失败', 'data': None})
logger.info("6号专家 阶段2完成 | prompt_len=%d", len(optimized_prompt))
# 保存
try:
db.session.add(Prompt(
input_text=raw_input,
generated_text=optimized_prompt,
user_id=uid,
created_at=datetime.utcnow()
))
db.session.commit()
except Exception as e:
db.session.rollback()
logger.warning("6号专家 保存失败: %s", e)
try:
PromptHistory.add_history(
user_id=uid,
original_input=raw_input,
generated_prompt=optimized_prompt,
template_name=f'智能提示词优化6号专家两阶段-{core_intent}'
)
UserStatistics.update_statistics(uid)
except Exception as e:
logger.warning("6号专家 历史保存失败: %s", e)
return jsonify({
'code': 200,
'message': 'success',
'data': {
'method': 'two-stage-pipeline',
'api_calls': 2,
'intent_analysis': intent_data,
'optimized_prompt': optimized_prompt
}
})
except Exception as e:
logger.exception("6号专家 生成失败")
return jsonify({'code': 500, 'message': str(e) or '生成失败,请重试', 'data': None})

View File

@@ -0,0 +1,396 @@
{% extends "base.html" %}
{% block title %}5号专家提示词+样例){% endblock %}
{% block content %}
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-gem"></i> 提示词生成器 5号</h1>
<p class="subtitle">三阶段 · 消歧分析 → 生成优化提示词 → 立即执行样例 · 真正可复用</p>
</div>
<div class="diff-badge">
<span class="badge-tag">核心创新</span>
<span class="diff-detail">一次请求返回两样东西:可复制的优化提示词(你拿走用)+ 立即执行的样例(看效果)</span>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step"><span class="step-num">1</span><span class="step-label">消歧分析</span></div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step"><span class="step-num">2</span><span class="step-label">生成提示词</span></div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step"><span class="step-num">3</span><span class="step-label">执行样例</span></div>
</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如写一篇关于agent协助的设计方案..."></textarea>
<span class="input-hint">系统将生成可复用的优化提示词 + 即时执行的样例结果</span>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-magic"></i> 生成提示词 + 样例
</button>
</form>
</div>
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在消歧分析...</p>
<div class="loading-steps">
<span class="load-step active">消歧识别</span>
<span class="load-step">生成提示词</span>
<span class="load-step">执行样例</span>
</div>
<p class="loading-sub">3 次 API 调用,约 15-30 秒</p>
</div>
</div>
<div id="resultArea" style="display:none;">
<!-- 消歧分析 -->
<div class="result-card">
<div class="result-section disamb-section">
<h3><i class="fas fa-balance-scale"></i> 消歧分析</h3>
<div class="disamb-grid">
<div class="disamb-item">
<span class="disamb-label">判定</span>
<span class="disamb-value" id="coreIntent"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">置信度</span>
<span class="disamb-value" id="confidenceVal"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">子类</span>
<span class="disamb-value" id="subCategory"></span>
</div>
<div class="disamb-item full-width">
<span class="disamb-label">依据</span>
<span class="disamb-value" id="disambNote" style="font-size:0.85rem;"></span>
</div>
</div>
</div>
</div>
<!-- 主产出:优化提示词 -->
<div class="result-card prompt-hero">
<div class="result-section">
<div class="result-header">
<div>
<h3><i class="fas fa-star" style="color:#f59e0b;"></i> 优化后的提示词</h3>
<p class="hero-subtitle">这是你的核心产出——复制后可在任何 LLM 中复用</p>
</div>
<button class="btn-copy btn-copy-hero" id="copyPromptBtn">
<i class="fas fa-copy"></i> 复制提示词
</button>
</div>
<div class="result-content prompt-content" id="optimizedPrompt"></div>
</div>
</div>
<!-- 样例结果 -->
<div class="result-card sample-card">
<div class="result-section">
<div class="result-header">
<div>
<h3><i class="fas fa-flask" style="color:#6366f1;"></i> 即时执行样例</h3>
<p class="hero-subtitle">用上方提示词立即调模型产出的结果——证明提示词质量</p>
</div>
<button class="btn-copy" id="copySampleBtn">
<i class="fas fa-copy"></i> 复制样例
</button>
</div>
<div class="result-content sample-content" id="sampleOutput"></div>
</div>
</div>
</div>
</div>
</div>
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #fffbeb 0%, #faf5ff 100%);
padding: 3rem 1.5rem;
}
.expert-container { max-width: 800px; margin: 0 auto; }
.page-header { text-align: center; margin-bottom: 1.5rem; }
.page-header h1 {
font-size: 2rem; font-weight: 700; color: var(--text-color);
display: flex; align-items: center; justify-content: center; gap: 0.75rem;
}
.page-header h1 i {
background: linear-gradient(135deg, #f59e0b, #8b5cf6);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.page-header .subtitle { color: var(--text-light); font-size: 1rem; }
.diff-badge {
background: white; border: 1px dashed #8b5cf6; border-radius: 10px;
padding: 0.75rem 1rem; margin-bottom: 1.5rem; display: flex;
align-items: center; gap: 0.75rem; font-size: 0.85rem;
}
.badge-tag {
background: linear-gradient(135deg, #f59e0b, #8b5cf6); color: white;
padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600; white-space: nowrap;
}
.diff-detail { color: var(--text-secondary); }
.flow-steps {
display: flex; align-items: center; justify-content: center;
gap: 0.75rem; margin-bottom: 2rem; padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.step-num {
width: 36px; height: 36px; border-radius: 50%;
background: linear-gradient(135deg, #f59e0b, #8b5cf6);
color: white; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.9rem;
}
.step-label { font-size: 0.8rem; color: var(--text-light); font-weight: 500; }
.step-arrow { color: var(--text-muted); font-size: 0.85rem; margin-top: -14px; }
.expert-card {
background: white; border-radius: 16px; padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group { margin-bottom: 1.5rem; }
.input-group label { display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5rem; }
.input-group textarea {
width: 100%; padding: 1rem; border: 2px solid var(--border-color);
border-radius: 12px; font-size: 0.95rem; font-family: inherit; line-height: 1.6;
resize: vertical; min-height: 140px; transition: border-color 0.2s ease;
}
.input-group textarea:focus { outline: none; border-color: #8b5cf6; box-shadow: 0 0 0 3px rgba(139,92,246,0.1); }
.input-hint { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); }
button[type="submit"] {
width: 100%; padding: 0.875rem;
background: linear-gradient(135deg, #f59e0b, #8b5cf6);
color: white; border: none; border-radius: 12px; font-size: 1.05rem;
font-weight: 600; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
button[type="submit"]:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(139,92,246,0.3); }
button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
.loading-card {
background: white; border-radius: 16px; padding: 3rem 2rem; text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-top: 1.5rem;
}
.loading-spinner {
width: 48px; height: 48px; border: 3px solid var(--border-color);
border-top-color: #8b5cf6; border-radius: 50%; margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 1.05rem; color: var(--text-color); font-weight: 500; margin-bottom: 1.5rem; }
.loading-sub { font-size: 0.8rem; color: var(--text-muted); margin-top: 1rem; }
.loading-steps { display: flex; gap: 1.5rem; justify-content: center; }
.load-step { font-size: 0.8rem; color: var(--text-muted); padding: 0.35rem 0.75rem; border-radius: 20px; background: #f1f5f9; transition: all 0.5s ease; }
.load-step.active { background: linear-gradient(135deg, #f59e0b, #8b5cf6); color: white; }
.result-card {
background: white; border-radius: 16px; overflow: hidden; margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.result-section { padding: 1.5rem 2rem; }
.result-section h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; }
.disamb-section { background: #fffdf5; }
.disamb-section h3 i { color: #f59e0b; }
.disamb-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.disamb-item { background: white; border-radius: 10px; padding: 1rem; border: 1px solid #fef3c7; }
.disamb-item.full-width { grid-column: 1 / -1; }
.disamb-label { display: block; font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.disamb-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
/* 提示词卡片 - 主产出 */
.prompt-hero { border: 2px solid #fbbf24; }
.prompt-hero .result-section { padding-bottom: 1.5rem; }
.hero-subtitle { font-size: 0.8rem; color: var(--text-muted); margin: 0; }
.btn-copy-hero {
background: linear-gradient(135deg, #f59e0b, #d97706) !important;
color: white !important; border: none !important;
font-weight: 600 !important; padding: 0.5rem 1rem !important;
}
.btn-copy-hero:hover { box-shadow: 0 2px 8px rgba(245,158,11,0.4) !important; }
.prompt-content {
background: #fffbeb !important; border: 1px solid #fde68a;
max-height: 500px; overflow-y: auto;
}
/* 样例卡片 */
.sample-card { border-left: 3px solid #6366f1; }
.sample-card .result-section h3 i { color: #6366f1; }
.sample-content {
background: #f8fafc !important;
max-height: 400px; overflow-y: auto;
}
.result-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
.result-header h3 { margin-bottom: 0; }
.btn-copy {
display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.875rem;
border-radius: 6px; border: 1px solid var(--border-color); background: white;
cursor: pointer; font-size: 0.85rem; color: var(--text-secondary); transition: all 0.2s ease;
white-space: nowrap;
}
.btn-copy:hover { background: #f1f5f9; }
.btn-copy.copied { background: #10b981 !important; color: white !important; border-color: #10b981 !important; }
.result-content {
margin-top: 0; background: #f8fafc; border-radius: 10px; padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 0.9rem;
line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--text-color);
}
.toast-container { position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; }
.toast { padding: 0.75rem 1.25rem; border-radius: 8px; color: white; font-size: 0.875rem; font-weight: 500; animation: slideIn 0.3s ease; }
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@media (max-width: 768px) {
.expert-layout { padding: 1.5rem 1rem; }
.expert-card { padding: 1.25rem; }
.disamb-grid { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block scripts %}
<script>
(function() {
var form = document.getElementById('expertForm');
var textarea = document.getElementById('inputText');
var generateBtn = document.getElementById('generateBtn');
var loadingArea = document.getElementById('loadingArea');
var loadingText = document.getElementById('loadingText');
var resultArea = document.getElementById('resultArea');
var loadSteps = document.querySelectorAll('.load-step');
function showToast(msg, type) {
var c = document.getElementById('toastContainer');
var t = document.createElement('div');
t.className = 'toast ' + type;
t.textContent = msg;
c.appendChild(t);
setTimeout(function(){ t.style.opacity='0'; t.style.transition='all 0.3s ease'; setTimeout(function(){ t.remove(); }, 300); }, 3500);
}
function animateLoadStep(i) {
loadSteps.forEach(function(s, j) { s.classList.toggle('active', j <= i); });
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) { showToast('请输入需求描述', 'error'); textarea.focus(); return; }
if (input.length < 10) { showToast('需求过短请至少输入10个字', 'error'); textarea.focus(); return; }
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 三阶段处理中...';
animateLoadStep(0); loadingText.textContent = '阶段1/3消歧分析...';
var t1 = setTimeout(function(){ animateLoadStep(1); loadingText.textContent = '阶段2/3生成优化提示词...'; }, 1500);
var t2 = setTimeout(function(){ animateLoadStep(2); loadingText.textContent = '阶段3/3执行样例验证...'; }, 4000);
try {
var resp = await fetch('/api/expert-generate-5/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input })
});
var data = await resp.json();
clearTimeout(t1); clearTimeout(t2);
if (data.code === 200) {
var ia = data.data.intent_analysis;
var conf = ia.confidence || 0;
// 消歧区块
document.getElementById('coreIntent').textContent = ia.core_intent;
document.getElementById('confidenceVal').textContent = (conf * 100).toFixed(0) + '%' + (conf >= 0.85 ? ' ✓' : conf >= 0.7 ? ' ⚠' : ' ✗');
document.getElementById('subCategory').textContent = ia.sub_category || ia.domain;
document.getElementById('disambNote').textContent = ia.disambiguation_note || '未触发消歧规则';
// 主产出:优化提示词
document.getElementById('optimizedPrompt').textContent = data.data.optimized_prompt;
// 样例结果
document.getElementById('sampleOutput').textContent = data.data.sample_output;
loadingArea.style.display = 'none';
resultArea.style.display = '';
document.querySelector('.prompt-hero').scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败', 'error');
}
} catch (err) {
console.error(err);
clearTimeout(t1); clearTimeout(t2);
loadingArea.style.display = 'none';
showToast('网络请求失败', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-magic"></i> 生成提示词 + 样例';
loadSteps.forEach(function(s) { s.classList.remove('active'); });
}
});
// 复制提示词
document.getElementById('copyPromptBtn').addEventListener('click', function() {
copyText('optimizedPrompt', this);
});
// 复制样例
document.getElementById('copySampleBtn').addEventListener('click', function() {
copyText('sampleOutput', this);
});
function copyText(elementId, btn) {
var text = document.getElementById(elementId).textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function(){
showCopied(btn);
}).catch(function(){ fallbackCopy(text, btn); });
} else {
fallbackCopy(text, btn);
}
}
function fallbackCopy(text, btn) {
var ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta); showCopied(btn);
}
function showCopied(btn) {
var orig = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check"></i> 已复制';
btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = orig; btn.classList.remove('copied'); }, 2000);
}
})();
</script>
{% endblock %}

View File

@@ -0,0 +1,357 @@
{% extends "base.html" %}
{% block title %}6号专家纯提示词{% endblock %}
{% block content %}
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-bolt"></i> 提示词生成器 6号</h1>
<p class="subtitle">两阶段 · 消歧分析 → 生成优化提示词 · 去掉执行样例,更快更轻量</p>
</div>
<div class="diff-badge">
<span class="badge-tag">与5号的差异</span>
<span class="diff-detail">裁掉阶段3执行样例2次API调用约10-15秒只输出可复用的优化提示词</span>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step"><span class="step-num">1</span><span class="step-label">消歧分析</span></div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step"><span class="step-num">2</span><span class="step-label">生成提示词</span></div>
</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如写一篇关于agent协助的设计方案..."></textarea>
<span class="input-hint">系统将生成可复用的优化提示词(仅此一物,拿走即用)</span>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-bolt"></i> 生成优化提示词
</button>
</form>
</div>
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在消歧分析...</p>
<div class="loading-steps">
<span class="load-step active">消歧识别</span>
<span class="load-step">生成提示词</span>
</div>
<p class="loading-sub">2 次 API 调用,约 10-15 秒</p>
</div>
</div>
<div id="resultArea" style="display:none;">
<!-- 消歧分析 -->
<div class="result-card">
<div class="result-section disamb-section">
<h3><i class="fas fa-balance-scale"></i> 消歧分析</h3>
<div class="disamb-grid">
<div class="disamb-item">
<span class="disamb-label">判定</span>
<span class="disamb-value" id="coreIntent"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">置信度</span>
<span class="disamb-value" id="confidenceVal"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">子类</span>
<span class="disamb-value" id="subCategory"></span>
</div>
<div class="disamb-item full-width">
<span class="disamb-label">依据</span>
<span class="disamb-value" id="disambNote" style="font-size:0.85rem;"></span>
</div>
</div>
</div>
</div>
<!-- 主产出:优化提示词 -->
<div class="result-card prompt-hero">
<div class="result-section">
<div class="result-header">
<div>
<h3><i class="fas fa-star" style="color:#06b6d4;"></i> 优化后的提示词</h3>
<p class="hero-subtitle">复制后可在任何 LLM 中复用</p>
</div>
<button class="btn-copy btn-copy-hero" id="copyPromptBtn">
<i class="fas fa-copy"></i> 复制提示词
</button>
</div>
<div class="result-content prompt-content" id="optimizedPrompt"></div>
</div>
</div>
</div>
</div>
</div>
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #ecfeff 0%, #f0f9ff 100%);
padding: 3rem 1.5rem;
}
.expert-container { max-width: 800px; margin: 0 auto; }
.page-header { text-align: center; margin-bottom: 1.5rem; }
.page-header h1 {
font-size: 2rem; font-weight: 700; color: var(--text-color);
display: flex; align-items: center; justify-content: center; gap: 0.75rem;
}
.page-header h1 i {
background: linear-gradient(135deg, #06b6d4, #3b82f6);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.page-header .subtitle { color: var(--text-light); font-size: 1rem; }
.diff-badge {
background: white; border: 1px dashed #06b6d4; border-radius: 10px;
padding: 0.75rem 1rem; margin-bottom: 1.5rem; display: flex;
align-items: center; gap: 0.75rem; font-size: 0.85rem;
}
.badge-tag {
background: linear-gradient(135deg, #06b6d4, #3b82f6); color: white;
padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600; white-space: nowrap;
}
.diff-detail { color: var(--text-secondary); }
.flow-steps {
display: flex; align-items: center; justify-content: center;
gap: 0.75rem; margin-bottom: 2rem; padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.step-num {
width: 36px; height: 36px; border-radius: 50%;
background: linear-gradient(135deg, #06b6d4, #3b82f6);
color: white; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.9rem;
}
.step-label { font-size: 0.8rem; color: var(--text-light); font-weight: 500; }
.step-arrow { color: var(--text-muted); font-size: 0.85rem; margin-top: -14px; }
.expert-card {
background: white; border-radius: 16px; padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group { margin-bottom: 1.5rem; }
.input-group label { display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5rem; }
.input-group textarea {
width: 100%; padding: 1rem; border: 2px solid var(--border-color);
border-radius: 12px; font-size: 0.95rem; font-family: inherit; line-height: 1.6;
resize: vertical; min-height: 140px; transition: border-color 0.2s ease;
}
.input-group textarea:focus { outline: none; border-color: #06b6d4; box-shadow: 0 0 0 3px rgba(6,182,212,0.1); }
.input-hint { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); }
button[type="submit"] {
width: 100%; padding: 0.875rem;
background: linear-gradient(135deg, #06b6d4, #3b82f6);
color: white; border: none; border-radius: 12px; font-size: 1.05rem;
font-weight: 600; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
button[type="submit"]:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(6,182,212,0.3); }
button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
.loading-card {
background: white; border-radius: 16px; padding: 3rem 2rem; text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-top: 1.5rem;
}
.loading-spinner {
width: 48px; height: 48px; border: 3px solid var(--border-color);
border-top-color: #06b6d4; border-radius: 50%; margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 1.05rem; color: var(--text-color); font-weight: 500; margin-bottom: 1.5rem; }
.loading-sub { font-size: 0.8rem; color: var(--text-muted); margin-top: 1rem; }
.loading-steps { display: flex; gap: 1.5rem; justify-content: center; }
.load-step { font-size: 0.8rem; color: var(--text-muted); padding: 0.35rem 0.75rem; border-radius: 20px; background: #f1f5f9; transition: all 0.5s ease; }
.load-step.active { background: linear-gradient(135deg, #06b6d4, #3b82f6); color: white; }
.result-card {
background: white; border-radius: 16px; overflow: hidden; margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.result-section { padding: 1.5rem 2rem; }
.result-section h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 0.25rem; display: flex; align-items: center; gap: 0.5rem; }
.disamb-section { background: #f0fdfa; }
.disamb-section h3 i { color: #06b6d4; }
.disamb-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.disamb-item { background: white; border-radius: 10px; padding: 1rem; border: 1px solid #ccfbf1; }
.disamb-item.full-width { grid-column: 1 / -1; }
.disamb-label { display: block; font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.disamb-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
.prompt-hero { border: 2px solid #67e8f9; }
.prompt-hero .result-section { padding-bottom: 1.5rem; }
.hero-subtitle { font-size: 0.8rem; color: var(--text-muted); margin: 0; }
.btn-copy-hero {
background: linear-gradient(135deg, #06b6d4, #0891b2) !important;
color: white !important; border: none !important;
font-weight: 600 !important; padding: 0.5rem 1rem !important;
}
.btn-copy-hero:hover { box-shadow: 0 2px 8px rgba(6,182,212,0.4) !important; }
.prompt-content {
background: #f0fdfa !important; border: 1px solid #99f6e4;
max-height: 600px; overflow-y: auto;
}
.result-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
.result-header h3 { margin-bottom: 0; }
.btn-copy {
display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.875rem;
border-radius: 6px; border: 1px solid var(--border-color); background: white;
cursor: pointer; font-size: 0.85rem; color: var(--text-secondary); transition: all 0.2s ease;
white-space: nowrap;
}
.btn-copy:hover { background: #f1f5f9; }
.btn-copy.copied { background: #10b981 !important; color: white !important; border-color: #10b981 !important; }
.result-content {
margin-top: 0; background: #f8fafc; border-radius: 10px; padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 0.9rem;
line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--text-color);
}
.toast-container { position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; }
.toast { padding: 0.75rem 1.25rem; border-radius: 8px; color: white; font-size: 0.875rem; font-weight: 500; animation: slideIn 0.3s ease; }
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@media (max-width: 768px) {
.expert-layout { padding: 1.5rem 1rem; }
.expert-card { padding: 1.25rem; }
.disamb-grid { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block scripts %}
<script>
(function() {
var form = document.getElementById('expertForm');
var textarea = document.getElementById('inputText');
var generateBtn = document.getElementById('generateBtn');
var loadingArea = document.getElementById('loadingArea');
var loadingText = document.getElementById('loadingText');
var resultArea = document.getElementById('resultArea');
var loadSteps = document.querySelectorAll('.load-step');
function showToast(msg, type) {
var c = document.getElementById('toastContainer');
var t = document.createElement('div');
t.className = 'toast ' + type;
t.textContent = msg;
c.appendChild(t);
setTimeout(function(){ t.style.opacity='0'; t.style.transition='all 0.3s ease'; setTimeout(function(){ t.remove(); }, 300); }, 3500);
}
function animateLoadStep(i) {
loadSteps.forEach(function(s, j) { s.classList.toggle('active', j <= i); });
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) { showToast('请输入需求描述', 'error'); textarea.focus(); return; }
if (input.length < 10) { showToast('需求过短请至少输入10个字', 'error'); textarea.focus(); return; }
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 两阶段处理中...';
animateLoadStep(0); loadingText.textContent = '阶段1/2消歧分析...';
var t1 = setTimeout(function(){ animateLoadStep(1); loadingText.textContent = '阶段2/2生成优化提示词...'; }, 1500);
try {
var resp = await fetch('/api/expert-generate-6/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input })
});
var data = await resp.json();
clearTimeout(t1);
if (data.code === 200) {
var ia = data.data.intent_analysis;
var conf = ia.confidence || 0;
document.getElementById('coreIntent').textContent = ia.core_intent;
document.getElementById('confidenceVal').textContent = (conf * 100).toFixed(0) + '%' + (conf >= 0.85 ? ' ✓' : conf >= 0.7 ? ' ⚠' : ' ✗');
document.getElementById('subCategory').textContent = ia.sub_category || ia.domain;
document.getElementById('disambNote').textContent = ia.disambiguation_note || '未触发消歧规则';
document.getElementById('optimizedPrompt').textContent = data.data.optimized_prompt;
loadingArea.style.display = 'none';
resultArea.style.display = '';
document.querySelector('.prompt-hero').scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败', 'error');
}
} catch (err) {
console.error(err);
clearTimeout(t1);
loadingArea.style.display = 'none';
showToast('网络请求失败', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-bolt"></i> 生成优化提示词';
loadSteps.forEach(function(s) { s.classList.remove('active'); });
}
});
document.getElementById('copyPromptBtn').addEventListener('click', function() {
copyText('optimizedPrompt', this);
});
function copyText(elementId, btn) {
var text = document.getElementById(elementId).textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function(){
showCopied(btn);
}).catch(function(){ fallbackCopy(text, btn); });
} else {
fallbackCopy(text, btn);
}
}
function fallbackCopy(text, btn) {
var ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta); showCopied(btn);
}
function showCopied(btn) {
var orig = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check"></i> 已复制';
btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = orig; btn.classList.remove('copied'); }, 2000);
}
})();
</script>
{% endblock %}

File diff suppressed because it is too large Load Diff