Object-Oriented Prompt Engineering for JavaEst. 2026 · MIT License
PrOOPt

“A Java library that keeps LLMs on a need-to-know basis.”

Chapter Two

Getting Started with PrOOPt

From an empty pom.xml to your first governed LLM call, in roughly one cup of coffee.

What You Will Need

Not much, as it happens. PrOOPt runs anywhere Java runs, and the local model runs on a CPU like everybody did in the old days.

ResourceRequirement
Java17+
RAMLOCAL tier: 3 GB minimum (Phi-3.5 Mini INT4 ≈ 2 GB model + JVM). Cloud-only: 512 MB
GPUNot required — CPU inference via ONNX Runtime
OSAny (x86-64 or ARM64); native libraries are auto-selected
Maven3.9+

One Dependency, Four Modules

PrOOPt ships as a small family of Maven artifacts. You will almost always start with prooopt-core, then add a runtime for wherever you want your tokens generated.

ModulePurpose
prooopt-coreAnnotations, AOP, autoboxer, orchestrator, stream API
prooopt-runtime-localONNX Runtime on-device inference — nothing leaves the JVM
prooopt-runtime-cloudAnthropic + OpenAI cloud adapters
prooopt-examplesWorked examples: Legal Analyzer, Linear System, BODMAS solver
<dependency>
    <groupId>io.github.argonizer</groupId>
    <artifactId>prooopt-core</artifactId>
    <version>${prooopt.version}</version>
</dependency>
<dependency>
    <groupId>io.github.argonizer</groupId>
    <artifactId>prooopt-runtime-cloud</artifactId>
    <version>${prooopt.version}</version>
</dependency>

Your First @PromptFunction

Here is the entire trick, and we will be honest about how strange it looks the first time: you write a method, and the method returns null. On purpose. Forever.

public class ContractReader {

    @PromptFunction(
        model = ModelTier.LOCAL,
        prompt = "Extract the contract date: {text}",
        description = "Extracts signing date"
    )
    public LocalDate extractDate(String text) {
        text = text.trim(); // optional pre-processing
        return null;        // PrOOPt takes over
    }
}

When the call reaches that return null, PrOOPt intercepts it, fills the prompt template with your arguments, appends format instructions derived from your return type, routes the prompt to the declared model tier, and converts the model's reply into a real LocalDate. To the rest of your codebase, this is just a method. It can be mocked, wrapped, profiled and unit-tested like any other method. That is rather the point.

Pick a Tier (or Let the Router Do It)

Each function carries its own model tier. That is per-function governance, not per-application guesswork.

ModelTierMeaning
LOCALOn-device inference via ONNX Runtime — nothing leaves the JVM
CLOUD_FASTCloud API, cheap and fast model
CLOUD_ADVANCEDCloud API, flagship model
AUTOThe router picks a tier from the estimated token count

Credentials, the Boring (Important) Way

API keys are never hardcoded. Set them as environment variables or system properties, and reference them from prooopt.yaml:

# prooopt.yaml
models:
  cloud:
    apiKey: ${ANTHROPIC_API_KEY}

Your First Orchestrated Run

Wire it all together with the builder. Register your annotated instances, hand the runtime an input, and let the orchestrator plan the work.

LegalAnalyzer analyzer = new LegalAnalyzer();

PrOOPtRuntime prooopt = PrOOPt.builder()
        .router(new CloudModelRouter(anthropicAdapter))
        .registerInstance(analyzer)
        .build();

Object result = prooopt.orchestrate(analyzer, contractText);

And here is what you will see in the logs — every decision, every tier, every masked argument, every estimated cent:

[PROOOPT][PROMPT_FUNCTION][START] traceId=abc123 function=generateSummary
        tier=CLOUD_ADVANCED inputs={text=***CONTRACT-TEXT***}
[PROOOPT][PROMPT_FUNCTION][END]   traceId=abc123 function=generateSummary
        durationMs=842 result=This mutual non-disclosure...
[PROOOPT][ORCHESTRATOR][SUMMARY]  traceId=abc123 totalMs=1204 functions=5
        code=2 local=2 cloud=1 tokens=~312 estCost=$0.0020

That last line deserves a moment of appreciation. Five functions ran. Two were plain Java and cost nothing. Two ran on the local model and cost electricity. One went to the cloud and cost a fifth of a cent. You did not have to instrument any of this. The audit trail is a by-product of execution, not a chore you do afterwards.

Where to Next?

You now have a working PrOOPt installation and one slightly miraculous method that returns null and means it. The Tutorials walk through the rest of the toolbox, one short lesson at a time: deterministic tools, the orchestrator's DAG plans, pipelines, plan caching, and keeping secrets secret.