Konfigurierbare Agentenformulare
Dieser Leitfaden erklärt, wie Sie Agenten-Konfigurationsformulare definieren, die es Administratoren ermöglichen, Agentenprofile ohne Codeänderungen zu erstellen und anzupassen.
Überblick
Das SDK verwendet das Formular-Dualitätsmuster, bei dem ein einziges Pydantic-Modell zwei Zwecken dient:
- Formularmodus: Felder enthalten
FormkitElement-Instanzen, die das UI-Formular definieren - Datenmodus: Felder enthalten primitive Werte, die die validierte Konfiguration speichern
Dieses Muster stellt sicher, dass das Formularschema und das Datenmodell nicht desynchronisiert werden können.
Grundlegendes Muster
Definieren einer konfigurierbaren AgentConfig
from typing import Annotated
from pydantic import Field
from swiss_ai_hub.core.agents.agent_config import AgentConfig
from swiss_ai_hub.core.i18n.locale_string import LocaleString
from swiss_ai_hub.core.form.constraints import Ge, Le
from swiss_ai_hub.core.form.elements import InputNumber, InputText
class MyAgentConfig(AgentConfig):
# Field with duality: str for data mode, InputText for form mode
model_name: Annotated[
str | InputText,
Field(description="The LLM model to use"),
] = "gpt-4"
# Numeric field with constraints
temperature: Annotated[
float | InputNumber,
Field(description="LLM temperature"),
Ge(0.0), # Minimum value
Le(1.0), # Maximum value
] = 0.7
@classmethod
def as_form(cls) -> "MyAgentConfig":
"""Create form-mode config with FormKit elements for UI rendering."""
base = AgentConfig.as_form()
return cls(
# Inherit base fields from AgentConfig
agent_id=base.agent_id,
name=base.name,
description=base.description,
icon=base.icon,
# Define form elements for custom fields
model_name=InputText(
label=LocaleString(en="Model", de="Modell"),
help=LocaleString(en="Select the LLM model to use"),
),
temperature=InputNumber(
label=LocaleString(en="Temperature", de="Temperatur"),
help=LocaleString(en="Controls response creativity (0=focused, 1=creative)"),
min=0.0,
max=1.0,
step=0.1,
),
)Registrierung beim AgentRunner
from swiss_ai_hub.agent.runners.agent_runner import AgentRunner
from .MyAgent import MyAgent
from .MyAgentConfig import MyAgentConfig
async def main():
runner = AgentRunner(
agent_type=MyAgent,
agent_config=MyAgentConfig.as_form(), # Form mode!
)
await runner.run_forever()Der Aufruf von as_form() erstellt eine Konfigurationsinstanz im Formularmodus. Wenn der Agent sich über Discovery registriert, wird das Formularschema extrahiert und gespeichert. Administratoren können dann Profile über die Admin-Benutzeroberfläche erstellen.
Verfügbare FormKit-Elemente
Texteingabe
from swiss_ai_hub.core.form.elements import InputText
system_prompt: Annotated[str | InputText, Field()] = "You are a helpful assistant."
# In as_form():
system_prompt=InputText(
label=LocaleString(en="System Prompt"),
help=LocaleString(en="Instructions for the agent"),
required=True,
)Numerische Eingabe
from swiss_ai_hub.core.form.elements import InputNumber
from swiss_ai_hub.core.form.constraints import Ge, Le
max_tokens: Annotated[int | InputNumber, Field(), Ge(1), Le(4096)] = 1024
# In as_form():
max_tokens=InputNumber(
label=LocaleString(en="Max Tokens"),
min=1,
max=4096,
step=1,
)Boolescher Umschalter
from swiss_ai_hub.core.form.elements import ToggleSwitch
enable_citations: Annotated[bool | ToggleSwitch, Field()] = True
# In as_form():
enable_citations=ToggleSwitch(
label=LocaleString(en="Enable Citations"),
help=LocaleString(en="Include source citations in responses"),
)Dropdown-Auswahl
from swiss_ai_hub.core.form.elements import Select
response_format: Annotated[str | Select, Field()] = "text"
# In as_form():
response_format=Select(
label=LocaleString(en="Response Format"),
options=[
{"label": "Plain Text", "value": "text"},
{"label": "Markdown", "value": "markdown"},
{"label": "JSON", "value": "json"},
],
)Mehrsprachige Eingabe
from swiss_ai_hub.core.i18n.locale_string import LocaleString
from swiss_ai_hub.core.form.elements import LocaleInput
greeting: Annotated[LocaleString | LocaleInput, Field()]
# In as_form():
greeting=LocaleInput(
label=LocaleString(en="Greeting Message"),
help=LocaleString(en="Shown when conversation starts"),
)Modell-Auswahl
from swiss_ai_hub.core.form.elements import ModelSelect
llm_model: Annotated[str | ModelSelect, Field()] = "gpt-4"
# In as_form():
llm_model=ModelSelect(
label=LocaleString(en="Language Model"),
help=LocaleString(en="Select from available models"),
)Das ModelSelect-Element wird zur Laufzeit automatisch aus dem LiteLLM-Modellregister befüllt.
Formularsichere Constraints
Standard-Pydantic-Constraints (Field(ge=0, le=1)) funktionieren nicht mit dem Dualitätsmuster, da sie FormkitElement-Typen nicht validieren können. Verwenden Sie stattdessen die vom SDK bereitgestellten Constraints:
from swiss_ai_hub.core.form.constraints import Ge, Le, Gt, Lt, MinLen, MaxLen, Pattern
# Numeric constraints
temperature: Annotated[float | InputNumber, Ge(0.0), Le(1.0)] = 0.7
min_score: Annotated[float | InputNumber, Gt(0.0)] = 0.1 # Greater than (exclusive)
# String constraints
api_key: Annotated[str | InputText, MinLen(10), MaxLen(100)] = ""
agent_id: Annotated[str | InputText, Pattern(r"^[a-z0-9_-]+$")] = ""Diese Constraints überspringen die Validierung, wenn das Feld ein FormkitElement enthält, wodurch die Pydantic-Validierung in beiden Modi funktioniert.
Nicht-konfigurierbare Felder
Einige Felder sollten nicht im Formular erscheinen (bereitstellungsspezifische Konfiguration). Lassen Sie die FormKit-Element-Alternative weg:
class MyAgentConfig(AgentConfig):
# Configurable (appears in form) - has FormKit alternative
model_name: Annotated[str | InputText, Field()] = "gpt-4"
# Non-configurable (set at deployment) - no FormKit alternative
channel_config: TeamsConfig
@classmethod
def as_form(cls, channel_config: TeamsConfig) -> "MyAgentConfig":
base = AgentConfig.as_form()
return cls(
agent_id=base.agent_id,
name=base.name,
description=base.description,
icon=base.icon,
model_name=InputText(label=LocaleString(en="Model")),
channel_config=channel_config, # Actual value, not FormKit element
)Nicht-konfigurierbare Felder werden zur Laufzeit mit der vom Benutzer übermittelten Konfiguration zusammengeführt. Das Formular zeigt nur konfigurierbare Felder an.
Verschachtelte Formulare
Formulare können andere Formulare unter Verwendung des Group-Elements enthalten:
from swiss_ai_hub.core.form.form import Form
from swiss_ai_hub.core.form.elements import InputNumber, ModelSelect
class LLMConfig(Form):
"""Nested form for LLM settings."""
model_name: Annotated[str | ModelSelect, Field()] = "gpt-4"
temperature: Annotated[float | InputNumber, Ge(0.0), Le(1.0)] = 0.7
max_tokens: Annotated[int | InputNumber, Ge(1), Le(4096)] = 1024
@classmethod
def as_form(cls) -> "LLMConfig":
return cls(
model_name=ModelSelect(label=LocaleString(en="Model")),
temperature=InputNumber(label=LocaleString(en="Temperature"), min=0.0, max=1.0),
max_tokens=InputNumber(label=LocaleString(en="Max Tokens"), min=1, max=4096),
)
class MyAgentConfig(AgentConfig):
llm: Annotated[LLMConfig, Field(title="LLM Settings")]
@classmethod
def as_form(cls) -> "MyAgentConfig":
base = AgentConfig.as_form()
return cls(
agent_id=base.agent_id,
name=base.name,
description=base.description,
icon=base.icon,
llm=LLMConfig.as_form(), # Nested form
)Verschachtelte Formulare werden in der Benutzeroberfläche automatisch als einklappbare Group-Elemente gerendert.
Repeater-Formulare (Arrays)
Für Listen von konfigurierbaren Elementen verwenden Sie eine Liste von Formularen:
class ExampleForm(Form):
"""Single example for few-shot learning."""
input_text: Annotated[str | InputText, Field()] = ""
expected_output: Annotated[str | InputText, Field()] = ""
@classmethod
def as_form(cls) -> "ExampleForm":
return cls(
input_text=InputText(label=LocaleString(en="Input")),
expected_output=InputText(label=LocaleString(en="Expected Output")),
)
class MyAgentConfig(AgentConfig):
examples: Annotated[list[ExampleForm], Field(title="Few-Shot Examples")]
@classmethod
def as_form(cls) -> "MyAgentConfig":
base = AgentConfig.as_form()
return cls(
agent_id=base.agent_id,
name=base.name,
description=base.description,
icon=base.icon,
examples=[ExampleForm.as_form()], # List with one template
)Listen von Formularen werden als Repeater-Elemente gerendert, die es Benutzern ermöglichen, Elemente dynamisch hinzuzufügen/zu entfernen.
Zugriff auf Konfiguration in Schritten
Der Dispatcher injiziert die validierte Konfiguration über Typannotationen in Schrittmethoden:
from swiss_ai_hub.agent.workflow.decorators.step import step
class MyAgent(Agent):
@step()
async def process_message(
self,
event: UserMessageEvent,
agent_config: MyAgentConfig, # Injected by dispatcher
) -> ResponseEvent:
# Access configuration values
model = agent_config.model_name
temperature = agent_config.temperature
# Use nested config
max_tokens = agent_config.llm.max_tokens
# ...Die Konfiguration wird über RPC abgerufen, wenn der Agent ein StartEvent erhält, gegen das Pydantic-Modell validiert und für die Dauer des Laufs zwischengespeichert.
Bedingte Feldsichtbarkeit
Felder basierend auf den Werten anderer Felder anzeigen oder verbergen:
use_custom_prompt: Annotated[bool | ToggleSwitch, Field()] = False
custom_prompt: Annotated[str | InputText, Field()] = ""
# In as_form():
use_custom_prompt=ToggleSwitch(label=LocaleString(en="Use Custom Prompt")),
custom_prompt=InputText(
label=LocaleString(en="Custom Prompt"),
condition_if="$get(use_custom_prompt).value === true", # FormKit condition
)Der Parameter condition_if verwendet die Ausdruckssyntax von FormKit. Das Feld wird nur angezeigt, wenn die Bedingung als wahr ausgewertet wird.
Vollständiges Beispiel
from typing import Annotated
from pydantic import Field
from swiss_ai_hub.agent.agents.agent import Agent
from swiss_ai_hub.agent.runners.agent_runner import AgentRunner
from swiss_ai_hub.agent.workflow.decorators.step import step
from swiss_ai_hub.core.agents.agent_config import AgentConfig
from swiss_ai_hub.core.i18n.locale_string import LocaleString
from swiss_ai_hub.core.events.control.stop_event import StopEvent
from swiss_ai_hub.core.events.control.user_message_event import UserMessageEvent
from swiss_ai_hub.core.form.constraints import Ge, Le
from swiss_ai_hub.core.form.elements import InputNumber, InputText, ModelSelect, ToggleSwitch
class QAAgentConfig(AgentConfig):
"""Configuration for a question-answering agent."""
model_name: Annotated[str | ModelSelect, Field(description="LLM model")] = "gpt-4"
temperature: Annotated[float | InputNumber, Field(), Ge(0.0), Le(1.0)] = 0.3
system_prompt: Annotated[str | InputText, Field()] = "You are a helpful assistant."
enable_citations: Annotated[bool | ToggleSwitch, Field()] = True
@classmethod
def as_form(cls) -> "QAAgentConfig":
base = AgentConfig.as_form()
return cls(
agent_id=base.agent_id,
name=base.name,
description=base.description,
icon=base.icon,
model_name=ModelSelect(
label=LocaleString(en="Model", de="Modell"),
help=LocaleString(en="Language model for generating responses"),
),
temperature=InputNumber(
label=LocaleString(en="Temperature", de="Temperatur"),
help=LocaleString(en="Lower = more focused, Higher = more creative"),
min=0.0,
max=1.0,
step=0.1,
),
system_prompt=InputText(
label=LocaleString(en="System Prompt", de="System-Prompt"),
help=LocaleString(en="Instructions for the assistant"),
),
enable_citations=ToggleSwitch(
label=LocaleString(en="Enable Citations", de="Zitate aktivieren"),
help=LocaleString(en="Include source references in answers"),
),
)
class QAAgent(Agent):
@step()
async def answer_question(
self,
event: UserMessageEvent,
agent_config: QAAgentConfig,
) -> StopEvent:
# Use configuration
model = agent_config.model_name
temp = agent_config.temperature
citations = agent_config.enable_citations
# ... generate answer using config ...
return StopEvent()
async def main():
runner = AgentRunner(
agent_type=QAAgent,
agent_config=QAAgentConfig.as_form(),
)
await runner.run_forever()Best Practices
Feld-Benennung
- Verwenden Sie beschreibende Feldnamen, die für Administratoren verständlich sind
- Stellen Sie
help-Text bereit, der erklärt, was jede Einstellung bewirkt - Fügen Sie sinnvolle Standardwerte hinzu
Validierung
- Verwenden Sie immer Constraints (
Ge,Le,Pattern) für numerische Felder und String-Felder - Validieren Sie frühzeitig, um zu verhindern, dass ungültige Konfigurationen gespeichert werden
Lokalisierung
- Verwenden Sie
LocaleStringfür alle benutzerspezifischen Texte (Labels, Hilfe, Beschreibungen) - Unterstützen Sie mindestens Deutsch und Englisch (
de,en)
Testen
- Testen Sie sowohl den Formularmodus (
as_form()) als auch den Datenmodus (mit tatsächlichen Werten) - Überprüfen Sie die Generierung des Formularschemas mit
config.to_formkit_form() - Testen Sie die Konfigurationsinjektion in Schrittmethoden
