Add Prompt Egineering examples
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -41,6 +41,8 @@
|
||||
<module>model-context-protocol/web-search/brave-chatbot</module>
|
||||
<module>model-context-protocol/sampling/mcp-weather-webmvc-server</module>
|
||||
<module>model-context-protocol/sampling/mcp-sampling-client</module>
|
||||
|
||||
<module>prompt-engineering/prompt-engineering-patterns</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
||||
33
prompt-engineering/prompt-engineering-patterns/.gitignore
vendored
Normal file
33
prompt-engineering/prompt-engineering-patterns/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
19
prompt-engineering/prompt-engineering-patterns/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
19
prompt-engineering/prompt-engineering-patterns/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
23
prompt-engineering/prompt-engineering-patterns/README.md
Normal file
23
prompt-engineering/prompt-engineering-patterns/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Prompt Engineering with Spring AI
|
||||
|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
This repository contains practical implementations of Prompt Engineering techniques using [Spring AI](https://docs.spring.io/spring-ai/reference/index.html). These examples accompany the blog post: [Spring AI Prompt Engineering Patterns](https://spring.io/blog/2025/04/14/spring-ai-prompt-engineering-patterns).
|
||||
|
||||
The examples and patterns are based on the comprehensive [Prompt Engineering Guide](https://www.kaggle.com/whitepaper-prompt-engineering) that covers the theory, principles, and patterns of effective prompt engineering.
|
||||
|
||||
Here we shows how to translate those concepts into working Java code using Spring AI's fluent [ChatClient API](https://docs.spring.io/spring-ai/reference/api/chatclient.html).
|
||||
|
||||
|
||||
#### Structure
|
||||
|
||||
The examples follow the same structure and patterns outlined in the original Goolge Prompt Egineering guide, making it easy to reference between theory and implementation.
|
||||
|
||||
## Resources
|
||||
|
||||
- [Spring AI Documentation](https://docs.spring.io/spring-ai/reference/index.html)
|
||||
- [ChatClient API Reference](https://docs.spring.io/spring-ai/reference/api/chatclient.html)
|
||||
- [Google's Prompt Engineering Guide](https://www.kaggle.com/whitepaper-prompt-engineering)
|
||||
- [Spring AI Prompt Engineering Patterns Blog](https://spring.io/blog/2025/04/14/spring-ai-prompt-engineering-patterns)
|
||||
259
prompt-engineering/prompt-engineering-patterns/mvnw
vendored
Executable file
259
prompt-engineering/prompt-engineering-patterns/mvnw
vendored
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
149
prompt-engineering/prompt-engineering-patterns/mvnw.cmd
vendored
Normal file
149
prompt-engineering/prompt-engineering-patterns/mvnw.cmd
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.2
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
|
||||
}
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
55
prompt-engineering/prompt-engineering-patterns/pom.xml
Normal file
55
prompt-engineering/prompt-engineering-patterns/pom.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.4</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>org.springframework.ai.example</groupId>
|
||||
<artifactId>prompt-engineering-patterns</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>prompt-engineering-patterns</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring-ai.version>1.0.0-M7</spring-ai.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-anthropic</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-bom</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,821 @@
|
||||
package org.springframework.ai.example.prompt_engineering;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.ai.chat.client.ChatClient;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class PromptEngineeringApplication {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PromptEngineeringApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner commandLineRunner(ChatClient.Builder chatClientBuilder) {
|
||||
return args -> {
|
||||
|
||||
ChatClient chatClient = chatClientBuilder.build();
|
||||
|
||||
//@formatter:off
|
||||
// 2. Prompting techniques (page 13)
|
||||
|
||||
// 2.1 General prompting / zero shot (page 15)
|
||||
pt_zero_shot(chatClient);
|
||||
|
||||
// 2.2 One-shot & few-shot
|
||||
pt_ones_shot_few_shots(chatClient);
|
||||
|
||||
// 2.3 System, contextual and role prompting
|
||||
|
||||
// 2.3.1 System prompting
|
||||
pt_system_prompting_1(chatClient);
|
||||
pt_system_prompting_2(chatClient);
|
||||
pt_system_prompting_2_springai_style(chatClient);
|
||||
|
||||
// 2.3.2 Role prompting
|
||||
pt_role_prompting_1(chatClient);
|
||||
pt_role_prompting_2(chatClient); // in a humorous style.
|
||||
|
||||
// 2.3.3 Contextual prompting
|
||||
pt_contextual_prompting(chatClient);
|
||||
|
||||
// 2.4 Step-back prompting
|
||||
pt_step_back_prompting(chatClient.mutate());
|
||||
|
||||
// 2.5 Chain of Thought (CoT)
|
||||
pt_chain_of_thought_zero_shot(chatClient);
|
||||
pt_chain_of_thought_singleshot_fewshots(chatClient);
|
||||
|
||||
// 2.6 Self-consistency
|
||||
pt_self_consistency(chatClient);
|
||||
|
||||
// 2.7 Tree of Thoughts (ToT)
|
||||
pt_tree_of_thoughts_game(chatClient);
|
||||
pt_tree_of_thoughts_problem(chatClient);
|
||||
|
||||
|
||||
// 2.8 Automatic Prompt Engineering
|
||||
pt_automatic_prompt_engineering(chatClient);
|
||||
|
||||
// 2.9 Code prompting
|
||||
|
||||
// 2.9.1 Prompts for writing code
|
||||
pt_code_prompting_writing_code(chatClient);
|
||||
|
||||
// 2.9.2 Prompts for explaining code
|
||||
pt_code_prompting_explaining_code(chatClient);
|
||||
|
||||
// 2.9.3 Prompts for translating code
|
||||
pt_code_prompting_translating_code(chatClient);
|
||||
//@formatter:on
|
||||
};
|
||||
}
|
||||
|
||||
// 2.1 General prompting / zero shot (page 15)
|
||||
public void pt_zero_shot(ChatClient chatClient) {
|
||||
// General prompting / zero shot (page 15)
|
||||
|
||||
enum Sentiment {
|
||||
POSITIVE, NEUTRAL, NEGATIVE
|
||||
}
|
||||
|
||||
Sentiment reviewSentiment = chatClient.prompt("""
|
||||
Classify movie reviews as POSITIVE, NEUTRAL or NEGATIVE.
|
||||
Review: "Her" is a disturbing study revealing the direction
|
||||
humanity is headed if AI is allowed to keep evolving,
|
||||
unchecked. I wish there were more movies like this masterpiece.
|
||||
Sentiment:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(0.1)
|
||||
.maxTokens(5)
|
||||
.build())
|
||||
.call()
|
||||
.entity(Sentiment.class);
|
||||
|
||||
System.out.println("Output: " + reviewSentiment);
|
||||
}
|
||||
|
||||
// 2.2 One-shot & few-shot
|
||||
public void pt_ones_shot_few_shots(ChatClient chatClient) {
|
||||
|
||||
// One-shot & few-shot
|
||||
// When creating prompts for AI models, it is helpful to provide examples.
|
||||
// These examples can help the model understand what you are asking for.
|
||||
// - one-shot prompt: provides a single example to the model.
|
||||
// - few-shot prompt: provides multiple examples to the model.
|
||||
|
||||
// A few-shot prompt example, let's use the same gemini-pro model
|
||||
// configuration settings as before, other than increasing the token limit to
|
||||
// accommodate the
|
||||
// need for a longer response. (page 16)
|
||||
|
||||
String pizzaOrder = chatClient.prompt("""
|
||||
Parse a customer's pizza order into valid JSON
|
||||
|
||||
EXAMPLE 1:
|
||||
I want a small pizza with cheese, tomato sauce, and pepperoni.
|
||||
JSON Response:
|
||||
```
|
||||
{
|
||||
"size": "small",
|
||||
"type": "normal",
|
||||
"ingredients": ["cheese", "tomato sauce", "peperoni"]
|
||||
}
|
||||
```
|
||||
|
||||
EXAMPLE 2:
|
||||
Can I get a large pizza with tomato sauce, basil and mozzarella.
|
||||
JSON Response:
|
||||
```
|
||||
{
|
||||
"size": "large",
|
||||
"type": "normal",
|
||||
"ingredients": ["tomato sauce", "basil", "mozzarella"]
|
||||
}
|
||||
```
|
||||
|
||||
Now, I would like a large pizza, with the first half cheese and mozzarella.
|
||||
And the other tomato sauce, ham and pineapple.
|
||||
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(0.1)
|
||||
// Increasing the token limit to accommodate the need for a longer response.
|
||||
.maxTokens(250)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + pizzaOrder);
|
||||
|
||||
// NOTE: The number of examples you need for few-shot prompting depends on a few
|
||||
// factors,
|
||||
// including the complexity of the task, the quality of the examples, and the
|
||||
// capabilities of the
|
||||
// generative AI (gen AI) model you are using. As a general rule of thumb, you
|
||||
// should use at
|
||||
// least three to five examples for few-shot prompting.
|
||||
//
|
||||
// To generate output that is robust to a variety of inputs, it is important to
|
||||
// include edge
|
||||
// cases in your examples. Edge cases are inputs that are unusual or unexpected,
|
||||
// but that the model should still be able to handle.
|
||||
}
|
||||
|
||||
// Sets the overall context and purpose for the language model. It
|
||||
// defines the 'big picture' of what the model should be doing, like translating
|
||||
// a language, classifying a review etc.
|
||||
|
||||
// 2.3.1 System prompting (1)
|
||||
public void pt_system_prompting_1(ChatClient chatClient) {
|
||||
|
||||
String movieReview = chatClient
|
||||
.prompt()
|
||||
.system("Classify movie reviews as positive, neutral or negative. Only return the label in uppercase.")
|
||||
.user("""
|
||||
Review: "Her" is a disturbing study revealing the direction
|
||||
humanity is headed if AI is allowed to keep evolving,
|
||||
unchecked. It's so disturbing I couldn't watch it.
|
||||
|
||||
Sentiment:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(5)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + movieReview);
|
||||
|
||||
// We increased the temperature to get a higher creativity level, and I
|
||||
// specified a higher token limit. However, because of my clear instruction on
|
||||
// how to return the output the model didn't return extra text
|
||||
}
|
||||
|
||||
// You could use a system prompt to generate a code snippet that is compatible
|
||||
// with a specific programming language, or you could use a system prompt to
|
||||
// return a certain structure like output in JSON format.
|
||||
|
||||
// 2.3.1 System prompting (2)
|
||||
public void pt_system_prompting_2(ChatClient chatClient) {
|
||||
|
||||
String movieReview = chatClient
|
||||
.prompt()
|
||||
.system("""
|
||||
Classify movie reviews as positive, neutral or negative. Return
|
||||
valid JSON.
|
||||
|
||||
Use the Schema:
|
||||
|
||||
```
|
||||
MOVIE:
|
||||
{
|
||||
"sentiment": String "POSITIVE" | "NEGATIVE" | "NEUTRAL",
|
||||
"name": String
|
||||
}
|
||||
MOVIE REVIEWS:
|
||||
{
|
||||
"movie_reviews": [MOVIE]
|
||||
}
|
||||
```
|
||||
""")
|
||||
.user("""
|
||||
Review: "Her" is a disturbing study revealing the direction
|
||||
humanity is headed if AI is allowed to keep evolving,
|
||||
unchecked. It's so disturbing I couldn't watch it.
|
||||
|
||||
JSON Response:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + movieReview);
|
||||
|
||||
}
|
||||
|
||||
// 2.3.1 (3) spring-ai System prompting - SpringAI style
|
||||
public void pt_system_prompting_2_springai_style(ChatClient chatClient) throws JsonProcessingException {
|
||||
|
||||
record MovieReviews(Movie[] movie_reviews) {
|
||||
enum Sentiment {
|
||||
POSITIVE, NEUTRAL, NEGATIVE
|
||||
}
|
||||
|
||||
record Movie(Sentiment sentiment, String name) {
|
||||
}
|
||||
}
|
||||
|
||||
MovieReviews movieReviews = chatClient
|
||||
.prompt()
|
||||
.system("""
|
||||
Classify movie reviews as positive, neutral or negative. Return
|
||||
valid JSON.
|
||||
""")
|
||||
.user("""
|
||||
Review: "Her" is a disturbing study revealing the direction
|
||||
humanity is headed if AI is allowed to keep evolving,
|
||||
unchecked. It's so disturbing I couldn't watch it.
|
||||
|
||||
JSON Response:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.entity(MovieReviews.class);
|
||||
|
||||
System.out.println("Output: " + objectMapper.writeValueAsString(movieReviews));
|
||||
}
|
||||
|
||||
// 2.3.2 Role prompting (1)
|
||||
public void pt_role_prompting_1(ChatClient chatClient) {
|
||||
|
||||
// Goal Act as travel guide and provide 3 travel suggestions
|
||||
|
||||
String movieReview = chatClient
|
||||
.prompt()
|
||||
.system("""
|
||||
I want you to act as a travel guide. I will write to you
|
||||
about my location and you will suggest 3 places to visit near
|
||||
me. In some cases, I will also give you the type of places I
|
||||
will visit.
|
||||
""")
|
||||
.user("""
|
||||
My suggestion: "I am in Amsterdam and I want to visit only museums."
|
||||
Travel Suggestions:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + movieReview);
|
||||
|
||||
}
|
||||
|
||||
// 2.3.2 Role prompting (2)
|
||||
public void pt_role_prompting_2(ChatClient chatClient) {
|
||||
|
||||
// Goal Act as travel guide and provide 3 travel suggestions
|
||||
//
|
||||
// In a humorous style.
|
||||
|
||||
String movieReview = chatClient
|
||||
.prompt()
|
||||
.system("""
|
||||
I want you to act as a travel guide. I will write to you about
|
||||
my location and you will suggest 3 places to visit near me in
|
||||
a humorous style.
|
||||
""")
|
||||
.user("""
|
||||
My suggestion: "I am in Amsterdam and I want to visit only museums."
|
||||
Travel Suggestions:
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + movieReview);
|
||||
}
|
||||
|
||||
// 2.3.3 Contextual prompting
|
||||
public void pt_contextual_prompting(ChatClient chatClient) {
|
||||
|
||||
// Goal: Suggest articles for a blog about retro games
|
||||
|
||||
String movieReview = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Suggest 3 topics to write an article about with a few lines of
|
||||
description of what this article should contain.
|
||||
|
||||
Context: {context}
|
||||
""")
|
||||
.param("context", "You are writing for a blog about retro 80's arcade video games."))
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + movieReview);
|
||||
|
||||
}
|
||||
|
||||
// 2.4 Step-back prompting
|
||||
public void pt_step_back_prompting(ChatClient.Builder chatClientBuilder) {
|
||||
|
||||
// (SpringAI tip) Set common options for the chat client.
|
||||
var chatClient = chatClientBuilder
|
||||
.defaultOptions(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.8)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
// Goal: Write a storyline for a level of a first-person shooter video game.
|
||||
|
||||
String stepBack = chatClient
|
||||
.prompt("""
|
||||
Based on popular first-person shooter action games, what are
|
||||
5 fictional key settings that contribute to a challenging and
|
||||
engaging level storyline in a first-person shooter video game?
|
||||
""")
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("StepBack Output: " + stepBack + "\n");
|
||||
|
||||
String story = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Write a one paragraph storyline for a new level of a first-
|
||||
person shooter video game that is challenging and engaging.
|
||||
|
||||
Context: {step-back}
|
||||
""")
|
||||
.param("step-back", stepBack))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + story);
|
||||
|
||||
}
|
||||
|
||||
// 2.5 Chain of Thought (CoT)
|
||||
|
||||
// 'zero-shot' Chain of thought.
|
||||
public void pt_chain_of_thought_zero_shot(ChatClient chatClient) {
|
||||
|
||||
String output = chatClient
|
||||
.prompt("""
|
||||
When I was 3 years old, my partner was 3 times my age. Now,
|
||||
I am 20 years old. How old is my partner?
|
||||
|
||||
Let's think step by step.
|
||||
""")
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
public void pt_chain_of_thought_singleshot_fewshots(ChatClient chatClient) {
|
||||
|
||||
String output = chatClient
|
||||
.prompt("""
|
||||
Q: When my brother was 2 years old, I was double his age. Now
|
||||
I am 40 years old. How old is my brother? Let's think step
|
||||
by step.
|
||||
A: When my brother was 2 years, I was 2 * 2 = 4 years old.
|
||||
That's an age difference of 2 years and I am older. Now I am 40
|
||||
years old, so my brother is 40 - 2 = 38 years old. The answer
|
||||
is 38.
|
||||
Q: When I was 3 years old, my partner was 3 times my age. Now,
|
||||
I am 20 years old. How old is my partner? Let's think step
|
||||
by step.
|
||||
A:
|
||||
""")
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
// 2.6 Self-consistency
|
||||
public void pt_self_consistency(ChatClient chatClient) {
|
||||
|
||||
String email = """
|
||||
Hi,
|
||||
I have seen you use Wordpress for your website. A great open
|
||||
source content management system. I have used it in the past
|
||||
too. It comes with lots of great user plugins. And it's pretty
|
||||
easy to set up.
|
||||
I did notice a bug in the contact form, which happens when
|
||||
you select the name field. See the attached screenshot of me
|
||||
entering text in the name field. Notice the JavaScript alert
|
||||
box that I inv0k3d.
|
||||
But for the rest it's a great website. I enjoy reading it. Feel
|
||||
free to leave the bug in the website, because it gives me more
|
||||
interesting things to read.
|
||||
Cheers,
|
||||
Harry the Hacker.
|
||||
""";
|
||||
|
||||
record EmailClassification(Classification classification, String reasoning) {
|
||||
enum Classification {
|
||||
IMPORTANT, NOT_IMPORTANT
|
||||
}
|
||||
}
|
||||
|
||||
int importantCount = 0;
|
||||
int notImportantCount = 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
|
||||
EmailClassification output = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Email: {email}
|
||||
Classify the above email as IMPORTANT or NOT IMPORTANT. Let's
|
||||
think step by step and explain why.
|
||||
""")
|
||||
.param("email", email))
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-5-haiku-latest")
|
||||
.temperature(1.0)
|
||||
.topK(60)
|
||||
.topP(0.9)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.entity(EmailClassification.class);
|
||||
|
||||
if (output.classification() == EmailClassification.Classification.IMPORTANT) {
|
||||
importantCount++;
|
||||
} else {
|
||||
notImportantCount++;
|
||||
}
|
||||
|
||||
System.out
|
||||
.println("Classification: [" + output.classification() + "], Reason: " + output.reasoning() + "\n");
|
||||
}
|
||||
|
||||
if (importantCount > notImportantCount) {
|
||||
System.out.println("The email is IMPORTANT. Count: " + importantCount);
|
||||
} else {
|
||||
System.out.println("The email is NOT IMPORTANT. Count: " + notImportantCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 2.7 Tree of Thoughts (ToT)
|
||||
// Implementation of Section 2.7: Tree of Thoughts (ToT) - Game solving example
|
||||
public void pt_tree_of_thoughts_game(ChatClient chatClient) {
|
||||
// Step 1: Generate multiple initial moves
|
||||
String initialMoves = chatClient
|
||||
.prompt("""
|
||||
You are playing a game of chess. The board is in the starting position.
|
||||
Generate 3 different possible opening moves. For each move:
|
||||
1. Describe the move in algebraic notation
|
||||
2. Explain the strategic thinking behind this move
|
||||
3. Rate the move's strength from 1-10
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.temperature(0.7)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Initial Moves: " + initialMoves + "\n");
|
||||
|
||||
// Step 2: Evaluate and select the most promising move
|
||||
String bestMove = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Analyze these opening moves and select the strongest one:
|
||||
{moves}
|
||||
|
||||
Explain your reasoning step by step, considering:
|
||||
1. Position control
|
||||
2. Development potential
|
||||
3. Long-term strategic advantage
|
||||
|
||||
Then select the single best move.
|
||||
""").param("moves", initialMoves))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Best Move: " + bestMove + "\n");
|
||||
|
||||
// Step 3: Explore future game states from the best move
|
||||
String gameProjection = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Based on this selected opening move:
|
||||
{best_move}
|
||||
|
||||
Project the next 3 moves for both players. For each potential branch:
|
||||
1. Describe the move and counter-move
|
||||
2. Evaluate the resulting position
|
||||
3. Identify the most promising continuation
|
||||
|
||||
Finally, determine the most advantageous sequence of moves.
|
||||
""").param("best_move", bestMove))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Game Projection: " + gameProjection + "\n");
|
||||
}
|
||||
|
||||
// Implementation of Section 2.7: Tree of Thoughts (ToT) - Problem solving
|
||||
public void pt_tree_of_thoughts_problem(ChatClient chatClient) {
|
||||
String problem = "Design a system to recommend movies to users based on their viewing history.";
|
||||
|
||||
// Step 1: Generate multiple solution approaches
|
||||
String approaches = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Problem: {problem}
|
||||
|
||||
Generate 3 different approaches to solve this problem:
|
||||
1. A content-based filtering approach
|
||||
2. A collaborative filtering approach
|
||||
3. A hybrid approach
|
||||
|
||||
For each approach, describe:
|
||||
- The core algorithm/technique
|
||||
- Key data requirements
|
||||
- Potential advantages and limitations
|
||||
""").param("problem", problem))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Approaches: " + approaches + "\n");
|
||||
|
||||
// Step 2: Evaluate approaches and select the most promising
|
||||
String bestApproach = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Evaluate these solution approaches:
|
||||
{approaches}
|
||||
|
||||
Compare them based on:
|
||||
1. Scalability
|
||||
2. Accuracy
|
||||
3. Implementation complexity
|
||||
4. Cold-start handling
|
||||
|
||||
Select the most promising approach with detailed reasoning.
|
||||
""").param("approaches", approaches))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Best Approach: " + bestApproach + "\n");
|
||||
|
||||
// Step 3: Refine and elaborate on the selected approach
|
||||
String refinedSolution = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Based on the selected approach:
|
||||
{best_approach}
|
||||
|
||||
Develop a detailed implementation plan:
|
||||
1. System architecture
|
||||
2. Data processing pipeline
|
||||
3. Algorithm implementation details
|
||||
4. Evaluation methodology
|
||||
|
||||
Address any limitations identified earlier and propose solutions.
|
||||
""").param("best_approach", bestApproach))
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Refined Solution: " + refinedSolution + "\n");
|
||||
}
|
||||
// 2.8 Automatic Prompt Engineering
|
||||
|
||||
public void pt_automatic_prompt_engineering(ChatClient chatClient) {
|
||||
|
||||
String orderVariants = chatClient
|
||||
.prompt("""
|
||||
We have a band merchandise t-shirt webshop, and to train a
|
||||
chatbot we need various ways to order: "One Metallica t-shirt
|
||||
size S". Generate 10 variants, with the same semantics but keep
|
||||
the same meaning.
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.9)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Variants: " + orderVariants + "\n");
|
||||
|
||||
String output = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Please perform BLEU (Bilingual Evaluation Understudy) evaluation on the following variants:
|
||||
----
|
||||
{variants}
|
||||
----
|
||||
|
||||
Select the instruction candidate with the highest evaluation score.
|
||||
""").param("variants", orderVariants))
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(1.0)
|
||||
.topK(40)
|
||||
.topP(0.9)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
// 2.9.1 Prompts for writing code
|
||||
public void pt_code_prompting_writing_code(ChatClient chatClient) {
|
||||
|
||||
// Goal: Write a prompt to write code in Bash to rename files in a folder.
|
||||
|
||||
String output = chatClient
|
||||
.prompt("""
|
||||
Write a code snippet in Bash, which asks for a folder name.
|
||||
Then it takes the contents of the folder and renames all the
|
||||
files inside by prepending the name draft to the file name.
|
||||
""")
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(0.1)
|
||||
.topP(1.0)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
// 2.9.2 Prompts for explaining code
|
||||
public void pt_code_prompting_explaining_code(ChatClient chatClient) {
|
||||
|
||||
// Goal: Write a prompt to explain Bash code.
|
||||
|
||||
String code = """
|
||||
#!/bin/bash
|
||||
echo "Enter the folder name: "
|
||||
read folder_name
|
||||
if [ ! -d "$folder_name" ]; then
|
||||
echo "Folder does not exist."
|
||||
exit 1
|
||||
fi
|
||||
files=( "$folder_name"/* )
|
||||
for file in "${files[@]}"; do
|
||||
new_file_name="draft_$(basename "$file")"
|
||||
mv "$file" "$new_file_name"
|
||||
done
|
||||
echo "Files renamed successfully."
|
||||
""";
|
||||
|
||||
String output = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Explain to me the below Bash code:
|
||||
```
|
||||
{code}
|
||||
```
|
||||
""").param("code", code))
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(0.1)
|
||||
.topP(1.0)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
// 2.9.3 Prompts for translating code
|
||||
public void pt_code_prompting_translating_code(ChatClient chatClient) {
|
||||
|
||||
// Goal: Write a prompt to translate Bash code to Python
|
||||
|
||||
String bashCode = """
|
||||
```bash
|
||||
#!/bin/bash
|
||||
echo "Enter the folder name: "
|
||||
read folder_name
|
||||
if [ ! -d "$folder_name" ]; then
|
||||
echo "Folder does not exist."
|
||||
exit 1
|
||||
fi
|
||||
files=( "$folder_name"/* )
|
||||
for file in "${files[@]}"; do
|
||||
new_file_name="draft_$(basename "$file")"
|
||||
mv "$file" "$new_file_name"
|
||||
done
|
||||
echo "Files renamed successfully."
|
||||
```
|
||||
""";
|
||||
|
||||
String output = chatClient
|
||||
.prompt()
|
||||
.user(u -> u.text("""
|
||||
Translate the below Bash code to a Python snippet:
|
||||
{code}
|
||||
""").param("code", bashCode))
|
||||
.options(ChatOptions.builder()
|
||||
.model("claude-3-7-sonnet-latest")
|
||||
.temperature(0.1)
|
||||
.topP(1.0)
|
||||
.maxTokens(1024)
|
||||
.build())
|
||||
.call()
|
||||
.content();
|
||||
|
||||
System.out.println("Output: " + output + "\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
spring.application.name=prompt-engineering
|
||||
spring.main.web-application-type=none
|
||||
|
||||
# 1. First you start by choosing a model. E.g. Anthropic Claude, OpenAI, etc.
|
||||
|
||||
# 2. Once you choose your model you will need to figure out the model configuration.
|
||||
# Most LLMs come with various configuration options that control the LLM’s output.
|
||||
|
||||
# 2.1 select the provider's specifc model and set the connection and access options
|
||||
spring.ai.anthropic.chat.options.model=claude-3-7-sonnet-latest
|
||||
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
|
||||
#spring.ai.openai.api-key=${OPENAI_API_KEY}
|
||||
|
||||
# 2.2 Output length controls the number of tokens to generate in a response.
|
||||
spring.ai.anthropic.chat.options.max-tokens=500
|
||||
|
||||
# 2.3 Sampling controls the randomness of the model’s output.
|
||||
|
||||
# 2.3.1 Temperature
|
||||
# Temperature controls the degree of randomness in token selection.
|
||||
# Lower temperatures are good for prompts that expect a more deterministic response,
|
||||
# while higher temperatures can lead to more diverse or unexpected results.
|
||||
spring.ai.anthropic.chat.options.temperature=0.7
|
||||
|
||||
# 2.3.2 Top-K and top-P
|
||||
# The best way to choose between top-K and top-P is to experiment with both methods
|
||||
# (or both together) and see which one produces the results you are looking for.
|
||||
|
||||
# Top-K
|
||||
# Selects the top K most likely tokens from the model’s predicted distribution.
|
||||
# The higher top-K, the more creative and varied the model’s output;
|
||||
# the lower top-K, the more restive and factual the model’s output.
|
||||
#spring.ai.anthropic.chat.options.top-k=
|
||||
|
||||
# Top-P
|
||||
# Selects the top tokens whose cumulative probability does not exceed a certain value (P).
|
||||
# Values for P range from 0 (greedy decoding) to 1 (all tokens in the LLM’s vocabulary).
|
||||
#spring.ai.anthropic.chat.options.top-p=1.0
|
||||
|
||||
# ...
|
||||
#spring.ai.anthropic.chat.options.stop-sequences=
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user