import OpenAI from 'openai';
const openai = new OpenAI();
const tools: OpenAI.ChatCompletionTool[] = [{
type: 'function',
function: {
name: 'get_user_orders',
description: 'Aduce istoricul de comenzi al unui utilizator pe baza adresei de email.',
parameters: {
type: 'object',
properties: {
email: { type: 'string', description: 'Adresa de email a clientului' },
limit: { type: 'number', description: 'Numărul maxim de comenzi returnate' }
},
required: ['email']
}
}
}];
async function handleAgentRequest(userPrompt: string) {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: userPrompt }],
tools
});
const toolCall = response.choices[0].message.tool_calls?.[0];
if (toolCall && toolCall.function.name === 'get_user_orders') {
const { email, limit } = JSON.parse(toolCall.function.arguments);
// Apelăm DB-ul nostru securizat prin ORM / Query Builder
const orders = await db.orders.findMany({
where: { userEmail: email },
take: limit || 5
});
return orders;
}
}Am pus în producție un agent simplu bazat pe Function Calling acum vreo jumătate de an și am scăpat de 80% din tichetele de tipul „fă-mi un export rapid”. Vă arăt cum am legat OpenAI direct la un DB de PostgreSQL în mod sigur, fără să-i dau modelului acces să scrie SQL de capul lui.
De ce e periculos să-l lași să scrie SQL direct
Am văzut o grămadă de tutoriale pe net care îi dau LLM-ului schema bazei de date în prompt și îi zic: „Scrie un query SQL bazat pe întrebarea utilizatorului”. Să nu faceți asta niciodată în producție.
Pe lângă riscul evident de SQL injection (utilizatorul poate face prompt injection și să ceară ștergerea tabelelor), modelele o mai iau pe arătură. Ba inventează coloane, ba fac JOIN-uri complet greșite care îți blochează baza de date în lock-uri infinite.
Soluția reală? Îi expui funcții stricte, exact ca un API intern. Modelul nu scrie cod, ci doar decide când are nevoie de date și ce argumente să îți trimită pentru a apela o funcție pe care tu ai scris-o și ai securizat-o deja în backend.
Cum arată arhitectura și cifrele din spate
La un proiect cu vreo 12k comenzi pe lună, am vrut ca echipa de suport să poată întreba natural: „Câte comenzi a dat Vasile de pe mailul vasile@yahoo.com în ultimele 3 luni?”.
Fluxul pe care l-am construit arată așa:
- Userul trimite întrebarea în chat.
- Trimitem întrebarea la OpenAI împreună cu o listă de
tools(funcțiile noastre descrise în JSON). - Modelul analizează textul și returnează un răspuns special:
tool_calls. Practic, ne spune: „Vreau să rulezi funcțiaget_user_orderscu parametrulemail: 'vasile@yahoo.com'”. - Serverul nostru prinde răspunsul ăsta, rulează query-ul SQL predefinit (folosind parametri curați, fără concatenări de string-uri) și obține datele din DB.
- Trimitem datele înapoi la OpenAI.
- OpenAI formulează răspunsul final, politicos și ușor de citit.
Trade-off-ul principal: Latența. Un round-trip complet durează cam 1.8 - 2.5 secunde. Pentru suport intern e absolut perfect și a economisit zeci de ore de muncă manuală. Totuși, dacă vrei să pui asta direct în fața clienților în aplicație, s-ar putea ca latența să fie deranjantă fără un UX foarte bine pus la punct, cu streaming și skeleton loaders.
Implementarea tehnică
În exemplul de mai jos aveți schema exactă pe care o trimitem către OpenAI și cum prindem decizia modelului în backend-ul nostru de Node.js.