diff --git a/samples/pom.xml b/samples/pom.xml
index c2acc9c41..b04dd2cc7 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -22,6 +22,7 @@
secure-poem-multiple-models
secure-sql-chatbot
sql-chatbot
+ weather-agent
diff --git a/samples/weather-agent/README.md b/samples/weather-agent/README.md
new file mode 100644
index 000000000..407545337
--- /dev/null
+++ b/samples/weather-agent/README.md
@@ -0,0 +1,63 @@
+# Chatbot example
+
+This example demonstrates how to create an AI agent using Quarkus LangChain4j.
+
+## Running the example
+
+A prerequisite to running this example is to provide your OpenAI API key.
+
+```
+export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=
+```
+
+Then, simply run the project in Dev mode:
+
+```
+mvn quarkus:dev
+```
+
+## Using the example
+
+Execute:
+
+```
+curl http://localhost:8080/weather?city=Athens
+```
+
+and you should get a response a like so:
+
+```
+The weather in Athens today is mostly cloudy, with a maximum temperature of 15.6°C and a minimum of 7.4°C. There is no expected precipitation and wind speeds can reach up to 8.1 km/h
+```
+
+## Using other model providers
+
+### Compatible OpenAI serving infrastructure
+
+Add `quarkus.langchain4j.openai.base-url=http://yourerver` to `application.properties`.
+
+In this case, `quarkus.langchain4j.openai.api-key` is generally not needed.
+
+### Ollama
+
+
+Replace:
+
+```xml
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-openai
+ ${quarkus-langchain4j.version}
+
+```
+
+with
+
+```xml
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-ollama
+ ${quarkus-langchain4j.version}
+
+```
+
diff --git a/samples/weather-agent/pom.xml b/samples/weather-agent/pom.xml
new file mode 100644
index 000000000..4ebd9b3a6
--- /dev/null
+++ b/samples/weather-agent/pom.xml
@@ -0,0 +1,135 @@
+
+
+ 4.0.0
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-sample-weather-agent
+ Quarkus LangChain4j - Sample - Weather Agent
+ 1.0-SNAPSHOT
+
+
+ 3.13.0
+ true
+ 17
+ UTF-8
+ UTF-8
+ quarkus-bom
+ io.quarkus
+ 3.15.1
+ true
+ 3.2.5
+ 999-SNAPSHOT
+
+
+
+
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+
+
+
+ io.quarkus
+ quarkus-rest-jackson
+
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-openai
+ ${quarkus-langchain4j.version}
+
+
+ io.quarkus
+ quarkus-cache
+
+
+
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-openai-deployment
+ ${quarkus-langchain4j.version}
+ test
+ pom
+
+
+ *
+ *
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+
+
+
+ build
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ maven-surefire-plugin
+ 3.5.1
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+
+
+ maven-failsafe-plugin
+ 3.5.1
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+ native
+
+
+
+
+
+
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/CityExtractorAgent.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/CityExtractorAgent.java
new file mode 100644
index 000000000..ee911a066
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/CityExtractorAgent.java
@@ -0,0 +1,21 @@
+package io.quarkiverse.langchain4j.weather.agent;
+
+import dev.langchain4j.agent.tool.Tool;
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+
+@ApplicationScoped
+@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)
+public interface CityExtractorAgent {
+
+ @UserMessage("""
+ You are given one question and you have to extract city name from it
+ Only reply the city name if it exists or reply 'unknown_city' if there is no city name in question
+
+ Here is the question: {question}
+ """)
+ @Tool("Extracts the city from a question")
+ String extractCity(String question);
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherForecastAgent.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherForecastAgent.java
new file mode 100644
index 000000000..e050e9372
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherForecastAgent.java
@@ -0,0 +1,24 @@
+package io.quarkiverse.langchain4j.weather.agent;
+
+import dev.langchain4j.service.SystemMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import io.quarkiverse.langchain4j.weather.agent.geo.GeoCodingService;
+import io.quarkiverse.langchain4j.weather.agent.weather.WeatherForecastService;
+
+@RegisterAiService(tools = { CityExtractorAgent.class, WeatherForecastService.class, GeoCodingService.class})
+public interface WeatherForecastAgent {
+
+ @SystemMessage("""
+ You are a meteorologist, and you need to answer questions asked by the user about weather using at most 3 lines.
+
+ The weather information is a JSON object and has the following fields:
+
+ maxTemperature is the maximum temperature of the day in Celsius degrees
+ minTemperature is the minimum temperature of the day in Celsius degrees
+ precipitation is the amount of water in mm
+ windSpeed is the speed of wind in kilometers per hour
+ weather is the overall weather.
+ """)
+ String chat(String query);
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherResource.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherResource.java
new file mode 100644
index 000000000..95ece59af
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/WeatherResource.java
@@ -0,0 +1,27 @@
+package io.quarkiverse.langchain4j.weather.agent;
+
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import org.jboss.resteasy.reactive.RestQuery;
+
+
+@Path("/weather")
+public class WeatherResource {
+
+ private final WeatherForecastAgent agent;
+
+ public WeatherResource(WeatherForecastAgent agent) {
+ this.agent = agent;
+ }
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String getWeather(@RestQuery @DefaultValue("Manilla") String city) {
+ return agent.chat(String.format("What is the weather in %s ?", city));
+ }
+
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoCodingService.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoCodingService.java
new file mode 100644
index 000000000..6e23ce893
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoCodingService.java
@@ -0,0 +1,22 @@
+package io.quarkiverse.langchain4j.weather.agent.geo;
+
+import dev.langchain4j.agent.tool.Tool;
+import io.quarkus.cache.CacheResult;
+import io.quarkus.rest.client.reactive.ClientQueryParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
+import org.jboss.resteasy.reactive.RestQuery;
+
+@RegisterRestClient(configKey = "geocoding")
+@Path("/v1")
+public interface GeoCodingService {
+
+ @GET
+ @Path("/search")
+ @CacheResult(cacheName = "geo-results")
+ @ClientQueryParam(name = "count", value = "1")
+ @Tool("Finds the latitude and longitude of a given city")
+ GeoResults search(@RestQuery String name);
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResult.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResult.java
new file mode 100644
index 000000000..404eb810f
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResult.java
@@ -0,0 +1,4 @@
+package io.quarkiverse.langchain4j.weather.agent.geo;
+
+public record GeoResult(double latitude, double longitude) {
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResults.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResults.java
new file mode 100644
index 000000000..03322a5c6
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/geo/GeoResults.java
@@ -0,0 +1,11 @@
+package io.quarkiverse.langchain4j.weather.agent.geo;
+
+import java.util.List;
+
+public record GeoResults(List results) {
+
+ public GeoResult getFirst() {
+ return results.get(0);
+ }
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/Daily.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/Daily.java
new file mode 100644
index 000000000..cf16c72e3
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/Daily.java
@@ -0,0 +1,29 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+import java.util.Arrays;
+
+public record Daily(double[] temperature_2m_max,
+ double[] temperature_2m_min,
+ double[] precipitation_sum,
+ double[] wind_speed_10m_max,
+ int[] weather_code) {
+
+ public DailyWeatherData getFirstDay() {
+ return new DailyWeatherData(temperature_2m_max[0],
+ temperature_2m_min[0],
+ precipitation_sum[0],
+ wind_speed_10m_max[0],
+ weather_code[0]);
+ }
+
+ @Override
+ public String toString() {
+ return "Daily{" + "temperature_2m_max=" + Arrays.toString(temperature_2m_max)
+ + ", temperature_2m_min=" + Arrays.toString(temperature_2m_min)
+ + ", precipitation_sum=" + Arrays.toString(precipitation_sum)
+ + ", wind_speed_10m_max=" + Arrays.toString(wind_speed_10m_max)
+ + ", weather_code=" + Arrays.toString(weather_code)
+ + '}';
+ }
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyUnits.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyUnits.java
new file mode 100644
index 000000000..f6d9799f0
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyUnits.java
@@ -0,0 +1,7 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+public record DailyUnits(String time,
+ String temperature_2m_max,
+ String precipitation_sum,
+ String wind_speed_10m_max) {
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyWeatherData.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyWeatherData.java
new file mode 100644
index 000000000..25f46a4e6
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/DailyWeatherData.java
@@ -0,0 +1,23 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+import io.vertx.core.json.JsonObject;
+
+public record DailyWeatherData(double temperature_2m_max,
+ double temperature_2m_min,
+ double precipitation_sum,
+ double wind_speed_10m_max,
+ int weather_code) {
+
+
+ public JsonObject toJson() {
+ JsonObject json = new JsonObject();
+ json.put("maxTemperature", temperature_2m_max());
+ json.put("minTemperature", temperature_2m_min());
+ json.put("precipitation", precipitation_sum());
+ json.put("windSpeed", wind_speed_10m_max());
+ json.put("weather", WmoCode.translate(weather_code()));
+
+ return json;
+ }
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecast.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecast.java
new file mode 100644
index 000000000..fbae14887
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecast.java
@@ -0,0 +1,6 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+public record WeatherForecast(DailyUnits daily_units, Daily daily) {
+
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecastService.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecastService.java
new file mode 100644
index 000000000..4827f18b0
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WeatherForecastService.java
@@ -0,0 +1,27 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+import dev.langchain4j.agent.tool.Tool;
+import io.quarkus.rest.client.reactive.ClientQueryParam;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
+import org.jboss.resteasy.reactive.RestQuery;
+
+@RegisterRestClient(configKey = "openmeteo")
+@Path("/v1")
+public interface WeatherForecastService {
+
+ @GET
+ @Path("/forecast")
+ @Tool("Forecasts the weather for the given latitude and longitude")
+ @ClientQueryParam(name = "forecast_days", value = "1")
+ @ClientQueryParam(name = "daily", value = {
+ "temperature_2m_max",
+ "temperature_2m_min",
+ "precipitation_sum",
+ "wind_speed_10m_max",
+ "weather_code"
+ })
+ WeatherForecast forecast(@RestQuery double latitude, @RestQuery double longitude);
+
+}
diff --git a/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WmoCode.java b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WmoCode.java
new file mode 100644
index 000000000..195b0bcb8
--- /dev/null
+++ b/samples/weather-agent/src/main/java/io/quarkiverse/langchain4j/weather/agent/weather/WmoCode.java
@@ -0,0 +1,31 @@
+package io.quarkiverse.langchain4j.weather.agent.weather;
+
+import java.util.Arrays;
+
+public enum WmoCode {
+
+ CLEAR_SKY(0), MAINLY_CLEAR(1), PARTLY_CLOUDY(2), OVERCAST(3),
+ FOG(45), DEPOSITING_RIME_FOG(46), DRIZZLE_LIGHT(51), DRIZZLE_MEDIUM(53),
+ DRIZZLE_DENSE(55), FREEZING_DRIZZLE_LIGHT(56), FREEZING_DRIZZLE_DENSE(57),
+ RAIN_SLIGHT(61), RAIN_MODERATE(63), RAIN_HEAVY(65), FREEZING_RAIN_LIGHT(66), FREEZING_RAIN_HEAVY(67),
+ SNOW_FALL_SLIGHT(71), SNOW_FALL_MODERATE(73), SNOW_FALL_HEAVY(75), SNOW_GRAINS(77),
+ RAIN_SHOWERS_SLIGHT(80), RAIN_SHOWERS_MODERATE(81), RAIN_SHOWERS_VIOLENT(82),
+ SNOW_SHOWERS_SLIGHT(85), SNOW_SHOWERS_HEAVY(86), THUNDERSTORM(95),
+ THUNDERSTORM_SLIGHT_HAIL(96), THUNDERSTORM_HEAVY_HAIL(99);
+
+ final int code;
+
+ WmoCode(int code) {
+ this.code = code;
+ }
+
+ public static WmoCode translate(int code) {
+ WmoCode[] values = WmoCode.values();
+
+ return Arrays.stream(values)
+ .filter(wmoCode -> code == wmoCode.code)
+ .findFirst()
+ .orElse(null);
+ }
+
+}
diff --git a/samples/weather-agent/src/main/resources/application.properties b/samples/weather-agent/src/main/resources/application.properties
new file mode 100644
index 000000000..d25dc6921
--- /dev/null
+++ b/samples/weather-agent/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+quarkus.langchain4j.log-requests=true
+quarkus.langchain4j.log-responses=true
+
+quarkus.rest-client.geocoding.url=https://geocoding-api.open-meteo.com
+quarkus.rest-client.openmeteo.url=https://api.open-meteo.com