mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
refactor(memory): refactor memory extract/evaluating logic and messages building in memory modules
This commit is contained in:
@@ -1,92 +1,148 @@
|
||||
package work.slhaf.partner.module.memory.selector.evaluator;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import kotlin.Unit;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||
import work.slhaf.partner.api.agent.factory.component.abstracts.AbstractAgentModule;
|
||||
import work.slhaf.partner.api.agent.factory.component.abstracts.ActivateModel;
|
||||
import work.slhaf.partner.api.agent.factory.component.annotation.Init;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.common.thread.InteractionThreadPoolExecutor;
|
||||
import work.slhaf.partner.core.action.ActionCapability;
|
||||
import work.slhaf.partner.core.action.ActionCore;
|
||||
import work.slhaf.partner.core.cognition.CognitionCapability;
|
||||
import work.slhaf.partner.core.cognition.ContextBlock;
|
||||
import work.slhaf.partner.core.memory.pojo.ActivatedMemorySlice;
|
||||
import work.slhaf.partner.module.TaskBlock;
|
||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorBatchInput;
|
||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorBatchResult;
|
||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorInput;
|
||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorResult;
|
||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.SliceSummary;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class SliceSelectEvaluator extends AbstractAgentModule.Sub<EvaluatorInput, List<ActivatedMemorySlice>> implements ActivateModel {
|
||||
private InteractionThreadPoolExecutor executor;
|
||||
|
||||
@InjectCapability
|
||||
private ActionCapability actionCapability;
|
||||
@InjectCapability
|
||||
private CognitionCapability cognitionCapability;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
@Init
|
||||
public void init() {
|
||||
executor = InteractionThreadPoolExecutor.getInstance();
|
||||
executor = actionCapability.getExecutor(ActionCore.ExecutorType.VIRTUAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ActivatedMemorySlice> execute(EvaluatorInput evaluatorInput) {
|
||||
log.debug("[SliceSelectEvaluator] 切片评估模块开始...");
|
||||
List<ActivatedMemorySlice> memorySlices = evaluatorInput.getMemorySlices();
|
||||
List<Callable<Void>> tasks = new ArrayList<>();
|
||||
Queue<ActivatedMemorySlice> queue = new ConcurrentLinkedDeque<>();
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
if (memorySlices == null || memorySlices.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
tasks.add(() -> {
|
||||
int thisCount = count.incrementAndGet();
|
||||
log.debug("[SliceSelectEvaluator] 评估[{}]开始", thisCount);
|
||||
List<SliceSummary> sliceSummaryList = new ArrayList<>();
|
||||
Map<Long, ActivatedMemorySlice> map = new HashMap<>();
|
||||
try {
|
||||
setSliceSummaryList(memorySlices, sliceSummaryList, map);
|
||||
EvaluatorBatchInput batchInput = EvaluatorBatchInput.builder()
|
||||
.text(evaluatorInput.getInput())
|
||||
.memory_slices(sliceSummaryList)
|
||||
.history(evaluatorInput.getMessages())
|
||||
.build();
|
||||
log.debug("[SliceSelectEvaluator] 评估[{}]输入: {}", thisCount, JSONObject.toJSONString(batchInput));
|
||||
EvaluatorResult evaluatorResult = formattedChat(
|
||||
List.of(new Message(Message.Character.USER, JSONUtil.toJsonStr(batchInput))),
|
||||
EvaluatorResult.class
|
||||
);
|
||||
log.debug("[SliceSelectEvaluator] 评估[{}]结果: {}", thisCount, JSONObject.toJSONString(evaluatorResult));
|
||||
for (Long result : evaluatorResult.getResults()) {
|
||||
ActivatedMemorySlice slice = map.get(result);
|
||||
if (slice != null) {
|
||||
queue.offer(slice);
|
||||
log.debug("切片评估模块开始...");
|
||||
List<ActivatedMemorySlice> preparedSlices = evaluatorInput.getMemorySlices();
|
||||
List<ActivatedMemorySlice> result = new ArrayList<>();
|
||||
CountDownLatch latch = new CountDownLatch(preparedSlices.size());
|
||||
|
||||
Message contextMessage = cognitionCapability.contextWorkspace().resolve(List.of(
|
||||
ContextBlock.VisibleDomain.COGNITION,
|
||||
ContextBlock.VisibleDomain.MEMORY
|
||||
)).encodeToMessage();
|
||||
|
||||
for (ActivatedMemorySlice slice : preparedSlices) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
EvaluatorBatchInput batchInput = new EvaluatorBatchInput(evaluatorInput.getInputs(), slice);
|
||||
List<Message> messages = List.of(
|
||||
contextMessage,
|
||||
resolveTaskMessage(batchInput)
|
||||
);
|
||||
EvaluatorBatchResult batchResult = formattedChat(messages, EvaluatorBatchResult.class);
|
||||
if (batchResult.isPassed()) {
|
||||
synchronized (result) {
|
||||
result.add(slice);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[SliceSelectEvaluator] 评估[{}]出现错误: {}", thisCount, e.getLocalizedMessage());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
executor.invokeAll(tasks, 30, TimeUnit.SECONDS);
|
||||
log.debug("[SliceSelectEvaluator] 评估模块结束, 输出队列: {}", queue);
|
||||
return new ArrayList<>(queue);
|
||||
}
|
||||
|
||||
private void setSliceSummaryList(List<ActivatedMemorySlice> memorySlices, List<SliceSummary> sliceSummaryList,
|
||||
Map<Long, ActivatedMemorySlice> map) {
|
||||
for (ActivatedMemorySlice memorySlice : memorySlices) {
|
||||
SliceSummary sliceSummary = new SliceSummary();
|
||||
sliceSummary.setId(memorySlice.getTimestamp());
|
||||
sliceSummary.setSummary(memorySlice.getSummary());
|
||||
sliceSummary.setDate(memorySlice.getDate());
|
||||
sliceSummaryList.add(sliceSummary);
|
||||
map.put(memorySlice.getTimestamp(), memorySlice);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
log.debug("切片评估模块结束...");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Message resolveTaskMessage(EvaluatorBatchInput batchInput) {
|
||||
return new TaskBlock() {
|
||||
@Override
|
||||
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
||||
appendListElement(document, root, "new_inputs", "input", batchInput.getInputs().entrySet(), (inputElement, input) -> {
|
||||
inputElement.setAttribute("datetime", input.getKey().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
inputElement.setTextContent(input.getValue());
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
appendChildElement(document, root, "memory_slice", (element) -> {
|
||||
ActivatedMemorySlice slice = batchInput.getActivatedMemorySlice();
|
||||
appendTextElement(document, element, "slice_summary", slice.getSummary());
|
||||
appendChildElement(document, element, "source_messages", (messagesElement) -> {
|
||||
List<Message> messages = slice.getMessages();
|
||||
int size = messages.size();
|
||||
if (size > 10) {
|
||||
// 展示前两条
|
||||
appendMessageElement(document, messagesElement, messages.subList(0, 2));
|
||||
|
||||
// 中间省略说明
|
||||
int middleStart = Math.max(2, (size - 4) / 2);
|
||||
int middleEnd = Math.min(size - 2, middleStart + 4);
|
||||
int omittedBeforeMiddle = middleStart - 2;
|
||||
appendTextElement(document, messagesElement, "omitted_messages", "省略了 " + omittedBeforeMiddle + " 条消息");
|
||||
|
||||
// 中间四条
|
||||
appendMessageElement(document, messagesElement, messages.subList(middleStart, middleEnd));
|
||||
|
||||
// 中间到结尾前的省略说明
|
||||
int omittedAfterMiddle = (size - 2) - middleEnd;
|
||||
if (omittedAfterMiddle > 0) {
|
||||
appendTextElement(document, messagesElement, "omitted_messages", "省略了 " + omittedAfterMiddle + " 条消息");
|
||||
}
|
||||
|
||||
// 展示末尾两条
|
||||
appendMessageElement(document, messagesElement, messages.subList(size - 2, size));
|
||||
} else {
|
||||
appendMessageElement(document, messagesElement, messages);
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
private void appendMessageElement(Document document, Element parent, List<Message> messages) {
|
||||
appendRepeatedElements(document, parent, "message", messages, (messageElement, message) -> {
|
||||
messageElement.setAttribute("role", message.getRole().name().toLowerCase(Locale.ROOT));
|
||||
messageElement.setTextContent(message.getContent());
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
}.encodeToMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String modelKey() {
|
||||
return "slice_evaluator";
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package work.slhaf.partner.module.memory.selector.evaluator.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.core.memory.pojo.ActivatedMemorySlice;
|
||||
|
||||
import java.util.List;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class EvaluatorBatchInput {
|
||||
private String text;
|
||||
private List<Message> history;
|
||||
private List<SliceSummary> memory_slices;
|
||||
private Map<LocalDateTime, String> inputs;
|
||||
private ActivatedMemorySlice activatedMemorySlice;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package work.slhaf.partner.module.memory.selector.evaluator.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class EvaluatorResult {
|
||||
private List<Long> results;
|
||||
public class EvaluatorBatchResult {
|
||||
private boolean passed;
|
||||
}
|
||||
@@ -2,15 +2,15 @@ package work.slhaf.partner.module.memory.selector.evaluator.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.core.memory.pojo.ActivatedMemorySlice;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class EvaluatorInput {
|
||||
private String input;
|
||||
private List<Message> messages;
|
||||
private Map<LocalDateTime, String> inputs;
|
||||
private List<ActivatedMemorySlice> memorySlices;
|
||||
}
|
||||
|
||||
@@ -1,54 +1,47 @@
|
||||
package work.slhaf.partner.module.memory.selector.extractor;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import work.slhaf.partner.api.agent.factory.capability.annotation.InjectCapability;
|
||||
import work.slhaf.partner.api.agent.factory.component.abstracts.AbstractAgentModule;
|
||||
import work.slhaf.partner.api.agent.factory.component.abstracts.ActivateModel;
|
||||
import work.slhaf.partner.api.agent.factory.component.annotation.InjectModule;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.core.cognition.CognitionCapability;
|
||||
import work.slhaf.partner.core.memory.MemoryCapability;
|
||||
import work.slhaf.partner.core.memory.pojo.ActivatedMemorySlice;
|
||||
import work.slhaf.partner.core.cognition.ContextBlock;
|
||||
import work.slhaf.partner.module.TaskBlock;
|
||||
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
||||
import work.slhaf.partner.module.memory.selector.extractor.entity.ExtractorInput;
|
||||
import work.slhaf.partner.module.memory.selector.extractor.entity.ExtractorMatchData;
|
||||
import work.slhaf.partner.module.memory.selector.extractor.entity.ExtractorResult;
|
||||
import work.slhaf.partner.runtime.interaction.data.context.PartnerRunningFlowContext;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import static work.slhaf.partner.common.util.ExtractUtil.fixTopicPath;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class MemorySelectExtractor extends AbstractAgentModule.Sub<PartnerRunningFlowContext, ExtractorResult>
|
||||
implements ActivateModel {
|
||||
@InjectCapability
|
||||
private MemoryCapability memoryCapability;
|
||||
public class MemorySelectExtractor extends AbstractAgentModule.Sub<ExtractorInput, ExtractorResult> implements ActivateModel {
|
||||
@InjectCapability
|
||||
private CognitionCapability cognitionCapability;
|
||||
@InjectModule
|
||||
private MemoryRuntime memoryRuntime;
|
||||
|
||||
@Override
|
||||
public ExtractorResult execute(PartnerRunningFlowContext context) {
|
||||
public ExtractorResult execute(ExtractorInput input) {
|
||||
log.debug("[MemorySelectExtractor] 主题提取模块开始...");
|
||||
List<Message> chatMessages = cognitionCapability.snapshotChatMessages();
|
||||
ExtractorResult extractorResult;
|
||||
try {
|
||||
List<ActivatedMemorySlice> activatedMemorySlices = memoryCapability.getActivatedSlices();
|
||||
ExtractorInput extractorInput = ExtractorInput.builder()
|
||||
.text(context.getInput())
|
||||
.date(context.getInfo().getDateTime().toLocalDate())
|
||||
.history(chatMessages)
|
||||
.topic_tree(memoryRuntime.getTopicTree())
|
||||
.activatedMemorySlices(activatedMemorySlices)
|
||||
.build();
|
||||
log.debug("[MemorySelectExtractor] 主题提取输入: {}", JSONUtil.toJsonStr(extractorInput));
|
||||
List<Message> messages = List.of(
|
||||
resolveContextMessage(),
|
||||
resolveTaskMessage(input)
|
||||
);
|
||||
extractorResult = formattedChat(
|
||||
List.of(new Message(Message.Character.USER, JSONUtil.toJsonPrettyStr(extractorInput))),
|
||||
messages,
|
||||
ExtractorResult.class
|
||||
);
|
||||
log.debug("[MemorySelectExtractor] 主题提取结果: {}", extractorResult);
|
||||
@@ -61,6 +54,23 @@ public class MemorySelectExtractor extends AbstractAgentModule.Sub<PartnerRunnin
|
||||
return fix(extractorResult);
|
||||
}
|
||||
|
||||
private Message resolveTaskMessage(ExtractorInput input) {
|
||||
return new TaskBlock() {
|
||||
@Override
|
||||
protected void fillXml(@NotNull Document document, @NotNull Element root) {
|
||||
appendTextElement(document, root, "latest_input", input.getInput());
|
||||
appendTextElement(document, root, "current_date", input.getDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
||||
appendTextElement(document, root, "memory_topic_tree", input.getTopic_tree());
|
||||
}
|
||||
}.encodeToMessage();
|
||||
}
|
||||
|
||||
private Message resolveContextMessage() {
|
||||
return cognitionCapability.contextWorkspace().resolve(List.of(
|
||||
ContextBlock.VisibleDomain.COGNITION, ContextBlock.VisibleDomain.MEMORY
|
||||
)).encodeToMessage();
|
||||
}
|
||||
|
||||
private ExtractorResult fix(ExtractorResult extractorResult) {
|
||||
extractorResult.getMatches().forEach(m -> {
|
||||
if (m.getType().equals(ExtractorMatchData.Constant.DATE)) {
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package work.slhaf.partner.module.memory.selector.extractor.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import work.slhaf.partner.api.chat.pojo.Message;
|
||||
import work.slhaf.partner.core.memory.pojo.ActivatedMemorySlice;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class ExtractorInput {
|
||||
private String text;
|
||||
private String input;
|
||||
private String topic_tree;
|
||||
private LocalDate date;
|
||||
private List<Message> history;
|
||||
private List<ActivatedMemorySlice> activatedMemorySlices;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user