Skip to content

Commit feffe48

Browse files
authored
Merge pull request #1617 from cescoffier/few-shot-doc
Few-shots, and Image guides
2 parents 39a723f + 74e64dd commit feffe48

File tree

10 files changed

+438
-2
lines changed

10 files changed

+438
-2
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.quarkiverse.langchain4j.samples.fewshots;
2+
3+
import dev.langchain4j.service.SystemMessage;
4+
import dev.langchain4j.service.UserMessage;
5+
import io.quarkiverse.langchain4j.RegisterAiService;
6+
7+
@RegisterAiService
8+
@SystemMessage("You are an assistant that classifies text sentiment.")
9+
public interface SentimentAiService {
10+
11+
@UserMessage("""
12+
INPUT: This product is fantastic! // <1>
13+
OUTPUT: POSITIVE
14+
15+
INPUT: Terrible customer service.
16+
OUTPUT: NEGATIVE
17+
18+
INPUT: {text}
19+
OUTPUT:
20+
""")
21+
String classifySentiment(String text); // <2>
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package io.quarkiverse.langchain4j.samples.fewshots;
2+
3+
import jakarta.inject.Inject;
4+
import jakarta.ws.rs.GET;
5+
import jakarta.ws.rs.Path;
6+
import jakarta.ws.rs.Produces;
7+
import jakarta.ws.rs.QueryParam;
8+
import jakarta.ws.rs.core.MediaType;
9+
10+
@Path("/sentiment")
11+
@Produces(MediaType.TEXT_PLAIN)
12+
public class SentimentResource {
13+
@Inject
14+
SentimentAiService sentimentService;
15+
16+
@GET
17+
public String analyze(@QueryParam("text") String text) {
18+
return sentimentService.classifySentiment(text);
19+
}
20+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// tag::head[]
2+
package io.quarkiverse.langchain4j.samples.images;
3+
4+
import java.io.IOException;
5+
import java.nio.file.Files;
6+
import java.util.Base64;
7+
8+
import jakarta.inject.Inject;
9+
import jakarta.ws.rs.GET;
10+
import jakarta.ws.rs.Path;
11+
import jakarta.ws.rs.QueryParam;
12+
13+
import dev.langchain4j.data.image.Image;
14+
15+
@Path("/endpoint")
16+
public class Endpoint {
17+
18+
@Inject
19+
ImageAiService imageAiService;
20+
21+
// end::head[]
22+
// tag::url[]
23+
@GET
24+
@Path("/extract-menu")
25+
public String fromUrl(@QueryParam("u") String url) { // <1>
26+
return imageAiService.extractMenu(url);
27+
}
28+
// end::url[]
29+
30+
// tag::ocr[]
31+
@GET
32+
@Path("/ocr-process")
33+
public String passingImage() throws IOException {
34+
byte[] bytes = Files.readAllBytes(java.nio.file.Path.of("IMG_3283.jpg"));
35+
String b64 = Base64.getEncoder().encodeToString(bytes);
36+
Image img = Image.builder()
37+
.base64Data(b64)
38+
.mimeType("image/jpeg")
39+
.build();
40+
41+
return imageAiService.extractReceiptData(img);
42+
}
43+
// end::ocr[]
44+
45+
// tag::head[]
46+
}
47+
// end::head[]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.quarkiverse.langchain4j.samples.images;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
6+
import jakarta.inject.Inject;
7+
import jakarta.ws.rs.GET;
8+
import jakarta.ws.rs.Path;
9+
import jakarta.ws.rs.Produces;
10+
11+
@Path("/endpoint")
12+
public class EndpointGeneration {
13+
14+
@Inject
15+
ImageGenerationAiService imageGenerationAiService;
16+
17+
@GET
18+
@Path("/generate-image")
19+
@Produces("image/png")
20+
public byte[] generateImage() {
21+
var image = imageGenerationAiService.generateImage("a rabbit in a space suit");
22+
return readBytes(image.url());
23+
}
24+
25+
private byte[] readBytes(URI url) {
26+
try (var is = url.toURL().openStream()) {
27+
return is.readAllBytes();
28+
} catch (IOException e) {
29+
throw new RuntimeException("Failed to read image from URL: " + url, e);
30+
}
31+
}
32+
33+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// tag::head[]
2+
package io.quarkiverse.langchain4j.samples.images;
3+
4+
import dev.langchain4j.data.image.Image;
5+
import dev.langchain4j.service.SystemMessage;
6+
import dev.langchain4j.service.UserMessage;
7+
import io.quarkiverse.langchain4j.ImageUrl;
8+
import io.quarkiverse.langchain4j.RegisterAiService;
9+
10+
@RegisterAiService
11+
@SystemMessage("Extract and summarize text from the provided image.")
12+
public interface ImageAiService {
13+
// end::head[]
14+
15+
// tag::url[]
16+
@UserMessage("""
17+
Here is a menu image.
18+
Extract the list of items.
19+
""")
20+
String extractMenu(@ImageUrl String imageUrl); // <1>
21+
// end::url[]
22+
23+
// tag::ocr[]
24+
@UserMessage("""
25+
Extract the content of this receipt.
26+
Identify the vendor, date, location and paid amount and currency (euros or USD).
27+
Make sure the paid amount includes VAT.
28+
For each information, add the line from the receipt where you found it.
29+
""")
30+
String extractReceiptData(Image image); // <1>
31+
// end::ocr[]
32+
// tag::head[]
33+
}
34+
// end::head[]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// tag::head[]
2+
package io.quarkiverse.langchain4j.samples.images;
3+
4+
import dev.langchain4j.data.image.Image;
5+
import dev.langchain4j.service.SystemMessage;
6+
import dev.langchain4j.service.UserMessage;
7+
import io.quarkiverse.langchain4j.RegisterAiService;
8+
9+
@RegisterAiService
10+
@SystemMessage("You are an AI that generates images from text prompts.")
11+
public interface ImageGenerationAiService {
12+
// end::head[]
13+
14+
// tag::generation[]
15+
@UserMessage("Generate an image of a {subject}.")
16+
Image generateImage(String subject);
17+
// end::generation[]
18+
19+
// tag::head[]
20+
}
21+
// end::head[]

docs/modules/ROOT/nav.adoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,23 @@
1111
[.list-top-item]
1212
.Guides
1313

14+
* xref:guide-few-shots.adoc[Using few-shots in prompts]
1415
* xref:guide-prompt-engineering.adoc[Prompt engineering patterns]
1516
// * xref:guide-ai-services-patterns.adoc[AI Services patterns]
1617
* xref:guide-fault-tolerance.adoc[Fault Tolerance]
1718
* xref:guide-csv.adoc[Index CSVs in a RAG pipeline]
1819
* xref:guide-web-search.adoc[Using Tavily Web Search]
20+
* xref:guide-passing-image.adoc[Passing Images to Models]
21+
* xref:guide-generating-image.adoc[Generating Images]
1922
// * xref:guide-agentic-patterns.adoc[Implementing Agentic patterns]
2023
// * xref:guide-structured-output.adoc[Returning structured data from a model]
2124
// * xref:guide-streamed-responses.adoc[Using function calling]
2225
// * xref:guide-log.adoc[Logging Model Interactions]
2326
// * xref:guide-token.adoc[Tracking token usages]
24-
// * xref:guide-few-shots.adoc[Using few-shots in prompts]
27+
2528
// * xref:guide-local-models.adoc[Using local models]
2629
// * xref:guide-in-process-models.adoc[Using in-process models]
27-
// * xref:guide-passing-images.adoc[Passing Images to Models]
30+
2831
// * xref:guide-generating-images.adoc[Generating Images from Prompts]
2932
// Add evaluation and guardrails and testing guides
3033
// Give knowledge to AI models
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
= Implementing Few-Shot Prompting
2+
3+
include::./includes/attributes.adoc[]
4+
include::./includes/customization.adoc[]
5+
6+
Few-shot prompting lets you guide an LLM by embedding a handful of input–output examples directly in the prompt—no fine-tuning required.
7+
It strikes a balance between zero-shot and one-shot prompting, boosting accuracy on tasks like classification, extraction, and summarization while keeping your code simple.
8+
9+
This guide walks you through three implementation steps to build a sentiment-classification microservice using Quarkus LangChain4j.
10+
11+
== What is the Few-Shot Technique
12+
13+
Few-shot prompting is an in-context learning technique where you provide two or more _input–output demonstrations_ in the same prompt to establish a pattern the model can generalize.
14+
This approach:
15+
16+
* Improves accuracy over zero- and one-shot prompting for complex tasks.
17+
* Avoids the overhead of model fine-tuning or external retrieval.
18+
* Is ideal for tasks requiring a consistent output format or style.
19+
20+
However, this technique is not intended to be used to add new knowledge to the model, but rather to guide its output style and format.
21+
22+
== Step 1. Define the AI service
23+
24+
Declare your AI Service interface with CDI and LangChain4j annotations so Quarkus can manage it:
25+
In this example, we will create a service that classifies text sentiment as either positive or negative.
26+
27+
[source,java]
28+
----
29+
package io.quarkiverse.langchain4j.samples.fewshots;
30+
31+
import dev.langchain4j.service.SystemMessage;
32+
import dev.langchain4j.service.UserMessage;
33+
import io.quarkiverse.langchain4j.RegisterAiService;
34+
35+
36+
@RegisterAiService
37+
@SystemMessage("You are an assistant that classifies text sentiment.")
38+
public interface SentimentAiService {
39+
// few-shot method defined in Step 2
40+
}
41+
----
42+
43+
Here, `@RegisterAiService` creates the xref:ai-services.adoc[AI Service], and `@SystemMessage` supplies the global instruction for all methods in the service.
44+
45+
== Step 2. Add Few-Shots
46+
47+
Inside the same interface, define a method with `@UserMessage` that inlines your examples:
48+
49+
[source,java]
50+
----
51+
include::{examples-dir}/io/quarkiverse/langchain4j/samples/fewshots/SentimentAiService.java[]
52+
----
53+
54+
<1> The multi-line string contains two complete "INPUT: … OUTPUT: …" pairs followed by the placeholder `\{text}` for the new query.
55+
<2> When the method is invoked, Quarkus Langchain4j will replace `\{text}` with the actual text parameter.
56+
57+
[TIP]
58+
While in this example we added the few-shot example in the user message, they can also be added in the system message.
59+
60+
== Step 3. Invoke the AI Service from an HTTP Endpoint
61+
62+
Finally, expose a REST resource that injects and calls your AI Service:
63+
64+
[source,java]
65+
----
66+
include::{examples-dir}/io/quarkiverse/langchain4j/samples/fewshots/SentimentResource.java[]
67+
----
68+
69+
This `SentimentResource` bean handles HTTP GET requests, delegates to the few-shot prompt method, and returns the classification.
70+
The endpoint can be tested with a simple HTTP client or browser by accessing:
71+
72+
[source,curl]
73+
----
74+
curl http://localhost:8080/sentiment?text=I%20love%20this%20product!
75+
----
76+
77+
== Conclusion
78+
79+
By defining an AI service, embedding a few-shot prompt, and wiring it to a REST endpoint, you’ve built a complete sentiment-classification microservice with minimal code.
80+
Find more details about the few-shot technique in the xref:guide-prompt-engineering.adoc#few-shot[Prompt Engineering Guide].
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
= Generating Images
2+
3+
include::./includes/attributes.adoc[]
4+
include::./includes/customization.adoc[]
5+
6+
Text completions are powerful, but many modern AI use cases also demand image generation.
7+
With Quarkus LangChain4j’s Image model support, you can declare a service method that returns a `dev.langchain4j.data.image.Image` instance, letting your microservice request, receive, and expose AI‐generated visuals seamlessly.
8+
9+
== Prerequisites
10+
11+
* A Quarkus project with the `quarkus-langchain4j-openai` extension on the classpath (or another model provider that supports image models)
12+
* OpenAI (or another provider) API Key configured in `application.properties` or via `QUARKUS_LANGCHAIN4J_OPENAI_API_KEY`
13+
* An **Image Model** enabled (e.g. `dall-e-3` or `GPT-Image-1`) in your config:
14+
15+
[source,properties]
16+
----
17+
quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY}
18+
quarkus.langchain4j.openai.image-model.model-name=dall-e-3 # This is the default model, but you can specify another one if needed
19+
----
20+
21+
* Extended timeout for image generation, which can take longer than text completions. For example:
22+
23+
[source,properties]
24+
----
25+
quarkus.langchain4j.openai.timeout=60s
26+
----
27+
28+
== Step 1. Define the AI service
29+
30+
Declare an AI Service interface to encapsulate image generation calls:
31+
32+
[source,java]
33+
----
34+
include::{examples-dir}/io/quarkiverse/langchain4j/samples/images/ImageGenerationAiService.java[tags=head]
35+
----
36+
37+
Here, `@RegisterAiService` creates the xref:ai-services.adoc[AI Service], and `@SystemMessage` supplies the global instruction for all methods in the service.
38+
39+
== Step 2. Define the Generation Method
40+
41+
Add a method annotated with `@UserMessage` that returns Image.
42+
Quarkus LangChain4j will call the configured image model and wrap the response in an `Image` object.
43+
44+
[source,java]
45+
----
46+
include::{examples-dir}/io/quarkiverse/langchain4j/samples/images/ImageGenerationAiService.java[tags=head;generation]
47+
----
48+
49+
When invoked, Quarkus will substitute the subject into the prompt, send it to the AI model provider, and return the generated image (URL or Base64).
50+
51+
NOTE: When using OpenAI image model, the returned `Image` object contains a URL to the generated image, which you can use to display or download the image.
52+
53+
54+
== Step 3. Expose an HTTP Endpoint
55+
56+
Finally, expose a REST resource that injects and calls your AI Service:
57+
58+
Use the `Image` data type for local or in-memory images:
59+
60+
[source,java]
61+
----
62+
include::{examples-dir}/io/quarkiverse/langchain4j/samples/images/EndpointGeneration.java[]
63+
----
64+
65+
== Conclusion
66+
67+
You’ve now seen how to:
68+
1. Register an image‐generation AI service (`@RegisterAiService`).
69+
2. Declare a method returning `Image` with `@UserMessage`.
70+
3. Expose a REST endpoint that returns the generated image back to clients.
71+
72+
With this pattern, you can build rich, AI‐driven visual applications—everything from dynamic marketing graphics to in‐app illustration.
73+

0 commit comments

Comments
 (0)