refactor: enhance system prompt handling and tool integration in LlmChatAgent
This commit is contained in:
@@ -70,7 +70,7 @@ impl Hook for ToolValidationHook {
|
||||
|
||||
struct LlmChatAgentBuilder {
|
||||
client: Option<LlmClient>,
|
||||
custom_system_prompt: Option<String>,
|
||||
system_prompt_parts: Vec<String>,
|
||||
tools: Vec<Box<dyn ToolDyn>>,
|
||||
hooks: Vec<Box<dyn Hook>>,
|
||||
max_turns: usize,
|
||||
@@ -84,7 +84,7 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
client: None,
|
||||
custom_system_prompt: None,
|
||||
system_prompt_parts: Vec::new(),
|
||||
tools: Vec::new(),
|
||||
hooks: Vec::new(),
|
||||
max_turns: 10,
|
||||
@@ -99,7 +99,7 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
}
|
||||
|
||||
fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
|
||||
self.custom_system_prompt = Some(system_prompt.into());
|
||||
self.system_prompt_parts.push(system_prompt.into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -133,13 +133,23 @@ impl AgentBuilder<LlmMessage, LlmCompletionModel> for LlmChatAgentBuilder {
|
||||
client: self.client.expect("call .with_client() first"),
|
||||
};
|
||||
let mut agent = Agent::new(model);
|
||||
let tool_specs = self
|
||||
.tools
|
||||
.iter()
|
||||
.map(|tool| ToolPromptSpec {
|
||||
name: tool.tool_name().to_string(),
|
||||
description: tool.description(),
|
||||
parameters: tool.parameters(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let base_prompt = self.system_prompt_parts.join("\n\n");
|
||||
let system_prompt = build_tools_system_prompt(&base_prompt, &tool_specs);
|
||||
agent.tools = self.tools;
|
||||
agent.hooks = self.hooks;
|
||||
agent.default_max_turns = self.max_turns;
|
||||
agent.memory = self.memory_data;
|
||||
if let Some(sp) = self.custom_system_prompt {
|
||||
agent.system_prompt = Some(LlmMessage::system(&sp));
|
||||
}
|
||||
agent.context = self.context;
|
||||
agent.system_prompt = Some(LlmMessage::system(&system_prompt));
|
||||
agent
|
||||
}
|
||||
}
|
||||
@@ -196,6 +206,12 @@ impl Tool for EchoTool {
|
||||
// 6. System prompt builder
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct ToolPromptSpec {
|
||||
name: String,
|
||||
description: String,
|
||||
parameters: serde_json::Value,
|
||||
}
|
||||
|
||||
fn tool_names(agent: &Agent<LlmCompletionModel, LlmMessage>) -> Vec<String> {
|
||||
agent
|
||||
.tools()
|
||||
@@ -204,12 +220,12 @@ fn tool_names(agent: &Agent<LlmCompletionModel, LlmMessage>) -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_tools_system_prompt(base_prompt: &str, tool_names: &[String]) -> String {
|
||||
fn build_tools_system_prompt(base_prompt: &str, tool_specs: &[ToolPromptSpec]) -> String {
|
||||
let mut prompt = String::new();
|
||||
prompt.push_str(base_prompt);
|
||||
prompt.push_str("\n\nYou have access to the following tools.\n\n");
|
||||
|
||||
if tool_names.is_empty() {
|
||||
if tool_specs.is_empty() {
|
||||
prompt.push_str("(No tools available.)\n");
|
||||
} else {
|
||||
prompt.push_str("## JSON Response Format\n");
|
||||
@@ -220,7 +236,7 @@ fn build_tools_system_prompt(base_prompt: &str, tool_names: &[String]) -> String
|
||||
prompt.push_str(" \"reply_text\": \"your message to the user\",\n");
|
||||
prompt.push_str(" \"tool_calls\": [\n {\n");
|
||||
prompt.push_str(" \"tool_name\": \"tool_name_here\",\n");
|
||||
prompt.push_str(" \"args\": { /* tool-specific arguments */ }\n");
|
||||
prompt.push_str(" \"args\": { \"argument_name\": \"argument_value\" }\n");
|
||||
prompt.push_str(" }\n ]\n");
|
||||
prompt.push_str("}\n\n");
|
||||
prompt.push_str("If you don't need to use a tool, respond with:\n");
|
||||
@@ -230,8 +246,14 @@ fn build_tools_system_prompt(base_prompt: &str, tool_names: &[String]) -> String
|
||||
prompt.push_str("}\n\n");
|
||||
prompt.push_str("## Available Tools\n\n");
|
||||
|
||||
for name in tool_names {
|
||||
prompt.push_str(&format!("- {name}\n"));
|
||||
for tool in tool_specs {
|
||||
prompt.push_str(&format!("- {}\n", tool.name));
|
||||
prompt.push_str(&format!(" Description: {}\n", tool.description));
|
||||
prompt.push_str(" Arguments JSON Schema:\n");
|
||||
let parameters = serde_json::to_string_pretty(&tool.parameters)
|
||||
.unwrap_or_else(|_| tool.parameters.to_string());
|
||||
prompt.push_str(&indent_multiline(¶meters, " "));
|
||||
prompt.push_str("\n");
|
||||
}
|
||||
|
||||
prompt.push_str(
|
||||
@@ -251,6 +273,13 @@ fn build_tools_system_prompt(base_prompt: &str, tool_names: &[String]) -> String
|
||||
prompt
|
||||
}
|
||||
|
||||
fn indent_multiline(text: &str, indent: &str) -> String {
|
||||
text.lines()
|
||||
.map(|line| format!("{indent}{line}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 7. Config loading via dotenvy
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -318,16 +347,12 @@ async fn run_chat_agent(config: LlmConfig) -> Result<(), LlmError> {
|
||||
.build();
|
||||
|
||||
let names = tool_names(&agent);
|
||||
let system_prompt_text =
|
||||
build_tools_system_prompt("You are a helpful assistant with an echo tool.", &names);
|
||||
|
||||
let tool_hook = ToolValidationHook::new(names.clone());
|
||||
|
||||
let output = agent
|
||||
.prompt(LlmMessage::user(
|
||||
"Use the echo tool to echo 'Hello from the JSON harness!', then tell me what it said.",
|
||||
))
|
||||
.system_prompt(LlmMessage::system(&system_prompt_text))
|
||||
.max_turns(5)
|
||||
.add_hook(tool_hook)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user