import OpenAI from 'openai';
const openai = new OpenAI();
const tools: OpenAI.ChatCompletionTool[] = [{
type: 'function',
function: {
name: 'getOrdersSummary',
description: 'Obține totalul vânzărilor și numărul de comenzi dintr-un oraș pentru un interval',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'Numele orașului (ex: Cluj, Bucuresti)' },
daysAgo: { type: 'number', description: 'Numărul de zile analizate în urmă' }
},
required: ['city', 'daysAgo']
}
}
}];
async function handleAgentQuery(prompt: string) {
const messages: OpenAI.ChatCompletionMessageParam[] = [{ role: 'user', content: prompt }];
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools,
tool_choice: 'auto'
});
const toolCall = response.choices[0].message.tool_calls?.[0];
if (toolCall && toolCall.function.name === 'getOrdersSummary') {
const args = JSON.parse(toolCall.function.arguments);
// Execuție în DB securizat cu parametrii extrași
const dbResult = await db.raw('SELECT COUNT(*), SUM(total) FROM orders WHERE city = ? AND created_at > NOW() - INTERVAL ? DAY', [args.city, args.daysAgo]);
messages.push(response.choices[0].message);
messages.push({ role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(dbResult) });
return await openai.chat.completions.create({ model: 'gpt-4o', messages });
}
}Am adăugat recent un agent bazat pe OpenAI peste un PostgreSQL din producție care procesează în jur de 120k comenzi lunar. Echipa de operare voia rapoarte ad-hoc în limbaj natural de genul „câți clienți din Cluj au cumpărat peste 500 lei luna trecută” fără să deschidă un tichet la dev. După trei iterații eșuate, am ajuns la un pattern stabil bazat pe function calling care rulează în producție fără panică.
De ce SQL generat direct de LLM e o capcană
Prima tentativă a fost abordarea naivă: am pus schema bazei de date în system prompt și i-am zis modelului „generează o interogare SQL și o voi rula eu”. Dezastru garantat. Pe lângă riscul evident de SQL injection sau comenzi distructive dacă scapă vreun UPDATE prin halucinație, GPT-4o mai inventa coloane sau join-uri ilogice când schema depășea 15 tabele.
În plus, latența era uriașă. Să trimiți întreaga schemă DB la fiecare mesaj mănâncă tokeni pe pâine și încetinește răspunsul cu 2-3 secunde inutile. Am realizat rapid că modelul nu trebuie să aibă libertate totală pe SQL, ci trebuie să aibă acces doar la funcții strict delimitate.
Pattern-ul: Expuci intenții, nu schemă de DB
În loc să las LLM-ul să scrie instrucțiuni SELECT, am definit o serie de tools (funcții) extrem de specifice. Modelul primește doar semnătura acestor funcții și parametrii lor. Când un user pune o întrebare, GPT alege ce funcție să apeleze și cu ce argumente, iar codul meu de Node.js execută query-ul sanitarizat printr-un ORM sau un query builder (Knex/Prisma) pe o conexiune de DB cu permisiuni strict de READ ONLY.
După ce funcția returnează datele din baza de date sub formă de JSON, trimit acel rezultat înapoi la OpenAI cu rolul tool. Abia atunci modelul sintetizează răspunsul final într-un text lizibil pentru utilizator.
Trade-off-uri reale și bube întâlnite
Merge brici pentru 85% din cazuri, dar există nuanțe pe care nu ți le spune nimeni în tutoriale:
- Latență dublă: Un apel cu function calling necesită de regulă cel puțin 2 round-trips către API-ul OpenAI (unul să determine funcția, altul să genereze răspunsul după ce primește datele). Am redus latența totală de la 4.2 secunde la 1.1 secunde folosind un model mai mic (
gpt-4o-mini) doar pentru parsarea intenției și extragerea parametrilor. - Context bloat: Dacă expui mai mult de 10-12 funcții ca tools, acuratețea alegerii scade dramatic. Soluția mea a fost să grupez funcțiile pe domenii (ex:
analytics,orders,users) și să încărc dinamic doar orchestratorul necesar. - Mecanism de fallback: Dacă baza de date returnează un set uriaș de date (ex: 5.000 de rânduri), nu poți arunca tot JSON-ul înapoi în LLM pentru că depășești context window-ul și rupi bugetul. Am pus o limită hard de 50 de rânduri agregate în codul Node.js.
Voi cum gestionați accesul agenților AI la datele din producție? Ați mers pe vizualizări SQL dedicate sau preferați să izolați totul în microservicii cu endpoints bine definite?