diff --git a/src/openhuman/agent/loop_/instructions.rs b/src/openhuman/agent/loop_/instructions.rs index ea2f4d434..47df6c3a8 100644 --- a/src/openhuman/agent/loop_/instructions.rs +++ b/src/openhuman/agent/loop_/instructions.rs @@ -8,35 +8,14 @@ pub(crate) fn build_tool_instructions(tools_registry: &[Box]) -> Strin instructions.push_str("\n## Tool Use Protocol\n\n"); instructions.push_str("To use a tool, wrap a JSON object in tags:\n\n"); instructions.push_str("```\n\n{\"name\": \"tool_name\", \"arguments\": {\"param\": \"value\"}}\n\n```\n\n"); - - // Explicit anti-hallucination rules - instructions.push_str("### Rules (MUST follow)\n\n"); instructions.push_str( - "1. **ALWAYS use tags** when a task requires a tool. \ - NEVER narrate what you would do — emit the actual tags.\n", + "CRITICAL: Output actual tags—never describe steps or give examples.\n\n", ); - instructions.push_str( - "2. **NEVER describe a tool call in prose** (e.g. \"I'll run ls\" or \ - \"Let me check the file\") without also emitting the tags. \ - If you mention a tool, you must call it.\n", - ); - instructions.push_str( - "3. **Use the exact tool names** listed below. \ - Do not invent tool names that are not in the list.\n", - ); - instructions.push_str("4. You may use **multiple tool calls** in a single response.\n"); - instructions.push_str( - "5. After tool execution, results appear in tags. \ - Continue reasoning with the results until you can give a final answer.\n", - ); - instructions.push_str( - "6. Only respond **without** a tool call when the answer requires \ - no tool (e.g. general knowledge, math, or conversation).\n\n", - ); - - instructions.push_str("### Example\n\n"); - instructions.push_str("User: \"what's the date?\"\nCorrect response:\n\n{\"name\":\"shell\",\"arguments\":{\"command\":\"date\"}}\n\n\n"); - + instructions.push_str("Example: User says \"what's the date?\". You MUST respond with:\n\n{\"name\":\"shell\",\"arguments\":{\"command\":\"date\"}}\n\n\n"); + instructions.push_str("You may use multiple tool calls in a single response. "); + instructions.push_str("After tool execution, results appear in tags. "); + instructions + .push_str("Continue reasoning with the results until you can give a final answer.\n\n"); instructions.push_str("### Available Tools\n\n"); for tool in tools_registry { diff --git a/src/openhuman/agent/loop_/parse.rs b/src/openhuman/agent/loop_/parse.rs index 26183147a..3b3625c7a 100644 --- a/src/openhuman/agent/loop_/parse.rs +++ b/src/openhuman/agent/loop_/parse.rs @@ -82,51 +82,7 @@ pub(crate) fn parse_tool_calls_from_json_value(value: &serde_json::Value) -> Vec calls } -/// Parse bracket-style pseudo-syntax emitted by some models: -/// `{tool => "shell", args => { --command "ls" }}` -/// -/// Extracts tool name and converts `--key "value"` args into a JSON object. -fn parse_bracket_tool_call(inner: &str) -> Option { - static BRACKET_TOOL_RE: LazyLock = LazyLock::new(|| { - Regex::new(r#"(?s)\{?\s*tool\s*=>\s*"([^"]+)"\s*,\s*args\s*=>\s*\{(.*?)\}\s*\}?"#).unwrap() - }); - static ARG_RE: LazyLock = LazyLock::new(|| Regex::new(r#"--(\w+)\s+"([^"]*)"#).unwrap()); - - let cap = BRACKET_TOOL_RE.captures(inner.trim())?; - let name = cap.get(1)?.as_str().to_string(); - let args_body = cap.get(2)?.as_str(); - - let mut args = serde_json::Map::new(); - for arg_cap in ARG_RE.captures_iter(args_body) { - let key = arg_cap.get(1)?.as_str().to_string(); - let value = arg_cap.get(2)?.as_str().to_string(); - args.insert(key, serde_json::Value::String(value)); - } - - if name.is_empty() { - return None; - } - - tracing::debug!( - tool = name.as_str(), - parse_mode = "bracket_syntax", - "[parse] parsed bracket-style tool call" - ); - - Some(ParsedToolCall { - name, - arguments: serde_json::Value::Object(args), - }) -} - -const TOOL_CALL_OPEN_TAGS: [&str; 6] = [ - "", - "", - "", - "", - "[TOOL_CALL]", - "[tool_call]", -]; +const TOOL_CALL_OPEN_TAGS: [&str; 4] = ["", "", "", ""]; pub(crate) fn find_first_tag<'a>(haystack: &str, tags: &'a [&'a str]) -> Option<(usize, &'a str)> { tags.iter() @@ -140,8 +96,6 @@ pub(crate) fn matching_tool_call_close_tag(open_tag: &str) -> Option<&'static st "" => Some(""), "" => Some(""), "" => Some(""), - "[TOOL_CALL]" => Some("[/TOOL_CALL]"), - "[tool_call]" => Some("[/tool_call]"), _ => None, } } @@ -422,16 +376,7 @@ pub(crate) fn parse_tool_calls(response: &str) -> (String, Vec) } if !parsed_any { - // Try parsing bracket pseudo-syntax: - // {tool => "name", args => { --key "value" }} - if let Some(call) = parse_bracket_tool_call(inner) { - calls.push(call); - parsed_any = true; - } - } - - if !parsed_any { - tracing::warn!("Malformed tool_call body: could not parse tag content as JSON or bracket syntax"); + tracing::warn!("Malformed JSON: expected tool-call object in tag body"); } remaining = &after_open[close_idx + close_tag.len()..];