summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Peter Christen <mc@yacy.net>2025-12-06 19:21:57 +0100
committerMichael Peter Christen <mc@yacy.net>2025-12-06 19:21:57 +0100
commiteed704d23bc53e5d0359072e5e2134b469ae2eef (patch)
tree2518c10e9e478a4c04845db3b81442ee9dfca7cb
parented9ea238b1e7d4a89d77acea790b08b6f2b1ed49 (diff)
added RAG configuration page
-rw-r--r--defaults/yacy.init20
-rw-r--r--htroot/AILab.html2
-rw-r--r--htroot/RAGConfig_p.html61
-rw-r--r--htroot/env/templates/submenuAI.template1
-rw-r--r--htroot/yacychat.html2
-rw-r--r--source/net/yacy/htroot/AILab.java4
-rw-r--r--source/net/yacy/htroot/RAGConfig_p.java36
-rw-r--r--source/net/yacy/htroot/yacychat.java38
-rw-r--r--source/net/yacy/http/servlets/RAGProxyServlet.java24
9 files changed, 150 insertions, 38 deletions
diff --git a/defaults/yacy.init b/defaults/yacy.init
index d61ddafe8..306ab5969 100644
--- a/defaults/yacy.init
+++ b/defaults/yacy.init
@@ -1416,11 +1416,15 @@ crawler.userAgent.clienttimeout = 10000
# experiments with timeout requests
timeoutrequests = true
-# interface decorations
-decoration.audio = false
-decoration.grafics.linkstructure = true
-decoration.hostanalysis = false
-decoration.simpleheadernavbar = navbar-default
-
-# ai settings
-ai.production_models = {} \ No newline at end of file
+# interface decorations
+decoration.audio = false
+decoration.grafics.linkstructure = true
+decoration.hostanalysis = false
+decoration.simpleheadernavbar = navbar-default
+
+# ai settings
+ai.production_models = {}
+ai.system-prompt = You are a smart and helpful chatbot. If possible, use friendly emojies.
+ai.llm-system-prefix = \n\nYou may receive additional expert knowledge in the user prompt after a 'Additional Information' headline to enhance your knowledge. Use it only if applicable.
+ai.llm-user-prefix = \n\nAdditional Information:\n\nbelow you find a collection of texts that might be useful to generate a response. Do not discuss these documents, just use them to answer the question above.\n\n
+ai.llm-query-generator-prefix = Make a list of search words with low document frequency for the following prompt; use a JSON Array:
diff --git a/htroot/AILab.html b/htroot/AILab.html
index 09f007ba4..cbf330393 100644
--- a/htroot/AILab.html
+++ b/htroot/AILab.html
@@ -299,7 +299,7 @@
<img src="env/grafics/AILab_RAG.png" alt="RAG configuration" width="128" height="128" />
</div>
<div class="quest-actions">
- <a class="btn btn-info btn-sm" href="LLMSelection_p.html#productionModelsTable">Configure RAG roles</a>
+ <a class="btn btn-primary btn-sm" href="RAGConfig_p.html">Wire RAG prompts</a>
<a class="btn btn-info btn-sm" href="yacychat.html">Test in Chat</a><br />
<span class="quest-note">Set the search-query and qapairs columns to connect retrieval to your chat flow.</span>
</div>
diff --git a/htroot/RAGConfig_p.html b/htroot/RAGConfig_p.html
new file mode 100644
index 000000000..51317e78d
--- /dev/null
+++ b/htroot/RAGConfig_p.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>YaCy '#[clientname]#': Wire RAG Retrieval</title>
+ #%env/templates/metas.template%#
+ <style type="text/css">
+ .rag-card {
+ border: 1px solid #e6e9ef;
+ border-left: 6px solid #5bc0de;
+ padding: 18px;
+ margin-bottom: 16px;
+ background: #fff;
+ box-shadow: 0 3px 10px rgba(0,0,0,0.06);
+ }
+ .rag-card h3 {
+ margin-top: 0;
+ margin-bottom: 8px;
+ color: #0f2b46;
+ }
+ .rag-card p {
+ margin: 0 0 10px 0;
+ color: #3b4a5e;
+ }
+ .rag-textarea {
+ width: 100%;
+ resize: vertical;
+ }
+ </style>
+ </head>
+ <body id="IndexControl">
+ #%env/templates/header.template%#
+ #%env/templates/submenuAI.template%#
+
+ <h2>Wire RAG Retrieval</h2>
+ <p>Tune how YaCy constructs prompts and search queries for Retrieval Augmented Generation.</p>
+
+ <form method="post" action="RAGConfig_p.html">
+ <div class="rag-card">
+ <h3>System Prompt</h3>
+ <p>This is sent as the system message for chats. Keep it concise and friendly.</p>
+ <textarea name="ai.system-prompt" class="rag-textarea form-control" rows="5">#[ai.system-prompt]#</textarea>
+ </div>
+
+ <div class="rag-card">
+ <h3>User Retrieval Prefix</h3>
+ <p>Prepended before attached search snippets in RAG mode to tell the LLM how to use them.</p>
+ <textarea name="ai.llm-user-prefix" class="rag-textarea form-control" rows="5">#[ai.llm-user-prefix]#</textarea>
+ </div>
+
+ <div class="rag-card">
+ <h3>Query Generator Prefix</h3>
+ <p>Prompt given to the model that generates search queries from user requests.</p>
+ <textarea name="ai.llm-query-generator-prefix" class="rag-textarea form-control" rows="5">#[ai.llm-query-generator-prefix]#</textarea>
+ </div>
+
+ <button type="submit" class="btn btn-primary">Save RAG Settings</button>
+ </form>
+
+ #%env/templates/footer.template%#
+ </body>
+</html>
diff --git a/htroot/env/templates/submenuAI.template b/htroot/env/templates/submenuAI.template
index a5badb975..614dff4a1 100644
--- a/htroot/env/templates/submenuAI.template
+++ b/htroot/env/templates/submenuAI.template
@@ -3,6 +3,7 @@
<ul class="SubMenu">
<li><a href="AILab.html" class="MenuItemLink">AI Lab</a></li>
<li><a href="LLMSelection_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">LLM Selection</a></li>
+ <li><a href="RAGConfig_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">RAG Config</a></li>
<li><a href="yacychat.html" class="MenuItemLink">Chat</a></li>
</ul>
</div>
diff --git a/htroot/yacychat.html b/htroot/yacychat.html
index d8fc210dc..26ee71cd1 100644
--- a/htroot/yacychat.html
+++ b/htroot/yacychat.html
@@ -601,7 +601,7 @@
<script src="js/marked.umd.js"></script>
<script src="js/index.umd.min.js"></script>
<script type="text/javascript">
- const SYSTEM_PROMPT = 'You are a smart and helpful chatbot. If possible, use friendly emojies.';
+ const SYSTEM_PROMPT = '#[system_prompt]#';
const defaultApiHost = '';
const STORAGE_KEY = 'yacychat_recent_pairs';
diff --git a/source/net/yacy/htroot/AILab.java b/source/net/yacy/htroot/AILab.java
index 461a8c32e..e479f4a39 100644
--- a/source/net/yacy/htroot/AILab.java
+++ b/source/net/yacy/htroot/AILab.java
@@ -79,7 +79,9 @@ public class AILab {
prop.put("ailab_index_status", hasIndex ? "ready" : "pending");
prop.putNum("ailab_index_count", indexDocs);
prop.putNum("ailab_index_needed", indexNeeded);
- prop.put("ailab_rag_status", hasRagRole ? "ready" : "pending");
+ // consider the RAG configuration page visit as completing the RAG quest
+ final boolean ragVisited = "true".equalsIgnoreCase(sb.getConfig("ui.RAGConfig_p.visited", "false"));
+ prop.put("ailab_rag_status", (hasRagRole || ragVisited) ? "ready" : "pending");
prop.put("ailab_shield_status", hasShield ? "ready" : "pending");
return prop;
diff --git a/source/net/yacy/htroot/RAGConfig_p.java b/source/net/yacy/htroot/RAGConfig_p.java
new file mode 100644
index 000000000..819d9832d
--- /dev/null
+++ b/source/net/yacy/htroot/RAGConfig_p.java
@@ -0,0 +1,36 @@
+// RAGConfig_p.java
+// Configure RAG prompt strings
+
+package net.yacy.htroot;
+
+import net.yacy.cora.protocol.RequestHeader;
+import net.yacy.search.Switchboard;
+import net.yacy.server.serverObjects;
+import net.yacy.server.serverSwitch;
+
+public class RAGConfig_p {
+
+ public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, final serverSwitch env) {
+ final Switchboard sb = (Switchboard) env;
+ final serverObjects prop = new serverObjects();
+
+ if (post != null) {
+ final String systemPrompt = post.get("ai.system-prompt", "").trim();
+ final String userPrefix = post.get("ai.llm-user-prefix", "").trim();
+ final String queryPrefix = post.get("ai.llm-query-generator-prefix", "").trim();
+
+ sb.setConfig("ai.system-prompt", systemPrompt);
+ sb.setConfig("ai.llm-user-prefix", userPrefix);
+ sb.setConfig("ai.llm-query-generator-prefix", queryPrefix);
+ }
+
+ // mark page as visited for gamification/quest tracking
+ sb.setConfig("ui.RAGConfig_p.visited", "true");
+
+ prop.put("ai.system-prompt", sb.getConfig("ai.system-prompt", net.yacy.http.servlets.RAGProxyServlet.LLM_SYSTEM_PROMPT_DEFAULT));
+ prop.put("ai.llm-user-prefix", sb.getConfig("ai.llm-user-prefix", "\n\nAdditional Information:\n\nbelow you find a collection of texts that might be useful to generate a response. Do not discuss these documents, just use them to answer the question above.\n\n"));
+ prop.put("ai.llm-query-generator-prefix", sb.getConfig("ai.llm-query-generator-prefix", "Make a list of search words with low document frequency for the following prompt; use a JSON Array: "));
+
+ return prop;
+ }
+}
diff --git a/source/net/yacy/htroot/yacychat.java b/source/net/yacy/htroot/yacychat.java
index db9d14eee..df573c7c1 100644
--- a/source/net/yacy/htroot/yacychat.java
+++ b/source/net/yacy/htroot/yacychat.java
@@ -12,22 +12,26 @@ import net.yacy.server.serverSwitch;
public class yacychat {
public static serverObjects respond(@SuppressWarnings("unused") final RequestHeader header, final serverObjects post, final serverSwitch env) {
- // return variable that accumulates replacements
- final Switchboard sb = (Switchboard) env;
-
- final serverObjects prop = new serverObjects();
- String body = post == null ? "" : post.get("BODY", "");
- JSONObject bodyj = new JSONObject();
- if (body.length() > 0) {
- try {
- bodyj = new JSONObject(new JSONTokener(body));
- } catch (JSONException e) {
- // silently catch this
- }
- }
-
- // return rewrite properties
- return prop;
+ // return variable that accumulates replacements
+ final Switchboard sb = (Switchboard) env;
+
+ final serverObjects prop = new serverObjects();
+ String body = post == null ? "" : post.get("BODY", "");
+ JSONObject bodyj = new JSONObject();
+ if (body.length() > 0) {
+ try {
+ bodyj = new JSONObject(new JSONTokener(body));
+ } catch (JSONException e) {
+ // silently catch this
+ }
+ }
+
+ // system prompt comes from configuration; default is empty
+ final String systemPrompt = sb.getConfig("ai.system-prompt", net.yacy.http.servlets.RAGProxyServlet.LLM_SYSTEM_PROMPT_DEFAULT);
+ prop.put("system_prompt", systemPrompt);
+
+ // return rewrite properties
+ return prop;
}
-} \ No newline at end of file
+}
diff --git a/source/net/yacy/http/servlets/RAGProxyServlet.java b/source/net/yacy/http/servlets/RAGProxyServlet.java
index 2108f57f2..3c8fe535d 100644
--- a/source/net/yacy/http/servlets/RAGProxyServlet.java
+++ b/source/net/yacy/http/servlets/RAGProxyServlet.java
@@ -87,9 +87,11 @@ public class RAGProxyServlet extends HttpServlet {
private static final long serialVersionUID = 3411544789759643137L;
- private static String LLM_SYSTEM_PREFIX = "\n\nYou may receive additional expert knowledge in the user prompt after a 'Additional Information' headline to enhance your knowledge. Use it only if applicable.";
- private static String LLM_USER_PREFIX = "\n\nAdditional Information:\n\nbelow you find a collection of texts that might be useful to generate a response. Do not discuss these documents, just use them to answer the question above.\n\n";
-
+ public static final String LLM_SYSTEM_PROMPT_DEFAULT = "You are a smart and helpful chatbot. If possible, use friendly emojies.";
+ private static final String LLM_SYSTEM_PREFIX_DEFAULT = "\n\nYou may receive additional expert knowledge in the user prompt after a 'Additional Information' headline to enhance your knowledge. Use it only if applicable.";
+ private static final String LLM_USER_PREFIX_DEFAULT = "\n\nAdditional Information:\n\nbelow you find a collection of texts that might be useful to generate a response. Do not discuss these documents, just use them to answer the question above.\n\n";
+ private static final String LLM_QUERY_GENERATOR_PREFIX_DEFAULT = "Make a list of search words with low document frequency for the following prompt; use a JSON Array: ";
+
@Override
public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
@@ -136,6 +138,7 @@ public class RAGProxyServlet extends HttpServlet {
String body = bodyBuilder.toString();
JSONObject bodyObject;
try {
+ final Switchboard sb = Switchboard.getSwitchboard();
// get system message and user prompt
bodyObject = new JSONObject(body);
// get chat functions
@@ -153,11 +156,13 @@ public class RAGProxyServlet extends HttpServlet {
// get messages and prepare user message attachments
JSONArray messages = bodyObject.optJSONArray("messages");
+ final String systemPrefix = sb.getConfig("ai.llm-system-prefix", LLM_SYSTEM_PREFIX_DEFAULT);
+ final String userPrefix = sb.getConfig("ai.llm-user-prefix", LLM_USER_PREFIX_DEFAULT);
for (int i = 0; i < messages.length(); i++) {
JSONObject message = messages.getJSONObject(i);
if (message.optString("role", "").equals("user")) {
UserObject userObject = new UserObject(message);
- userObject.attachAttachment(LLM_USER_PREFIX);
+ userObject.attachAttachment(userPrefix);
}
}
UserObject userObject = new UserObject(messages.getJSONObject(messages.length() - 1));
@@ -170,9 +175,10 @@ public class RAGProxyServlet extends HttpServlet {
String searchResultMarkdown = "";
if (rag) {
// modify system and user prompt here in bodyObject to enable RAG
- searchResultQuery = this.searchWordsForPrompt(llm4tldr.llm, llm4tldr.model, user);
+ final String queryPrefix = sb.getConfig("ai.llm-query-generator-prefix", LLM_QUERY_GENERATOR_PREFIX_DEFAULT);
+ searchResultQuery = this.searchWordsForPrompt(llm4tldr.llm, llm4tldr.model, queryPrefix + user);
searchResultMarkdown = searchResultsAsMarkdown(searchResultQuery, 10);
- user += LLM_USER_PREFIX;
+ user += userPrefix;
user += searchResultMarkdown;
userObject.setContentText(user);
}
@@ -614,8 +620,6 @@ public class RAGProxyServlet extends HttpServlet {
return this.title;
}
}
-
-
/**
* Creates slices of a given text. We want slices of average same size,
@@ -660,9 +664,9 @@ public class RAGProxyServlet extends HttpServlet {
}
private String searchWordsForPrompt(LLM llm, String model, String prompt) {
- String question = "Make a list of search words with low document frequency for the following prompt; use a JSON Array: " + prompt;
+ String question = LLM_QUERY_GENERATOR_PREFIX_DEFAULT + prompt;
try {
- LLM.Context context = new LLM.Context(LLM_SYSTEM_PREFIX);
+ LLM.Context context = new LLM.Context(LLM_SYSTEM_PREFIX_DEFAULT);
context.addPrompt(question);
Set<String> singlewords = new LinkedHashSet<>();
String[] a = LLM.stringsFromChat(llm.chat(model, context, LLM.listSchema, 200));