Move hardcoded LLM prompts to template files (#3934)
Co-authored-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ use rmcp::model::Tool;
|
|||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@@ -14,8 +15,15 @@ use tokio::sync::RwLock;
|
|||||||
use crate::agents::tool_vectordb::ToolVectorDB;
|
use crate::agents::tool_vectordb::ToolVectorDB;
|
||||||
use crate::conversation::message::Message;
|
use crate::conversation::message::Message;
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
|
use crate::prompt_template::render_global_file;
|
||||||
use crate::providers::{self, base::Provider};
|
use crate::providers::{self, base::Provider};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ToolSelectorContext {
|
||||||
|
tools: String,
|
||||||
|
query: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum RouterToolSelectionStrategy {
|
pub enum RouterToolSelectionStrategy {
|
||||||
Vector,
|
Vector,
|
||||||
@@ -282,15 +290,21 @@ impl RouterToolSelector for LLMToolSelector {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tools) = relevant_tools {
|
if let Some(tools) = relevant_tools {
|
||||||
// Use LLM to search through tools
|
// Use template to generate the prompt
|
||||||
let prompt = format!(
|
let context = ToolSelectorContext {
|
||||||
"Given the following tools:\n{}\n\nFind the most relevant tools for the query: {}\n\nReturn the tools in this exact format for each tool:\nTool: <tool_name>\nDescription: <tool_description>\nSchema: <tool_schema>",
|
tools: tools.clone(),
|
||||||
tools, query
|
query: query.to_string(),
|
||||||
);
|
};
|
||||||
let system_message = Message::user().with_text("You are a tool selection assistant. Your task is to find the most relevant tools based on the user's query.");
|
|
||||||
|
let user_prompt =
|
||||||
|
render_global_file("router_tool_selector.md", &context).map_err(|e| {
|
||||||
|
ToolError::ExecutionError(format!("Failed to render prompt template: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let user_message = Message::user().with_text(&user_prompt);
|
||||||
let response = self
|
let response = self
|
||||||
.llm_provider
|
.llm_provider
|
||||||
.complete(&prompt, &[system_message], &[])
|
.complete("", &[user_message], &[])
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ToolError::ExecutionError(format!("Failed to search tools: {}", e)))?;
|
.map_err(|e| ToolError::ExecutionError(format!("Failed to search tools: {}", e)))?;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::config::permission::PermissionLevel;
|
|||||||
use crate::config::PermissionManager;
|
use crate::config::PermissionManager;
|
||||||
use crate::conversation::message::{Message, MessageContent, ToolRequest};
|
use crate::conversation::message::{Message, MessageContent, ToolRequest};
|
||||||
use crate::conversation::Conversation;
|
use crate::conversation::Conversation;
|
||||||
|
use crate::prompt_template::render_global_file;
|
||||||
use crate::providers::base::Provider;
|
use crate::providers::base::Provider;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
@@ -13,6 +14,11 @@ use serde_json::Value;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct PermissionJudgeContext {
|
||||||
|
// Empty struct for now since the current template doesn't need variables
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates the tool definition for checking read-only permissions.
|
/// Creates the tool definition for checking read-only permissions.
|
||||||
fn create_read_only_tool() -> Tool {
|
fn create_read_only_tool() -> Tool {
|
||||||
Tool::new(
|
Tool::new(
|
||||||
@@ -133,12 +139,12 @@ pub async fn detect_read_only_tools(
|
|||||||
let tool = create_read_only_tool();
|
let tool = create_read_only_tool();
|
||||||
let check_messages = create_check_messages(tool_requests);
|
let check_messages = create_check_messages(tool_requests);
|
||||||
|
|
||||||
|
let context = PermissionJudgeContext {};
|
||||||
|
let system_prompt = render_global_file("permission_judge.md", &context)
|
||||||
|
.unwrap_or_else(|_| "You are a good analyst and can detect operations whether they have read-only operations.".to_string());
|
||||||
|
|
||||||
let res = provider
|
let res = provider
|
||||||
.complete(
|
.complete(&system_prompt, check_messages.messages(), &[tool.clone()])
|
||||||
"You are a good analyst and can detect operations whether they have read-only operations.",
|
|
||||||
check_messages.messages(),
|
|
||||||
&[tool.clone()],
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// Process the response and return an empty vector if the response is invalid
|
// Process the response and return an empty vector if the response is invalid
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
You are a good analyst and can detect operations whether they have read-only operations.
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
You are a tool selection assistant. Your task is to find the most relevant tools based on the user's query.
|
||||||
|
|
||||||
|
Given the following tools:
|
||||||
|
{{ tools }}
|
||||||
|
|
||||||
|
Find the most relevant tools for the query: {{ query }}
|
||||||
|
|
||||||
|
Return the tools in this exact format for each tool:
|
||||||
|
Tool: <tool_name>
|
||||||
|
Description: <tool_description>
|
||||||
|
Schema: <tool_schema>
|
||||||
Reference in New Issue
Block a user