- 调整了项目结构,将 chat 相关的类移动到 agent/core/chat 包中

- 添加了配置管理功能,支持从文件加载配置
- 添加CoreModel、SliceEvaluator、TaskTrigger、TopicExtractor几个必要模型,具体交互逻辑等待完善
- 优化了记忆图谱的初始化和序列化逻辑,并添加了新的modelPrompt、chatMessages字段
This commit is contained in:
2025-04-14 23:09:09 +08:00
parent 4ccfdf2622
commit 527781cdae
23 changed files with 354 additions and 25 deletions

View File

@@ -1,15 +1,15 @@
package work.slhaf.chat;
package work.slhaf.agent.core.chat;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import work.slhaf.chat.constant.Constant;
import work.slhaf.chat.pojo.ChatBody;
import work.slhaf.chat.pojo.ChatResponse;
import work.slhaf.chat.pojo.Message;
import work.slhaf.chat.pojo.PrimaryChatResponse;
import work.slhaf.agent.core.chat.constant.Constant;
import work.slhaf.agent.core.chat.pojo.ChatBody;
import work.slhaf.agent.core.chat.pojo.ChatResponse;
import work.slhaf.agent.core.chat.pojo.Message;
import work.slhaf.agent.core.chat.pojo.PrimaryChatResponse;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package work.slhaf.chat.constant;
package work.slhaf.agent.core.chat.constant;
public class Constant {

View File

@@ -1,4 +1,4 @@
package work.slhaf.chat.pojo;
package work.slhaf.agent.core.chat.pojo;
import lombok.*;

View File

@@ -1,4 +1,4 @@
package work.slhaf.chat.pojo;
package work.slhaf.agent.core.chat.pojo;
import lombok.AllArgsConstructor;
import lombok.Builder;

View File

@@ -1,4 +1,4 @@
package work.slhaf.chat.pojo;
package work.slhaf.agent.core.chat.pojo;
import lombok.*;

View File

@@ -1,4 +1,4 @@
package work.slhaf.chat.pojo;
package work.slhaf.agent.core.chat.pojo;
import lombok.Getter;
import lombok.Setter;

View File

@@ -0,0 +1,92 @@
package work.slhaf.agent.core.config;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import work.slhaf.agent.core.models.core.CoreModel;
import work.slhaf.agent.core.models.slice.SliceEvaluator;
import work.slhaf.agent.core.models.task.TaskTrigger;
import work.slhaf.agent.core.models.topic.TopicExtractor;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Scanner;
@Data
@Slf4j
public class Config {
private static final String CONFIG_FILE_PATH = "./data/config/config.json";
private static Config config;
private String agentId;
private HashMap<String, ModelConfig> modelConfig;
private WebSocketConfig webSocketConfig;
public static Config load() throws IOException {
if (config == null) {
File file = new File(CONFIG_FILE_PATH);
if (file.exists()) {
config = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8).toBean(Config.class);
} else {
Config tempConfig = new Config();
Scanner scanner = new Scanner(System.in);
System.out.print("输入智能体名称: ");
tempConfig.setAgentId(scanner.nextLine());
System.out.println("\r\n--------模型配置--------\r\n");
HashMap<String, ModelConfig> modelConfig = new HashMap<>();
for (int i = 0; i < 4; i++) {
String modelKey = switch (i) {
case 0 -> {
System.out.println("CoreModel:");
yield CoreModel.MODEL_KEY;
}
case 1 -> {
System.out.println("SliceEvaluator:");
yield SliceEvaluator.MODEL_KEY;
}
case 2 -> {
System.out.println("TaskTrigger:");
yield TaskTrigger.MODEL_KEY;
}
case 3 -> {
System.out.println("TopicExtractor:");
yield TopicExtractor.MODEL_KEY;
}
default -> throw new RuntimeException();
};
System.out.println(modelKey);
ModelConfig temp = new ModelConfig();
System.out.print("apikey: ");
temp.setApikey(scanner.nextLine());
System.out.print("baseUrl: ");
temp.setBaseUrl(scanner.nextLine());
System.out.print("model: ");
temp.setModel(scanner.nextLine());
modelConfig.put(modelKey, temp);
}
tempConfig.setModelConfig(modelConfig);
System.out.println("\r\n--------服务配置--------\r\n");
System.out.print("WebSocket port: ");
WebSocketConfig wsConfig = new WebSocketConfig();
wsConfig.setPort(scanner.nextInt());
//保存配置文件
String str = JSONUtil.toJsonPrettyStr(tempConfig);
FileUtils.writeStringToFile(file,str,StandardCharsets.UTF_8);
log.info("配置已保存");
config = tempConfig;
}
}
return config;
}
}

View File

@@ -0,0 +1,10 @@
package work.slhaf.agent.core.config;
import lombok.Data;
@Data
public class ModelConfig {
private String apikey;
private String baseUrl;
private String model;
}

View File

@@ -0,0 +1,8 @@
package work.slhaf.agent.core.config;
import lombok.Data;
@Data
public class WebSocketConfig {
private Integer port;
}

View File

@@ -3,11 +3,12 @@ package work.slhaf.agent.core.memory;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.agent.core.memory.content.MemorySlice;
import work.slhaf.agent.core.chat.pojo.Message;
import work.slhaf.agent.core.memory.exception.UnExistedTopicException;
import work.slhaf.agent.core.memory.node.MemoryNode;
import work.slhaf.agent.core.memory.node.TopicNode;
import work.slhaf.agent.core.memory.pojo.MemoryResult;
import work.slhaf.agent.core.memory.pojo.MemorySlice;
import work.slhaf.agent.core.memory.pojo.MemorySliceResult;
import work.slhaf.agent.core.memory.pojo.PersistableObject;
@@ -88,6 +89,16 @@ public class MemoryGraph extends PersistableObject {
*/
private LocalDate cacheDate;
/**
* 智能体涉及到的各个模块中模型的prompt
*/
private HashMap<String, String> modelPrompt;
/**
* 主模型的聊天记录
*/
private List<Message> chatMessages;
public MemoryGraph(String id) {
this.id = id;
this.topicNodes = new HashMap<>();
@@ -96,6 +107,7 @@ public class MemoryGraph extends PersistableObject {
this.staticMemory = new HashMap<>();
this.memoryNodeCacheCounter = new ConcurrentHashMap<>();
this.memorySliceCache = new ConcurrentHashMap<>();
this.modelPrompt = new HashMap<>();
}
public static MemoryGraph initialize(String id) {
@@ -104,18 +116,20 @@ public class MemoryGraph extends PersistableObject {
Path filePath = getFilePath(id);
if (Files.exists(filePath)) {
if (memoryGraph == null && Files.exists(filePath)) {
try {
// 从文件加载
return deserialize(id);
memoryGraph = deserialize(id);
} catch (Exception e) {
System.err.println("加载序列化文件失败,创建新实例: " + e.getMessage());
return new MemoryGraph(id);
log.error("加载序列化文件失败,创建新实例");
System.exit(1);
}
} else {
// 创建新实例
return new MemoryGraph(id);
memoryGraph = new MemoryGraph(id);
}
return memoryGraph;
}
public void serialize() {

View File

@@ -3,8 +3,8 @@ package work.slhaf.agent.core.memory.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import work.slhaf.agent.core.memory.content.MemorySlice;
import work.slhaf.agent.core.memory.exception.NullSliceListException;
import work.slhaf.agent.core.memory.pojo.MemorySlice;
import work.slhaf.agent.core.memory.pojo.PersistableObject;
import java.io.*;

View File

@@ -1,7 +1,6 @@
package work.slhaf.agent.core.memory.pojo;
import lombok.Data;
import work.slhaf.agent.core.memory.content.MemorySlice;
import java.util.List;

View File

@@ -1,9 +1,8 @@
package work.slhaf.agent.core.memory.content;
package work.slhaf.agent.core.memory.pojo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.chat.pojo.Message;
import work.slhaf.agent.core.memory.pojo.PersistableObject;
import work.slhaf.agent.core.chat.pojo.Message;
import java.io.Serial;
import java.util.List;

View File

@@ -1,7 +1,6 @@
package work.slhaf.agent.core.memory.pojo;
import lombok.Data;
import work.slhaf.agent.core.memory.content.MemorySlice;
@Data
public class MemorySliceResult {

View File

@@ -0,0 +1,39 @@
package work.slhaf.agent.core.models.common;
import lombok.Data;
import work.slhaf.agent.core.chat.ChatClient;
import work.slhaf.agent.core.chat.constant.Constant;
import work.slhaf.agent.core.chat.pojo.Message;
import work.slhaf.agent.core.config.Config;
import work.slhaf.agent.core.config.ModelConfig;
import work.slhaf.agent.core.memory.MemoryGraph;
import java.util.ArrayList;
import java.util.List;
@Data
public class Model {
protected ChatClient chatClient;
protected String prompt;
protected List<Message> messages;
protected static void setModel(Config config, Model model, String model_key, String prompt) {
MemoryGraph memoryGraph = MemoryGraph.initialize(config.getAgentId());
ModelConfig modelConfig = config.getModelConfig().get(model_key);
if (memoryGraph.getModelPrompt().containsKey(model_key)) {
model.setPrompt(memoryGraph.getModelPrompt().get(model_key));
} else {
model.setPrompt(prompt);
memoryGraph.getModelPrompt().put(model_key, prompt);
}
if (memoryGraph.getChatMessages() == null) {
List<Message> tempMessages = new ArrayList<>();
tempMessages.add(new Message(Constant.Character.SYSTEM, model.getPrompt()));
model.setMessages(tempMessages);
memoryGraph.setChatMessages(tempMessages);
} else {
model.setMessages(memoryGraph.getChatMessages());
}
model.setChatClient(new ChatClient(modelConfig.getBaseUrl(), modelConfig.getApikey(), modelConfig.getModel()));
}
}

View File

@@ -0,0 +1,60 @@
package work.slhaf.agent.core.models.common;
public class ModelConstant {
public static final String CORE_MODEL_PROMPT = """
你是一个智能助理,专为提供个性化对话体验而设计。你拥有持久记忆系统,能够根据过去的对话和个人信息提供相关回复。请遵循以下规则:
1. **记忆管理**
- 你可以通过查询过去的对话或事件来提供更相关的建议和回答。记住,所有记忆都应在用户的明确要求下进行更新。
- 每次回复前,你应查询并注入相关的记忆片段(切片)以补充上下文。如果记忆片段不足或需要更多信息,请尝试主动提问获取详细信息。
2. **对话上下文**
- 使用提供的对话摘要来建立对话的上下文。始终根据对话主题(如生活、健康、学习等)来提供切合的回应。
- 对话切片应包含当前话题以及与用户相关的历史信息。请在系统提示中加载适当的主题路径。
3. **任务调度与推理**
- 在对话中,如果发现用户有明确的任务需求(例如:设置提醒、安排会议等),请识别并提取任务意图。对于潜在任务,可以通过询问用户进一步确认。
- 如果任务触发条件符合,请在回复中告知用户任务已经识别并推送至任务调度系统。
4. **行为约束**
- 确保回答始终清晰、礼貌并且尽可能帮助用户。避免提供无关或不准确的信息。
- 如果用户提出要求超出合理范围(例如设置过于晚的提醒),请礼貌地进行反向确认或建议。
5. **用户个性化**
- 对话中涉及到用户的偏好、爱好、历史事件等应当进行个性化回应。你可以使用记忆系统中的用户信息来增加对话的自然度和亲和力。
6. **动态记忆更新**
- 动态更新记忆时,要确保准确地记录用户的需求和变动。更新过程应具有透明性,并且在更新前可以询问用户确认。
系统目标:始终提供最相关、最个性化的回复,并通过持续回顾与更新来提高系统的长期记忆能力。
""";
public static final String SLICE_EVALUATOR_PROMPT = """
你是一个智能助理,专为提供个性化对话体验而设计。你拥有持久记忆系统,能够根据过去的对话和个人信息提供相关回复。请遵循以下规则:
1. **记忆管理**
- 你可以通过查询过去的对话或事件来提供更相关的建议和回答。记住,所有记忆都应在用户的明确要求下进行更新。
- 每次回复前,你应查询并注入相关的记忆片段(切片)以补充上下文。如果记忆片段不足或需要更多信息,请尝试主动提问获取详细信息。
2. **对话上下文**
- 使用提供的对话摘要来建立对话的上下文。始终根据对话主题(如生活、健康、学习等)来提供切合的回应。
- 对话切片应包含当前话题以及与用户相关的历史信息。请在系统提示中加载适当的主题路径。
3. **任务调度与推理**
- 在对话中,如果发现用户有明确的任务需求(例如:设置提醒、安排会议等),请识别并提取任务意图。对于潜在任务,可以通过询问用户进一步确认。
- 如果任务触发条件符合,请在回复中告知用户任务已经识别并推送至任务调度系统。
4. **行为约束**
- 确保回答始终清晰、礼貌并且尽可能帮助用户。避免提供无关或不准确的信息。
- 如果用户提出要求超出合理范围(例如设置过于晚的提醒),请礼貌地进行反向确认或建议。
5. **用户个性化**
- 对话中涉及到用户的偏好、爱好、历史事件等应当进行个性化回应。你可以使用记忆系统中的用户信息来增加对话的自然度和亲和力。
6. **动态记忆更新**
- 动态更新记忆时,要确保准确地记录用户的需求和变动。更新过程应具有透明性,并且在更新前可以询问用户确认。
系统目标:始终提供最相关、最个性化的回复,并通过持续回顾与更新来提高系统的长期记忆能力。
""";
}

View File

@@ -0,0 +1,25 @@
package work.slhaf.agent.core.models.core;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.core.config.Config;
import work.slhaf.agent.core.models.common.Model;
import work.slhaf.agent.core.models.common.ModelConstant;
@EqualsAndHashCode(callSuper = true)
@Data
public class CoreModel extends Model {
public static final String MODEL_KEY = "core_model";
private static CoreModel coreModel;
public static CoreModel initialize(Config config) {
if (coreModel == null) {
coreModel = new CoreModel();
coreModel.setPrompt(ModelConstant.CORE_MODEL_PROMPT);
setModel(config, coreModel, MODEL_KEY, coreModel.getPrompt());
}
return coreModel;
}
}

View File

@@ -0,0 +1,28 @@
package work.slhaf.agent.core.models.slice;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.core.config.Config;
import work.slhaf.agent.core.models.common.Model;
import work.slhaf.agent.core.models.common.ModelConstant;
@EqualsAndHashCode(callSuper = true)
@Data
public class SliceEvaluator extends Model {
public static final String MODEL_KEY = "slice_evaluator";
private static SliceEvaluator sliceEvaluator;
public static SliceEvaluator initialize(Config config) {
if (sliceEvaluator == null) {
sliceEvaluator = new SliceEvaluator();
sliceEvaluator.setPrompt(ModelConstant.SLICE_EVALUATOR_PROMPT);
setModel(config,sliceEvaluator, MODEL_KEY, sliceEvaluator.getPrompt());
}
return sliceEvaluator;
}
}

View File

@@ -0,0 +1,25 @@
package work.slhaf.agent.core.models.task;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.core.config.Config;
import work.slhaf.agent.core.models.common.Model;
import work.slhaf.agent.core.models.common.ModelConstant;
@EqualsAndHashCode(callSuper = true)
@Data
public class TaskTrigger extends Model {
public static final String MODEL_KEY = "task_trigger";
private static TaskTrigger taskTrigger;
public static TaskTrigger initialize(Config config) {
if (taskTrigger == null) {
taskTrigger = new TaskTrigger();
taskTrigger.setPrompt(ModelConstant.SLICE_EVALUATOR_PROMPT);
setModel(config,taskTrigger, MODEL_KEY, taskTrigger.getPrompt());
}
return taskTrigger;
}
}

View File

@@ -0,0 +1,26 @@
package work.slhaf.agent.core.models.topic;
import lombok.Data;
import lombok.EqualsAndHashCode;
import work.slhaf.agent.core.config.Config;
import work.slhaf.agent.core.models.common.Model;
import work.slhaf.agent.core.models.common.ModelConstant;
@EqualsAndHashCode(callSuper = true)
@Data
public class TopicExtractor extends Model {
public static final String MODEL_KEY = "topic_extractor";
private static TopicExtractor topicExtractor;
public static TopicExtractor initialize(Config config) {
if (topicExtractor == null) {
topicExtractor = new TopicExtractor();
topicExtractor.setPrompt(ModelConstant.SLICE_EVALUATOR_PROMPT);
setModel(config,topicExtractor, MODEL_KEY, topicExtractor.getPrompt());
}
return topicExtractor;
}
}

View File

@@ -3,7 +3,7 @@ package memory;
import org.junit.Before;
import org.junit.Test;
import work.slhaf.agent.core.memory.MemoryGraph;
import work.slhaf.agent.core.memory.content.MemorySlice;
import work.slhaf.agent.core.memory.pojo.MemorySlice;
import work.slhaf.agent.core.memory.node.MemoryNode;
import work.slhaf.agent.core.memory.node.TopicNode;

View File

@@ -3,7 +3,7 @@ package memory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import work.slhaf.agent.core.memory.MemoryGraph;
import work.slhaf.agent.core.memory.content.MemorySlice;
import work.slhaf.agent.core.memory.pojo.MemorySlice;
import work.slhaf.agent.core.memory.exception.UnExistedTopicException;
import work.slhaf.agent.core.memory.node.MemoryNode;
import work.slhaf.agent.core.memory.node.TopicNode;