diff --git a/docs/api_expert_generate_3_open.md b/docs/api_expert_generate_3_open.md
new file mode 100644
index 0000000..4d640bd
--- /dev/null
+++ b/docs/api_expert_generate_3_open.md
@@ -0,0 +1,108 @@
+# 智能提示词优化3号专家 - 对外接口文档
+
+供 Android 等外部应用调用的 REST API。
+
+## 接口地址
+
+```
+POST http://101.43.95.130:5002/api/open/expert-generate-3
+```
+
+## 请求
+
+- **Method**: POST
+- **Content-Type**: application/json
+
+### 请求体参数
+
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| input_text | string | 是 | 用户需求描述 |
+| temperature | number | 否 | 生成随机性,0-2,默认 0.7 |
+| max_tokens | int | 否 | 最大生成 token 数,100-4000,默认 1000 |
+| timeout | int | 否 | 超时时间(秒),10-300,默认 60 |
+| uid | int | 否 | 用户ID,用于历史记录归属,不传则使用默认用户 |
+
+### 示例
+
+```json
+{
+ "input_text": "android内存溢出分析",
+ "temperature": 0.7,
+ "max_tokens": 1000,
+ "timeout": 60,
+ "uid": 1
+}
+```
+
+## 响应
+
+### 成功 (code=200)
+
+```json
+{
+ "code": 200,
+ "message": "success",
+ "data": {
+ "intent_analysis": {
+ "core_intent": "技术",
+ "domain": "xxx",
+ "key_requirements": ["..."],
+ "expected_output": "...",
+ "constraints": ["..."]
+ },
+ "generated_prompt": "生成的完整提示词文本"
+ }
+}
+```
+
+### 失败
+
+```json
+{
+ "code": 400,
+ "message": "错误描述",
+ "data": null
+}
+```
+
+常见 code:400 参数错误,429 重复提交,500 服务器错误。
+
+## Android 调用示例 (Kotlin + Retrofit)
+
+```kotlin
+// 请求体
+data class ExpertGenerate3Request(
+ val input_text: String,
+ val temperature: Float? = 0.7f,
+ val max_tokens: Int? = 1000,
+ val timeout: Int? = 60,
+ val uid: Int? = null
+)
+
+// 响应体
+data class ExpertGenerate3Response(
+ val code: Int,
+ val message: String,
+ val data: Data?
+)
+data class Data(
+ val intent_analysis: IntentAnalysis,
+ val generated_prompt: String
+)
+
+// 使用
+val api = Retrofit.Builder()
+ .baseUrl("http://101.43.95.130:5002/")
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ .create(YourApi::class.java)
+
+api.expertGenerate3(ExpertGenerate3Request(input_text = "android内存溢出分析"))
+```
+
+## 注意事项
+
+- 项目已启用 CORS,支持跨域调用
+- 对外接口不做 8 秒内重复提交校验,由客户端自行控制
+- 建议在客户端增加防重复点击、超时重试等逻辑
diff --git a/exampleAiApp01/README.md b/exampleAiApp01/README.md
new file mode 100644
index 0000000..935743b
--- /dev/null
+++ b/exampleAiApp01/README.md
@@ -0,0 +1,29 @@
+# 提示词智能优化 Android App
+
+调用提示词大师 3 号专家接口,实现智能提示词生成。
+
+## 环境
+
+- Android Studio(推荐使用 AGP 8.7+、Gradle 8.9)
+- minSdk 24,targetSdk 34
+- Java 17
+
+## 接口
+
+- 地址:`POST http://101.43.95.130:5002/api/open/expert-generate-3`
+- 参数:`input_text`(必填)、`temperature`、`max_tokens`、`timeout`、`uid`
+
+## 构建
+
+```bash
+cd exampleAiApp01
+./gradlew assembleDebug
+```
+
+或在 Android Studio 中打开 `exampleAiApp01` 目录,直接运行到真机或模拟器。
+
+## 注意事项
+
+- 需配置网络权限,并允许明文 HTTP(101.43.95.130 为 HTTP)
+- 确保设备可访问 `101.43.95.130:5002`
+- 如缺少启动图标,可在 Android Studio 中通过 `File > New > Image Asset` 生成
diff --git a/exampleAiApp01/app/build.gradle.kts b/exampleAiApp01/app/build.gradle.kts
new file mode 100644
index 0000000..b579446
--- /dev/null
+++ b/exampleAiApp01/app/build.gradle.kts
@@ -0,0 +1,46 @@
+plugins {
+ id("com.android.application")
+}
+
+android {
+ namespace = "com.example.promptoptimizer"
+ compileSdk = 34
+
+ defaultConfig {
+ applicationId = "com.example.promptoptimizer"
+ minSdk = 24
+ targetSdk = 34
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ buildFeatures {
+ viewBinding = true
+ }
+}
+
+dependencies {
+ implementation("androidx.core:core-ktx:1.12.0")
+ implementation("androidx.appcompat:appcompat:1.6.1")
+ implementation("com.google.android.material:material:1.11.0")
+ implementation("androidx.constraintlayout:constraintlayout:2.1.4")
+
+ // Retrofit
+ implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ implementation("com.squareup.retrofit2:converter-gson:2.9.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
+}
+
diff --git a/exampleAiApp01/app/proguard-rules.pro b/exampleAiApp01/app/proguard-rules.pro
new file mode 100644
index 0000000..92cd3ca
--- /dev/null
+++ b/exampleAiApp01/app/proguard-rules.pro
@@ -0,0 +1,3 @@
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class com.example.promptoptimizer.api.** { *; }
diff --git a/exampleAiApp01/app/src/main/AndroidManifest.xml b/exampleAiApp01/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6a1e966
--- /dev/null
+++ b/exampleAiApp01/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/MainActivity.java b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/MainActivity.java
new file mode 100644
index 0000000..54d6c52
--- /dev/null
+++ b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/MainActivity.java
@@ -0,0 +1,146 @@
+package com.example.promptoptimizer;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.example.promptoptimizer.api.ExpertGenerate3Api;
+import com.example.promptoptimizer.model.ExpertGenerate3Request;
+import com.example.promptoptimizer.model.ExpertGenerate3Response;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.card.MaterialCardView;
+
+import java.util.Arrays;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+/**
+ * 提示词智能优化 - 主界面
+ */
+public class MainActivity extends AppCompatActivity {
+
+ private EditText etInput;
+ private MaterialButton btnGenerate;
+ private ProgressBar progressBar;
+ private TextView tvLoading;
+ private MaterialCardView cardResult;
+ private TextView tvIntent;
+ private TextView tvPrompt;
+ private MaterialButton btnCopy;
+
+ private boolean isSubmitting = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ etInput = findViewById(R.id.etInput);
+ btnGenerate = findViewById(R.id.btnGenerate);
+ progressBar = findViewById(R.id.progressBar);
+ tvLoading = findViewById(R.id.tvLoading);
+ cardResult = findViewById(R.id.cardResult);
+ tvIntent = findViewById(R.id.tvIntent);
+ tvPrompt = findViewById(R.id.tvPrompt);
+ btnCopy = findViewById(R.id.btnCopy);
+
+ btnGenerate.setOnClickListener(v -> doGenerate());
+ btnCopy.setOnClickListener(v -> copyPrompt());
+ }
+
+ private void doGenerate() {
+ if (isSubmitting) return;
+
+ String input = etInput.getText() != null ? etInput.getText().toString().trim() : "";
+ if (input.isEmpty()) {
+ Toast.makeText(this, R.string.error_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ isSubmitting = true;
+ progressBar.setVisibility(View.VISIBLE);
+ tvLoading.setVisibility(View.VISIBLE);
+ btnGenerate.setEnabled(false);
+ cardResult.setVisibility(View.GONE);
+
+ ExpertGenerate3Request request = new ExpertGenerate3Request(input);
+ ExpertGenerate3Api api = RetrofitClient.getApi();
+
+ api.generate(request).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call,
+ Response response) {
+ isSubmitting = false;
+ progressBar.setVisibility(View.GONE);
+ tvLoading.setVisibility(View.GONE);
+ btnGenerate.setEnabled(true);
+
+ if (response.isSuccessful() && response.body() != null) {
+ ExpertGenerate3Response body = response.body();
+ if (body.getCode() == 200 && body.getData() != null) {
+ showResult(body);
+ } else {
+ Toast.makeText(MainActivity.this,
+ body.getMessage() != null ? body.getMessage() : getString(R.string.error_network),
+ Toast.LENGTH_LONG).show();
+ }
+ } else {
+ Toast.makeText(MainActivity.this, R.string.error_network, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ isSubmitting = false;
+ progressBar.setVisibility(View.GONE);
+ tvLoading.setVisibility(View.GONE);
+ btnGenerate.setEnabled(true);
+ Toast.makeText(MainActivity.this, R.string.error_network + ": " + t.getMessage(),
+ Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+
+ private void showResult(ExpertGenerate3Response response) {
+ ExpertGenerate3Response.ResponseData data = response.getData();
+ if (data == null) return;
+
+ StringBuilder intentSb = new StringBuilder();
+ ExpertGenerate3Response.IntentAnalysis ia = data.getIntentAnalysis();
+ if (ia != null) {
+ intentSb.append("核心意图:").append(ia.getCoreIntent()).append("\n");
+ intentSb.append("专业领域:").append(ia.getDomain()).append("\n");
+ intentSb.append("预期输出:").append(ia.getExpectedOutput()).append("\n");
+ if (ia.getKeyRequirements() != null && ia.getKeyRequirements().length > 0) {
+ intentSb.append("关键需求:\n").append(Arrays.toString(ia.getKeyRequirements())).append("\n");
+ }
+ if (ia.getConstraints() != null && ia.getConstraints().length > 0) {
+ intentSb.append("约束条件:\n").append(Arrays.toString(ia.getConstraints()));
+ }
+ }
+ tvIntent.setText(intentSb.toString());
+ tvPrompt.setText(data.getGeneratedPrompt() != null ? data.getGeneratedPrompt() : "");
+ cardResult.setVisibility(View.VISIBLE);
+ }
+
+ private void copyPrompt() {
+ String text = tvPrompt.getText() != null ? tvPrompt.getText().toString() : "";
+ if (text.isEmpty()) return;
+
+ ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm != null) {
+ cm.setPrimaryClip(ClipData.newPlainText("prompt", text));
+ Toast.makeText(this, R.string.toast_copied, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/RetrofitClient.java b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/RetrofitClient.java
new file mode 100644
index 0000000..ca0589f
--- /dev/null
+++ b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/RetrofitClient.java
@@ -0,0 +1,46 @@
+package com.example.promptoptimizer;
+
+import com.example.promptoptimizer.api.ExpertGenerate3Api;
+
+import okhttp3.OkHttpClient;
+import okhttp3.logging.HttpLoggingInterceptor;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class RetrofitClient {
+
+ private static final String BASE_URL = "http://101.43.95.130:5002/";
+
+ private static Retrofit retrofit;
+ private static ExpertGenerate3Api api;
+
+ public static ExpertGenerate3Api getApi() {
+ if (api == null) {
+ api = getRetrofit().create(ExpertGenerate3Api.class);
+ }
+ return api;
+ }
+
+ private static Retrofit getRetrofit() {
+ if (retrofit == null) {
+ HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
+ interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
+
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(90, TimeUnit.SECONDS)
+ .readTimeout(90, TimeUnit.SECONDS)
+ .writeTimeout(90, TimeUnit.SECONDS)
+ .addInterceptor(interceptor)
+ .build();
+
+ retrofit = new Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(client)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+ }
+ return retrofit;
+ }
+}
diff --git a/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/api/ExpertGenerate3Api.java b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/api/ExpertGenerate3Api.java
new file mode 100644
index 0000000..34330f8
--- /dev/null
+++ b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/api/ExpertGenerate3Api.java
@@ -0,0 +1,17 @@
+package com.example.promptoptimizer.api;
+
+import com.example.promptoptimizer.model.ExpertGenerate3Response;
+import com.example.promptoptimizer.model.ExpertGenerate3Request;
+
+import retrofit2.Call;
+import retrofit2.http.Body;
+import retrofit2.http.POST;
+
+/**
+ * 智能提示词优化3号专家 API
+ */
+public interface ExpertGenerate3Api {
+
+ @POST("api/open/expert-generate-3")
+ Call generate(@Body ExpertGenerate3Request request);
+}
diff --git a/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Request.java b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Request.java
new file mode 100644
index 0000000..eee55b3
--- /dev/null
+++ b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Request.java
@@ -0,0 +1,71 @@
+package com.example.promptoptimizer.model;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * 请求体
+ */
+public class ExpertGenerate3Request {
+
+ @SerializedName("input_text")
+ private String inputText;
+
+ @SerializedName("temperature")
+ private Float temperature;
+
+ @SerializedName("max_tokens")
+ private Integer maxTokens;
+
+ @SerializedName("timeout")
+ private Integer timeout;
+
+ @SerializedName("uid")
+ private Integer uid;
+
+ public ExpertGenerate3Request(String inputText) {
+ this.inputText = inputText;
+ this.temperature = 0.7f;
+ this.maxTokens = 1000;
+ this.timeout = 60;
+ }
+
+ public String getInputText() {
+ return inputText;
+ }
+
+ public void setInputText(String inputText) {
+ this.inputText = inputText;
+ }
+
+ public Float getTemperature() {
+ return temperature;
+ }
+
+ public void setTemperature(Float temperature) {
+ this.temperature = temperature;
+ }
+
+ public Integer getMaxTokens() {
+ return maxTokens;
+ }
+
+ public void setMaxTokens(Integer maxTokens) {
+ this.maxTokens = maxTokens;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public Integer getUid() {
+ return uid;
+ }
+
+ public void setUid(Integer uid) {
+ this.uid = uid;
+ }
+}
diff --git a/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Response.java b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Response.java
new file mode 100644
index 0000000..4c50840
--- /dev/null
+++ b/exampleAiApp01/app/src/main/java/com/example/promptoptimizer/model/ExpertGenerate3Response.java
@@ -0,0 +1,120 @@
+package com.example.promptoptimizer.model;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * 响应体
+ */
+public class ExpertGenerate3Response {
+
+ private int code;
+ private String message;
+
+ @SerializedName("data")
+ private ResponseData data;
+
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public ResponseData getData() {
+ return data;
+ }
+
+ public void setData(ResponseData data) {
+ this.data = data;
+ }
+
+ public static class ResponseData {
+ @SerializedName("intent_analysis")
+ private IntentAnalysis intentAnalysis;
+
+ @SerializedName("generated_prompt")
+ private String generatedPrompt;
+
+ public IntentAnalysis getIntentAnalysis() {
+ return intentAnalysis;
+ }
+
+ public void setIntentAnalysis(IntentAnalysis intentAnalysis) {
+ this.intentAnalysis = intentAnalysis;
+ }
+
+ public String getGeneratedPrompt() {
+ return generatedPrompt;
+ }
+
+ public void setGeneratedPrompt(String generatedPrompt) {
+ this.generatedPrompt = generatedPrompt;
+ }
+ }
+
+ public static class IntentAnalysis {
+ @SerializedName("core_intent")
+ private String coreIntent;
+
+ @SerializedName("domain")
+ private String domain;
+
+ @SerializedName("key_requirements")
+ private String[] keyRequirements;
+
+ @SerializedName("expected_output")
+ private String expectedOutput;
+
+ @SerializedName("constraints")
+ private String[] constraints;
+
+ public String getCoreIntent() {
+ return coreIntent;
+ }
+
+ public void setCoreIntent(String coreIntent) {
+ this.coreIntent = coreIntent;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ public String[] getKeyRequirements() {
+ return keyRequirements;
+ }
+
+ public void setKeyRequirements(String[] keyRequirements) {
+ this.keyRequirements = keyRequirements;
+ }
+
+ public String getExpectedOutput() {
+ return expectedOutput;
+ }
+
+ public void setExpectedOutput(String expectedOutput) {
+ this.expectedOutput = expectedOutput;
+ }
+
+ public String[] getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(String[] constraints) {
+ this.constraints = constraints;
+ }
+ }
+}
diff --git a/exampleAiApp01/app/src/main/res/drawable/edit_bg.xml b/exampleAiApp01/app/src/main/res/drawable/edit_bg.xml
new file mode 100644
index 0000000..63379e6
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/drawable/edit_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/res/drawable/ic_launcher_foreground.xml b/exampleAiApp01/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..76b0e86
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/res/layout/activity_main.xml b/exampleAiApp01/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..cb597d3
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..5ed0a2d
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..5ed0a2d
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/exampleAiApp01/app/src/main/res/values/colors.xml b/exampleAiApp01/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..5a56c1b
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #F8FAFC
+ #1E3A8A
+
diff --git a/exampleAiApp01/app/src/main/res/values/strings.xml b/exampleAiApp01/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..094726c
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/values/strings.xml
@@ -0,0 +1,14 @@
+
+
+ 提示词智能优化
+ 请描述您的需求,系统将进行专业分析并生成高质量提示词
+ 生成专家提示词
+ 生成结果
+ 需求分析
+ 生成的专家提示词
+ 复制提示词
+ 已复制到剪贴板
+ 正在分析需求并生成专业提示词…
+ 网络请求失败,请检查网络后重试
+ 请输入您的需求
+
diff --git a/exampleAiApp01/app/src/main/res/values/themes.xml b/exampleAiApp01/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..685b7e1
--- /dev/null
+++ b/exampleAiApp01/app/src/main/res/values/themes.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/exampleAiApp01/build.gradle.kts b/exampleAiApp01/build.gradle.kts
new file mode 100644
index 0000000..a0dd15f
--- /dev/null
+++ b/exampleAiApp01/build.gradle.kts
@@ -0,0 +1,3 @@
+plugins {
+ id("com.android.application") version "8.7.3" apply false
+}
diff --git a/exampleAiApp01/gradle.properties b/exampleAiApp01/gradle.properties
new file mode 100644
index 0000000..f0a2e55
--- /dev/null
+++ b/exampleAiApp01/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+android.useAndroidX=true
+kotlin.code.style=official
+android.nonTransitiveRClass=true
diff --git a/exampleAiApp01/gradle/wrapper/gradle-wrapper.properties b/exampleAiApp01/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..09523c0
--- /dev/null
+++ b/exampleAiApp01/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/exampleAiApp01/settings.gradle.kts b/exampleAiApp01/settings.gradle.kts
new file mode 100644
index 0000000..70a134c
--- /dev/null
+++ b/exampleAiApp01/settings.gradle.kts
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "PromptOptimizer"
+include(":app")
diff --git a/logs/app.log b/logs/app.log
index 0835bd1..4f53c28 100644
--- a/logs/app.log
+++ b/logs/app.log
@@ -2927,3 +2927,4 @@ werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'expert
[in /home/renjianbo/aitsc/src/flask_prompt_master/__init__.py:120]
2026-02-28 22:15:25,968 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
2026-02-28 22:22:12,598 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
+2026-03-01 22:57:49,275 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82]
diff --git a/logs/gunicorn.pid b/logs/gunicorn.pid
index 85fb85f..e87c90b 100644
--- a/logs/gunicorn.pid
+++ b/logs/gunicorn.pid
@@ -1 +1 @@
-22948
+21107
diff --git a/logs/gunicorn_access.log b/logs/gunicorn_access.log
index ef8a059..75eb7eb 100644
--- a/logs/gunicorn_access.log
+++ b/logs/gunicorn_access.log
@@ -15248,3 +15248,6 @@
206.168.34.39 - - [01/Mar/2026:01:05:25 +0800] "PRI * HTTP/2.0" 404 207 "-" "-" 828
206.168.34.39 - - [01/Mar/2026:01:05:26 +0800] "GET /favicon.ico HTTP/1.1" 404 207 "-" "Mozilla/5.0 (compatible; CensysInspect/1.1; +https://about.censys.io/)" 806
206.168.34.39 - - [01/Mar/2026:01:05:45 +0800] "GET /security.txt HTTP/1.1" 404 207 "-" "Mozilla/5.0 (compatible; CensysInspect/1.1; +https://about.censys.io/)" 659
+123.139.40.200 - - [01/Mar/2026:22:58:01 +0800] "GET / HTTP/1.1" 200 216259 "http://101.43.95.130:5002/expert-generate-3" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0" 712510
+123.139.40.200 - - [01/Mar/2026:22:58:01 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "http://101.43.95.130:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0" 5764
+123.139.40.200 - - [01/Mar/2026:22:58:02 +0800] "GET /api/check-login HTTP/1.1" 200 35 "http://101.43.95.130:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0" 5242
diff --git a/logs/gunicorn_error.log b/logs/gunicorn_error.log
index 05f84f4..1efceda 100644
--- a/logs/gunicorn_error.log
+++ b/logs/gunicorn_error.log
@@ -10587,3 +10587,36 @@ werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'expert
[2026-03-01 02:01:44 +0800] [31755] [ERROR] Worker (pid:696) was sent SIGTERM!
[2026-03-01 02:01:44 +0800] [31755] [ERROR] Worker (pid:831) was sent SIGTERM!
[2026-03-01 02:01:44 +0800] [31755] [INFO] Shutting down: Master
+[2026-03-01 22:57:51 +0800] [21107] [INFO] Starting gunicorn 21.2.0
+[2026-03-01 22:57:51 +0800] [21107] [INFO] Gunicorn服务器启动中...
+[2026-03-01 22:57:51 +0800] [21107] [INFO] Listening at: http://0.0.0.0:5002 (21107)
+[2026-03-01 22:57:51 +0800] [21107] [INFO] Using worker: sync
+[2026-03-01 22:57:51 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 22:57:51 +0800] [21286] [INFO] Booting worker with pid: 21286
+[2026-03-01 22:57:51 +0800] [21286] [INFO] 工作进程 21286 已启动
+[2026-03-01 22:57:51 +0800] [21286] [INFO] 工作进程 21286 初始化完成
+[2026-03-01 22:57:51 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 22:57:51 +0800] [21293] [INFO] Booting worker with pid: 21293
+[2026-03-01 22:57:51 +0800] [21293] [INFO] 工作进程 21293 已启动
+[2026-03-01 22:57:51 +0800] [21293] [INFO] 工作进程 21293 初始化完成
+[2026-03-01 22:57:51 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 22:57:51 +0800] [21294] [INFO] Booting worker with pid: 21294
+[2026-03-01 22:57:51 +0800] [21294] [INFO] 工作进程 21294 已启动
+[2026-03-01 22:57:51 +0800] [21294] [INFO] 工作进程 21294 初始化完成
+[2026-03-01 22:57:51 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 22:57:51 +0800] [21295] [INFO] Booting worker with pid: 21295
+[2026-03-01 22:57:51 +0800] [21295] [INFO] 工作进程 21295 已启动
+[2026-03-01 22:57:51 +0800] [21295] [INFO] 工作进程 21295 初始化完成
+[2026-03-01 22:57:51 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 22:57:51 +0800] [21296] [INFO] Booting worker with pid: 21296
+[2026-03-01 22:57:51 +0800] [21296] [INFO] 工作进程 21296 已启动
+[2026-03-01 22:57:51 +0800] [21296] [INFO] 工作进程 21296 初始化完成
+[2026-03-01 23:03:09 +0800] [21107] [CRITICAL] WORKER TIMEOUT (pid:21296)
+[2026-03-01 23:03:09 +0800] [21296] [INFO] 工作进程 21296 异常退出
+[2026-03-01 23:03:09 +0800] [21296] [INFO] Worker exiting (pid: 21296)
+[2026-03-01 23:03:09 +0800] [21107] [ERROR] Worker (pid:21296) exited with code 1
+[2026-03-01 23:03:09 +0800] [21107] [ERROR] Worker (pid:21296) exited with code 1.
+[2026-03-01 23:03:09 +0800] [21107] [INFO] 工作进程 [booting] 即将启动
+[2026-03-01 23:03:09 +0800] [2175] [INFO] Booting worker with pid: 2175
+[2026-03-01 23:03:09 +0800] [2175] [INFO] 工作进程 2175 已启动
+[2026-03-01 23:03:09 +0800] [2175] [INFO] 工作进程 2175 初始化完成
diff --git a/src/flask_prompt_master/routes/__pycache__/expert_generate_3.cpython-312.pyc b/src/flask_prompt_master/routes/__pycache__/expert_generate_3.cpython-312.pyc
index facd71b..4fadad7 100644
Binary files a/src/flask_prompt_master/routes/__pycache__/expert_generate_3.cpython-312.pyc and b/src/flask_prompt_master/routes/__pycache__/expert_generate_3.cpython-312.pyc differ
diff --git a/src/flask_prompt_master/routes/expert_generate_3.py b/src/flask_prompt_master/routes/expert_generate_3.py
index 1079400..dd66cc0 100644
--- a/src/flask_prompt_master/routes/expert_generate_3.py
+++ b/src/flask_prompt_master/routes/expert_generate_3.py
@@ -140,6 +140,84 @@ def _resolve_user_id():
return 1
+def _do_generate(raw_input, temperature, max_tokens, timeout, user_id, skip_dedup=False):
+ """核心生成逻辑,供 Web API 和对外接口共用"""
+ if not skip_dedup:
+ req_key = (user_id, 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 {'code': 429, 'message': '请勿重复提交,请稍后再试', 'data': None}
+ _dedup_cache[req_key] = now_ts
+ if len(_dedup_cache) > 500:
+ _dedup_cache.clear()
+
+ resp1 = _llm_client.chat.completions.create(
+ model="deepseek-chat",
+ messages=[
+ {"role": "system", "content": INTENT_PROMPT_3},
+ {"role": "user", "content": raw_input}
+ ],
+ temperature=0.1,
+ timeout=timeout
+ )
+ 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 field in ['core_intent', 'domain', 'key_requirements', 'expected_output', 'constraints', 'keywords']:
+ if field not in intent_data:
+ raise ValueError("缺少字段: " + field)
+ if intent_data['core_intent'] not in ('技术', '创意', '分析', '咨询'):
+ intent_data['core_intent'] = '技术'
+ for arr_field in ['key_requirements', 'constraints', 'keywords']:
+ v = intent_data.get(arr_field)
+ if not isinstance(v, list) or len(v) == 0:
+ intent_data[arr_field] = ['未指定']
+ except json.JSONDecodeError:
+ raise ValueError("意图分析格式有误")
+ except ValueError:
+ raise
+
+ tpl = EXPERT_TEMPLATES_3.get(intent_data['core_intent'], FALLBACK_TEMPLATE_3)
+ analysis_str = json.dumps(intent_data, ensure_ascii=False, indent=2)
+ resp2 = _llm_client.chat.completions.create(
+ model="deepseek-chat",
+ messages=[
+ {"role": "system", "content": tpl.format(analysis=analysis_str)},
+ {"role": "user", "content": raw_input}
+ ],
+ temperature=temperature,
+ max_tokens=max_tokens,
+ timeout=timeout
+ )
+ result_prompt = (resp2.choices[0].message.content or "").strip()
+ if not result_prompt:
+ raise ValueError("生成结果为空")
+
+ try:
+ db.session.add(Prompt(input_text=raw_input, generated_text=result_prompt, user_id=user_id))
+ db.session.commit()
+ except Exception as e:
+ db.session.rollback()
+ logger.warning("3号专家 保存 Prompt 失败: %s", e)
+ try:
+ PromptHistory.add_history(
+ user_id=user_id,
+ original_input=raw_input,
+ generated_prompt=result_prompt,
+ template_name=TEMPLATE_NAME_3
+ )
+ UserStatistics.update_statistics(user_id)
+ except Exception as e:
+ logger.warning("3号专家 保存历史失败: %s", e)
+
+ return {
+ 'code': 200,
+ 'message': 'success',
+ 'data': {'intent_analysis': intent_data, 'generated_prompt': result_prompt}
+ }
+
+
@expert_generate_3_bp.route('/expert-generate-3', methods=['GET'])
def expert_generate_3_page():
"""智能提示词优化3号专家 - 页面"""
@@ -148,7 +226,20 @@ def expert_generate_3_page():
@expert_generate_3_bp.route('/api/expert-generate-3/generate', methods=['POST'])
def expert_generate_3_api():
- """智能提示词优化3号专家 - 两阶段生成 API,含历史记录"""
+ """智能提示词优化3号专家 - 两阶段生成 API(Web 用),含历史记录"""
+ return _handle_generate_api(skip_dedup=False, user_id=None)
+
+
+@expert_generate_3_bp.route('/api/open/expert-generate-3', methods=['POST'])
+def expert_generate_3_open_api():
+ """
+ 智能提示词优化3号专家 - 对外接口,供 Android 等外部应用调用。
+ POST JSON: input_text(必填), temperature, max_tokens, timeout, uid(可选)
+ """
+ return _handle_generate_api(skip_dedup=True, user_id=None)
+
+
+def _handle_generate_api(skip_dedup=False, user_id=None):
try:
if not request.is_json:
return jsonify({'code': 400, 'message': '请求必须是JSON格式', 'data': None})
@@ -167,79 +258,16 @@ def expert_generate_3_api():
timeout = int(timeout) if timeout is not None else 60
timeout = max(10, min(300, timeout))
- uid = _resolve_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()
+ uid = user_id if user_id is not None else payload.get('uid')
+ if uid is not None:
+ uid = int(uid)
+ else:
+ uid = _resolve_user_id()
- resp1 = _llm_client.chat.completions.create(
- model="deepseek-chat",
- messages=[
- {"role": "system", "content": INTENT_PROMPT_3},
- {"role": "user", "content": raw_input}
- ],
- temperature=0.1,
- timeout=timeout
- )
- 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 field in ['core_intent', 'domain', 'key_requirements', 'expected_output', 'constraints', 'keywords']:
- if field not in intent_data:
- raise ValueError("缺少字段: " + field)
- if intent_data['core_intent'] not in ('技术', '创意', '分析', '咨询'):
- intent_data['core_intent'] = '技术'
- for arr_field in ['key_requirements', 'constraints', 'keywords']:
- v = intent_data.get(arr_field)
- if not isinstance(v, list) or len(v) == 0:
- intent_data[arr_field] = ['未指定']
- except json.JSONDecodeError as e:
- logger.error("3号专家 JSON解析失败: %s", e)
- return jsonify({'code': 500, 'message': '意图分析格式有误,请重试', 'data': None})
- except ValueError as e:
- logger.error("3号专家 数据验证失败: %s", e)
- return jsonify({'code': 500, 'message': str(e), 'data': None})
- tpl = EXPERT_TEMPLATES_3.get(intent_data['core_intent'], FALLBACK_TEMPLATE_3)
- analysis_str = json.dumps(intent_data, ensure_ascii=False, indent=2)
- resp2 = _llm_client.chat.completions.create(
- model="deepseek-chat",
- messages=[
- {"role": "system", "content": tpl.format(analysis=analysis_str)},
- {"role": "user", "content": raw_input}
- ],
- temperature=temperature,
- max_tokens=max_tokens,
- timeout=timeout
- )
- result_prompt = (resp2.choices[0].message.content or "").strip()
- if not result_prompt:
- return jsonify({'code': 500, 'message': '生成失败,请重试', 'data': None})
- try:
- db.session.add(Prompt(input_text=raw_input, generated_text=result_prompt, user_id=uid))
- db.session.commit()
- except Exception as e:
- db.session.rollback()
- logger.warning("3号专家 保存 Prompt 失败: %s", e)
- try:
- PromptHistory.add_history(
- user_id=uid,
- original_input=raw_input,
- generated_prompt=result_prompt,
- template_name=TEMPLATE_NAME_3
- )
- UserStatistics.update_statistics(uid)
- except Exception as e:
- logger.warning("3号专家 保存历史失败: %s", e)
- return jsonify({
- 'code': 200,
- 'message': 'success',
- 'data': {'intent_analysis': intent_data, 'generated_prompt': result_prompt}
- })
+ result = _do_generate(raw_input, temperature, max_tokens, timeout, uid, skip_dedup=skip_dedup)
+ return jsonify(result)
+ except ValueError as e:
+ return jsonify({'code': 400, 'message': str(e), 'data': None})
except Exception as e:
logger.exception("智能提示词优化3号专家失败")
return jsonify({'code': 500, 'message': str(e) or '生成失败,请重试', 'data': None})