What is a uvm agent?

A uvm_agent is a fundamental component of a testbench used for verification of a specific design block or module. The purpose of a uvm_agent is to manage and coordinate various verification components, such as a monitor, driver, sequencer, and possibly other user-defined components, to interact with the design under test (DUT).

Code
// Filename : basic_agent.sv

class basic_agent extends uvm_agent;
    `uvm_component_utils(basic_agent)
    
    basic_driver drvr;
    basic_sequencer seqr;
    basic_monitor mon;
    basic_config cfg;
    
    
    extern         function      new(string name, uvm_component parent);
    extern virtual function void build_phase(uvm_phase phase);
    extern virtual function void connect_phase(uvm_phase phase);
    extern virtual function void end_of_elaboration_phase(uvm_phase phase);
    extern virtual function void start_of_simulation_phase(uvm_phase phase);
    
    extern virtual task  main_phase(uvm_phase phase);
    
    extern virtual function void extract_phase(uvm_phase phase);
    extern virtual function void check_phase(uvm_phase phase);
    extern virtual function void report_phase(uvm_phase phase);
    extern virtual function void final_phase(uvm_phase phase);
    
endclass

//------------------------------------------------------------------//

function basic_agent::new(string name, uvm_component parent);
    super.new(name, parent);

endfunction: new

//------------------------------------------------------------------//

function void basic_agent::build_phase(uvm_phase phase);
    super.build_phase(phase);

    drvr = basic_driver::type_id::create("drvr", this);
    seqr = basic_sequencer::type_id::create("seqr", this);
    mon = basic_monitor::type_id::create("mon", this);
    
endfunction: build_phase

//------------------------------------------------------------------//

function void basic_agent::connect_phase(uvm_phase phase);
    super.connect_phase(phase);

    drvr.seq_item_port.connect(seqr.seq_item_export);
endfunction: connect_phase

//------------------------------------------------------------------//

function void basic_agent::end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    
endfunction: end_of_elaboration_phase

//------------------------------------------------------------------//

function void basic_agent::start_of_simulation_phase(uvm_phase phase);
    super.start_of_simulation_phase(phase);
    
endfunction: start_of_simulation_phase

//------------------------------------------------------------------//

task basic_agent::main_phase(uvm_phase phase);
    super.main_phase(phase);
    phase.raise_objection(this);
    
    phase.drop_objection(this);
endtask: main_phase

//------------------------------------------------------------------//

function void basic_agent::extract_phase(uvm_phase phase);
    super.extract_phase(phase);

endfunction: extract_phase

//------------------------------------------------------------------//

function void basic_agent::check_phase(uvm_phase phase);
    super.check_phase(phase);

endfunction: check_phase

//------------------------------------------------------------------//

function void basic_agent::report_phase(uvm_phase phase);
    super.report_phase(phase);

endfunction: report_phase

//------------------------------------------------------------------//

function void basic_agent::final_phase(uvm_phase phase);
    super.final_phase(phase);

endfunction: final_phase
Theory Questions
    Yes, it is allowed to write code inside the agent. UVM only advises not to write time consuming code in agent, but that is not a hard rule. Depending on iones requirement, the implementation of uvm_agent can be modified.
    Yes, it is advised to use an agent to instantiate various components inside and then use the agent inside the environment. This is done to ensure that the agent acts as a small replacable block where the entire driver, monitor, scoreboard etc is treated as a plug and plag unit.
    UVM agents can be either active, containing a sequencer and driver, or passive, containing only a monitor.
    A UVM agent extends from the uvm_component base class.
    get_is_active()
    A passive UVM agent is preferred when the goal is to monitor signals without driving stimulus, such as for coverage collection or protocol checking.