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:
@@ -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)
|
||||
|
||||
319
src/flask_prompt_master/routes/expert_generate_5.py
Normal file
319
src/flask_prompt_master/routes/expert_generate_5.py
Normal 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})
|
||||
300
src/flask_prompt_master/routes/expert_generate_6.py
Normal file
300
src/flask_prompt_master/routes/expert_generate_6.py
Normal 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})
|
||||
396
src/flask_prompt_master/templates/expert_generate_5.html
Normal file
396
src/flask_prompt_master/templates/expert_generate_5.html
Normal 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 %}
|
||||
357
src/flask_prompt_master/templates/expert_generate_6.html
Normal file
357
src/flask_prompt_master/templates/expert_generate_6.html
Normal 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
Reference in New Issue
Block a user