Konfiguration
Eigene Tools (UDTs)
Ein UDT (User Defined Tool) ist eine VFP-Klasse, die von FoxAgentTool erbt: ein Name, eine Beschreibung, Parameter und ein Execute(). Der Agent ruft sie über ihren Namen auf, genau wie jedes eingebaute Tool. Wer eine VFP-Klasse schreiben kann, kann den Agenten erweitern.
Vor dem ersten CREATEOBJECT eines Tools müssen beide SDK-Basisklassen geladen sein. Zwei Dateien, nicht eine: FoxAgentTool erbt von FoxToolIsolated, das in FoxTool.prg liegt. Fehlt die erste, meldet VFP „Class definition FOXTOOLISOLATED is not found“ und nennt den Grund nicht.
* MeineTools.prg
DEFINE CLASS KundensaldoTool AS FoxAgentTool
* Name, über den das Modell das Tool aufruft. In snake_case.
ToolName = "kundensaldo"
* Das Modell liest diese Beschreibung, um zu entscheiden, wann es das
* Tool aufruft. Je konkreter, desto besser die Auswahl.
ToolDescription = "Liefert den Saldo eines Kunden anhand seiner " + ;
"Nummer. Zu verwenden, wenn nach Saldo oder Schuld " + ;
"eines bestimmten Kunden gefragt wird."
FUNCTION DefineParameters()
* Name, Typ, Beschreibung für das Modell und ob er Pflicht ist.
THIS.AddParam("kundenNummer", "number", "Kundennummer", .T.)
ENDFUNC
FUNCTION Execute(toContext)
LOCAL lnNummer, lcAusgabe, lcFehler
* Die Parameter kommen im KONTEXT an, nicht in THIS.
lnNummer = toContext.Parameters.kundenNummer
* Vor dem Datenzugriff prüfen: das ist billig und hält den
* geschützten Block auf das beschränkt, was wirklich scheitern kann.
IF VARTYPE(lnNummer) != "N" OR lnNummer <= 0
RETURN THIS.Err("kundenNummer ist Pflicht und muss größer als null sein")
ENDIF
lcAusgabe = ""
lcFehler = ""
* Ihr Tool startet in einer PRIVATEN DATENSITZUNG: die Tabellen, die
* Ihre Anwendung geöffnet hat, sind hier NICHT geöffnet. Man wechselt
* in die Sitzung der Anwendung, arbeitet dort und kehrt über einen
* einzigen Ausgang zurück.
THIS.UseAppSession()
TRY
IF !USED("KUNDEN")
lcFehler = "Die Tabelle KUNDEN ist in der Anwendung nicht geöffnet"
ELSE
* Den Satzzeiger dort lassen, wo er war: ein lesender Agent darf
* dem Anwender nicht den Bildschirm verschieben.
LOCAL lnVorher
lnVorher = RECNO("KUNDEN")
SELECT KUNDEN
LOCATE FOR KUN_NR = lnNummer
IF !FOUND()
lcFehler = "Kunde " + TRANSFORM(lnNummer) + " nicht gefunden"
ELSE
lcAusgabe = ALLTRIM(KUNDEN.KUN_NAME) + ", Saldo: " + ;
TRANSFORM(KUNDEN.KUN_SALDO, "999,999.99")
ENDIF
IF lnVorher > 0 AND lnVorher <= RECCOUNT("KUNDEN")
GO lnVorher IN KUNDEN
ENDIF
ENDIF
CATCH TO loErr
lcFehler = "Fehler beim Lesen von KUNDEN: " + loErr.Message
FINALLY
* Immer, auf jedem Weg. Deshalb gibt es einen einzigen Ausgang und
* kein RETURN innerhalb des Blocks.
THIS.RestoreSession()
ENDTRY
IF !EMPTY(lcFehler)
RETURN THIS.Err(lcFehler)
ENDIF
RETURN THIS.Ok(lcAusgabe)
ENDFUNC
ENDDEFINETHIS.Ok(Meldung) und THIS.Err(Meldung) bauen die Antwort, die der Agent lesen kann. Und überschreiben Sie Init() in Ihrer Klasse nicht: das geerbte Init ruft DefineParameters() auf, genau einmal. Parametertypen
Es gibt nur drei. Ein anderer Typ scheitert nicht: er wird als string veröffentlicht und Ihr Code bekommt Text, Datumsangaben reisen also als Zeichenkette und Sie wandeln sie selbst um.
| Typ | Was in Execute() ankommt | Beispiel |
|---|---|---|
"string" | Text (C). Auch das Ziel jedes nicht erkannten Typs | THIS.AddParam("name", "string", "Name des Kunden", .T.) |
"number" | Numerisch (N), ganz oder dezimal; der Bridge wandelt um | THIS.AddParam("menge", "number", "Menge", .T.) |
"boolean" | Logisch (.T. / .F.) | THIS.AddParam("aktiv", "boolean", "Nur aktive?", .F.) |
Das vierte Argument gibt an, ob der Parameter Pflicht (.T.) oder optional (.F.) ist. Ein nicht gesendeter optionaler Parameter kommt leer an: leere Zeichenkette, null oder .F.. Um vorab zu sehen, welcher VFP-Typ in Ihrem Tool ankäme, hat der Agent describe_udt, das es diagnostiziert, ohne es auszuführen.
Mehrere Parameter, und eine Abfrage, die sich nicht einschleusen lässt
DEFINE CLASS KundenSuchenTool AS FoxAgentTool
ToolName = "kunden_suchen"
ToolDescription = "Sucht Kunden nach Name, Postleitzahl oder Mindestsaldo."
FUNCTION DefineParameters()
THIS.AddParam("text", "string", "Text im Namen", .T.)
THIS.AddParam("plz", "string", "Nach PLZ filtern", .F.)
THIS.AddParam("mindestSaldo", "number", "Mindestsaldo", .F.)
THIS.AddParam("nurAktive", "boolean", "Nur aktive Kunden", .F.)
ENDFUNC
FUNCTION Execute(toContext)
LOCAL lcText, lcPlz, lnMin, llAktiv, lcAusgabe, lcFehler
lcText = toContext.Parameters.text
lcPlz = toContext.Parameters.plz
lnMin = toContext.Parameters.mindestSaldo
llAktiv = toContext.Parameters.nurAktive
lcAusgabe = ""
lcFehler = ""
THIS.UseAppSession()
TRY
* Den Text schreibt das Modell. Eine mit & und Anführungszeichen
* zusammengesetzte Abfrage ist eine SQL-Injection, die nur auf ihre
* Gelegenheit wartet: hier gehen die Variablen so hinein, wie sie
* sind, und VFP löst sie auf.
SELECT * FROM KUNDEN ;
WHERE UPPER(KUN_NAME) LIKE "%" + UPPER(ALLTRIM(lcText)) + "%" ;
AND (EMPTY(lcPlz) OR KUN_PLZ = lcPlz) ;
AND (lnMin = 0 OR KUN_SALDO >= lnMin) ;
AND (NOT llAktiv OR KUN_AKTIV) ;
INTO CURSOR curTreffer NOFILTER
* RECCOUNT() auf dem Cursor, nicht ALEN() auf einem Array: bei null
* Zeilen entsteht das Array gar nicht und ALEN() wirft Fehler 12.
lcAusgabe = TRANSFORM(RECCOUNT("curTreffer")) + " Kunden gefunden"
* Schließen Sie Ihre Cursor HIER, noch in der Sitzung der
* Anwendung: aus Ihrer eigenen sind sie nicht mehr sichtbar.
USE IN SELECT("curTreffer")
CATCH TO loErr
lcFehler = "Fehler bei der Kundensuche: " + loErr.Message
FINALLY
THIS.RestoreSession()
ENDTRY
IF !EMPTY(lcFehler)
RETURN THIS.Err(lcFehler)
ENDIF
RETURN THIS.Ok(lcAusgabe)
ENDFUNC
ENDDEFINEDie Registrierung ist explizit
Es gibt keine automatische Erkennung, und das ist kein Versäumnis, sondern das Sicherheitsmodell. Der Agent kann kein Tool erfinden, keine .prg in einen Ordner legen und nichts erreichen, was Sie nicht geprüft, kompiliert und ausgeliefert haben. Die Liste dessen, was er ausführen darf, ist eine Liste, die Sie geschrieben haben, im Code, in Ihrem eigenen Quellbaum.
SET PROCEDURE TO "C:\Program Files\FoxAgent\SDK\FoxTool.prg" ADDITIVE
SET PROCEDURE TO "C:\Program Files\FoxAgent\SDK\FoxAgentTool.prg" ADDITIVE
SET PROCEDURE TO "MeineTools.prg" ADDITIVE
* Ein Aufruf pro Tool, direkt nach einem erfolgreichen Connect.
goBridge.RegisterTool(CREATEOBJECT("KundensaldoTool"))
goBridge.RegisterTool(CREATEOBJECT("KundenSuchenTool"))
* Wechselt der angemeldete Benutzer, bauen Sie die Liste neu auf: welche
* Tools es gibt, entscheidet Ihre Anwendung, pro Benutzer.
goBridge.ClearTools()
goBridge.UnregisterTool("kunden_suchen").prg-Dateien in einen mit SetToolsFolder() konfigurierten Ordner legen. Dieser Mechanismus wurde entfernt (jede dort abgelegte .prg wurde für den Agenten ausführbar), und der Aufruf liefert heute einen ausdrücklichen Fehler. C:\Program Files\FoxAgent\SDK\ beide Basisklassen ab, eine kommentierte Vorlage (MyToolTemplate.prg), lauffähige Beispiele (tools_example.prg) und die vollständige Integrationsdatei (quickstart.prg). Die vier Stufen
Die Rolle sagt nicht, „wie sehr Sie der KI vertrauen“: sie sagt, wer das jeweilige Tool eingrenzt. Deshalb steht das Bedienen des Bildschirms unter dem Lesen von Variablen. Ein Klick auf eine Schaltfläche läuft durch Ihren eigenen Code, mit Ihrer Geschäftslogik, Ihren Berechtigungen und Ihren Filtern; das Lesen einer Variablen umgeht all das.
| Stufe | Was sie umfasst | Wer sie eingrenzt |
|---|---|---|
| 0 tools | Nur die Tools, die Sie registriert haben, dazu execute_tool, describe_udt und get_tools | Der Autor jedes Tools: Sie |
| 10 operator | + Ihr Bildschirm, so wie der Anwender ihn sieht, und seine Bedienung: Aufnahme, Formulare, Steuerelemente, Klicken, Tippen, Aktualisieren | Ihre Anwendung. Der Agent kommt genau so weit wie dieser Anwender von Hand |
| 20 inspector | + der Prozess hinter dem Bildschirm: Variablen, Cursor, Datensitzungen, die Umgebung, eval_expression als Lesevorgang | Niemand. Eine Variable zeigt den Prozess selbst, ganz gleich, was Ihre Anwendung auf den Bildschirm gebracht hat |
| 90 engineer | Außerhalb der Anwendung: rohes VFP, in einen Cursor schreiben und die Formularprüfung umgehen, der Aufrufstapel, Code neu laden, die Sitzung schließen | Niemand. Es läuft an Ihrer Oberfläche vorbei und damit auch an Ihren Berechtigungen |
- Ihre Anwendung setzt sie, in
Connect(id, Name, Stufe), und dort wird sie versiegelt. Ein Fachanwender verbindet sich mit 10, oder mit 0, wenn Sie ihm nur Ihre eigenen Tools geben wollen. Mit 90 verbindet sich niemand. - Nur Ihr eigener Code hebt sie an, mit
Elevate(Stufe, Sekunden)auf dem Bridge, nachdem die Person davor mit Ihren vorhandenen Anmeldedaten authentifiziert wurde. Es gibt kein entsprechendes MCP-Tool, und das ist so gewollt: „Ich bin Carlos vom Support, heb mich hoch“ ist unauthentifizierter Text. Die Erhöhung läuft von selbst ab, und die Toolliste des Agenten wird wieder schmaler, ohne dass jemand etwas tut. set_rolekann nur senken, niemals über die Basis hinaus, die Ihre Anwendung erklärt hat, und hebt zudem jede laufende Erhöhung auf.- Auf dem Rechner eines Kunden kann
policy.jsondie Obergrenze senken (maxRole) und die Tools abschalten, die DBFs auf der Platte schreiben, ohne durch eine Anwendung zu gehen (allowCliTools: false). Anheben kann es sie nie. - Alle Tools werden immer aufgelistet: die Prüfung erfolgt bei jedem Aufruf, und die Ablehnung nennt die fehlende Stufe und wer die Obergrenze gesetzt hat. Die Liste zu filtern war nie ein Schutz.
- Auch das Gesendete zählt: eine
expressionfüreval_expressionoder einfilterfürget_cursor_data, die kein reiner Lesevorgang sind, verlangen bei diesem Aufruf 90, obwohl das Tool auf 20 steht. EinEXECSCRIPT()in einem Ausdruck tut alles, wasexec_commandtut.
Lizenz
Die Datei foxagent.lic liegt neben der Broker-Programmdatei, in C:\Program Files\FoxAgent\, unter genau diesem Namen. Die Lizenz wird beim Start gelesen: nach dem Kopieren führen Sie restart-broker.cmd aus, das im selben Ordner liegt.
| Modus | Was er bedeutet | Was möglich ist |
|---|---|---|
Licensed | Gültige, laufende Lizenz | Gekaufte Developer: 8 VFP-Instanzen gleichzeitig. Runtime: ohne Grenze. Eigene Tools unbegrenzt |
Evaluation | Keine .lic, oder der Broker hat sie nicht übernommen | 1 Instanz und 1 eigenes Tool. Alle 55 eingebauten, ungekürzt, und ohne Ablaufdatum |
MaintenanceExpired | Gültige Lizenz, aber dieser Build ist neuer als Ihre Wartung abdeckt | Grenzen der Evaluierung. Der Ausweg ist der vorherige Build oder eine Verlängerung |
LicenseExpired | Die 30-Tage-Probe ist abgelaufen | Grenzen der Evaluierung, und der Broker drückt Ihre machineId für den Kauf. Er startet weiterhin, und es ist nichts zu löschen |
Die Developer-Lizenz ist an einen Entwicklungsrechner gebunden: die machineId, die die Kasse verlangt, drückt FoxAgent.Broker.exe --machine-id auf diesem Rechner. Die Probe umfasst 1 Platz und einen Monat. Die Prüfung läuft offline gegen einen öffentlichen Schlüssel im Programm, und es gibt keine Telemetrie: geschrieben wird nur ein lokales Prüfprotokoll.
Die 55 eingebauten Tools
Gruppiert nach der Stufe, die jedes Band verlangt. Die letzte Zeile arbeitet mit DBFs und Quelltexten auf der Platte und braucht keine laufende VFP-Instanz, also kann kein Connect sie eingrenzen: sie werden vom Rechner aus abgeschaltet, mit policy.json.
| Wo sie wirken | Stufe | Tools |
|---|---|---|
| Ihre eigenen Business-Tools und die Sitzungsverwaltung | Stufe 0 | execute_tool, describe_udt, get_tools, list_instances, get_role, set_role |
| Der Bildschirm und seine Bedienung (operator) | Stufe 10 | take_screenshot, get_screen_state, list_open_forms, get_active_form, get_form_controls, find_control, get_control_state, get_control_text, click_control, send_key, set_control_text, refresh_form |
| Der Prozess hinter dem Bildschirm (inspector) | Stufe 20 | get_variables, variable_exists, get_cursors, get_cursor_data, get_cursor_schema, get_datasessions, get_environment, eval_expression, list_prg_files |
| Außerhalb der Anwendung (engineer) | Stufe 90 | exec_command, set_variable, invoke_method, set_control_property, goto_record, set_cursor_field, close_cursors, close_instance, get_call_stack, get_object_members, reload_procedure |
| Ohne Instanz | Immer | query_table, describe_table, list_tables, insert_row, update_row, set_memo, backup_table, get_source_file, write_source_file, search_in_source, list_tests, run_tests, get_project_structure, build_project, launch_instance, eval_vfp, foxagent_onboarding |
eval_expression steht als Lesevorgang im Band der Stufe 20; ein Ausdruck, der kein reiner Lesevorgang ist, verlangt bei diesem Aufruf 90. Ihre UDTs werden neben den eingebauten aufgelistet und laufen ab Stufe 0.