SDK
4. fejezet

Tool típusok és mikor melyiket

A platform öt interfészt és számos abstract alaposztályt biztosít. Ez a fejezet segít eldönteni, melyikből érdemes kiindulni.

4.1   Döntési útmutató

Az első kérdés mindig az: ki aktiválja a tool-t?

Aktiváló Interfész Tipikus use case
Az LLM dönti el a felhasználói kérdés alapján IChatbotTool Külső API, adatbázis-lekérdezés, egyedi logika, PDF, web keresés
Orchestrátor aktiválja – explicit prioritás szerint IOrchestratorActivatedTool Oldalanalízis, összefoglalás, RAG retrieval, interaktív Q&A
A platform – domain match alapján, minden kérésre IOrchestratorTool (→ AbstractOrchestratorTool) Domain-specifikus, összetett koordinációs logika
Nem a chat pipeline – offline batch indítás IIngestionTool Tudásbázis feltöltés, NER annotáció, gráf építés
Nem a chat pipeline – külső hívó indítja explicit módon, feladat-azonosítóval IAsyncTaskTool Hosszú futású, chat-től független backfill/batch feladat, lekérdezhető állapottal

Ha a tool-nak hiányos információ esetén vissza kell kérdezni a felhasználóhoz, az IClarifiableTool-t kell implementálni – ez az IChatbotTool és az IOrchestratorActivatedTool kombinációja, és az orchestrátor aktiválja.

4.2   IChatbotTool – LLM routing aktiválja

A platform a felhasználói kérdés alapján LLM-mel dönti el, hogy ezt a tool-t kell-e aktiválni. A döntés a getExecutableInstruction() tartalmán alapul. Az isEligible() metódus az LLM döntése előtt fut, és nem-funkcionális feltételeket (jogosultság, domain) ellenőriz.

ℹ️
Az IOrchestratorActivatedTool-t implementáló osztályok ki vannak zárva az LLM routing-ból – a platform sosem irányítja hozzájuk a kéréseket az automatikus tool-választáson keresztül.

Platform abstract osztályok – IChatbotTool alapon

OsztályMire valóVisszatérési mód
AbstractWebSearchTool Valós idejű web keresés; az eredmény kontextusba vagy kész válaszba kerülhet CONTEXT vagy ANSWERED
Közvetlen IChatbotTool impl. Egyedi logika (pl. időjárás, menetrend, intézményi adatbázis) bármelyik

4.3   IOrchestratorActivatedTool – csak orchestrátor aktiválja

Ezeket a tool-okat az LLM routing sosem aktiválja – az isEligible() sem fut le rajtuk. Kizárólag egy futó orchestrátor dönthet arról, mikor és milyen prioritással indítja el őket.

A prioritásrendszer

Minden IOrchestratorActivatedTool implementáláshoz kötelező a getPriority() metódus. A kisebb szám korábban fut:

@Override
public int getPriority() {
    return 0;  // az orchestrátor elsőként futtatja
}

Platform abstract osztályok – IOrchestratorActivatedTool alapon

OsztályMire valóPriority
AbstractPageAnalysisTool Oldalanalízis alapja – ebből örökölnek a specializált tool-ok 0
AbstractSummaryTool Gráf-alapú összefoglalás generálás 0
AbstractEntityAnalysisTool Entitás elemzés és kinyerés a tudásgráfból; a getExecutableInstruction() igen/nem kérdése írja le, mikor alkalmas a kérdés kiszolgálására 0
AbstractContextSearchTool Kontextus-előállítás deep fallback esetén; a mélykereső koordinátor kereső tool-ja 0
AbstractNeighborPageSearchTool Opcionális környezet-kereső a mélykereső koordinátorhoz (→ 6.10) 0
AbstractDeepSearchCoordinatorTool Mélykereső koordinátor deep fallback tool-ként (→ 6.10) 0
AbstractRAGRetrievalTool Domain-specifikus RAG retrieval; az orchestrátor fallback-ként mindig aktiválja Integer.MAX_VALUE
AbstractMcpTool Adatbeszerzés MCP szerverről, platform által kezelt tisztázó körrel, megerősítéssel és kiszivárgás-ellenőrzéssel (→ 14. fejezet) tetszőleges

Konstruktor és bekötés – no-arg + @PostConstruct

A plugin-aktiválási mechanizmus (Java ServiceLoader, orchestrátoroknál emellett a PluginLoader reflection-alapú példányosítása is) miatt minden plugin tool osztálynak – nem csak az orchestrátoroknak, hanem a fenti platform abstract osztályok (AbstractPageAnalysisTool és leszármazottai, AbstractNERIngestionTool, AbstractRAGRetrievalTool stb.) leszármazottainak is – publikus, paraméter nélküli konstruktorral kell rendelkeznie. A domain-specifikus függőségeket (NER ingestion tool, saját LLM asszisztens stb.) ezért nem lehet konstruktor-paraméterként átadni; a bevett minta:

@Component
public class MyCharacterTool extends AbstractEntityAnalysisTool {

    @Autowired
    private MyNERIngestionTool nerTool;

    @Autowired
    private MyCharacterAssistant characterAssistant;

    public MyCharacterTool() {
        super();
    }

    @PostConstruct
    @Override
    public void init() {
        setAbstractNERIngestionTool(nerTool);
        setToolAssistant(characterAssistant);
    }

    @Override
    public int getPriority() {
        return 0;
    }
}

Referencia implementáció a kódbázisban: EgriCsillagokCharacterTool (AbstractEntityAnalysisTool leszármazott) és testvérei, EgriCsillagokSummaryTool, EgriCsillagokContextSearchTool – mindegyik ugyanezt a no-arg konstruktor + @PostConstruct init() mintát követi, csak a bekötött setterek térnek el az adott alaposztály szerint.

⚠️
Ugyanez a szabály vonatkozik az orchestrátorokra is – részletesen az 5.3 alfejezet tárgyalja.

4.4   IClarifiableTool – interaktív Q&A

Az IClarifiableTool az IChatbotTool és az IOrchestratorActivatedTool kombinációja. Ha a tool hiányos paraméterezetést észlel, kérdést tesz fel a felhasználónak; a kapott válasszal folytatódik a feldolgozás.

Mivel IOrchestratorActivatedTool-t is implementál, kizárólag orchestrátor aktiválhatja – az LLM routing nem irányítja hozzá a kéréseket.

A getPriority() értéke tipikusan 0 (az orchestrátor elsőként kérdez), a domain megadható (null = domain-független).

A részletes megvalósítási útmutató a 7. fejezetben olvasható.

4.5   IOrchestratorTool – orchestrátor implementáció

Az IOrchestratorTool marker interfész: jelöli, hogy egy osztály orchestrátort valósít meg. A platform domain-egyezés alapján, minden kérésre automatikusan aktiválja – az LLM routing nem vesz részt benne.

Plugin fejlesztőként az AbstractOrchestratorTool abstract osztályból kell örökölni, amely implementálja az IChatbotTool-t és az IOrchestratorTool-t egyaránt. Kötelező feltételek:

A részletes implementálási útmutató az 5. fejezetben olvasható.

4.6   IIngestionTool – offline tudásbázis feltöltés

Az IIngestionTool kiterjeszti az IChatbotTool-t, de ki van zárva a chat pipeline-ból – a platform soha nem aktiválja felhasználói kérés hatására. Kizárólag az ingestion pipeline hívja meg az executeIngestion() metódust, jellemzően REST API-on keresztül indított batch feldolgozás részeként.

Főbb szabályok:

A platform AbstractNERIngestionTool abstract osztályt biztosít, amely a Spark-alapú NER pipeline-t (entitáskinyerés, gráf építés) tartalmazza; a fejlesztőnek a domain-specifikus részt kell implementálnia. Részletek a 10. fejezetben.

💡
Nagyon nagy korpuszoknál (több tízezer dokumentum, milliós oldalszám) a mondat-szintű vektorizálás mérete és költsége aránytalanná válhat – erre a platform egy alternatív, page-level ingestion/retrieval módot kínál, domain+system alapon bekapcsolható. Részletek: 10.5 alfejezet, a döntési hook: 5.6 alfejezet.

4.7   Teljes áttekintő táblázat

Interfész / osztály LLM routing Orchestrátor aktiválja Chat pipeline Ingestion Async Task Service
IChatbotTool ✅—✅——
AbstractWebSearchTool ✅—✅——
IOrchestratorActivatedTool ❌✅———
AbstractPageAnalysisTool (+ leszármazottak) ❌✅———
AbstractRAGRetrievalTool ❌✅ (fallback)———
IClarifiableTool ❌✅✅ (clarify endpoint)——
AbstractMcpTool ❌✅✅ (clarify endpoint)——
IOrchestratorTool / AbstractOrchestratorTool ❌—✅ (domain match)——
IIngestionTool / AbstractNERIngestionTool ❌—❌✅—
IAsyncTaskTool / AbstractGraphEnrichmentTask ❌—❌—✅

4.8   Tool-ok együttműködése

Egy IOrchestratorActivatedTool építhet egy másik, ugyanabban a prioritáscsoport-futtatásban részt vevő tool kimenetére. A platform két, egymást kiegészítő mechanizmust kínál erre, attól függően, hogy a kapcsolat kiegészítő jellegű-e, vagy a működés szempontjából elengedhetetlen.

InterfészMikor használdAktiválás garantált?
IContextConsumerTool A másik tool kimenete csak pontosítja/kiegészíti az eredményt – hiányában is értelmes, csak esetleg általánosabb választ ad Nem – csak akkor kap adatot, ha a másik tool egyébként is lefutott
IToolDependent A másik tool kimenete nélkül a működés értelmezhetetlen Igen – az orchestrátor kikényszeríti a megjelölt tool-ok lefutását
IAgentCoordinator A szükséges tool-ok köre és sorrendje a feladat tartalmától függ – a coordinator futásidőben, dinamikusan hozza létre a rész-lépéseket Rész-lépésenként igen – a rész-lépés saját requiredToolNames mezője kényszeríti ki

IAgentCoordinator – dinamikus feladat-szétbontás

A fenti két mechanizmus egy már létező, statikus tool-halmazra épít. Ha a szükséges tool-ok köre és sorrendje csak a feladat tényleges tartalmából derül ki - futásidőben, esetenként eltérő módon -, az IAgentCoordinator interfész ad erre megoldást: egy tool a saját algoritmusa szerint bonthatja rész-lépésekre a feladatot, rész-lépésenként pontosan megjelölve, melyik tool-nak kell rajta aktiválódnia. A teljes szerződést, a rész-lépés-fa végrehajtási szabályait és a tool-ok együttműködésének egy negyedik, orchestrátort teljesen megkerülő csatornáját (közvetlen, kódbeli metódushívás két tool között) a 6. fejezet tárgyalja.

IContextConsumerTool – opcionális kiegészítés

Az orchestrátor egy implementáló tool-t mindig az executeWithPriorContext(...) metódusán keresztül hajt végre, és paraméterként átadja az addig lefutott tool-ok összesített kontextus-pufferének pillanatképét (kulcs: taskNo:toolName alakú completion-kulcs; a map soha nem null). A tool-ok singleton példányok, amelyek párhuzamos kéréseket szolgálnak ki, ezért a kapott kontextust nem szabad példánymezőben tárolni - csak az adott hívás idejére, paraméterként vagy helyi változóként használható. A kontextus nélküli executeWithResult(...) meghívása egy ilyen tool-on hiba: az interfész alapértelmezése UnsupportedOperationException-t dob, így azt nem kell implementálni.

public class SuggestionTool implements IOrchestratorActivatedTool, IContextConsumerTool<Object> {

    @Override
    public ToolExecutionResult executeWithPriorContext(List<String> systems, String userPrompt,
            String userLang, List<String> importantWords,
            Map<String, List<PromptAndContext>> priorToolContexts,
            IResponseEmitter responseEmitter,
            CallbackWithErrorHandler<OpenAIResponseVO, Exception> finalCallback) {
        // lehet null – nem hibaeset, ilyenkor általános javaslatokra esünk vissza
        List<PromptAndContext> extractedEntities = priorToolContexts.get("1:entity_extraction");
        ...
    }

    @Override
    public int getPriority() { return 10; } // magasabb szám = később fut
}

Ha a tool tisztázó kérdést is feltehet, az IClarifiableContextConsumerTool-t implementálja: a tisztázó kör folytatásakor az orchestrátor az executeWithClarificationAndPriorContext(session, priorToolContexts, ...) metódust hívja, a kontextus nélküli executeWithClarification(...) alapértelmezése itt is kivételt dob.

⚠️
Ez a mechanizmus nem garantálja, hogy a hivatkozott tool egyáltalán lefut ebben a körben – az LLM suitability teszt döntésén vagy más aktiválási feltételen múlik. Ha ez a garancia szükséges, az IToolDependent a megfelelő eszköz.

IToolDependent – garantált függőség

A getRequiredTools() konkrét, típusos tool-példányokat sorol fel – nem neveket –, amelyekre a működés épül. Az orchestrátor ezeket a saját prioritáscsoportjuk előtt garantáltan lefuttatja, akkor is, ha az LLM suitability teszt önmagában nem aktiválta volna őket. Minden megjelölt tool-nak szigorúan kisebb prioritással kell futnia, mint a rá támaszkodó tool-nak – induláskor a platform ellenőrzi ezt, és hibát naplóz, ha a sorrend rossz.

A lefutott függőségek kontextusát az executeWithRequiredContext(...) egyetlen hívása kapja meg paraméterként (nem egy korábbi, elkülönült hívással beállított mezőből) – ez singleton scope-ú tool-ok esetén is szálbiztos marad, mert nincs hívások közötti megosztott állapot.

@Component
public class RiskAssessmentTool implements IOrchestratorActivatedTool, IToolDependent {

    @Autowired
    private PatientLookupTool upstreamPatientTool;

    @Override
    public Set<IChatbotTool<?>> getRequiredTools() {
        return Set.of(upstreamPatientTool);
    }

    @Override
    public int getPriority() { return 10; } // az upstreamPatientTool.getPriority()-nál nagyobb!

    @Override
    public ToolExecutionResult executeWithRequiredContext(
            List<String> systems, String userPrompt, String userLang, List<String> importantWords,
            Map<String, List<PromptAndContext>> requiredToolContexts,
            FluxSink<TextGenerationResponse> progressEmitter,
            CallbackWithErrorHandler<OpenAIResponseVO, Exception> finalCallback) {

        List<PromptAndContext> patientData = requiredToolContexts.get(upstreamPatientTool.getName());
        if (patientData == null || patientData.isEmpty()) {
            return null; // az upstream tool nem adott adatot – nincs miből dolgozni
        }
        // ... kockázatértékelés a patientData alapján
        return ToolExecutionResult.asGuaranteedContext(getName(), resultDocs);
    }

    @Override
    public ToolExecutionResult executeWithResult(List<String> systems, String userPrompt, String userLang,
            List<String> importantWords, FluxSink<TextGenerationResponse> progressEmitter,
            CallbackWithErrorHandler<OpenAIResponseVO, Exception> finalCallback) {
        // Csak akkor hívódik, ha a tool nem az orchestrátor prioritáscsoport-motorján
        // keresztül aktiválódott (pl. explicit toolName aktiválás) – ilyenkor a
        // requiredToolContexts üres lesz, a tool a saját fallback-jét futtathatja
        // (pl. az upstream tool közvetlen meghívásával).
        return executeWithRequiredContext(systems, userPrompt, userLang, importantWords, Map.of(),
                progressEmitter, finalCallback);
    }
}
💡
A requiredToolContexts map kulcsa a függő tool egyszerű neve (getName()), nem a belső taskNo:toolName completion-kulcs – ezt az orchestrátor már leképezve adja át.

Ha egy tool olyan tartalmat ad tovább egy másik tool-nak, ami kizárólag a fogadó tool-nak szól – akár IContextConsumerTool, akár IToolDependent csatornán –, és nem szabad, hogy bekerüljön a végső LLM-válasz vagy a RAG pipeline kontextusába, a dokumentumot InternalContextDocument-ként érdemes becsomagolni a szokásos TextAnswer helyett. Az orchestrátor a feldolgozás összegzésekor minden InternalContextDocument példányt kiszűr, mielőtt a kontextus a következő lépéshez (RAG, végső LLM hívás) kerülne – függetlenül attól, hogy a tartalma strukturált adat (pl. JSON-serializált bean) vagy egy tool által LLM-mel szintetizált leírás. A megkülönböztetés nem a tartalom jellegén, hanem a címzetten múlik: kizárólag tool-nak szól, vagy a végfelhasználói válasznak is.

HashSet<UniqueTextDocument> docs = new HashSet<>();
docs.addAll(TextAnswer.asSet(normalizedSummary));            // a végső válaszba is bekerülhet
docs.addAll(InternalContextDocument.asSet(toJson(data)));    // csak a következő tool-nak szól
return ToolExecutionResult.asGuaranteedContext(getName(), docs);
for (UniqueTextDocument doc : patientData) {
    if (doc instanceof InternalContextDocument && doc.getText() != null) {
        PatientCaseData data = objectMapper.readValue(doc.getText(), PatientCaseData.class);
        // ...
    }
}

4.9   IAsyncTaskTool – külső hívó indítja explicit módon

Az IAsyncTaskTool sem a chat pipeline-nak, sem az ingestion pipeline-nak nem része – egy külső hívó a Platform API-n keresztül, a feladat típusazonosítójának megnevezésével indítja el, strukturáltan megadott bemeneti paraméterekkel. A végrehajtás a háttérben zajlik, állapota egy visszaadott feladat-azonosítóval, a végrehajtástól függetlenül kérdezhető le.

A részletes implementálási útmutató a 12. fejezetben, a hozzá tartozó végpontok pedig a 13. fejezetben olvashatók.