feat(harness): add information gain stop and audit
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
package com.superbiz.agent.harness.progress;
|
||||
|
||||
import com.superbiz.agent.harness.contract.EvidenceStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class DiagnosisProgressTracker {
|
||||
|
||||
private final int stopAfterConsecutiveNoGain;
|
||||
private final Set<ToolScopeIdentity> completedScopes = new LinkedHashSet<>();
|
||||
private final List<CompletedToolCall> completedToolCalls = new ArrayList<>();
|
||||
private int consecutiveNoGain;
|
||||
private DiagnosisCollectionState collectionState = DiagnosisCollectionState.COLLECTING;
|
||||
private DiagnosisStopReason stopReason;
|
||||
private String pendingToolCallId;
|
||||
private boolean stopInstructionDelivered;
|
||||
|
||||
public DiagnosisProgressTracker(int stopAfterConsecutiveNoGain) {
|
||||
if (stopAfterConsecutiveNoGain <= 0) {
|
||||
throw new IllegalArgumentException("stopAfterConsecutiveNoGain must be positive");
|
||||
}
|
||||
this.stopAfterConsecutiveNoGain = stopAfterConsecutiveNoGain;
|
||||
}
|
||||
|
||||
public synchronized void applyPreviousObservation(PreviousObservation observation) {
|
||||
if (pendingToolCallId == null) {
|
||||
if (observation != null) {
|
||||
throw new IllegalArgumentException("No Tool observation is pending evaluation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (observation == null) {
|
||||
throw new IllegalArgumentException("Previous Tool observation must be evaluated");
|
||||
}
|
||||
if (!pendingToolCallId.equals(observation.toolCallId())) {
|
||||
throw new IllegalArgumentException("Previous Tool observation ID is out of order");
|
||||
}
|
||||
pendingToolCallId = null;
|
||||
applyGain(observation.informationGain());
|
||||
}
|
||||
|
||||
public synchronized boolean isDuplicate(String toolName, String normalizedScope) {
|
||||
return completedScopes.contains(new ToolScopeIdentity(toolName, normalizedScope));
|
||||
}
|
||||
|
||||
public synchronized void recordDuplicateScope() {
|
||||
applyGain(InformationGain.NO_GAIN);
|
||||
}
|
||||
|
||||
public synchronized void recordCompleted(CompletedToolCall call, EvidenceStatus evidenceStatus) {
|
||||
if (collectionState == DiagnosisCollectionState.SATURATED) {
|
||||
throw new IllegalStateException("Cannot record Tool completion after saturation");
|
||||
}
|
||||
if (evidenceStatus != EvidenceStatus.EVIDENCE_FOUND
|
||||
&& evidenceStatus != EvidenceStatus.NO_EVIDENCE) {
|
||||
throw new IllegalArgumentException("Completed Tool requires a successful evidence status");
|
||||
}
|
||||
ToolScopeIdentity scope = new ToolScopeIdentity(call.toolName(), call.normalizedScope());
|
||||
if (!completedScopes.add(scope)) {
|
||||
throw new IllegalStateException("Completed Tool scope was already recorded");
|
||||
}
|
||||
completedToolCalls.add(call);
|
||||
if (evidenceStatus == EvidenceStatus.NO_EVIDENCE) {
|
||||
applyGain(InformationGain.NO_GAIN);
|
||||
} else {
|
||||
pendingToolCallId = call.toolCallId();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean claimStopInstruction() {
|
||||
if (collectionState != DiagnosisCollectionState.SATURATED) {
|
||||
return false;
|
||||
}
|
||||
if (stopInstructionDelivered) {
|
||||
return false;
|
||||
}
|
||||
stopInstructionDelivered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void markBudgetLimitReached() {
|
||||
if (stopReason == null) {
|
||||
stopReason = DiagnosisStopReason.BUDGET_LIMIT_REACHED;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized DiagnosisProgressSnapshotState snapshot() {
|
||||
return new DiagnosisProgressSnapshotState(
|
||||
consecutiveNoGain,
|
||||
collectionState,
|
||||
stopReason,
|
||||
pendingToolCallId,
|
||||
stopInstructionDelivered,
|
||||
completedToolCalls);
|
||||
}
|
||||
|
||||
public int stopAfterConsecutiveNoGain() {
|
||||
return stopAfterConsecutiveNoGain;
|
||||
}
|
||||
|
||||
private void applyGain(InformationGain gain) {
|
||||
if (collectionState == DiagnosisCollectionState.SATURATED) {
|
||||
throw new IllegalStateException("Collection is already saturated");
|
||||
}
|
||||
if (gain == InformationGain.GAINED) {
|
||||
consecutiveNoGain = 0;
|
||||
return;
|
||||
}
|
||||
consecutiveNoGain++;
|
||||
if (consecutiveNoGain >= stopAfterConsecutiveNoGain) {
|
||||
collectionState = DiagnosisCollectionState.SATURATED;
|
||||
stopReason = DiagnosisStopReason.INFORMATION_SATURATED;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user