diff options
| author | Michael Peter Christen <mc@yacy.net> | 2025-12-06 19:21:57 +0100 |
|---|---|---|
| committer | Michael Peter Christen <mc@yacy.net> | 2025-12-06 19:21:57 +0100 |
| commit | eed704d23bc53e5d0359072e5e2134b469ae2eef (patch) | |
| tree | 2518c10e9e478a4c04845db3b81442ee9dfca7cb | |
| parent | ed9ea238b1e7d4a89d77acea790b08b6f2b1ed49 (diff) | |
added RAG configuration page
| -rw-r--r-- | defaults/yacy.init | 20 | ||||
| -rw-r--r-- | htroot/AILab.html | 2 | ||||
| -rw-r--r-- | htroot/RAGConfig_p.html | 61 | ||||
| -rw-r--r-- | htroot/env/templates/submenuAI.template | 1 | ||||
| -rw-r--r-- | htroot/yacychat.html | 2 | ||||
| -rw-r--r-- | source/net/yacy/htroot/AILab.java | 4 | ||||
| -rw-r--r-- | source/net/yacy/htroot/RAGConfig_p.java | 36 | ||||
| -rw-r--r-- | source/net/yacy/htroot/yacychat.java | 38 | ||||
| -rw-r--r-- | source/net/yacy/http/servlets/RAGProxyServlet.java | 24 |
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)); |
