Developers working with LangChain4j often define prompt templates using annotations like @UserMessage and @SystemMessage. To increase reusability and personalization of prompts, especially for system-level instructions, LangChain4j provides the @V annotation to inject runtime method arguments directly into these templates. This post explains how to make your AI service prompts dynamic and maintainable using the @V annotation.
What is @V?
The @V annotation allows you to inject method parameters into prompt templates. These templates can be defined via:
· @UserMessage
· @SystemMessage
· AiServices.systemMessageProvider(Function)
Using @V, you can make your prompts dynamic and context-aware without hardcoding values.
Example
public interface StoryTeller { @SystemMessage("You are a masterful storyteller, renowned for your wisdom and creativity—much like {{authorLike}}.") String chat(@UserMessage String userMessage, @V("authorLike") String authorLike); }
Here,
· The @SystemMessage uses a template with {{authorLike}}.
· The method parameter authorLike is annotated with @V("authorLike"), allowing it to be substituted dynamically.
· When chat("Tell me a story about courage", "J.K. Rowling") is called, the system message becomes:
o "You are a masterful storyteller, renowned for your wisdom and creativity—much like J.K. Rowling."
Advantages
· Personalized AI behavior: Customize system behavior to each interaction.
· Maintainability: Centralize and reuse templates.
· Cleaner code: Avoid string concatenation and hardcoded prompt text.
Follow below step-by-step procedure to build the working application.
Step 1: Create new maven project ‘parameterize-system-messages’.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sample.app</groupId> <artifactId>parameterize-system-messages</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <java.version>21</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.10</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-bom</artifactId> <version>1.0.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-ollama-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.6.0</version> </dependency> <!-- https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api --> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> <!-- Important --> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Step 3: Create a package com.sample.app.dto and define ChatResponse class.
ChatResponse.java
package com.sample.app.dto; public class ChatResponse { private String message; public ChatResponse() { } public ChatResponse(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
Step 4: Create com.sample.app.interfaces package and define StoryTeller interface.
StoryTeller.java
package com.sample.app.interfaces; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.V; public interface StoryTeller { @SystemMessage("You are a masterful storyteller, renowned for your wisdom and creativity—much like {{authorLike}}") String chat(@UserMessage String userMessage, @V("authorLike") String authorLike); }
Step 5: Create a package com.sample.app.config and define LangchainConfig and SwaggerConfig classes.
LangchainConfig.java
package com.sample.app.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import dev.langchain4j.http.client.spring.restclient.SpringRestClientBuilderFactory; import dev.langchain4j.model.ollama.OllamaChatModel; @Configuration public class LangchainConfig { @Bean public OllamaChatModel ollamaLanguageModel() { return OllamaChatModel.builder().baseUrl("http://localhost:11434").modelName("llama3.2") .httpClientBuilder(new SpringRestClientBuilderFactory().create()) // explicitly use Spring's HTTP client .build(); } }
SwaggerConfig.java
package com.sample.app.config; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; @Configuration @OpenAPIDefinition(info = @Info(title = "Chat service Application", version = "v1")) public class SwaggerConfig { }
Step 6: Create a package com.sample.app.service and define ChatService class.
ChatService.java
package com.sample.app.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.sample.app.dto.ChatResponse; import com.sample.app.interfaces.StoryTeller; import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.service.AiServices; @Service public class ChatService { @Autowired private OllamaChatModel chatModel; public ChatResponse chat(String userMessage, String authorLike) { StoryTeller storyTeller = AiServices.create(StoryTeller.class, chatModel); String story = storyTeller.chat(userMessage, authorLike); return new ChatResponse(story); } }
Step 7: Create com.sample.app.controller package and define ChatController class.
ChatController.java
package com.sample.app.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.sample.app.dto.ChatResponse; import com.sample.app.service.ChatService; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; @RestController @RequestMapping("/api/story-teller") @CrossOrigin("*") @Tag(name = "Chat Controller", description = "This section contains APIs related to Chat APIs Powered by Ollama") public class ChatController { @Autowired private ChatService chatService; @PostMapping public ChatResponse chat(@RequestBody @Valid ChatRequestBody chatRequestBody) { return chatService.chat(chatRequestBody.getMessage(), chatRequestBody.getAuthor()); } private static class ChatRequestBody { @NotEmpty private String message; @NotEmpty private String author; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } } }
Step 8: Define main application class in com.sample.app package.
App.java
package com.sample.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
Build the project
Navigate to the project root directory where pom.xml is located and execute following command to generate the artifact.
mvn clean install
Upon successful execution of the command, you can see lanhchain- parameterize-system-messages-0.0.1-SNAPSHOT.jar file in the target folder.
$ ls ./target/ classes maven-status generated-sources parameterize-system-messages-0.0.1-SNAPSHOT.jar generated-test-sources parameterize-system-messages-0.0.1-SNAPSHOT.jar.original maven-archiver test-classes
Run the Application
Execute following command to run the application.
java -jar ./target/parameterize-system-messages-0.0.1-SNAPSHOT.jar --server.port=2345
Open the url http://localhost:2345/swagger-ui/index.html in the browser, and execute the api /api/story-teller with below payload.
{ "message": "Tell me a story about Forest Animals", "author": "William Shakespeare" }
You will see response like below.
{ "message": "Gather 'round, good gentles, and heed my tale of Forest's Finest,\n\nIn days of yore, when sunbeams filtered through the trees,\nA realm of wonder waited, where creatures roam with ease.\nThe forest whispered secrets to those who'd listen deep,\nAnd I shall share a legend that within its heart doth creep.\n\n'Twas in this enchanted land, where ancient oaks did stand,\nThat lived a wise old badger, named Bramble by the hand.\nHis coat was gray as moss, his eyes like stars above,\nHe knew the language of the trees and the creatures' love.\n\nBramble spent his days exploring, from dawn till dusk's descent,\nAnd learned to live in harmony with nature's gentle bent.\nHe befriended a fox named Ruby, with fur as red as fire,\nTogether they'd roam, their footsteps quiet, like a heart's desire.\n\nOne autumn eve, when leaves did rustle and the wind did sigh,\nA young rabbit named Thumper stumbled upon a hidden glade, where sunbeams shone high.\nHe chanced upon an ancient beehive, half-hidden in the grass,\nWhere bees did dance and weave their honeycomb with golden pass.\n\nThumper's curiosity got the better of him, as he drew near,\nAnd Bramble, sensing trouble, rushed to calm his young peer.\nBut lo! The bees, in defense of their home, did swarm and sting,\nAnd Thumper, trembling with fear, thought all was lost, his heart would sing.\n\nRuby, quick-witted fox, did intervene with cunning guile,\nShe spoke the language of the bees, and calmed their angry smile.\nThe badger Bramble, wise in forest lore, did offer a clue,\nThat honey's sweetness lies within its core, and not just its hue.\n\nThumper learned that day, amidst his friends' gentle care,\nTo respect and appreciate the intricate web they share.\nFor in the forest's heart, where creatures roam and thrive,\nLies a balance of give-and-take, a harmony to survive.\n\nAnd so, dear friends, when next you venture into the woods,\nRemember Bramble, Ruby, Thumper, and the lessons they imbued.\nIn this realm of wonder, where trees do whisper low,\nListen closely, for the forest's secrets are waiting to bestow.\n\nFor in its ancient heart, a wisdom beats like a drum,\nA rhythm that echoes through each living being's hum.\nLet Bramble's story be your guide, on winding paths untrod,\nAnd may the lessons of the forest forever in your heart be stored." }
You can download the application from this link.
Previous Next Home
No comments:
Post a Comment