Module: PWN::AI::Agent::Registry
- Defined in:
- lib/pwn/ai/agent/registry.rb
Overview
Central registry for pwn-ai agent tools.
Each file under lib/pwn/ai/agent/tools/*.rb calls
PWN::AI::Agent::Registry.register(...) at load time to declare a
JSON-Schema (what the LLM sees) and a handler lambda (what pwn runs).
Registry.definitions(...) returns the OpenAI-format tools: array;
Registry.lookup(name:) returns the entry for dispatch.
Import chain (circular-import safe):
agent/registry.rb (no deps on tool files)
^
agent/tools/*.rb (require registry, call .register at top level)
^
agent/loop.rb (calls Registry.discover then .definitions)
DYNAMIC TOOL-SET SLIMMING (local-model scaffolding)
Shipping all ~47 tool schemas on every call overwhelms a 35B local model — it mis-routes (extro_rf_tune for a git question) because the choice space is huge. When PWN::Env[:agent][:tool_router] is truthy AND definitions(relevance: request) is passed, the pool is reduced to CORE_TOOLS + the top-K keyword-ranked matches. Routing accuracy is fed back into Metrics under name:'tool_router' so the router itself becomes a learned component.
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- CORE_TOOLS =
%w[shell pwn_eval memory_remember memory_recall mistakes_record mistakes_resolve learning_note_outcome].freeze
Class Method Summary collapse
-
.all ⇒ Object
- Supported Method Parameters
entries = PWN::AI::Agent::Registry.all.
-
.authors ⇒ Object
- Author(s)
0day Inc.
-
.definitions(opts = {}) ⇒ Object
- Supported Method Parameters
tools = PWN::AI::Agent::Registry.definitions( enabled: 'optional - Array of toolset names to include; nil = all whose check passes', relevance: 'optional - user request; when set AND :tool_router is enabled, slim to CORE + top-K keyword matches', top_k: 'optional - keyword-ranked tools to keep beyond CORE (default 10)' ).
-
.discover(opts = {}) ⇒ Object
- Supported Method Parameters
names = PWN::AI::Agent::Registry.discover( force: 'optional - re-require tool files even if already discovered (default false)' ).
-
.help ⇒ Object
Display Usage for this Module.
-
.lookup(opts = {}) ⇒ Object
- Supported Method Parameters
entry = PWN::AI::Agent::Registry.lookup( name: 'required - registered tool name' ).
-
.rank(opts = {}) ⇒ Object
- Supported Method Parameters
ranked = PWN::AI::Agent::Registry.rank( query: 'required - user request text', entries: 'optional - Entry pool to rank (default .all)' ).
-
.register(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::Registry.register( name: 'required - tool name exposed to the model', toolset: 'required - grouping for enable/disable (terminal, file, pwn, memory…)', schema: 'required - OpenAI function schema description:, parameters:', handler: 'required - ->(args_hash) { ... } returning a JSON-serialisable object', check: 'optional - -> { bool } gate; tool only advertised when truthy', max_chars: 'optional - cap on serialised result (default 24_000)' ).
-
.toolsets ⇒ Object
- Supported Method Parameters
names = PWN::AI::Agent::Registry.toolsets.
Class Method Details
.all ⇒ Object
- Supported Method Parameters
entries = PWN::AI::Agent::Registry.all
87 88 89 |
# File 'lib/pwn/ai/agent/registry.rb', line 87 public_class_method def self.all @entries.values end |
.authors ⇒ Object
- Author(s)
0day Inc. support@0dayinc.com
201 202 203 |
# File 'lib/pwn/ai/agent/registry.rb', line 201 public_class_method def self. "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n" end |
.definitions(opts = {}) ⇒ Object
- Supported Method Parameters
tools = PWN::AI::Agent::Registry.definitions( enabled: 'optional - Array of toolset names to include; nil = all whose check passes', relevance: 'optional - user request; when set AND :tool_router is enabled, slim to CORE + top-K keyword matches', top_k: 'optional - keyword-ranked tools to keep beyond CORE (default 10)' )
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/pwn/ai/agent/registry.rb', line 105 public_class_method def self.definitions(opts = {}) enabled = opts[:enabled] enabled = enabled.map(&:to_s) if enabled pool = @entries.values.select { |e| (enabled.nil? || enabled.include?(e.toolset)) && safe_check(entry: e) } if opts[:relevance] && router_enabled? keep = rank(query: opts[:relevance], entries: pool).first(opts[:top_k] || 10).map(&:name) names = (CORE_TOOLS + keep).uniq pool = pool.select { |e| names.include?(e.name) } end pool.map { |e| { type: 'function', function: e.schema } } end |
.discover(opts = {}) ⇒ Object
- Supported Method Parameters
names = PWN::AI::Agent::Registry.discover( force: 'optional - re-require tool files even if already discovered (default false)' )
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/pwn/ai/agent/registry.rb', line 162 public_class_method def self.discover(opts = {}) force = opts[:force] ||= false return @entries.keys if @discovered && !force tools_dir = File.join(__dir__, 'tools') if Dir.exist?(tools_dir) Dir[File.join(tools_dir, '*.rb')].each do |f| require f rescue StandardError, LoadError => e warn "[pwn-ai] failed to load tool #{File.basename(f)}: #{e.class}: #{e.}" end end @discovered = true @entries.keys end |
.help ⇒ Object
Display Usage for this Module
207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/pwn/ai/agent/registry.rb', line 207 public_class_method def self.help puts <<~USAGE USAGE: PWN::AI::Agent::Registry.discover PWN::AI::Agent::Registry.definitions(enabled: %w[terminal pwn]) PWN::AI::Agent::Registry.definitions(relevance: 'nmap sweep 10/8') # slim (needs :tool_router) PWN::AI::Agent::Registry.rank(query: 'run a shell command') PWN::AI::Agent::Registry.lookup(name: 'shell') # => Entry PWN::AI::Agent::Registry.toolsets # => ["memory","pwn","skills","terminal"] PWN::AI::Agent::Registry.register(name:, toolset:, schema:, handler:) #{self}.authors USAGE end |
.lookup(opts = {}) ⇒ Object
- Supported Method Parameters
entry = PWN::AI::Agent::Registry.lookup( name: 'required - registered tool name' )
79 80 81 82 |
# File 'lib/pwn/ai/agent/registry.rb', line 79 public_class_method def self.lookup(opts = {}) name = opts[:name] @entries[name.to_s] end |
.rank(opts = {}) ⇒ Object
- Supported Method Parameters
ranked = PWN::AI::Agent::Registry.rank( query: 'required - user request text', entries: 'optional - Entry pool to rank (default .all)' )
C1 advantage-weighted router: score = α·keyword_sim + β·(tool rolling success_rate − global rate) + γ·UCB1(tool). Untried tools get an exploration bonus; tools that outperform the fleet get an exploitation bonus. Thompson sampling is available via Metrics.thompson for stochastic routing.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/pwn/ai/agent/registry.rb', line 131 public_class_method def self.rank(opts = {}) query = opts[:query].to_s.downcase entries = opts[:entries] || all return entries if query.strip.empty? tokens = query.scan(/[a-z0-9_]{3,}/).uniq # C1 — advantage-weighted router: # score = α·keyword_sim + β·advantage + γ·UCB(tool) # UCB gives untried / low-N tools an exploration bonus so a single # early failure (before its dep was installed) does not blacklist # it forever; advantage prefers tools that outperform the fleet. alpha = 1.0 beta = 0.3 gamma = 0.2 scored = entries.map do |e| hay = "#{e.name} #{e.toolset} #{e.schema[:description]} #{Array(e.schema.dig(:parameters, :properties)&.keys).join(' ')}".downcase sim = tokens.count { |t| hay.include?(t) } adv = defined?(Metrics) && Metrics.respond_to?(:advantage) ? Metrics.advantage(name: e.name) : 0.0 ucb = defined?(Metrics) && Metrics.respond_to?(:ucb) ? Metrics.ucb(name: e.name) : 0.5 [e, sim, (alpha * sim) + (beta * adv) + (gamma * ucb)] end scored.reject { |_, sim, _| sim.zero? } .sort_by { |_, _, s| -s } .map(&:first) end |
.register(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::AI::Agent::Registry.register( name: 'required - tool name exposed to the model', toolset: 'required - grouping for enable/disable (terminal, file, pwn, memory…)', schema: 'required - OpenAI function schema description:, parameters:', handler: 'required - ->(args_hash) { ... } returning a JSON-serialisable object', check: 'optional - -> { bool } gate; tool only advertised when truthy', max_chars: 'optional - cap on serialised result (default 24_000)' )
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/pwn/ai/agent/registry.rb', line 58 public_class_method def self.register(opts = {}) name = opts[:name].to_s raise 'ERROR: name is required' if name.empty? raise 'ERROR: schema is required' unless opts[:schema] raise 'ERROR: handler is required' unless opts[:handler].respond_to?(:call) @entries[name] = Entry.new( name: name, toolset: opts[:toolset].to_s, schema: opts[:schema], handler: opts[:handler], check: opts[:check] ||= -> { true }, max_chars: opts[:max_chars] ||= 24_000 ) end |
.toolsets ⇒ Object
- Supported Method Parameters
names = PWN::AI::Agent::Registry.toolsets
94 95 96 |
# File 'lib/pwn/ai/agent/registry.rb', line 94 public_class_method def self.toolsets @entries.values.map(&:toolset).uniq.sort end |