1
1
/*
2
- * Copyright 2023-2024 the original author or authors.
2
+ * Copyright 2023-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
21
21
import java .util .Map ;
22
22
import java .util .stream .Collectors ;
23
23
24
+ import org .springframework .lang .Nullable ;
24
25
import reactor .core .publisher .Flux ;
25
26
import reactor .core .publisher .Mono ;
26
27
import reactor .core .scheduler .Schedulers ;
49
50
* @author Christian Tzolov
50
51
* @author Timo Salm
51
52
* @author Ilayaperumal Gopinathan
53
+ * @author Thomas Vitale
52
54
* @since 1.0.0
53
55
*/
54
56
public class QuestionAnswerAdvisor implements CallAroundAdvisor , StreamAroundAdvisor {
@@ -57,7 +59,7 @@ public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdv
57
59
58
60
public static final String FILTER_EXPRESSION = "qa_filter_expression" ;
59
61
60
- private static final String DEFAULT_USER_TEXT_ADVISE = """
62
+ private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = new PromptTemplate ( """
61
63
62
64
Context information is below, surrounded by ---------------------
63
65
@@ -68,13 +70,13 @@ public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdv
68
70
Given the context and provided history information and not prior knowledge,
69
71
reply to the user comment. If the answer is not in the context, inform
70
72
the user that you can't answer the question.
71
- """ ;
73
+ """ ) ;
72
74
73
75
private static final int DEFAULT_ORDER = 0 ;
74
76
75
77
private final VectorStore vectorStore ;
76
78
77
- private final String userTextAdvise ;
79
+ private final PromptTemplate promptTemplate ;
78
80
79
81
private final SearchRequest searchRequest ;
80
82
@@ -88,7 +90,7 @@ public class QuestionAnswerAdvisor implements CallAroundAdvisor, StreamAroundAdv
88
90
* @param vectorStore The vector store to use
89
91
*/
90
92
public QuestionAnswerAdvisor (VectorStore vectorStore ) {
91
- this (vectorStore , SearchRequest .builder ().build (), DEFAULT_USER_TEXT_ADVISE );
93
+ this (vectorStore , SearchRequest .builder ().build (), DEFAULT_PROMPT_TEMPLATE , true , DEFAULT_ORDER );
92
94
}
93
95
94
96
/**
@@ -97,9 +99,11 @@ public QuestionAnswerAdvisor(VectorStore vectorStore) {
97
99
* @param vectorStore The vector store to use
98
100
* @param searchRequest The search request defined using the portable filter
99
101
* expression syntax
102
+ * @deprecated in favor of the builder: {@link #builder(VectorStore)}
100
103
*/
104
+ @ Deprecated
101
105
public QuestionAnswerAdvisor (VectorStore vectorStore , SearchRequest searchRequest ) {
102
- this (vectorStore , searchRequest , DEFAULT_USER_TEXT_ADVISE );
106
+ this (vectorStore , searchRequest , DEFAULT_PROMPT_TEMPLATE , true , DEFAULT_ORDER );
103
107
}
104
108
105
109
/**
@@ -110,9 +114,12 @@ public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchReques
110
114
* expression syntax
111
115
* @param userTextAdvise The user text to append to the existing user prompt. The text
112
116
* should contain a placeholder named "question_answer_context".
117
+ * @deprecated in favor of the builder: {@link #builder(VectorStore)}
113
118
*/
119
+ @ Deprecated
114
120
public QuestionAnswerAdvisor (VectorStore vectorStore , SearchRequest searchRequest , String userTextAdvise ) {
115
- this (vectorStore , searchRequest , userTextAdvise , true );
121
+ this (vectorStore , searchRequest , PromptTemplate .builder ().template (userTextAdvise ).build (), true ,
122
+ DEFAULT_ORDER );
116
123
}
117
124
118
125
/**
@@ -127,10 +134,13 @@ public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchReques
127
134
* blocking threads. If false the advisor will not protect the execution from blocking
128
135
* threads. This is useful when the advisor is used in a non-blocking environment. It
129
136
* is true by default.
137
+ * @deprecated in favor of the builder: {@link #builder(VectorStore)}
130
138
*/
139
+ @ Deprecated
131
140
public QuestionAnswerAdvisor (VectorStore vectorStore , SearchRequest searchRequest , String userTextAdvise ,
132
141
boolean protectFromBlocking ) {
133
- this (vectorStore , searchRequest , userTextAdvise , protectFromBlocking , DEFAULT_ORDER );
142
+ this (vectorStore , searchRequest , PromptTemplate .builder ().template (userTextAdvise ).build (), protectFromBlocking ,
143
+ DEFAULT_ORDER );
134
144
}
135
145
136
146
/**
@@ -146,17 +156,23 @@ public QuestionAnswerAdvisor(VectorStore vectorStore, SearchRequest searchReques
146
156
* threads. This is useful when the advisor is used in a non-blocking environment. It
147
157
* is true by default.
148
158
* @param order The order of the advisor.
159
+ * @deprecated in favor of the builder: {@link #builder(VectorStore)}
149
160
*/
161
+ @ Deprecated
150
162
public QuestionAnswerAdvisor (VectorStore vectorStore , SearchRequest searchRequest , String userTextAdvise ,
151
163
boolean protectFromBlocking , int order ) {
164
+ this (vectorStore , searchRequest , PromptTemplate .builder ().template (userTextAdvise ).build (), protectFromBlocking ,
165
+ order );
166
+ }
152
167
153
- Assert .notNull (vectorStore , "The vectorStore must not be null!" );
154
- Assert .notNull (searchRequest , "The searchRequest must not be null!" );
155
- Assert .hasText (userTextAdvise , "The userTextAdvise must not be empty!" );
168
+ QuestionAnswerAdvisor (VectorStore vectorStore , SearchRequest searchRequest , @ Nullable PromptTemplate promptTemplate ,
169
+ boolean protectFromBlocking , int order ) {
170
+ Assert .notNull (vectorStore , "vectorStore cannot be null" );
171
+ Assert .notNull (searchRequest , "searchRequest cannot be null" );
156
172
157
173
this .vectorStore = vectorStore ;
158
174
this .searchRequest = searchRequest ;
159
- this .userTextAdvise = userTextAdvise ;
175
+ this .promptTemplate = promptTemplate != null ? promptTemplate : DEFAULT_PROMPT_TEMPLATE ;
160
176
this .protectFromBlocking = protectFromBlocking ;
161
177
this .order = order ;
162
178
}
@@ -212,10 +228,7 @@ private AdvisedRequest before(AdvisedRequest request) {
212
228
213
229
var context = new HashMap <>(request .adviseContext ());
214
230
215
- // 1. Advise the system text.
216
- String advisedUserText = request .userText () + System .lineSeparator () + this .userTextAdvise ;
217
-
218
- // 2. Search for similar documents in the vector store.
231
+ // 1. Search for similar documents in the vector store.
219
232
String query = new PromptTemplate (request .userText (), request .userParams ()).render ();
220
233
var searchRequestToUse = SearchRequest .from (this .searchRequest )
221
234
.query (query )
@@ -224,20 +237,21 @@ private AdvisedRequest before(AdvisedRequest request) {
224
237
225
238
List <Document > documents = this .vectorStore .similaritySearch (searchRequestToUse );
226
239
227
- // 3 . Create the context from the documents.
240
+ // 2 . Create the context from the documents.
228
241
context .put (RETRIEVED_DOCUMENTS , documents );
229
242
230
243
String documentContext = documents .stream ()
231
244
.map (Document ::getText )
232
245
.collect (Collectors .joining (System .lineSeparator ()));
233
246
234
- // 4. Advise the user parameters.
235
- Map <String , Object > advisedUserParams = new HashMap <>(request .userParams ());
236
- advisedUserParams .put ("question_answer_context" , documentContext );
247
+ // 3. Augment the user prompt with the document context.
248
+ String augmentedUserText = this .promptTemplate .mutate ()
249
+ .template (request .userText () + System .lineSeparator () + this .promptTemplate .getTemplate ())
250
+ .build ()
251
+ .render (Map .of ("question_answer_context" , documentContext ));
237
252
238
253
AdvisedRequest advisedRequest = AdvisedRequest .from (request )
239
- .userText (advisedUserText )
240
- .userParams (advisedUserParams )
254
+ .userText (augmentedUserText )
241
255
.adviseContext (context )
242
256
.build ();
243
257
@@ -266,7 +280,7 @@ public static final class Builder {
266
280
267
281
private SearchRequest searchRequest = SearchRequest .builder ().build ();
268
282
269
- private String userTextAdvise = DEFAULT_USER_TEXT_ADVISE ;
283
+ private PromptTemplate promptTemplate ;
270
284
271
285
private boolean protectFromBlocking = true ;
272
286
@@ -283,9 +297,15 @@ public Builder searchRequest(SearchRequest searchRequest) {
283
297
return this ;
284
298
}
285
299
300
+ public Builder promptTemplate (PromptTemplate promptTemplate ) {
301
+ Assert .notNull (promptTemplate , "promptTemplate cannot be null" );
302
+ this .promptTemplate = promptTemplate ;
303
+ return this ;
304
+ }
305
+
286
306
public Builder userTextAdvise (String userTextAdvise ) {
287
307
Assert .hasText (userTextAdvise , "The userTextAdvise must not be empty!" );
288
- this .userTextAdvise = userTextAdvise ;
308
+ this .promptTemplate = PromptTemplate . builder (). template ( userTextAdvise ). build () ;
289
309
return this ;
290
310
}
291
311
@@ -300,7 +320,7 @@ public Builder order(int order) {
300
320
}
301
321
302
322
public QuestionAnswerAdvisor build () {
303
- return new QuestionAnswerAdvisor (this .vectorStore , this .searchRequest , this .userTextAdvise ,
323
+ return new QuestionAnswerAdvisor (this .vectorStore , this .searchRequest , this .promptTemplate ,
304
324
this .protectFromBlocking , this .order );
305
325
}
306
326
0 commit comments