fix: migliora parsing e system prompt per tool calling

- Nuovo formato TOOL:toolname|arg=val più semplice
- System prompt semplificato con lista chiara tool
- Parsing supporta sia formato nuovo che vecchio
- Validazione tool names esistenti
This commit is contained in:
opencode committed 2026-03-23 16:20:37 +01:00
1 parent a309c7e27c
commit 2fa06ec0af
1 file changed
+65 -16
+65 -16
View File
@@ -21,7 +21,12 @@ from langchain_core.tools import Tool
# TOOL CALL PATTERNS
# =============================================================================
TOOL_CALL_PATTERN = r'\[TOOL_CALL\]\s*(.*?)\s*\[\/TOOL_CALL\]'
# Old format: [TOOL_CALL]...[/TOOL_CALL]
TOOL_CALL_PATTERN_OLD = r'\[TOOL_CALL\]\s*(.*?)\s*\[\/TOOL_CALL\]'
# New format: TOOL:toolname|arg1=val1|arg2=val2
TOOL_CALL_PATTERN_NEW = r'^TOOL:(\w+)(?:\|(.*))?$'
TOOL_RESULT_PATTERN = r'\[TOOL_RESULT\]\s*(.*?)\s*\[\/TOOL_RESULT\]'
@@ -227,17 +232,29 @@ class OllamaToolAgent:
def _default_system_prompt(self) -> str:
"""System prompt di default"""
return """You are LangCode, a helpful AI assistant with access to tools.
return """You are LangCode AI. You have access to these tools:
When you need to use a tool, you MUST respond with this EXACT format:
1. system_info - Get system info (OS, Python version, etc). No arguments needed.
2. ls(path=".") - List directory contents. Use path="." for current directory.
3. execute(command) - Run shell commands. Example: execute("ls -la")
4. read_file(path) - Read file contents. Example: read_file("/path/to/file")
5. write_file(path, content) - Write to file.
6. search(directory, pattern) - Search for files. Example: search(".", "*.py")
[TOOL_CALL]
{"tool": "tool_name", "arguments": {"arg1": "value1", "arg2": "value2"}}
[/TOOL_CALL]
IMPORTANT RULES:
- ONLY use these exact tool names: system_info, ls, execute, read_file, write_file, search
- NEVER invent tool names
- When you need a tool, output ONLY this format (no other text):
After receiving the tool result, continue your response normally.
TOOL:system_info
TOOL:ls|path=.
TOOL:execute|command=ls -la
TOOL:read_file|path=/path/to/file
TOOL:write_file|path=/file.txt|content=hello
TOOL:search|directory=.|pattern=*.py
Available tools:"""
- After the tool result, you can respond normally.
- If no tool is needed, just respond normally."""
def _tool_instructions(self) -> str:
"""Genera le istruzioni per i tool disponibili"""
@@ -286,17 +303,45 @@ Available tools:"""
def _parse_tool_calls(self, text: str) -> List[ToolCall]:
"""Parse le chiamate tool dalla risposta del modello"""
tool_calls = []
matches = re.finditer(TOOL_CALL_PATTERN, text, re.DOTALL)
# Try new format first: TOOL:toolname|arg1=val1
lines = text.strip().split('\n')
for line in lines:
line = line.strip()
if line.startswith('TOOL:'):
# Parse: TOOL:toolname|arg1=val1|arg2=val2
match = re.match(r'^TOOL:(\w+)(?:\|(.+))?$', line)
if match:
tool_name = match.group(1)
if tool_name in self.tools:
args = {}
if match.group(2):
# Parse arguments: arg1=val1|arg2=val2
for arg_part in match.group(2).split('|'):
if '=' in arg_part:
key, val = arg_part.split('=', 1)
args[key] = val
tool_calls.append(ToolCall(
id=str(uuid.uuid4())[:8],
name=tool_name,
arguments=args
))
# If we found a valid tool call, return only that one
return tool_calls
# Try old format: [TOOL_CALL]{...}[/TOOL_CALL]
matches = re.finditer(TOOL_CALL_PATTERN_OLD, text, re.DOTALL)
for match in matches:
try:
data = json.loads(match.group(1))
tool_calls.append(ToolCall(
id=str(uuid.uuid4())[:8],
name=data.get('tool'),
arguments=data.get('arguments', {})
))
except json.JSONDecodeError:
tool_name = data.get('tool')
if tool_name and tool_name in self.tools:
tool_calls.append(ToolCall(
id=str(uuid.uuid4())[:8],
name=tool_name,
arguments=data.get('arguments', {})
))
except (json.JSONDecodeError, TypeError):
continue
return tool_calls
@@ -304,7 +349,11 @@ Available tools:"""
def _execute_tool(self, tool_call: ToolCall) -> Any:
"""Esegui un singolo tool"""
if tool_call.name not in self.tools:
return {"error": f"Tool '{tool_call.name}' not found"}
available = ", ".join(self.tools.keys())
return {
"error": f"Tool '{tool_call.name}' not found",
"available_tools": available
}
try:
tool = self.tools[tool_call.name]