跳转至

Ruby 独立消费者

在自己的应用中使用已发布的 praxo-core==0.9.0,要求 Ruby 3.1 或更新版本。 无需检出 PRAXO-CORE 源码、API Key、模型服务或 AxisVela 产品。安装需要 RubyGems 访问或已有 gem 缓存。

这是扩展语言 starter,只演示 allow/deny 路径,不在四个具备治理负向矩阵的试点语言范围内。

选择边界

适用于需要显式工具范围和 Policy 的嵌入式模型/工具循环。应用仍负责模型访问、真实身份、 持久审批、外部效果和隔离。它不是托管 Agent 平台、持久图引擎或沙箱服务。 选型前阅读何时不应选择 Core。

创建应用

在 Gemfile 中声明精确依赖:

source 'https://rubygems.org'

gem 'praxo-core', '0.8.0'

然后运行 bundle install。不要使用 path:、git:、vendored 检出或 $LOAD_PATH 替代已发布 gem。

应用源码

以下为 starter.rb,来自受测源码。

require 'praxo'

module Starter
  M = Praxo::Models

  class FixtureModel
    def initialize(target)
      @target = target
    end

    def generate(request)
      last = request.messages.last
      if last.role == M::TOOL
        M.response(message: M.message_assistant('recorded'))
      else
        M.response(message: M.message_assistant('', [
          M.tool_call(id: 'record-1', name: 'record', arguments: { 'target' => @target })
        ]))
      end
    end
  end

  class RecordTool
    attr_reader :calls

    def initialize
      @calls = 0
    end

    def call(_arguments)
      @calls += 1
      'recorded'
    end
  end

  def self.run_scenario(target)
    tool = RecordTool.new
    registry = Praxo::Agent::ToolRegistry.new
    registry.register(Praxo::Agent::FuncTool.new(
      'record', 'Record a target in an in-memory counter.',
      ->(arguments) { tool.call(arguments) }
    ))
    options = Praxo::Agent::AgentOptions.new(FixtureModel.new(target))
    options.tools = registry
    options.allowed_tools = ['record']
    options.policy = lambda do |action|
      case action.kind
      when 'model.generate'
        M.decision(allowed: true)
      when 'tool.invoke'
        M.decision(
          allowed: action.tool_name == 'record' && action.arguments['target'] == 'public',
          reason: 'only the public target is permitted'
        )
      else
        M.decision(allowed: false, reason: 'unsupported action')
      end
    end
    result = Praxo::Agent.run_agent(
      options, M.session(id: "consumer-#{target}"), 'Record the requested target.'
    )
    [result.output, tool.calls, nil]
  rescue StandardError => error
    ['', tool.calls, error]
  end

  def self.run(out = $stdout)
    output, calls, error = run_scenario('public')
    unless error.nil? && output == 'recorded' && calls == 1
      raise "allow verification failed: output=#{output.inspect} effects=#{calls} error=#{error}"
    end
    out.puts 'allow: recorded; effects=1'

    _, calls, error = run_scenario('private')
    unless error && error.message.include?('policy denied') && calls.zero?
      raise "deny verification failed: effects=#{calls} error=#{error}"
    end
    out.puts 'deny: policy rejected; effects=0'
  end
end

Starter.run if $PROGRAM_NAME == __FILE__

验收测试

以下为 test_starter.rb,来自受测源码。

require_relative 'starter'

output, calls, error = Starter.run_scenario('public')
raise "allow: output=#{output.inspect} effects=#{calls} error=#{error}" unless error.nil? && output == 'recorded' && calls == 1

_, calls, error = Starter.run_scenario('private')
raise "deny: effects=#{calls} error=#{error}" unless error && error.message.include?('policy denied') && calls.zero?

puts 'ruby consumer tests passed'

验证

bundle install
bundle exec ruby test_starter.rb

程序预期输出:

allow: recorded; effects=1
deny: policy rejected; effects=0

模型是确定性 fixture,不是真实 LLM。allowed_tools 限制工具名,Policy 额外检查目标 参数。被拒绝的 private 目标不得到达内存效果计数器。替换为真实适配器后仍需保留这些测试。

若使用无 Bundler 的源码检出,可运行 ruby -I language/ruby/lib test_starter.rb; 该路径不算独立消费者证据。

范围

本 starter 仅证明指定 gem 与 fixture 行为,不包含治理负向矩阵,也不证明真实身份、 持久审批、效果对账或隔离。后续参考 Ruby 实现指南及 安全模型。