mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
refactor(memory): migrate slice/topic/date lookup to Result flow and unify MemoryLookupException reporting
This commit is contained in:
@@ -4,6 +4,7 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
|||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.Capability;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -13,7 +14,7 @@ public interface MemoryCapability {
|
|||||||
|
|
||||||
MemoryUnit getMemoryUnit(String unitId);
|
MemoryUnit getMemoryUnit(String unitId);
|
||||||
|
|
||||||
MemorySlice getMemorySlice(String unitId, String sliceId);
|
Result<MemorySlice> getMemorySlice(String unitId, String sliceId);
|
||||||
|
|
||||||
MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary);
|
MemoryUnit updateMemoryUnit(List<Message> chatMessages, String summary);
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import work.slhaf.partner.framework.agent.model.pojo.Message;
|
|||||||
import work.slhaf.partner.framework.agent.state.State;
|
import work.slhaf.partner.framework.agent.state.State;
|
||||||
import work.slhaf.partner.framework.agent.state.StateSerializable;
|
import work.slhaf.partner.framework.agent.state.StateSerializable;
|
||||||
import work.slhaf.partner.framework.agent.state.StateValue;
|
import work.slhaf.partner.framework.agent.state.StateValue;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
|
import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -71,17 +73,25 @@ public class MemoryCore implements StateSerializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
public MemorySlice getMemorySlice(String unitId, String sliceId) {
|
public Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
||||||
MemoryUnit memoryUnit = memoryUnits.get(unitId);
|
MemoryUnit memoryUnit = memoryUnits.get(unitId);
|
||||||
if (memoryUnit == null || memoryUnit.getSlices() == null) {
|
if (memoryUnit == null || memoryUnit.getSlices() == null) {
|
||||||
return null;
|
return Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
for (MemorySlice slice : memoryUnit.getSlices()) {
|
for (MemorySlice slice : memoryUnit.getSlices()) {
|
||||||
if (sliceId.equals(slice.getId())) {
|
if (sliceId.equals(slice.getId())) {
|
||||||
return slice;
|
return Result.success(slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@CapabilityMethod
|
@CapabilityMethod
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
|||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.AgentComponent;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -66,14 +67,14 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider {
|
|||||||
Function<Map<String, Object>, String> invoker = params -> {
|
Function<Map<String, Object>, String> invoker = params -> {
|
||||||
String unitId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "unit_id");
|
String unitId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "unit_id");
|
||||||
String sliceId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "slice_id");
|
String sliceId = BuiltinActionRegistry.BuiltinActionDefinition.requireString(params, "slice_id");
|
||||||
MemorySlice slice = memoryCapability.getMemorySlice(unitId, sliceId);
|
Result<MemorySlice> sliceResult = memoryCapability.getMemorySlice(unitId, sliceId);
|
||||||
|
if (sliceResult.exceptionOrNull() != null) {
|
||||||
if (slice == null) {
|
|
||||||
return JSONObject.of(
|
return JSONObject.of(
|
||||||
"ok", false,
|
"ok", false,
|
||||||
"message", "Memory slice not found"
|
"message", sliceResult.exceptionOrNull().getLocalizedMessage()
|
||||||
).toJSONString();
|
).toJSONString();
|
||||||
}
|
}
|
||||||
|
MemorySlice slice = sliceResult.getOrThrow();
|
||||||
|
|
||||||
MemoryUnit unit = memoryCapability.getMemoryUnit(unitId);
|
MemoryUnit unit = memoryCapability.getMemoryUnit(unitId);
|
||||||
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
cognitionCapability.contextWorkspace().register(new ContextBlock(
|
||||||
@@ -85,7 +86,7 @@ class BuiltinCapabilityActionProvider implements BuiltinActionProvider {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return JSONObject.of(
|
return JSONObject.of(
|
||||||
"ok", false,
|
"ok", true,
|
||||||
"message", "Memory slice found and recalled into context"
|
"message", "Memory slice found and recalled into context"
|
||||||
).toJSONString();
|
).toJSONString();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import work.slhaf.partner.core.memory.MemoryCapability;
|
|||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
||||||
|
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
|
||||||
@@ -17,8 +18,8 @@ import work.slhaf.partner.framework.agent.model.pojo.Message;
|
|||||||
import work.slhaf.partner.framework.agent.state.State;
|
import work.slhaf.partner.framework.agent.state.State;
|
||||||
import work.slhaf.partner.framework.agent.state.StateSerializable;
|
import work.slhaf.partner.framework.agent.state.StateSerializable;
|
||||||
import work.slhaf.partner.framework.agent.state.StateValue;
|
import work.slhaf.partner.framework.agent.state.StateValue;
|
||||||
import work.slhaf.partner.module.memory.runtime.exception.UnExistedDateIndexException;
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
import work.slhaf.partner.module.memory.runtime.exception.UnExistedTopicException;
|
import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
||||||
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -105,16 +106,26 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
private List<SliceRef> findByTopicPath(String topicPath) {
|
private List<SliceRef> findByTopicPath(String topicPath) {
|
||||||
String normalizedPath = normalizeTopicPath(topicPath);
|
String normalizedPath = normalizeTopicPath(topicPath);
|
||||||
List<SliceRef> refs = topicSlices.get(normalizedPath);
|
List<SliceRef> refs = topicSlices.get(normalizedPath);
|
||||||
if (refs == null || refs.isEmpty()) {
|
if (refs == null) {
|
||||||
throw new UnExistedTopicException("不存在的主题: " + normalizedPath);
|
ExceptionReporterHandler.INSTANCE.report(new MemoryLookupException(
|
||||||
|
"Unexisted topic path: " + normalizedPath,
|
||||||
|
normalizedPath,
|
||||||
|
"TOPIC"
|
||||||
|
));
|
||||||
|
return List.of();
|
||||||
}
|
}
|
||||||
return new ArrayList<>(refs);
|
return new ArrayList<>(refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SliceRef> findByDate(LocalDate date) {
|
private List<SliceRef> findByDate(LocalDate date) {
|
||||||
List<SliceRef> refs = dateIndex.get(date);
|
List<SliceRef> refs = dateIndex.get(date);
|
||||||
if (refs == null || refs.isEmpty()) {
|
if (refs == null) {
|
||||||
throw new UnExistedDateIndexException("不存在的日期索引: " + date);
|
ExceptionReporterHandler.INSTANCE.report(new MemoryLookupException(
|
||||||
|
"Unexisted date index: " + date,
|
||||||
|
date.toString(),
|
||||||
|
"DATE_INDEX"
|
||||||
|
));
|
||||||
|
return List.of();
|
||||||
}
|
}
|
||||||
return new ArrayList<>(refs);
|
return new ArrayList<>(refs);
|
||||||
}
|
}
|
||||||
@@ -160,10 +171,11 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
|
|
||||||
private ActivatedMemorySlice buildActivatedMemorySlice(SliceRef ref) {
|
private ActivatedMemorySlice buildActivatedMemorySlice(SliceRef ref) {
|
||||||
MemoryUnit memoryUnit = memoryCapability.getMemoryUnit(ref.getUnitId());
|
MemoryUnit memoryUnit = memoryCapability.getMemoryUnit(ref.getUnitId());
|
||||||
MemorySlice memorySlice = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId());
|
Result<MemorySlice> memorySliceResult = memoryCapability.getMemorySlice(ref.getUnitId(), ref.getSliceId());
|
||||||
if (memoryUnit == null || memorySlice == null) {
|
if (memorySliceResult.exceptionOrNull() != null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
MemorySlice memorySlice = memorySliceResult.getOrThrow();
|
||||||
List<Message> messages = sliceMessages(memoryUnit, memorySlice);
|
List<Message> messages = sliceMessages(memoryUnit, memorySlice);
|
||||||
LocalDate date = Instant.ofEpochMilli(memorySlice.getTimestamp())
|
LocalDate date = Instant.ofEpochMilli(memorySlice.getTimestamp())
|
||||||
.atZone(ZoneId.systemDefault())
|
.atZone(ZoneId.systemDefault())
|
||||||
@@ -254,7 +266,7 @@ public class MemoryRuntime extends AbstractAgentModule.Standalone implements Sta
|
|||||||
try {
|
try {
|
||||||
dateIndex.put(LocalDate.parse(date), decodeSliceRefs(dateObject.getJSONArray("refs")));
|
dateIndex.put(LocalDate.parse(date), decodeSliceRefs(dateObject.getJSONArray("refs")));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("[MemoryRuntime] 跳过非法日期索引: {}", date, e);
|
log.warn("skip invalid date index: {}", date, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package work.slhaf.partner.module.memory.runtime.exception;
|
||||||
|
|
||||||
|
import work.slhaf.partner.framework.agent.exception.AgentRuntimeException;
|
||||||
|
import work.slhaf.partner.framework.agent.exception.ExceptionReport;
|
||||||
|
|
||||||
|
public class MemoryLookupException extends AgentRuntimeException {
|
||||||
|
|
||||||
|
private final String lookupKey;
|
||||||
|
private final String lookupTarget;
|
||||||
|
|
||||||
|
public MemoryLookupException(String message, String lookupKey, String lookupTarget) {
|
||||||
|
super(message);
|
||||||
|
this.lookupKey = lookupKey;
|
||||||
|
this.lookupTarget = lookupTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryLookupException(String message, String lookupKey, String lookupTarget, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.lookupKey = lookupKey;
|
||||||
|
this.lookupTarget = lookupTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExceptionReport toReport() {
|
||||||
|
ExceptionReport report = super.toReport();
|
||||||
|
report.getExtra().put("lookupKey", lookupKey);
|
||||||
|
report.getExtra().put("lookupTarget", lookupTarget);
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package work.slhaf.partner.module.memory.runtime.exception;
|
|
||||||
|
|
||||||
public class UnExistedDateIndexException extends RuntimeException {
|
|
||||||
public UnExistedDateIndexException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package work.slhaf.partner.module.memory.runtime.exception;
|
|
||||||
|
|
||||||
public class UnExistedTopicException extends RuntimeException {
|
|
||||||
public UnExistedTopicException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,8 +16,7 @@ import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAg
|
|||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
||||||
import work.slhaf.partner.module.memory.runtime.exception.UnExistedDateIndexException;
|
import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
||||||
import work.slhaf.partner.module.memory.runtime.exception.UnExistedTopicException;
|
|
||||||
import work.slhaf.partner.module.memory.selector.evaluator.SliceSelectEvaluator;
|
import work.slhaf.partner.module.memory.selector.evaluator.SliceSelectEvaluator;
|
||||||
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorInput;
|
import work.slhaf.partner.module.memory.selector.evaluator.entity.EvaluatorInput;
|
||||||
import work.slhaf.partner.module.memory.selector.extractor.MemorySelectExtractor;
|
import work.slhaf.partner.module.memory.selector.extractor.MemorySelectExtractor;
|
||||||
@@ -114,7 +113,7 @@ public class MemorySelector extends AbstractAgentModule.Running<PartnerRunningFl
|
|||||||
);
|
);
|
||||||
|
|
||||||
ExtractorResult extractorResult = memorySelectExtractor.execute(input);
|
ExtractorResult extractorResult = memorySelectExtractor.execute(input);
|
||||||
if (extractorResult.isRecall() || !extractorResult.getMatches().isEmpty()) {
|
if (!extractorResult.getMatches().isEmpty()) {
|
||||||
List<ActivatedMemorySlice> activatedSlices = selectAndEvaluateMemory(snapshotInputs, extractorResult);
|
List<ActivatedMemorySlice> activatedSlices = selectAndEvaluateMemory(snapshotInputs, extractorResult);
|
||||||
updateMemoryContext(activatedSlices);
|
updateMemoryContext(activatedSlices);
|
||||||
}
|
}
|
||||||
@@ -207,7 +206,6 @@ public class MemorySelector extends AbstractAgentModule.Running<PartnerRunningFl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<ActivatedMemorySlice> selectAndEvaluateMemory(Map<LocalDateTime, String> snapshotInputs, ExtractorResult extractorResult) {
|
private List<ActivatedMemorySlice> selectAndEvaluateMemory(Map<LocalDateTime, String> snapshotInputs, ExtractorResult extractorResult) {
|
||||||
log.debug("[MemorySelector] 触发记忆回溯...");
|
|
||||||
LinkedHashMap<String, ActivatedMemorySlice> candidates = new LinkedHashMap<>();
|
LinkedHashMap<String, ActivatedMemorySlice> candidates = new LinkedHashMap<>();
|
||||||
setMemoryCandidates(candidates, extractorResult.getMatches());
|
setMemoryCandidates(candidates, extractorResult.getMatches());
|
||||||
EvaluatorInput evaluatorInput = EvaluatorInput.builder()
|
EvaluatorInput evaluatorInput = EvaluatorInput.builder()
|
||||||
@@ -231,7 +229,7 @@ public class MemorySelector extends AbstractAgentModule.Running<PartnerRunningFl
|
|||||||
for (ActivatedMemorySlice recalledSlice : recalledSlices) {
|
for (ActivatedMemorySlice recalledSlice : recalledSlices) {
|
||||||
candidates.putIfAbsent(recalledSlice.getUnitId() + ":" + recalledSlice.getSliceId(), recalledSlice);
|
candidates.putIfAbsent(recalledSlice.getUnitId() + ":" + recalledSlice.getSliceId(), recalledSlice);
|
||||||
}
|
}
|
||||||
} catch (UnExistedDateIndexException | UnExistedTopicException e) {
|
} catch (MemoryLookupException e) {
|
||||||
log.error("[MemorySelector] 不存在的记忆索引", e);
|
log.error("[MemorySelector] 不存在的记忆索引", e);
|
||||||
log.error("[MemorySelector] 错误索引: {}", match.getText());
|
log.error("[MemorySelector] 错误索引: {}", match.getText());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.w3c.dom.Document;
|
|||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import work.slhaf.partner.core.cognition.CognitionCapability;
|
import work.slhaf.partner.core.cognition.CognitionCapability;
|
||||||
import work.slhaf.partner.core.cognition.ContextBlock;
|
import work.slhaf.partner.core.cognition.ContextBlock;
|
||||||
|
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
||||||
@@ -43,15 +44,10 @@ public class MemorySelectExtractor extends AbstractAgentModule.Sub<ExtractorInpu
|
|||||||
messages,
|
messages,
|
||||||
ExtractorResult.class
|
ExtractorResult.class
|
||||||
);
|
);
|
||||||
extractorResult = result.fold(
|
extractorResult = result.onFailure(ExceptionReporterHandler.INSTANCE::report).fold(
|
||||||
value -> {
|
value -> value,
|
||||||
log.debug("[MemorySelectExtractor] 主题提取结果: {}", value);
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
exception -> {
|
exception -> {
|
||||||
log.error("[MemorySelectExtractor] 主题提取出错: ", exception);
|
|
||||||
ExtractorResult fallback = new ExtractorResult();
|
ExtractorResult fallback = new ExtractorResult();
|
||||||
fallback.setRecall(false);
|
|
||||||
fallback.setMatches(List.of());
|
fallback.setMatches(List.of());
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ import java.util.List;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ExtractorResult {
|
public class ExtractorResult {
|
||||||
private boolean recall;
|
|
||||||
private List<ExtractorMatchData> matches;
|
private List<ExtractorMatchData> matches;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package work.slhaf.partner.module.memory.updater;
|
package work.slhaf.partner.module.memory.updater;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -25,7 +24,6 @@ import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
|||||||
import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer;
|
import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer;
|
import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput;
|
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult;
|
|
||||||
import work.slhaf.partner.runtime.PartnerRunningFlowContext;
|
import work.slhaf.partner.runtime.PartnerRunningFlowContext;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -126,9 +124,7 @@ public class MemoryUpdater extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
RollingRecord record = updateMemory(chatIncrement);
|
RollingRecord record = updateMemory(chatIncrement);
|
||||||
if (record != null) {
|
|
||||||
dialogRollingService.rollMessages(chatIncrement, fullChatSnapshot.size(), CONTEXT_RETAIN_DIVISOR, record.unitId, record.sliceId, record.summary);
|
dialogRollingService.rollMessages(chatIncrement, fullChatSnapshot.size(), CONTEXT_RETAIN_DIVISOR, record.unitId, record.sliceId, record.summary);
|
||||||
}
|
|
||||||
|
|
||||||
if (refreshMemoryId) {
|
if (refreshMemoryId) {
|
||||||
memoryCapability.refreshMemorySession();
|
memoryCapability.refreshMemorySession();
|
||||||
@@ -167,23 +163,23 @@ public class MemoryUpdater extends AbstractAgentModule.Running<PartnerRunningFlo
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
SummarizeInput summarizeInput = new SummarizeInput(chatSnapshot, memoryRuntime.getTopicTree());
|
SummarizeInput summarizeInput = new SummarizeInput(chatSnapshot, memoryRuntime.getTopicTree());
|
||||||
log.debug("[MemoryUpdater] 记忆更新-总结流程-输入: {}", JSONObject.toJSONString(summarizeInput));
|
singleSummarizer.execute(summarizeInput.getChatMessages());
|
||||||
SummarizeResult summarizeResult = summarize(summarizeInput);
|
return multiSummarizer.execute(summarizeInput).fold(
|
||||||
log.debug("[MemoryUpdater] 记忆更新-总结流程-输出: {}", JSONObject.toJSONString(summarizeResult));
|
summarizeResult -> {
|
||||||
MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, summarizeResult.getSummary());
|
MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, summarizeResult.getSummary());
|
||||||
memoryRuntime.recordMemory(
|
memoryRuntime.recordMemory(
|
||||||
memoryUnit,
|
memoryUnit,
|
||||||
summarizeResult.getTopicPath(),
|
summarizeResult.getTopicPath(),
|
||||||
summarizeResult.getRelatedTopicPath()
|
summarizeResult.getRelatedTopicPath()
|
||||||
);
|
);
|
||||||
log.debug("[MemoryUpdater] 记忆更新流程结束...");
|
|
||||||
MemorySlice newSlice = memoryUnit.getSlices().getLast();
|
MemorySlice newSlice = memoryUnit.getSlices().getLast();
|
||||||
return new RollingRecord(memoryUnit.getId(), newSlice.getId(), newSlice.getSummary());
|
return new RollingRecord(memoryUnit.getId(), newSlice.getId(), newSlice.getSummary());
|
||||||
}
|
},
|
||||||
|
exp -> {
|
||||||
private SummarizeResult summarize(SummarizeInput summarizeInput) {
|
MemoryUnit memoryUnit = memoryCapability.updateMemoryUnit(chatSnapshot, "no summary, due to exception");
|
||||||
singleSummarizer.execute(summarizeInput.getChatMessages());
|
MemorySlice newSlice = memoryUnit.getSlices().getLast();
|
||||||
return multiSummarizer.execute(summarizeInput);
|
return new RollingRecord(memoryUnit.getId(), newSlice.getId(), newSlice.getSummary());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package work.slhaf.partner.module.memory.updater.summarizer;
|
package work.slhaf.partner.module.memory.updater.summarizer;
|
||||||
|
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.InjectModule;
|
||||||
import work.slhaf.partner.framework.agent.model.ActivateModel;
|
import work.slhaf.partner.framework.agent.model.ActivateModel;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput;
|
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeInput;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult;
|
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult;
|
||||||
@@ -17,25 +18,22 @@ import java.util.List;
|
|||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class MultiSummarizer extends AbstractAgentModule.Sub<SummarizeInput, SummarizeResult> implements ActivateModel {
|
public class MultiSummarizer extends AbstractAgentModule.Sub<SummarizeInput, Result<SummarizeResult>> implements ActivateModel {
|
||||||
|
|
||||||
@InjectModule
|
@InjectModule
|
||||||
private MemoryRuntime memoryRuntime;
|
private MemoryRuntime memoryRuntime;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SummarizeResult execute(SummarizeInput input) {
|
public @NotNull Result<SummarizeResult> execute(SummarizeInput input) {
|
||||||
log.debug("[MemorySummarizer] 整体摘要开始...");
|
return formattedChat(
|
||||||
SummarizeResult result = formattedChat(
|
|
||||||
List.of(new Message(Message.Character.USER, JSONUtil.toJsonPrettyStr(input))),
|
List.of(new Message(Message.Character.USER, JSONUtil.toJsonPrettyStr(input))),
|
||||||
SummarizeResult.class
|
SummarizeResult.class
|
||||||
).getOrThrow();
|
).onSuccess(this::fix);
|
||||||
log.debug("[MemorySummarizer] 整体摘要结果: {}", JSONObject.toJSONString(result));
|
|
||||||
return fix(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SummarizeResult fix(SummarizeResult result) {
|
private void fix(SummarizeResult result) {
|
||||||
if (result == null || result.getTopicPath() == null || result.getTopicPath().isEmpty()) {
|
if (result == null || result.getTopicPath() == null || result.getTopicPath().isEmpty()) {
|
||||||
return result;
|
return;
|
||||||
}
|
}
|
||||||
String topicPath = memoryRuntime.fixTopicPath(result.getTopicPath());
|
String topicPath = memoryRuntime.fixTopicPath(result.getTopicPath());
|
||||||
List<String> relatedTopicPath = new ArrayList<>();
|
List<String> relatedTopicPath = new ArrayList<>();
|
||||||
@@ -44,9 +42,9 @@ public class MultiSummarizer extends AbstractAgentModule.Sub<SummarizeInput, Sum
|
|||||||
}
|
}
|
||||||
result.setTopicPath(topicPath);
|
result.setTopicPath(topicPath);
|
||||||
result.setRelatedTopicPath(relatedTopicPath);
|
result.setRelatedTopicPath(relatedTopicPath);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String modelKey() {
|
public String modelKey() {
|
||||||
return "multi_summarizer";
|
return "multi_summarizer";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package work.slhaf.partner.module.memory.updater.summarizer;
|
package work.slhaf.partner.module.memory.updater.summarizer;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import work.slhaf.partner.core.action.ActionCapability;
|
import work.slhaf.partner.core.action.ActionCapability;
|
||||||
import work.slhaf.partner.core.action.ActionCore;
|
import work.slhaf.partner.core.action.ActionCore;
|
||||||
|
import work.slhaf.partner.framework.agent.exception.ExceptionReporterHandler;
|
||||||
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
import work.slhaf.partner.framework.agent.factory.capability.annotation.InjectCapability;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
import work.slhaf.partner.framework.agent.factory.component.abstracts.AbstractAgentModule;
|
||||||
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
|
import work.slhaf.partner.framework.agent.factory.component.annotation.Init;
|
||||||
@@ -41,7 +41,10 @@ public class SingleSummarizer extends AbstractAgentModule.Sub<List<Message>, Voi
|
|||||||
int index = i;
|
int index = i;
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
String summarized = singleExecute(JSONObject.of("content", content).toString());
|
String summarized = chat(List.of(new Message(Message.Character.USER, content))).onFailure(ExceptionReporterHandler.INSTANCE::report).fold(
|
||||||
|
res -> res,
|
||||||
|
exp -> content
|
||||||
|
);
|
||||||
chatMessages.set(index, new Message(Message.Character.ASSISTANT, summarized));
|
chatMessages.set(index, new Message(Message.Character.ASSISTANT, summarized));
|
||||||
} finally {
|
} finally {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
@@ -59,12 +62,7 @@ public class SingleSummarizer extends AbstractAgentModule.Sub<List<Message>, Voi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String singleExecute(String primaryContent) {
|
private String singleExecute(String primaryContent) {
|
||||||
try {
|
|
||||||
return chat(List.of(new Message(Message.Character.USER, primaryContent))).getOrThrow();
|
return chat(List.of(new Message(Message.Character.USER, primaryContent))).getOrThrow();
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("[SingleSummarizer] 单消息总结出错: ", e);
|
|
||||||
return primaryContent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class MemoryCoreTest {
|
|||||||
assertEquals("second-summary", appendedSlice.getSummary());
|
assertEquals("second-summary", appendedSlice.getSummary());
|
||||||
assertTrue(appendedSlice.getTimestamp() > 0);
|
assertTrue(appendedSlice.getTimestamp() > 0);
|
||||||
|
|
||||||
MemorySlice loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId());
|
MemorySlice loadedSlice = memoryCore.getMemorySlice(sessionId, appendedSlice.getId()).getOrThrow();
|
||||||
assertNotNull(loadedSlice);
|
assertNotNull(loadedSlice);
|
||||||
assertEquals(1, loadedSlice.getStartIndex());
|
assertEquals(1, loadedSlice.getStartIndex());
|
||||||
assertEquals(3, loadedSlice.getEndIndex());
|
assertEquals(3, loadedSlice.getEndIndex());
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
|||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
import work.slhaf.partner.core.memory.pojo.SliceRef;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
|
import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
||||||
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
import work.slhaf.partner.module.memory.selector.ActivatedMemorySlice;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -26,8 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class MemoryRuntimeTest {
|
class MemoryRuntimeTest {
|
||||||
|
|
||||||
@@ -204,6 +205,27 @@ class MemoryRuntimeTest {
|
|||||||
assertEquals("second", dateResult.getFirst().getSummary());
|
assertEquals("second", dateResult.getFirst().getSummary());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowMemoryLookupExceptionWhenTopicOrDateIndexDoesNotExist() throws Exception {
|
||||||
|
StubMemoryCapability memoryCapability = new StubMemoryCapability("session-test");
|
||||||
|
MemoryRuntime runtime = new MemoryRuntime();
|
||||||
|
setField(runtime, "memoryCapability", memoryCapability);
|
||||||
|
setField(runtime, "cognitionCapability", stubCognitionCapability(List.of(message("seed"))));
|
||||||
|
|
||||||
|
MemoryLookupException topicException = assertThrows(
|
||||||
|
MemoryLookupException.class,
|
||||||
|
() -> runtime.queryActivatedMemoryByTopicPath("topic/missing")
|
||||||
|
);
|
||||||
|
assertEquals("不存在的主题: topic/missing", topicException.getMessage());
|
||||||
|
|
||||||
|
MemoryLookupException dateException = assertThrows(
|
||||||
|
MemoryLookupException.class,
|
||||||
|
() -> runtime.queryActivatedMemoryByDate(LocalDate.parse("1970-01-01"))
|
||||||
|
);
|
||||||
|
assertEquals("不存在的日期索引: 1970-01-01", dateException.getMessage());
|
||||||
|
assertInstanceOf(MemoryLookupException.class, dateException);
|
||||||
|
}
|
||||||
|
|
||||||
private static final class StubMemoryCapability implements MemoryCapability {
|
private static final class StubMemoryCapability implements MemoryCapability {
|
||||||
private final String sessionId;
|
private final String sessionId;
|
||||||
private final Map<String, MemoryUnit> units = new HashMap<>();
|
private final Map<String, MemoryUnit> units = new HashMap<>();
|
||||||
@@ -222,15 +244,24 @@ class MemoryRuntimeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemorySlice getMemorySlice(String unitId, String sliceId) {
|
public Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
||||||
MemoryUnit unit = units.get(unitId);
|
MemoryUnit unit = units.get(unitId);
|
||||||
if (unit == null || unit.getSlices() == null) {
|
if (unit == null || unit.getSlices() == null) {
|
||||||
return null;
|
return Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
return unit.getSlices().stream()
|
return unit.getSlices().stream()
|
||||||
.filter(slice -> sliceId.equals(slice.getId()))
|
.filter(slice -> sliceId.equals(slice.getId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.map(Result::success)
|
||||||
|
.orElseGet(() -> Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import work.slhaf.partner.core.memory.MemoryCapability;
|
|||||||
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
import work.slhaf.partner.core.memory.pojo.MemorySlice;
|
||||||
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
import work.slhaf.partner.core.memory.pojo.MemoryUnit;
|
||||||
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
import work.slhaf.partner.framework.agent.model.pojo.Message;
|
||||||
|
import work.slhaf.partner.framework.agent.support.Result;
|
||||||
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
import work.slhaf.partner.module.memory.runtime.MemoryRuntime;
|
||||||
|
import work.slhaf.partner.module.memory.runtime.exception.MemoryLookupException;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer;
|
import work.slhaf.partner.module.memory.updater.summarizer.MultiSummarizer;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer;
|
import work.slhaf.partner.module.memory.updater.summarizer.SingleSummarizer;
|
||||||
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult;
|
import work.slhaf.partner.module.memory.updater.summarizer.entity.SummarizeResult;
|
||||||
@@ -84,9 +86,9 @@ class MemoryUpdaterTest {
|
|||||||
setField(updater, "singleSummarizer", singleSummarizer);
|
setField(updater, "singleSummarizer", singleSummarizer);
|
||||||
|
|
||||||
when(memoryRuntime.getTopicTree()).thenReturn("topic-tree");
|
when(memoryRuntime.getTopicTree()).thenReturn("topic-tree");
|
||||||
when(multiSummarizer.execute(Mockito.any())).thenReturn(
|
when(multiSummarizer.execute(Mockito.any())).thenReturn(Result.success(
|
||||||
summarizeResult("new-summary", "topic/main", List.of("topic/related"))
|
summarizeResult("new-summary", "topic/main", List.of("topic/related"))
|
||||||
);
|
));
|
||||||
|
|
||||||
MemoryUnit existingUnit = new MemoryUnit("session-1");
|
MemoryUnit existingUnit = new MemoryUnit("session-1");
|
||||||
existingUnit.getConversationMessages().addAll(List.of(
|
existingUnit.getConversationMessages().addAll(List.of(
|
||||||
@@ -134,9 +136,9 @@ class MemoryUpdaterTest {
|
|||||||
setField(updater, "singleSummarizer", singleSummarizer);
|
setField(updater, "singleSummarizer", singleSummarizer);
|
||||||
|
|
||||||
when(memoryRuntime.getTopicTree()).thenReturn("topic-tree");
|
when(memoryRuntime.getTopicTree()).thenReturn("topic-tree");
|
||||||
when(multiSummarizer.execute(Mockito.any())).thenReturn(
|
when(multiSummarizer.execute(Mockito.any())).thenReturn(Result.success(
|
||||||
summarizeResult("fresh-summary", "topic/root", List.of())
|
summarizeResult("fresh-summary", "topic/root", List.of())
|
||||||
);
|
));
|
||||||
|
|
||||||
Object rollingRecord = invokeUpdateMemory(updater, List.of(
|
Object rollingRecord = invokeUpdateMemory(updater, List.of(
|
||||||
message(Message.Character.USER, "first"),
|
message(Message.Character.USER, "first"),
|
||||||
@@ -258,15 +260,24 @@ class MemoryUpdaterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemorySlice getMemorySlice(String unitId, String sliceId) {
|
public Result<MemorySlice> getMemorySlice(String unitId, String sliceId) {
|
||||||
MemoryUnit unit = units.get(unitId);
|
MemoryUnit unit = units.get(unitId);
|
||||||
if (unit == null || unit.getSlices() == null) {
|
if (unit == null || unit.getSlices() == null) {
|
||||||
return null;
|
return Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
return unit.getSlices().stream()
|
return unit.getSlices().stream()
|
||||||
.filter(slice -> sliceId.equals(slice.getId()))
|
.filter(slice -> sliceId.equals(slice.getId()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.map(Result::success)
|
||||||
|
.orElseGet(() -> Result.failure(new MemoryLookupException(
|
||||||
|
"Memory slice not found: " + unitId + ":" + sliceId,
|
||||||
|
unitId + ":" + sliceId,
|
||||||
|
"MEMORY_SLICE"
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user