fix(planning): consult the plan model only when no deterministic strategy applies
Propose built every strategy to record the alternatives, and the model strategy's build is a live call to the plan role, so planning a direct action spent a model call on an alternative that could never be selected (B2, docs/planning.md). The model is now recorded as "not consulted" once a deterministic draft is chosen, and the approver's reason no longer lists it among the alternatives considered. A test counts the calls both ways.
This commit is contained in:
@@ -372,7 +372,7 @@ func (e *Executor) explain(p Plan, it volition.Intention, st Step, c capabilitie
|
|||||||
}
|
}
|
||||||
var alts []string
|
var alts []string
|
||||||
for _, a := range p.Alternatives {
|
for _, a := range p.Alternatives {
|
||||||
if !a.Selected && a.Why != "already failed" {
|
if !a.Selected && a.Why != "already failed" && !strings.HasPrefix(a.Why, "not consulted") {
|
||||||
alts = append(alts, a.Strategy)
|
alts = append(alts, a.Strategy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -510,6 +510,40 @@ func TestInvalidModelPlanNeverRuns(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A model is consulted only when no deterministic strategy applies (B2):
|
||||||
|
// planning a direct capability action must not spend a live model call on
|
||||||
|
// an alternative that cannot be selected.
|
||||||
|
func TestModelIsNotConsultedWhenADeterministicStrategyApplies(t *testing.T) {
|
||||||
|
f := newFixture(t)
|
||||||
|
calls := 0
|
||||||
|
f.exec.Planner.Model = func(context.Context, ids.ID, string, []capabilities.Capability) (string, ids.ID, error) {
|
||||||
|
calls++
|
||||||
|
return `{"steps":[]}`, ids.Nil, nil
|
||||||
|
}
|
||||||
|
it, _ := f.vol.CreateIntention(f.ctx, f.vol.Direct(f.es, "test"), volition.Intention{MindID: f.mind.ID, Description: "check the status page", Kind: volition.IntentAct,
|
||||||
|
Proposal: &volition.Proposal{Kind: "capability", Capability: "service.status", Params: map[string]any{"service": "atlas-worker"}}})
|
||||||
|
p, err := f.exec.Start(f.ctx, f.exec.rec(), it)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if calls != 0 {
|
||||||
|
t.Fatalf("the model was called %d time(s) although the direct strategy applied", calls)
|
||||||
|
}
|
||||||
|
recorded := false
|
||||||
|
for _, a := range p.Alternatives {
|
||||||
|
recorded = recorded || (a.Strategy == "model" && !a.Selected && strings.HasPrefix(a.Why, "not consulted"))
|
||||||
|
}
|
||||||
|
if p.Strategy != "direct" || !recorded {
|
||||||
|
t.Fatalf("expected the direct strategy with the model recorded as not consulted: %+v", p.Alternatives)
|
||||||
|
}
|
||||||
|
// With no deterministic strategy the model is consulted.
|
||||||
|
pursue, _ := f.vol.CreateIntention(f.ctx, f.vol.Direct(f.es, "test"), volition.Intention{MindID: f.mind.ID, Description: "make the dashboard faster", Kind: volition.IntentPursue})
|
||||||
|
f.exec.Start(f.ctx, f.exec.rec(), pursue) //nolint:errcheck
|
||||||
|
if calls != 1 {
|
||||||
|
t.Fatalf("the model should be consulted once when nothing else applies, got %d", calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Regression: an executor holding a stale "pending" copy of a step must not
|
// Regression: an executor holding a stale "pending" copy of a step must not
|
||||||
// move it back to "ready" after another executor has claimed it, or the
|
// move it back to "ready" after another executor has claimed it, or the
|
||||||
// step can be claimed and run a second time.
|
// step can be claimed and run a second time.
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ func (p *Planner) Propose(ctx context.Context, it volition.Intention, previous [
|
|||||||
alts = append(alts, Alternative{Strategy: s.name, Description: "tried in an earlier revision", Why: "already failed"})
|
alts = append(alts, Alternative{Strategy: s.name, Description: "tried in an earlier revision", Why: "already failed"})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if s.name == "model" && chosen != nil {
|
||||||
|
// A model is consulted only when no deterministic strategy
|
||||||
|
// applies; building it here would spend a live call on an
|
||||||
|
// alternative that cannot be selected.
|
||||||
|
alts = append(alts, Alternative{Strategy: s.name, Description: "model decomposition", ExpectedConfidence: s.confidence, Why: "not consulted: a deterministic strategy applies"})
|
||||||
|
continue
|
||||||
|
}
|
||||||
d, err := s.build()
|
d, err := s.build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
alts = append(alts, Alternative{Strategy: s.name, Description: err.Error(), Why: "not buildable"})
|
alts = append(alts, Alternative{Strategy: s.name, Description: err.Error(), Why: "not buildable"})
|
||||||
|
|||||||
Reference in New Issue
Block a user