返回 DeepSeek-Reasonix
turn_loop_state.go
根目录 / internal / agent / turn_loop_state.go
1 package agent
2
3 import (
4 "sort"
5 "sync"
6
7 "reasonix/internal/tool"
8 )
9
10 // turnLoopState groups per-turn loop-guard maps so parallel tool goroutines
11 // share one lock instead of unsynchronized maps on turnRuntime.
12 type turnLoopState struct {
13 mu sync.Mutex
14 dispatchClasses map[string]tool.CallClass
15 acceptedDecisions map[string]acceptedDecision
16 previousErrorCategories map[string]struct{}
17 }
18
19 func (s *turnLoopState) setDispatchClasses(classes map[string]tool.CallClass) {
20 s.mu.Lock()
21 defer s.mu.Unlock()
22 s.dispatchClasses = classes
23 }
24
25 func (s *turnLoopState) dispatchClass(id string) (tool.CallClass, bool) {
26 s.mu.Lock()
27 defer s.mu.Unlock()
28 class, ok := s.dispatchClasses[id]
29 return class, ok
30 }
31
32 func (s *turnLoopState) rememberDecision(id, question, answer string) {
33 s.rememberDecisionAmbiguity(id, question, answer, decisionAmbiguity{})
34 }
35
36 func (s *turnLoopState) rememberDecisionAmbiguity(id, question, answer string, ambiguity decisionAmbiguity) {
37 s.mu.Lock()
38 defer s.mu.Unlock()
39 if s.acceptedDecisions == nil {
40 s.acceptedDecisions = map[string]acceptedDecision{}
41 }
42 s.acceptedDecisions[id] = acceptedDecision{ID: id, Question: question, Answer: answer, Ambiguity: ambiguity}
43 }
44
45 func (s *turnLoopState) decision(id string) (acceptedDecision, bool) {
46 s.mu.Lock()
47 defer s.mu.Unlock()
48 dec, ok := s.acceptedDecisions[id]
49 return dec, ok
50 }
51
52 func (s *turnLoopState) snapshotDecisions() []acceptedDecision {
53 s.mu.Lock()
54 defer s.mu.Unlock()
55 out := make([]acceptedDecision, 0, len(s.acceptedDecisions))
56 for _, decision := range s.acceptedDecisions {
57 out = append(out, decision)
58 }
59 sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
60 return out
61 }
62
63 func (s *turnLoopState) advanceErrorCategories(current map[string]int) bool {
64 s.mu.Lock()
65 defer s.mu.Unlock()
66 hit := false
67 next := make(map[string]struct{}, len(current))
68 for category, count := range current {
69 if count >= 2 {
70 hit = true
71 }
72 if _, repeated := s.previousErrorCategories[category]; repeated {
73 hit = true
74 }
75 next[category] = struct{}{}
76 }
77 s.previousErrorCategories = next
78 return hit
79 }
80
80 lines GO