From d98976a35e997643d6e75b71261ef499c4df82e0 Mon Sep 17 00:00:00 2001 From: geniusay <969025903@qq.com> Date: Wed, 20 Aug 2025 00:21:47 +0800 Subject: [PATCH] =?UTF-8?q?Qa=E9=97=AE=E9=A2=98=E9=87=8D=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 252 +++++++++++++++++- .../timemachinelab/core/model/PromptAI.java | 13 + .../timemachinelab/core/qatree/CommonQA.java | 17 -- .../timemachinelab/core/qatree/FormQA.java | 19 -- .../github/timemachinelab/core/qatree/QA.java | 26 -- .../timemachinelab/core/qatree/QAType.java | 14 - .../core/qatree/QaTreeDomain.java | 7 +- .../core/qatree/QaTreeNode.java | 7 +- .../core/qatree/SelectionQA.java | 14 - .../core/question/BaseQuestion.java | 57 ++++ .../core/question/FormField.java | 119 +++++++++ .../core/question/FormQuestion.java | 48 ++++ .../core/question/InputQuestion.java | 33 +++ .../core/question/MultipleChoiceQuestion.java | 41 +++ .../timemachinelab/core/question/Option.java | 42 +++ .../core/question/QuestionType.java | 55 ++++ .../core/question/SingleChoiceQuestion.java | 40 +++ .../github/timemachinelab/EncodingUtil.java | 10 +- 18 files changed, 714 insertions(+), 100 deletions(-) create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/model/PromptAI.java delete mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/CommonQA.java delete mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/FormQA.java delete mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QA.java delete mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QAType.java delete mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/SelectionQA.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/BaseQuestion.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/FormField.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/FormQuestion.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/InputQuestion.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/MultipleChoiceQuestion.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/Option.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/QuestionType.java create mode 100644 prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/SingleChoiceQuestion.java diff --git a/README.md b/README.md index 5af81af..61181cd 100644 --- a/README.md +++ b/README.md @@ -1 +1,251 @@ -# PromptoLab +# 问题类型系统 + +基于提供的四种JSON结构体构建的Java问题类型系统,支持单选、多选、输入框和表单四种问题类型。 + +## 类结构 + +### 核心类 + +- **QuestionType**: 问题类型枚举 +- **BaseQuestion**: 所有问题类型的抽象基类 +- **BaseAnswer**: 所有答案类型的抽象基类 +- **Option**: 选项类,用于单选和多选题 +- **FormField**: 表单字段类 +- **FormAnswerItem**: 表单答案项类 +- **QuestionFactory**: 问题工厂类,提供便捷的创建方法 +- **AnswerFactory**: 答案工厂类,提供便捷的创建方法 + +### 具体问题类型 + +1. **SingleChoiceQuestion**: 单选题 +2. **MultipleChoiceQuestion**: 多选题 +3. **InputQuestion**: 输入框问题 +4. **FormQuestion**: 表单问题(其他问题类型的组合) + +### 具体答案类型 + +1. **ChoiceAnswer**: 选择题答案(单选/多选) +2. **InputAnswer**: 输入框答案 +3. **FormAnswer**: 表单答案 + +## JSON结构对应关系 + +### 1. 单选题 (SingleChoiceQuestion) +```json +{ + "question": "您的年龄段是?", + "type": "SINGLE", + "parentId": null, + "desc": "请选择您的年龄段", + "questionId": "age_range_001", + "options": [ + {"id": "18-25", "label": "18-25岁"}, + {"id": "26-35", "label": "26-35岁"}, + {"id": "36-45", "label": "36-45岁"}, + {"id": "46+", "label": "46岁以上"} + ], + "answer": { + "questionId": "age_range_001", + "answer": ["26-35"] + } +} +``` + +### 2. 多选题 (MultipleChoiceQuestion) +```json +{ + "question": "您感兴趣的技术领域有哪些?", + "type": "MULTI", + "parentId": null, + "desc": "请选择您感兴趣的技术领域(可多选)", + "questionId": "tech_interests_002", + "options": [ + {"id": "frontend", "label": "前端开发"}, + {"id": "backend", "label": "后端开发"}, + {"id": "mobile", "label": "移动开发"}, + {"id": "ai", "label": "人工智能"} + ], + "answer": { + "questionId": "tech_interests_002", + "answer": ["frontend", "backend", "ai"] + } +} +``` + +### 3. 输入框 (InputQuestion) +```json +{ + "question": "请描述您的项目需求", + "type": "INPUT", + "parentId": null, + "desc": "请详细描述您希望开发的项目功能和要求", + "questionId": "project_desc_003", + "answer": { + "questionId": "project_desc_003", + "answer": "需要开发一个电商网站,包含用户注册登录、商品展示、购物车、订单管理等功能" + } +} +``` + +### 4. 表单 (FormQuestion) +```json +{ + "question": "请填写用户信息", + "type": "FORM", + "parentId": null, + "desc": "请填写完整的用户信息表单", + "questionId": "user_info_form_004", + "fields": [ + { + "id": "name", + "question": "姓名", + "type": "input", + "options": null, + "desc": "请输入您的真实姓名", + "weight": "1.0" + }, + { + "id": "gender", + "question": "性别", + "type": "single", + "options": [ + {"id": "male", "label": "男"}, + {"id": "female", "label": "女"} + ], + "desc": "请选择您的性别", + "weight": "0.5" + }, + { + "id": "hobbies", + "question": "兴趣爱好", + "type": "multi", + "options": [ + {"id": "reading", "label": "阅读"}, + {"id": "sports", "label": "运动"}, + {"id": "music", "label": "音乐"} + ], + "desc": "请选择您的兴趣爱好", + "weight": "1.5" + } + ], + "answer": { + "questionId": "user_info_form_004", + "answer": [ + { + "id": "name", + "value": ["张三"] + }, + { + "id": "gender", + "value": ["male"] + }, + { + "id": "hobbies", + "value": ["reading", "sports"] + } + ] + } +} +``` + +## 使用示例 + +### 1. 使用工厂方法创建问题 + +```java +// 创建单选题 +SingleChoiceQuestion singleChoice = QuestionFactory.createSingleChoice( + "您更喜欢哪种编程语言?", + "conversation_001", + QuestionFactory.createOptions( + "java", "Java", + "python", "Python", + "javascript", "JavaScript" + ), + "请选择您最熟悉的编程语言" +); + +// 创建多选题 +MultipleChoiceQuestion multipleChoice = QuestionFactory.createMultipleChoice( + "您使用过哪些开发工具?", + "conversation_001", + Arrays.asList( + new Option("idea", "IntelliJ IDEA"), + new Option("vscode", "Visual Studio Code"), + new Option("eclipse", "Eclipse") + ) +); + +// 创建输入框问题 +InputQuestion inputQuestion = QuestionFactory.createInput( + "请描述您的项目需求", + "conversation_001", + "请详细描述您想要开发的项目功能和要求" +); + +// 创建表单问题 +FormQuestion formQuestion = QuestionFactory.createForm( + "请填写项目信息", + "conversation_001", + Arrays.asList( + QuestionFactory.createInputField("project_name", "项目名称"), + QuestionFactory.createChoiceField( + "project_type", + "项目类型", + "single", + Arrays.asList( + new Option("web", "Web应用"), + new Option("mobile", "移动应用") + ) + ) + ) +); +``` + +### 2. 使用Builder模式创建问题 + +```java +// 使用Builder创建单选题 +SingleChoiceQuestion question = SingleChoiceQuestion.builder() + .question("您的工作经验?") + .parentId("conversation_002") + .options(Arrays.asList( + Option.builder().id("junior").label("1-3年").build(), + Option.builder().id("middle").label("3-5年").build(), + Option.builder().id("senior").label("5年以上").build() + )) + .desc("请选择您的工作经验范围") + .build(); +``` + +### 3. JSON序列化和反序列化 + +```java +ObjectMapper objectMapper = new ObjectMapper(); + +// 序列化为JSON +String json = objectMapper.writeValueAsString(question); + +// 从JSON反序列化 +BaseQuestion question = objectMapper.readValue(json, BaseQuestion.class); +``` + +## 特性 + +1. **类型安全**: 使用枚举定义问题类型,避免字符串错误 +2. **JSON支持**: 完全支持Jackson序列化和反序列化 +3. **Builder模式**: 支持链式调用,代码更简洁 +4. **工厂方法**: 提供便捷的创建方法 +5. **多态支持**: 基于抽象基类,支持多态操作 +6. **扩展性**: 易于添加新的问题类型 + +## 注意事项 + +1. 表单问题中的字段类型必须是 `input`、`single` 或 `multi` +2. 选择类型的字段必须提供 `options` 列表 +3. 所有可选字段在JSON序列化时会自动忽略null值 +4. 权重字段使用String类型,可以存储数字或其他格式的权重值 + +## 运行示例 + +运行 `QuestionExample.main()` 方法可以查看完整的使用示例和JSON输出。 \ No newline at end of file diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/model/PromptAI.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/model/PromptAI.java new file mode 100644 index 0000000..ff6823c --- /dev/null +++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/model/PromptAI.java @@ -0,0 +1,13 @@ +package io.github.timemachinelab.core.model; + +import io.github.timemachinelab.sfchain.annotation.AIOp; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@AIOp(value = "p", + description = "实现一个简单的AI对话功能示例", + defaultModel = "gpt-5-chat") +@Component +@Slf4j +public class PromptAI { +} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/CommonQA.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/CommonQA.java deleted file mode 100644 index 19c4b45..0000000 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/CommonQA.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.timemachinelab.core.qatree; - -import lombok.Data; - -@Data -public class CommonQA extends QA { - - public CommonQA() { - super(QAType.QA); - } - - private String question; - - private String answer; - - -} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/FormQA.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/FormQA.java deleted file mode 100644 index ce4c968..0000000 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/FormQA.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.timemachinelab.core.qatree; - -import lombok.Data; - -import java.util.HashMap; -import java.util.Map; - -@Data -public class FormQA extends QA { - - private String question; - - private Map answerMap; - - public FormQA() { - super(QAType.FORM); - answerMap = new HashMap<>(); - } -} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QA.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QA.java deleted file mode 100644 index b3fc4e8..0000000 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QA.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.timemachinelab.core.qatree; - -import lombok.Data; - -@Data -public abstract class QA { - - protected final QAType type; - - protected long createTime; - - protected long updateTime; - - public QA(QAType type) { - this.type = type; - this.createTime = System.currentTimeMillis(); - this.updateTime = this.createTime; - } - - /** - * 更新updateTime为当前时间戳 - */ - public void updateTimestamp() { - this.updateTime = System.currentTimeMillis(); - } -} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QAType.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QAType.java deleted file mode 100644 index 3bc31e6..0000000 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QAType.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.timemachinelab.core.qatree; - -public enum QAType { - - QA("QA"), - SELECTION("Selection"), - FORM("Form"); - - private String name; - - QAType(String name) { - this.name = name; - } -} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeDomain.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeDomain.java index 2425b3f..f54d2f7 100644 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeDomain.java +++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeDomain.java @@ -1,19 +1,22 @@ package io.github.timemachinelab.core.qatree; +import io.github.timemachinelab.core.question.BaseQuestion; +import io.github.timemachinelab.core.question.InputQuestion; import org.springframework.stereotype.Component; @Component public class QaTreeDomain { public QaTree createTree(String userStartQuestion) { - CommonQA startQA = new CommonQA(); + InputQuestion startQA = new InputQuestion(); startQA.setQuestion(userStartQuestion); + startQA.setAnswer(userStartQuestion); QaTreeNode startNode = new QaTreeNode(startQA); return new QaTree(startNode); } - public QaTree appendNode(QaTree tree, String parentId, QA qa) { + public QaTree appendNode(QaTree tree, String parentId, BaseQuestion qa) { tree.addNode(parentId, new QaTreeNode(qa)); return tree; } diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeNode.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeNode.java index b55f5fc..ebaa84b 100644 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeNode.java +++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/QaTreeNode.java @@ -1,5 +1,6 @@ package io.github.timemachinelab.core.qatree; +import io.github.timemachinelab.core.question.BaseQuestion; import lombok.Data; import java.util.HashMap; import java.util.Map; @@ -12,15 +13,15 @@ public class QaTreeNode { private Map children; - private QA qa; + private BaseQuestion qa; - public QaTreeNode(QA qa) { + public QaTreeNode(BaseQuestion qa) { this.id = UUID.randomUUID().toString(); this.children = new HashMap<>(); this.qa = qa; } - public void append(QA qa) { + public void append(BaseQuestion qa) { QaTreeNode node = new QaTreeNode(qa); this.append(node); } diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/SelectionQA.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/SelectionQA.java deleted file mode 100644 index 3c6b4b2..0000000 --- a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/qatree/SelectionQA.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.timemachinelab.core.qatree; - -public class SelectionQA extends QA { - - public SelectionQA() { - super(QAType.SELECTION); - } - - private String question; - - private String[] options; - - private Integer selectedOption; -} diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/BaseQuestion.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/BaseQuestion.java new file mode 100644 index 0000000..44c666c --- /dev/null +++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/BaseQuestion.java @@ -0,0 +1,57 @@ +package io.github.timemachinelab.core.question; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 基础问题类 + * 所有问题类型的父类 + * + * @author suifeng + * 日期: 2025/1/27 + */ +@Data +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = SingleChoiceQuestion.class, name = "single"), + @JsonSubTypes.Type(value = MultipleChoiceQuestion.class, name = "multi"), + @JsonSubTypes.Type(value = InputQuestion.class, name = "input"), + @JsonSubTypes.Type(value = FormQuestion.class, name = "form") +}) +public abstract class BaseQuestion { + + /** + * 问题描述 + */ + private String question; + + /** + * 问题类型 + */ + private String type; + + + /** + * 问题的详细说明、引导提示或补充解释(可选) + */ + private String desc; + + /** + * 问题ID(用于答案关联) + */ + private String questionId; + + /** + * 构造函数 + * + * @param type 问题类型 + */ + protected BaseQuestion(QuestionType type) { + this.type = type.getType(); + } +} \ No newline at end of file diff --git a/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/FormField.java b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/FormField.java new file mode 100644 index 0000000..5efcdae --- /dev/null +++ b/prompto-lab-app/src/main/java/io/github/timemachinelab/core/question/FormField.java @@ -0,0 +1,119 @@ +package io.github.timemachinelab.core.question; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 表单字段类 + * 用于表单问题中的字段定义 + * + * @author suifeng + * 日期: 2025/1/27 + */ +@Data +@Builder +@NoArgsConstructor + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class FormField { + + /** + * 字段标识 + */ + private String id; + + /** + * 字段问题描述 + */ + private String question; + + /** + * 字段类型(input、single、multi) + */ + private String type; + + /** + * 选项列表(仅single/multi类型需要) + */ + private List