feat(api): integrate MCP tools support in Ollama chat route

Add support for Model Context Protocol (MCP) tools in the Ollama chat API. Detect model capabilities to use native tools or prompt engineering. Handle tool calls, execute them via MCPExecutor, and inject results back into the conversation for seamless tool-assisted responses. This enhances chat functionality by enabling external tool integration.
This commit is contained in:
Evandro Fonseca Junior committed 2025-10-28 22:50:03 -03:00
1 parent 745746afa9
commit 4739a13458
18 files changed
+1782 -10

No files matched your search

+195
View File
@@ -0,0 +1,195 @@
/**
* Templates de prompt engineering para modelos sem suporte nativo a function calling
*/
import type { MCPTool } from "@/lib/types/mcp-chat";
/**
* Gera prompt de instruções para uso de tools via prompt engineering
* @param tools Array de tools disponíveis
* @returns String com instruções formatadas
*/
export function generateToolsPrompt(tools: MCPTool[]): string {
if (tools.length === 0) {
return "";
}
const toolsDescription = tools
.map((tool, index) => {
const params = Object.entries(tool.inputSchema.properties || {})
.map(([name, schema]: [string, any]) => {
const required = tool.inputSchema.required?.includes(name)
? " (required)"
: " (optional)";
const type = schema.type || "any";
const desc = schema.description || "";
return ` - ${name}: ${type}${required}${desc ? ` - ${desc}` : ""}`;
})
.join("\n");
return `${index + 1}. **${tool.name}**
Description: ${tool.description}
Parameters:
${params || " (no parameters)"}`;
})
.join("\n\n");
return `
# FERRAMENTAS DISPONÍVEIS
Você tem acesso às seguintes ferramentas externas para auxiliar o usuário:
${toolsDescription}
## INSTRUÇÕES IMPORTANTES
1. **Quando usar ferramentas:**
- Use ferramentas quando precisar de informações em tempo real
- Use ferramentas quando precisar executar ações externas
- Use ferramentas quando o usuário solicitar dados específicos
2. **Como usar ferramentas:**
- Para chamar uma ferramenta, responda EXATAMENTE no seguinte formato JSON:
\`\`\`json
{
"tool_call": {
"name": "nome_da_ferramenta",
"parameters": {
"parametro1": "valor1",
"parametro2": "valor2"
}
}
}
\`\`\`
- NÃO adicione texto antes ou depois do JSON
- Certifique-se de que o JSON está bem formatado
- Use apenas ferramentas da lista acima
3. **REGRA CRUCIAL - NUNCA MENCIONE AO USUÁRIO:**
- NUNCA mencione que você está usando ferramentas
- NUNCA mencione MCPs ou APIs externas
- NUNCA diga "vou usar a ferramenta X"
- NUNCA explique o processo técnico de como obteve informações
- Apresente as informações naturalmente como se fossem seu conhecimento
4. **Após receber resultado da ferramenta:**
- Você receberá uma mensagem do sistema com o resultado
- Integre o resultado naturalmente na sua resposta
- Responda ao usuário de forma fluida e natural
- Cite informações específicas do resultado quando relevante
## EXEMPLO
Usuário: "Qual é o clima em São Paulo?"
Sua resposta:
\`\`\`json
{
"tool_call": {
"name": "get_weather",
"parameters": {
"city": "São Paulo"
}
}
}
\`\`\`
[Sistema retorna: {"temperature": 25, "condition": "sunny"}]
Sua resposta final ao usuário:
"O clima em São Paulo está ensolarado com 25°C."
**NÃO faça:**
"Vou consultar a ferramenta de clima para você..."
"Usei o get_weather e obtive..."
"De acordo com a API..."
`;
}
/**
* Gera prompt condensado para economia de tokens
* @param tools Array de tools disponíveis
* @returns String com instruções compactas
*/
export function generateCompactToolsPrompt(tools: MCPTool[]): string {
if (tools.length === 0) {
return "";
}
const toolsList = tools
.map((tool) => {
const params = Object.keys(tool.inputSchema.properties || {}).join(", ");
return `${tool.name}(${params}): ${tool.description}`;
})
.join("\n");
return `
TOOLS: ${tools.length} available
${toolsList}
FORMAT: {"tool_call":{"name":"tool_name","parameters":{...}}}
RULE: Never mention tools to user. Present results naturally.
`;
}
/**
* Extrai tool call de uma resposta do modelo
* @param response Resposta do modelo
* @returns Tool call extraído ou null
*/
export function extractToolCallFromResponse(response: string): {
name: string;
parameters: Record<string, any>;
} | null {
try {
// Tentar extrair JSON da resposta
const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/);
const jsonText = jsonMatch ? jsonMatch[1] : response;
const parsed = JSON.parse(jsonText.trim());
if (parsed.tool_call && parsed.tool_call.name) {
return {
name: parsed.tool_call.name,
parameters: parsed.tool_call.parameters || {},
};
}
return null;
} catch (error) {
// Tentar extrair manualmente
const nameMatch = response.match(/"name":\s*"([^"]+)"/);
if (nameMatch) {
const name = nameMatch[1];
const paramsMatch = response.match(/"parameters":\s*({[^}]+})/);
const parameters = paramsMatch ? JSON.parse(paramsMatch[1]) : {};
return { name, parameters };
}
return null;
}
}
/**
* Formata resultado de tool para injetar no contexto
* @param toolName Nome do tool
* @param result Resultado da execução
* @returns Mensagem formatada para o sistema
*/
export function formatToolResultForContext(
toolName: string,
result: any
): string {
const resultJson =
typeof result === "string" ? result : JSON.stringify(result, null, 2);
return `[TOOL RESULT - ${toolName}]
${resultJson}
[END TOOL RESULT]
Use as informações acima para responder ao usuário de forma natural. NÃO mencione que usou uma ferramenta.`;
}