Agent Reference#
This document provides detailed documentation for each agent in the PyHydroGeophysX multi-agent system, including their inputs, outputs, and responsibilities.
BaseAgent#
The abstract base class that all agents inherit from.
class BaseAgent:
def __init__(self, name, api_key, model, llm_provider):
self.name = name
self.api_key = api_key
self.model = model
self.llm_provider = llm_provider
self.llm_usage_ledger = [] # one dict per LLM call
def execute(self, input_data: Dict[str, Any]) -> Dict[str, Any]:
raise NotImplementedError
Key utility methods added in v0.3:
query_llm(prompt, system_message=None)Sends a prompt to the configured LLM provider. On the first call, the agent lazily loads
.github/agents/<name>.agent.mdand appends its body to the system message (YAML frontmatter is stripped automatically).save_results(output_dir)Persists all agent outputs to output_dir. Type-aware serialisation:
NumPy arrays →
<name>_<key>.npyPyGIMLi meshes /
DataContainer→<name>_<key>.bmsPandas DataFrames →
<name>_<key>.csvPlain JSON-serialisable values →
results.jsonAnything else → stub entry in
results.jsonwith{__type__, repr}
_retry_llm_call(fn, max_retries=3)(static)Wraps any callable that performs one LLM call. Retries up to
max_retriestimes with exponential back-off (sleep 2^attempt seconds) for transient rate-limit errors. Non-transient errors are propagated immediately without retry._load_agent_md_for_name(name)(static)Reads
.github/agents/<name>.agent.mdrelative to the repo root and returns the Markdown body with YAML frontmatter stripped. Returns""if the file is missing.
AgentCoordinator#
Purpose: Orchestrates multi-agent workflows and manages execution state.
This is not a processing agent but an orchestration layer that:
Registers agents
Manages workflow state
Coordinates agent execution (with optional checkpoint / resume)
Aggregates LLM cost across all registered agents
Validates environment dependencies before running
Key Methods (v0.3):
register_agent(name, instance)
# Add an agent to the workflow.
execute_workflow(config, dry_run=False, resume=False)
# Run the complete workflow.
# dry_run=True: validate, plan, and estimate cost without running agents.
# resume=True: skip steps for which a checkpoint already exists.
# config['user_request'] fills in the ERT files, instrument, petrophysical
# parameters and seismic file it names where config leaves them out;
# anything it asks for that the run does not do is listed in
# result['warnings'] (and in the preview's validation_warnings).
preview_workflow(config)
# Equivalent to execute_workflow(..., dry_run=True).
# Returns validation_warnings (including dependency checks), execution_plan,
# and cost_estimate_usd.
get_workflow_state()
# Return current status dict.
get_workflow_summary()
# Return aggregated statistics after (or during) a run:
# {
# 'status': ...,
# 'completed_steps': [...],
# 'total_steps': N,
# 'current_step': ...,
# 'available_results': [...],
# 'total_llm_cost_estimate_usd': 0.0034,
# 'total_llm_tokens': 8200,
# 'llm_calls': 12,
# }
save_workflow_results()
# Persist all agent outputs via each agent's save_results().
Checkpoint / Resume Example:
from PyHydroGeophysX.agents import AgentCoordinator
coordinator = AgentCoordinator(api_key=api_key, output_dir='./results')
# ... register agents ...
# First attempt: may fail at step 3 of 5
try:
results = coordinator.execute_workflow(config)
except Exception as exc:
print(f"Workflow failed: {exc}")
# Resume: steps 1 and 2 are loaded from checkpoints
results = coordinator.execute_workflow(config, resume=True)
Dependency Pre-check:
preview_workflow() automatically calls _check_dependencies(plan) to
test whether required packages (pygimli, gmsh, anthropic, and
google-genai or the older google-generativeai) are importable before
the workflow runs. Missing
dependencies appear in validation_warnings.
ContextInputAgent#
Purpose: Translates natural language workflow descriptions into structured configurations.
ContextInputAgent always sets a detailed self.system_message in
__init__ so that it correctly guides the LLM from the very first call,
even before the .agent.md augmentation hook fires:
# excerpt from __init__
self.system_message = (
"You are an expert workflow configuration interpreter for "
"PyHydroGeophysX. Translate natural-language geophysical workflow "
"requests into structured JSON configuration dictionaries..."
)
Inputs:
user_request(str): Natural language workflow descriptionavailable_data(dict, optional): Available files/instruments
Outputs:
workflow_config(dict): Structured configurationexplanation(str): Human-readable explanation
ERTLoaderAgent#
Purpose: Loads and validates ERT field data from various instruments.
- System Prompt:
You are an expert in electrical resistivity tomography (ERT) data processing. Your role is to load and validate ERT field data from various commercial instruments.
Inputs:
data_file(str): Path to ERT data fileinstrument(str): Instrument type (E4D, Syscal, ABEM, BERT)project_dir(str): Project directorycrs(str): Coordinate reference systemquality_check(bool): Whether to perform QC
Outputs:
ert_data(object): Loaded ERT dataset (PyGIMLi DataContainer)num_electrodes(int): Number of electrodesnum_measurements(int): Number of measurementsquality_metrics(dict): Data quality statistics
ERTInversionAgent#
Purpose: Performs ERT inversion (standard or time-lapse).
- System Prompt:
You are an expert in electrical resistivity tomography (ERT) inversion. Your role is to configure and execute ERT inversions, select appropriate regularization parameters, and interpret inversion results.
Inputs:
ert_data(object): ERT data (for standard inversion)time_lapse_data(list): List of ERT datasets (for time-lapse)inversion_mode(str): ‘standard’ or ‘time-lapse’time_lapse_method(str): ‘difference’, ‘ratio’, or ‘joint’temporal_regularization(float): Temporal smoothing weightinversion_params(dict): Lambda, max_iter, methoduse_structure_constraint(bool): Whether to use seismic structureseismic_structure(object): Optional seismic structure data
Outputs:
resistivity_model(array): Inverted resistivity modelmesh(object): PyGIMLi meshchi2_values(list): Chi-squared fit statisticscoverage(array): Model coverage/sensitivityfinal_models(array): Time-series models (for time-lapse)
InversionEvaluationAgent#
Purpose: Evaluates inversion quality and automatically optimizes parameters.
- System Prompt:
You are an expert in geophysical inversion quality assessment. Your role is to evaluate ERT inversion results based on data fit, model smoothness, and physical plausibility.
Inputs:
inversion_results(dict): Results from ERTInversionAgentert_data(object): Original ERT datainversion_params(dict): Current parametersauto_adjust(bool): Whether to auto-adjust parametersmax_attempts(int): Maximum re-inversion attempts
Outputs:
quality_score(float): Overall quality (0-100)quality_metrics(dict): Detailed metricscomponent_scores(dict): Individual component scoresrecommendations(list): Improvement suggestionsadjusted_params(dict): Optimized parametersfinal_results(dict): Best inversion results
Quality Metrics:
Data Fit: Chi-squared target (0.8-1.5 acceptable)
Smoothness: Model roughness evaluation
Physical Plausibility: Resistivity range (1-10,000 Ohm-m)
Convergence: Iteration stability
Coverage: Model sensitivity
DataFusionAgent#
Purpose: Coordinates multi-method geophysical workflows.
- System Prompt:
You are an expert in multi-method geophysical data fusion. You understand how different geophysical methods complement each other and can recommend optimal workflows for integrating multiple datasets.
Inputs:
fusion_pattern(str): Pattern name or ‘auto’methods(list): Available methodsworkflow_config(dict): Configuration for fusiondata(dict): Data for each methodoutput_dir(str): Results directory
Outputs:
fusion_pattern(str): Selected patternexecution_plan(list): Step-by-step planstatus(str): Success/failureinterpretation(str): AI interpretation of results
StructureConstraintAgent#
Purpose: Applies seismic velocity interfaces as structural constraints to ERT inversion.
- System Prompt:
You are an expert in structure-constrained geophysical inversion. You understand how to incorporate a priori geological information from seismic data into ERT inversions.
Inputs:
ert_data(object): ERT measurement dataseismic_data(object): Seismic travel time data (optional)velocity_model(array): Velocity model from seismic inversionmesh(object): PyGIMLi meshvelocity_thresholds(list): Thresholds for interface extractionmesh_quality(int): Constrained mesh qualitylambda(float): ERT regularization parameterlimits(list): Resistivity bounds [min, max]
Outputs:
resistivity_model(array): Constrained resistivity modelmesh(object): Constrained mesh with layer markerscell_markers(array): Cell layer identificationscoverage(array): Model coverageinterfaces(list): Extracted velocity interfacesstatistics(dict): Resistivity range, chi2, data fit, n_layers
PetrophysicsAgent#
Purpose: Converts resistivity to water content using layer-specific petrophysical models with Monte Carlo uncertainty quantification.
- System Prompt:
You are an expert in petrophysical modeling and hydrogeophysics. You understand how to convert electrical resistivity to water content using Archie’s law and modified petrophysical relationships.
Petrophysical Model:
Archie's Law (modified with surface conductivity):
sigma_bulk = sigma_fluid * phi^m * S^n + sigma_surface
Where:
- sigma_bulk: Bulk conductivity (1/resistivity)
- sigma_fluid: Fluid conductivity (1/rho_fluid)
- phi: Porosity
- S: Saturation (water content / porosity)
- m: Cementation exponent
- n: Saturation exponent
- sigma_surface: Surface conductivity (clay effect)
Default Layer Parameters:
Layer Type |
Porosity (phi) |
m |
n |
sigma_surface |
rho_fluid |
|---|---|---|---|---|---|
Regolith |
0.42 +/- 0.05 |
1.3 |
2.1 |
1/200 +/- 1/200 |
20 Ohm-m |
Bedrock |
0.25 +/- 0.15 |
1.9 |
1.7 |
0.0 +/- 0.0 |
20 Ohm-m |
Inputs:
resistivity_model(array): Resistivity valuesmesh(object): PyGIMLi meshcell_markers(array): Layer identificationslayer_params(dict): Parameters for each layern_realizations(int): Monte Carlo samples (default: 100)log_resistivity_std(array, optional): the inversion’s standard deviation of ln(resistivity) per cell, drawn with the parameters (see Water-content uncertainty)
Outputs:
water_content_mean(array): Mean water content per cellwater_content_std(array): Standard deviation (uncertainty)inversion_uncertainty(bool): whether that standard deviation includes the inversion’s ownsaturation_mean(array): Mean saturationsaturation_std(array): Saturation uncertaintystatistics(dict): WC range, mean WC, mean uncertainty
WaterContentAgent#
Purpose: General resistivity to water content conversion (simpler than PetrophysicsAgent).
- System Prompt:
You are an expert in petrophysical relationships and rock physics. Your role is to convert electrical resistivity to water content using appropriate models.
Inputs:
inversion_results(dict): ERT inversion resultspetrophysical_params(dict): Parameters for each layeruncertainty_analysis(bool): Whether to run Monte Carlon_realizations(int): MC realizations (default: 100)
Outputs:
water_content(array): Water content estimatesuncertainties(array): Uncertainty estimates (if MC enabled)statistics(dict): Summary statistics
SeismicAgent#
Purpose: Processes seismic refraction data and extracts velocity structures.
- System Prompt:
You are an expert in seismic refraction tomography (SRT). Your role is to process seismic travel time data, perform velocity inversions, and extract geological structure interfaces.
Inputs:
seismic_data(object or str): Seismic travel time data, loaded or as a file path (the same asseismic_file)velocity_threshold(float): Threshold for interface detectioninversion_params(dict): Seismic inversion parametersoutput_dir(str): Results directory
Outputs:
velocity_model(array): Velocity distributioninterface_coords(tuple): (x, z) coordinates of interfacemesh(object): Seismic inversion meshstatistics(dict): Velocity range, chi2, data fit
TDEMAgent#
Purpose: Performs Time-Domain Electromagnetic forward modeling and inversion.
Inputs:
layer_thicknesses(array): Layer thicknesses for 1D modelconductivity(array): Layer conductivitiessurvey_config(TDEMSurveyConfig): Survey parametersinversion_params(dict): Inversion configuration
Outputs:
forward_response(array): TDEM responserecovered_model(array): Inverted conductivity modelchi2(float): Data misfitstatistics(dict): Inversion statistics
ClimateDataAgent#
Purpose: Retrieves the daily weather at the survey site - precipitation, minimum and maximum air temperature and reference evapotranspiration - from the Open-Meteo historical-weather API (the ERA5 reanalysis; one HTTPS request, no API key, global coverage), with the antecedent-moisture features ERT is read against.
- System Prompt:
You are an expert in climate data analysis for hydrogeophysical studies. You understand how precipitation, evapotranspiration, and temperature affect subsurface moisture and resistivity measurements.
Inputs:
coords(tuple): Site longitude and latitude, incrs; a list of points is averaged, since an ERT line spans far less than one reanalysis celldates(tuple):(start_date, end_date)as YYYY-MM-DD, or a list of yearscrs(int or str): Coordinate reference system ofcoords(default 4326)ert_timestamps(list, optional): Survey times to align the series toantecedent_days(list, optional): Windows for antecedent precipitation totals (default 1, 3 and 7 days)output_dir(str, optional): Folder to saveclimate_data.csvand its metadata incsv_file(str, optional): A series saved earlier, read instead of fetching
Outputs:
climate_data(DataFrame): Dailyprcp(mm),tminandtmax(degC) andpet(FAO-56 reference evapotranspiration, mm)derived_features(dict): Antecedent precipitation totals and P - PETert_alignment(dict): The rows of the survey days, whenert_timestampswere givenmetadata(dict): The period, the source and its attribution, the site and the grid cell the values come fromnotes(list): Anything asked for that the source does not provide
ReportAgent#
Purpose: Generates reports from workflow results.
- System Prompt:
You are an expert in technical report writing for geophysical and hydrological studies. Your role is to synthesize results from ERT data processing, inversion, water content analysis, and climate data into clear, informative reports.
Report Sections:
Executive Summary
Data Processing Summary
Climate Data Summary (if available)
Inversion Results
Water Content Analysis
Climate-Resistivity Analysis (if climate data available)
Quality Assessment
Conclusions & Recommendations
Inputs:
workflow_data(dict): All data from workflow stepsconfig(dict): Original workflow configurationoutput_dir(str): Report output directory
Outputs:
report_path(str): Path to generated reportfigures(list): Generated figure pathssummary_stats(dict): Key statistics
Workflow Controller#
Purpose: Chooses each step of a BaseAgent.run_unified_agent_workflow()
run.
The entry point hands the parsed configuration to the controller in
PyHydroGeophysX.agents.runtime. Before each step the controller lists the
tools whose inputs the run already holds (load_ert_surveys,
fetch_climate, invert_ert, invert_time_lapse,
evaluate_inversion, convert_water_content, pick_first_breaks,
load_seismic_traveltimes, invert_seismic,
evaluate_seismic_inversion, extract_seismic_interfaces,
derive_structure, fuse_methods, load_tdem_data, invert_tdem,
evaluate_tdem_inversion, map_tdem_plan_view,
convert_tdem_water_content, load_mt_sites, invert_mt,
evaluate_mt_inversion, convert_mt_water_content,
load_gravmag_data, invert_gravmag, evaluate_gravmag_inversion,
load_gpr_line, process_gpr_line, load_model_output,
write_report), asks the model which one to run and
reads the result before choosing again. Without an API key it takes the first
runnable tool in registration order, which is dependency order.
Every method runs the same way: its data are read and drawn, inverted, and the inversion is evaluated before anything is built on it.
Reading draws the data.
load_ert_surveysdraws an apparent-resistivity pseudosection of each survey as it loads it (up to six of a long series, the first and last among them), each reading at the midpoint of its electrodes and a pseudo-depth of 0.19 of their spread (data_processing.ert_io.pseudosection_points, which the studio’s ERT page uses too);load_seismic_traveltimesdraws a travel-time file’s curves, one per shot;load_tdem_datathe decays of up to six soundings beside the station layout;load_mt_siteseach site’s apparent resistivity and phase;load_gravmag_datathe gravity or magnetic station values on a map, and says which field the table holds before anything is inverted;load_gpr_linea GPR line’s radargram as recorded. A GPR line is processed rather than inverted (process_gpr_line, see In an AQUAH run), and its depths use only a velocity somebody gave. The figures are written underraw_data/in the run folder, where the studio’s Live tab shows them as they appear, and they open the method’s figures in the report. The step summaries give the counts and ranges: for ERT, the electrodes, the readings and the apparent resistivity. The ERT reports (single survey and time-lapse) open with the surveys’ pseudosections side by side on one colour scale, and that figure is drawn whatever the request narrows the figures to.Evaluation.
evaluate_seismic_inversion,evaluate_tdem_inversion,evaluate_mt_inversionandevaluate_gravmag_inversionscore the model out of 100 asevaluate_inversionscores ERT, with the same chi-squared rule (agents._method_evaluation.chi2_fit_score): for seismic refraction the fit of the picks, the share of the model the rays cover, cells at a velocity bound and the convergence; for TDEM each sounding’s chi-squared, the soundings that resolved a model and the physical range of the resistivities; for MT each site’s RMS against Occam’s target, the static shifts and the physical range; for gravity and magnetics the residual anomaly’s chi-squared, the stations misfit by more than three errors, a model at the solver’s bound, and the beta search. Each draws its fit, writes recommendations, and below the threshold (quality_threshold, 70) leaves one short warning. The seismic evaluation re-inverts with lambda doubled when the fit is below the target and halved when above, up tomax_attemptsinversions, and keeps the best-scoring model. The others search their own regularization or hold it fixed on purpose, so they are scored, not re-run: Occam’s method takes the smoothest MT model that reaches its target, the potential-field inversion sweeps beta for chi-squared one, and the TDEM line inversion has its own smoothness search (auto_lambda), which the ground-TEM preset leaves off because a sounding of four or five gates fitted to chi-squared one is fitting noise; the TDEM evaluation says so, and how to turn it on, when the fit is off. The report’s Inversion Quality section gives the score, its components and any retries; the interfaces, plan-view maps and water content are built on the model the evaluation kept.Water content is drawn with its standard deviation. Each conversion draws the mean and the Monte Carlo standard deviation as it ends (the ERT section of the first, middle and last survey; a TEM survey’s section; an MT site or TEM sounding against depth, with one- and two-sigma bands), and the time-lapse report adds the standard deviation of every survey on one scale from zero beside the water content.
A seismic line given as raw SEG-Y runs in three steps: pick_first_breaks
(“Pick first-arrival travel times”) picks the first arrivals - with the sample
interval of the acquisition record beside the SEG-Y file when the header has
rounded it - places them on the survey geometry, picks again along its shot’s
first-arrival curve each pick that strays from it or stopped at time zero,
leaves out what still strays and the shots whose times disagree with their
reciprocals, and picks again - or leaves out - a pick that disagrees with the
neighbouring shots’ picks at the same geophone
(data_processing.seismic.pick_and_correct, screen_picks and
neighbour_shot_check, which the studio’s Seismic page uses too;
first_break_params takes repick, monotonic_check,
reciprocity_check and neighbour_check to turn each off). It draws the picks
on wiggle plots of shot gathers spread along the line
(seismic_shot_gathers.png, gathers_figure) and as travel-time curves;
invert_seismic inverts the travel times, with mesh and velocities scaled to
the survey where the request does not set them; extract_seismic_interfaces
(“Extract layer interfaces”) traces the threshold velocities
(velocity_threshold, 1200 m/s by default) through the model. A line given
as travel times starts at load_seismic_traveltimes.
map_tdem_plan_view (“Interpolate plan-view maps”) follows invert_tdem
when the request asks for the spatial distribution of a TEM survey. It kriges
log10 resistivity at several depths across the survey outline
(visualization.em_maps) and draws a second map of the kriging standard
deviation, over the survey’s basemap; a survey it cannot map, such as a single
line, is reported as not delivered with the reason.
invert_mt runs when the configuration names MT sites (mt_files: EDI,
EMTF XML, Z- or J-files, or folders of them; a request that names .edi
files sets it). It inverts each site in 1D by Occam’s method, with the options
of mt_params, and a line of three or more located sites also in 2D, with
mt_profile_params; convert_mt_water_content converts the layered
models when the request asks for water content and no ERT or TDEM model is
there to carry it.
Inputs: workflow_config dict, as ContextInputAgent.parse_request
produces it.
Outputs: (results, execution_plan, interpretation, report_files). The
execution plan lists the steps that ran, each with the reason it was chosen.
Set PHGX_LEGACY_WORKFLOW=1 to run the previous pipeline instead. It sorts
the request into one of eight workflow types (tdem, seismic,
model_output, time_lapse, data_fusion, ert_data_process,
direct_ert, custom) and runs that type’s fixed sequence. Its
classifier, once WorkflowOrchestratorAgent._detect_workflow_type, lives in
PyHydroGeophysX.agents._legacy_workflow.
Removed agents#
Three agents were removed in 0.5.0. Importing one still works, with a
DeprecationWarning that names its replacement; creating one raises the
same message.
Removed |
Use instead |
|---|---|
|
|
|
|
|
|
3D Mesh Builder#
Purpose: Builds and exports 3D meshes for ERT forward modeling and
inversion. It is not an agent: the 3D Mesh Builder Streamlit app
(python -m PyHydroGeophysX.gui_mesh3d) and the desktop studio’s 3D mesh page
call generate_mesh and save_outputs from
PyHydroGeophysX.core.mesh_3d, which build on Mesh3DCreator. The same
functions can be called directly:
from pathlib import Path
from PyHydroGeophysX.core.mesh_3d import generate_mesh, mesh_summary, save_outputs
config = {
"output_dir": "ert_mesh",
"array_type": "Surface grid",
"mesh_type": "Surface with topography",
"nx": 10, "ny": 6, "dx": 5.0, "dy": 5.0, "x_offset": 0.0, "y_offset": 0.0,
"topography_type": "Linear tilt", "z_base": 100.0, "tilt_x": 0.05, "tilt_y": 0.0,
"electrode_refinement": 0.5, # cell size at the electrodes (m)
"boundary_refinement": 2.0, # cell size at the domain boundary (m)
"attractor_distance": 5.0, # distance over which the refinement fades (m)
"mesh_engine": "PyGIMLi prism",
"para_depth": 30.0, "dz_fine": 0.5, "dz_coarse": 2.0, "boundary_extension": 1.4,
}
result = generate_mesh(config, log=print)
files = save_outputs(result["mesh"], result["electrodes"], Path("ert_mesh"), "ert_mesh",
["BMS mesh (.bms)", "VTK mesh (.vtk)", "Sensor positions (.csv)"])
print(mesh_summary(result["mesh"])) # cells, nodes, boundaries, dimension
Array types (array_type): "Surface grid" (nx, ny, dx,
dy, x_offset, y_offset), "Single borehole" and "Crosshole".
Mesh types (mesh_type): "Surface with topography" or "Box mesh".
Topography types (topography_type): "Flat" (z_flat),
"Linear tilt" (z_base, tilt_x, tilt_y), "Gaussian hill"
(hill_base, hill_amp, hill_sigma, hill_cx, hill_cy) and
"Custom expression" (topography_expr, a NumPy expression in x and
y).
Mesh engines (mesh_engine): "Auto", "Gmsh (tetrahedral)",
"PyGIMLi prism", "Structured grid" and "E4D (Triangle + TetGen)". Gmsh falls
back to the structured grid when it fails.
Returns: generate_mesh returns a dictionary with mesh (the PyGIMLi
mesh), electrodes (a DataFrame with columns n, x, y, z),
generator (the mesh engine that ran) and zones. save_outputs returns
the written paths under bms, mesh_structure, vtk and
sensors_csv.