AI zpracovani textu funguje: klic, adresa a tri skripty
Sluzba byla v katalogu bez pristupovych udaju a s appId 'ai-text', tedy mirila na aplikaci, ktera neexistuje. Konektor nemel co vyplnit a krok nemel co vykonat. - sluzba stoji na api.openai.com/v1 stejne jako OpenAI, overeni GET /models - model se bere z konektoru (ctx.config.model), ne z kazdeho kroku zvlast - skripty ai-text.classify, ai-text.extract, ai-text.generate Proc to neni jedna sluzba s OpenAI: tam se posila volny dotaz a vraci volny text. Tady je uloha pevna a vystup taky - category a confidence, objekt s popsanymi poli, text s poctem slov. Na to jde ve strome navazat podminkou, na volny text ne. Zjisteno u toho a zapsane do 21-realne-sluzby.md: proxy vraci na neznamou aplikaci 200 s prazdnym telem, takze overeni konektoru u sluzby bez verifyPath projde i tam, kde nic nebezi. Zatim neopraveno. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d71c111cf3
commit
aa0a783095
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* AI zpracovani textu: zarazeni do kategorie.
|
||||
*
|
||||
* Sluzba: https://api.openai.com/v1 (nebezi u nas, adresa je v katalogu)
|
||||
* Endpoint: POST /chat/completions
|
||||
*
|
||||
* Rozdil proti sluzbe OpenAI: tam se posila volny dotaz a vraci se volny text.
|
||||
* Tady je uloha pevna a **vystup taky** - `category` a `confidence`. Prave na
|
||||
* to se da ve strome navazat podminkou, coz u volneho textu nejde.
|
||||
*
|
||||
* Model se bere z konektoru (`ctx.config.model`), aby se nemusel vyplnovat
|
||||
* u kazdeho kroku znovu.
|
||||
*/
|
||||
|
||||
export const manifest = {
|
||||
id: 'ai-text.classify',
|
||||
name: 'Zařadit do kategorie',
|
||||
description:
|
||||
'Rozhodne, do které z uvedených kategorií text patří. Vrací kategorii ' +
|
||||
'a jistotu, takže se za krokem dá větvit podle toho, jak si je model jistý.',
|
||||
|
||||
inputs: [
|
||||
{
|
||||
id: 'categories',
|
||||
label: 'Seznam kategorií',
|
||||
type: 'string',
|
||||
required: true,
|
||||
hint: 'Oddělte čárkou, například: porucha, dotaz, reklamace.',
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
label: 'Text',
|
||||
type: 'string',
|
||||
required: true,
|
||||
multiline: true,
|
||||
hint: 'Co se má zařadit. Typicky předmět a tělo ticketu.',
|
||||
},
|
||||
{
|
||||
id: 'instructions',
|
||||
label: 'Doplňující pravidla',
|
||||
type: 'string',
|
||||
required: false,
|
||||
multiline: true,
|
||||
hint: 'Například: reklamací se rozumí jen stížnost na už dodané zboží.',
|
||||
},
|
||||
],
|
||||
|
||||
outputs: [
|
||||
{ id: 'category', label: 'Kategorie', type: 'string', required: true },
|
||||
{ id: 'confidence', label: 'Jistota', type: 'number', required: true },
|
||||
{ id: 'matched', label: 'Kategorie ze seznamu', type: 'boolean', required: true },
|
||||
],
|
||||
|
||||
timeoutMs: 45000,
|
||||
};
|
||||
|
||||
export async function run(inputs, ctx) {
|
||||
const { pick, first, text, num, need } = ctx.util;
|
||||
|
||||
const categories = inputs.categories
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
if (categories.length < 2) ctx.fail('Uveďte aspoň dvě kategorie, jinak není z čeho vybírat.');
|
||||
|
||||
const system =
|
||||
'Jsi klasifikátor. Vrátíš POUZE platný JSON ve tvaru ' +
|
||||
'{"category": "...", "confidence": 0.0}. Pole category musí být přesně jedna ' +
|
||||
`z těchto hodnot: ${categories.join(', ')}. Confidence je číslo od 0 do 1.` +
|
||||
(inputs.instructions ? ` Další pravidla: ${inputs.instructions}` : '');
|
||||
|
||||
const { body } = await ctx.http.post('/chat/completions', {
|
||||
model: ctx.config.model || 'gpt-4o-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: system },
|
||||
{ role: 'user', content: inputs.text },
|
||||
],
|
||||
response_format: { type: 'json_object' },
|
||||
});
|
||||
|
||||
const content = text(pick(pick(first(pick(body, 'choices')), 'message'), 'content'));
|
||||
if (!content) ctx.fail('Model nevrátil odpověď.', body);
|
||||
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch {
|
||||
ctx.fail(`Model nevrátil platný JSON: ${content}`);
|
||||
}
|
||||
|
||||
const category = need(text(pick(parsed, 'category')), 'kategorii');
|
||||
|
||||
/*
|
||||
* Model obcas vrati kategorii, ktera v seznamu neni, i kdyz ji ma zakazanou.
|
||||
* Neni to duvod krok shodit - vysledek se vrati a `matched` rekne, ze je
|
||||
* mimo seznam. Strom se pak muze rozhodnout, jestli mu verit.
|
||||
*/
|
||||
const matched = categories.some((item) => item.toLowerCase() === category.toLowerCase());
|
||||
if (!matched) ctx.log(`Model vrátil "${category}", což v seznamu není.`);
|
||||
|
||||
return {
|
||||
category,
|
||||
confidence: num(pick(parsed, 'confidence'), 0) ?? 0,
|
||||
matched,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user