lex-agency 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/lib/legion/extensions/agency/client.rb +17 -0
- data/lib/legion/extensions/agency/helpers/constants.rb +73 -0
- data/lib/legion/extensions/agency/helpers/efficacy_model.rb +132 -0
- data/lib/legion/extensions/agency/helpers/outcome_event.rb +48 -0
- data/lib/legion/extensions/agency/runners/agency.rb +113 -0
- data/lib/legion/extensions/agency/version.rb +9 -0
- data/lib/legion/extensions/agency.rb +16 -0
- metadata +66 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f8d9aa130b27351ac7ac2fe1e8e0e0e388adb4e57faa1ee9daa0da8470030868
|
|
4
|
+
data.tar.gz: 2a1535b7b18bcb4f5b6aff59cb1cc5c90d27a446e34cadfb649a1d55aa70ace5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 293a35bd4e2d348417428ab8432d3ff95ace43d7ace37cdaecf8c82ea21717df37fa0657282ad31b8b9387b8e25998a3bea8fa7c5f980d3d5a724c89d506d3fe
|
|
7
|
+
data.tar.gz: 9de9f8f5c7c7b4ec070c06c7bee78e6700ebe7bdba66d688abcbbbf1b9940439bbe34a1261d5a37d1d23ea4681ede1d3d0eea5f818b4ee6765e12a18631e238c
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Iverson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# lex-agency
|
|
2
|
+
|
|
3
|
+
Self-efficacy and agency modeling for LegionIO — implements Bandura's self-efficacy theory for agentic AI.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Tracks the agent's belief in its own ability to achieve outcomes across different domains. Uses Bandura's four sources of self-efficacy: mastery experiences (direct outcomes), vicarious learning (observing others), verbal persuasion (being told you can/can't), and physiological states. Efficacy scores determine whether the agent should attempt tasks and are used to prioritize domain engagement.
|
|
8
|
+
|
|
9
|
+
## Core Concept: Domain-Level Self-Efficacy
|
|
10
|
+
|
|
11
|
+
Each domain maintains an efficacy score (0.05–0.98) updated via EMA when outcomes are recorded. Failures hit harder than successes (asymmetric update):
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Direct mastery experience has the highest impact
|
|
15
|
+
client.record_mastery(domain: :terraform, outcome_type: :success, magnitude: 1.0)
|
|
16
|
+
|
|
17
|
+
# Learning from others has 0.4x the impact
|
|
18
|
+
client.record_vicarious(domain: :kubernetes, outcome_type: :success)
|
|
19
|
+
|
|
20
|
+
# Gate whether to attempt an action
|
|
21
|
+
result = client.should_attempt?(domain: :terraform, threshold: 0.3)
|
|
22
|
+
# => { should_attempt: true, efficacy: 0.72, label: :capable }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
client = Legion::Extensions::Agency::Client.new
|
|
29
|
+
|
|
30
|
+
# Record outcomes from all four sources
|
|
31
|
+
client.record_mastery(domain: :networking, outcome_type: :failure, attribution: :full_agency)
|
|
32
|
+
client.record_vicarious(domain: :security, outcome_type: :success, magnitude: 0.8)
|
|
33
|
+
client.record_persuasion(domain: :ml, positive: true, magnitude: 0.6)
|
|
34
|
+
client.record_physiological(domain: :reasoning, state: :energized)
|
|
35
|
+
|
|
36
|
+
# Query efficacy
|
|
37
|
+
client.check_efficacy(domain: :networking)
|
|
38
|
+
# => { efficacy: 0.38, label: :doubtful, success_rate: 0.2, history_count: 3 }
|
|
39
|
+
|
|
40
|
+
# Find strongest and weakest domains
|
|
41
|
+
client.strongest_domains(count: 3)
|
|
42
|
+
client.weakest_domains(count: 3)
|
|
43
|
+
|
|
44
|
+
# Maintenance (decay unused domains toward default)
|
|
45
|
+
client.update_agency
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Integration
|
|
49
|
+
|
|
50
|
+
Wire `should_attempt?` into lex-tick's `action_selection` phase to prevent the agent from attempting tasks in domains where it has learned it is ineffective. Call `record_mastery` after task completion to build accurate per-domain confidence over time.
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
bundle install
|
|
56
|
+
bundle exec rspec
|
|
57
|
+
bundle exec rubocop
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agency
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::Agency
|
|
8
|
+
|
|
9
|
+
attr_reader :efficacy_model
|
|
10
|
+
|
|
11
|
+
def initialize(efficacy_model: nil, **)
|
|
12
|
+
@efficacy_model = efficacy_model || Helpers::EfficacyModel.new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agency
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
# Initial self-efficacy for new domains (moderate confidence)
|
|
9
|
+
DEFAULT_EFFICACY = 0.5
|
|
10
|
+
|
|
11
|
+
# EMA alpha for efficacy updates (slow adaptation)
|
|
12
|
+
EFFICACY_ALPHA = 0.12
|
|
13
|
+
|
|
14
|
+
# How much mastery success boosts efficacy
|
|
15
|
+
MASTERY_BOOST = 0.15
|
|
16
|
+
|
|
17
|
+
# How much failure reduces efficacy (asymmetric — failures hit harder)
|
|
18
|
+
FAILURE_PENALTY = 0.20
|
|
19
|
+
|
|
20
|
+
# Vicarious learning multiplier (learning from others' outcomes)
|
|
21
|
+
VICARIOUS_MULTIPLIER = 0.4
|
|
22
|
+
|
|
23
|
+
# Verbal persuasion multiplier (being told you can/can't do something)
|
|
24
|
+
PERSUASION_MULTIPLIER = 0.25
|
|
25
|
+
|
|
26
|
+
# Physiological state influence on efficacy
|
|
27
|
+
PHYSIOLOGICAL_MULTIPLIER = 0.15
|
|
28
|
+
|
|
29
|
+
# Minimum efficacy (never zero — always some belief in possibility)
|
|
30
|
+
EFFICACY_FLOOR = 0.05
|
|
31
|
+
|
|
32
|
+
# Maximum efficacy (never perfectly certain)
|
|
33
|
+
EFFICACY_CEILING = 0.98
|
|
34
|
+
|
|
35
|
+
# Domain decay rate per tick (unused domains slowly regress toward default)
|
|
36
|
+
DECAY_RATE = 0.002
|
|
37
|
+
|
|
38
|
+
# Maximum tracked domains
|
|
39
|
+
MAX_DOMAINS = 100
|
|
40
|
+
|
|
41
|
+
# Maximum outcome history per domain
|
|
42
|
+
MAX_HISTORY_PER_DOMAIN = 50
|
|
43
|
+
|
|
44
|
+
# Maximum total outcome events
|
|
45
|
+
MAX_TOTAL_HISTORY = 500
|
|
46
|
+
|
|
47
|
+
# Sources of efficacy information (Bandura's four sources)
|
|
48
|
+
EFFICACY_SOURCES = %i[mastery vicarious persuasion physiological].freeze
|
|
49
|
+
|
|
50
|
+
# Agency attribution levels
|
|
51
|
+
ATTRIBUTION_LEVELS = {
|
|
52
|
+
full_agency: 0.8,
|
|
53
|
+
partial_agency: 0.5,
|
|
54
|
+
low_agency: 0.3,
|
|
55
|
+
no_agency: 0.0
|
|
56
|
+
}.freeze
|
|
57
|
+
|
|
58
|
+
# Outcome types
|
|
59
|
+
OUTCOME_TYPES = %i[success failure partial_success unexpected].freeze
|
|
60
|
+
|
|
61
|
+
# Efficacy level labels
|
|
62
|
+
EFFICACY_LABELS = {
|
|
63
|
+
(0.8..) => :highly_capable,
|
|
64
|
+
(0.6...0.8) => :capable,
|
|
65
|
+
(0.4...0.6) => :uncertain,
|
|
66
|
+
(0.2...0.4) => :doubtful,
|
|
67
|
+
(..0.2) => :helpless
|
|
68
|
+
}.freeze
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agency
|
|
6
|
+
module Helpers
|
|
7
|
+
class EfficacyModel
|
|
8
|
+
attr_reader :domains, :history
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@domains = {}
|
|
12
|
+
@history = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def efficacy_for(domain)
|
|
16
|
+
@domains[domain] ||= Constants::DEFAULT_EFFICACY
|
|
17
|
+
@domains[domain]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def efficacy_label(domain)
|
|
21
|
+
value = efficacy_for(domain)
|
|
22
|
+
Constants::EFFICACY_LABELS.each do |range, label|
|
|
23
|
+
return label if range.cover?(value)
|
|
24
|
+
end
|
|
25
|
+
:uncertain
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def record_outcome(event)
|
|
29
|
+
@history << event
|
|
30
|
+
update_efficacy(event)
|
|
31
|
+
trim_history
|
|
32
|
+
event
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def decay_all
|
|
36
|
+
@domains.each_key do |domain|
|
|
37
|
+
current = @domains[domain]
|
|
38
|
+
diff = Constants::DEFAULT_EFFICACY - current
|
|
39
|
+
@domains[domain] = (current + (diff * Constants::DECAY_RATE)).clamp(
|
|
40
|
+
Constants::EFFICACY_FLOOR, Constants::EFFICACY_CEILING
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
trim_domains
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def domain_history(domain)
|
|
47
|
+
@history.select { |e| e.domain == domain }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def success_rate(domain)
|
|
51
|
+
events = domain_history(domain)
|
|
52
|
+
return 0.0 if events.empty?
|
|
53
|
+
|
|
54
|
+
successes = events.count(&:success?)
|
|
55
|
+
successes.to_f / events.size
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def strongest_domains(count = 5)
|
|
59
|
+
@domains.sort_by { |_, v| -v }.first(count).to_h
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def weakest_domains(count = 5)
|
|
63
|
+
@domains.sort_by { |_, v| v }.first(count).to_h
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def overall_efficacy
|
|
67
|
+
return Constants::DEFAULT_EFFICACY if @domains.empty?
|
|
68
|
+
|
|
69
|
+
@domains.values.sum / @domains.size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def domain_count
|
|
73
|
+
@domains.size
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_h
|
|
77
|
+
{
|
|
78
|
+
domain_count: @domains.size,
|
|
79
|
+
overall_efficacy: overall_efficacy.round(4),
|
|
80
|
+
history_size: @history.size,
|
|
81
|
+
domains: @domains.transform_values { |v| v.round(4) }
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def update_efficacy(event)
|
|
88
|
+
domain = event.domain
|
|
89
|
+
current = efficacy_for(domain)
|
|
90
|
+
delta = compute_delta(event)
|
|
91
|
+
|
|
92
|
+
new_value = current + (Constants::EFFICACY_ALPHA * delta)
|
|
93
|
+
@domains[domain] = new_value.clamp(Constants::EFFICACY_FLOOR, Constants::EFFICACY_CEILING)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def compute_delta(event)
|
|
97
|
+
base = event.attributed_magnitude
|
|
98
|
+
multiplier = source_multiplier(event.source)
|
|
99
|
+
|
|
100
|
+
if event.success?
|
|
101
|
+
base * multiplier * Constants::MASTERY_BOOST / Constants::EFFICACY_ALPHA
|
|
102
|
+
else
|
|
103
|
+
-base * multiplier * Constants::FAILURE_PENALTY / Constants::EFFICACY_ALPHA
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def source_multiplier(source)
|
|
108
|
+
case source
|
|
109
|
+
when :mastery then 1.0
|
|
110
|
+
when :vicarious then Constants::VICARIOUS_MULTIPLIER
|
|
111
|
+
when :persuasion then Constants::PERSUASION_MULTIPLIER
|
|
112
|
+
when :physiological then Constants::PHYSIOLOGICAL_MULTIPLIER
|
|
113
|
+
else 0.5
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def trim_history
|
|
118
|
+
@history.shift(@history.size - Constants::MAX_TOTAL_HISTORY) if @history.size > Constants::MAX_TOTAL_HISTORY
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def trim_domains
|
|
122
|
+
return unless @domains.size > Constants::MAX_DOMAINS
|
|
123
|
+
|
|
124
|
+
sorted = @domains.sort_by { |_, v| (v - Constants::DEFAULT_EFFICACY).abs }
|
|
125
|
+
excess = @domains.size - Constants::MAX_DOMAINS
|
|
126
|
+
sorted.first(excess).each { |domain, _| @domains.delete(domain) } # rubocop:disable Style/HashEachMethods
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Agency
|
|
8
|
+
module Helpers
|
|
9
|
+
class OutcomeEvent
|
|
10
|
+
attr_reader :id, :domain, :outcome_type, :source, :magnitude, :attribution, :timestamp
|
|
11
|
+
|
|
12
|
+
def initialize(domain:, outcome_type:, source: :mastery, magnitude: 1.0, attribution: :full_agency)
|
|
13
|
+
@id = SecureRandom.uuid
|
|
14
|
+
@domain = domain
|
|
15
|
+
@outcome_type = outcome_type
|
|
16
|
+
@source = source
|
|
17
|
+
@magnitude = magnitude.clamp(0.0, 1.0)
|
|
18
|
+
@attribution = attribution
|
|
19
|
+
@timestamp = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def success?
|
|
23
|
+
%i[success partial_success].include?(@outcome_type)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def attributed_magnitude
|
|
27
|
+
level = Constants::ATTRIBUTION_LEVELS[@attribution] || 0.5
|
|
28
|
+
@magnitude * level
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_h
|
|
32
|
+
{
|
|
33
|
+
id: @id,
|
|
34
|
+
domain: @domain,
|
|
35
|
+
outcome_type: @outcome_type,
|
|
36
|
+
source: @source,
|
|
37
|
+
magnitude: @magnitude,
|
|
38
|
+
attribution: @attribution,
|
|
39
|
+
success: success?,
|
|
40
|
+
attributed_magnitude: attributed_magnitude.round(4),
|
|
41
|
+
timestamp: @timestamp
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Agency
|
|
6
|
+
module Runners
|
|
7
|
+
module Agency
|
|
8
|
+
include Legion::Extensions::Helpers::Lex
|
|
9
|
+
|
|
10
|
+
def efficacy_model
|
|
11
|
+
@efficacy_model ||= Helpers::EfficacyModel.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def record_mastery(domain:, outcome_type:, magnitude: 1.0, attribution: :full_agency, **)
|
|
15
|
+
event = Helpers::OutcomeEvent.new(
|
|
16
|
+
domain: domain, outcome_type: outcome_type, source: :mastery,
|
|
17
|
+
magnitude: magnitude, attribution: attribution
|
|
18
|
+
)
|
|
19
|
+
efficacy_model.record_outcome(event)
|
|
20
|
+
Legion::Logging.debug "[agency] mastery #{outcome_type} in #{domain} " \
|
|
21
|
+
"efficacy=#{efficacy_model.efficacy_for(domain).round(4)}"
|
|
22
|
+
{ success: true, event: event.to_h, efficacy: efficacy_model.efficacy_for(domain).round(4) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def record_vicarious(domain:, outcome_type:, magnitude: 1.0, **)
|
|
26
|
+
event = Helpers::OutcomeEvent.new(
|
|
27
|
+
domain: domain, outcome_type: outcome_type, source: :vicarious,
|
|
28
|
+
magnitude: magnitude, attribution: :partial_agency
|
|
29
|
+
)
|
|
30
|
+
efficacy_model.record_outcome(event)
|
|
31
|
+
Legion::Logging.debug "[agency] vicarious #{outcome_type} in #{domain}"
|
|
32
|
+
{ success: true, event: event.to_h, efficacy: efficacy_model.efficacy_for(domain).round(4) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def record_persuasion(domain:, positive: true, magnitude: 0.5, **)
|
|
36
|
+
outcome = positive ? :success : :failure
|
|
37
|
+
event = Helpers::OutcomeEvent.new(
|
|
38
|
+
domain: domain, outcome_type: outcome, source: :persuasion,
|
|
39
|
+
magnitude: magnitude, attribution: :partial_agency
|
|
40
|
+
)
|
|
41
|
+
efficacy_model.record_outcome(event)
|
|
42
|
+
Legion::Logging.debug "[agency] persuasion #{positive ? 'positive' : 'negative'} in #{domain}"
|
|
43
|
+
{ success: true, event: event.to_h, efficacy: efficacy_model.efficacy_for(domain).round(4) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def record_physiological(domain:, state: :energized, **)
|
|
47
|
+
outcome = %i[energized calm focused].include?(state) ? :success : :failure
|
|
48
|
+
magnitude = %i[energized calm focused].include?(state) ? 0.6 : 0.4
|
|
49
|
+
event = Helpers::OutcomeEvent.new(
|
|
50
|
+
domain: domain, outcome_type: outcome, source: :physiological,
|
|
51
|
+
magnitude: magnitude, attribution: :low_agency
|
|
52
|
+
)
|
|
53
|
+
efficacy_model.record_outcome(event)
|
|
54
|
+
Legion::Logging.debug "[agency] physiological #{state} in #{domain}"
|
|
55
|
+
{ success: true, event: event.to_h, efficacy: efficacy_model.efficacy_for(domain).round(4) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_agency(**)
|
|
59
|
+
efficacy_model.decay_all
|
|
60
|
+
Legion::Logging.debug "[agency] tick: domains=#{efficacy_model.domain_count} " \
|
|
61
|
+
"overall=#{efficacy_model.overall_efficacy.round(4)}"
|
|
62
|
+
{ success: true, stats: efficacy_model.to_h }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def check_efficacy(domain:, **)
|
|
66
|
+
{
|
|
67
|
+
success: true,
|
|
68
|
+
domain: domain,
|
|
69
|
+
efficacy: efficacy_model.efficacy_for(domain).round(4),
|
|
70
|
+
label: efficacy_model.efficacy_label(domain),
|
|
71
|
+
success_rate: efficacy_model.success_rate(domain).round(4),
|
|
72
|
+
history_count: efficacy_model.domain_history(domain).size
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def should_attempt?(domain:, threshold: 0.3, **)
|
|
77
|
+
efficacy = efficacy_model.efficacy_for(domain)
|
|
78
|
+
{
|
|
79
|
+
success: true,
|
|
80
|
+
domain: domain,
|
|
81
|
+
efficacy: efficacy.round(4),
|
|
82
|
+
threshold: threshold,
|
|
83
|
+
should_attempt: efficacy >= threshold,
|
|
84
|
+
label: efficacy_model.efficacy_label(domain)
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def strongest_domains(count: 5, **)
|
|
89
|
+
domains = efficacy_model.strongest_domains(count)
|
|
90
|
+
{
|
|
91
|
+
success: true,
|
|
92
|
+
domains: domains.transform_values { |v| v.round(4) },
|
|
93
|
+
count: domains.size
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def weakest_domains(count: 5, **)
|
|
98
|
+
domains = efficacy_model.weakest_domains(count)
|
|
99
|
+
{
|
|
100
|
+
success: true,
|
|
101
|
+
domains: domains.transform_values { |v| v.round(4) },
|
|
102
|
+
count: domains.size
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def agency_stats(**)
|
|
107
|
+
{ success: true, stats: efficacy_model.to_h }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'agency/version'
|
|
4
|
+
require_relative 'agency/helpers/constants'
|
|
5
|
+
require_relative 'agency/helpers/outcome_event'
|
|
6
|
+
require_relative 'agency/helpers/efficacy_model'
|
|
7
|
+
require_relative 'agency/runners/agency'
|
|
8
|
+
require_relative 'agency/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module Agency
|
|
13
|
+
extend Legion::Extensions::Core if defined?(Legion::Extensions::Core)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-agency
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matthew Iverson
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Implements Bandura's self-efficacy theory for LegionIO agents — tracks
|
|
27
|
+
belief in ability to achieve outcomes across domains, mastery experiences, vicarious
|
|
28
|
+
learning, and agency attribution.
|
|
29
|
+
email:
|
|
30
|
+
- matt@iverson.io
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- LICENSE
|
|
36
|
+
- README.md
|
|
37
|
+
- lib/legion/extensions/agency.rb
|
|
38
|
+
- lib/legion/extensions/agency/client.rb
|
|
39
|
+
- lib/legion/extensions/agency/helpers/constants.rb
|
|
40
|
+
- lib/legion/extensions/agency/helpers/efficacy_model.rb
|
|
41
|
+
- lib/legion/extensions/agency/helpers/outcome_event.rb
|
|
42
|
+
- lib/legion/extensions/agency/runners/agency.rb
|
|
43
|
+
- lib/legion/extensions/agency/version.rb
|
|
44
|
+
homepage: https://github.com/LegionIO/lex-agency
|
|
45
|
+
licenses:
|
|
46
|
+
- MIT
|
|
47
|
+
metadata:
|
|
48
|
+
rubygems_mfa_required: 'true'
|
|
49
|
+
rdoc_options: []
|
|
50
|
+
require_paths:
|
|
51
|
+
- lib
|
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '3.4'
|
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
requirements: []
|
|
63
|
+
rubygems_version: 3.6.9
|
|
64
|
+
specification_version: 4
|
|
65
|
+
summary: Self-efficacy and agency modeling for LegionIO
|
|
66
|
+
test_files: []
|