Die Beispiele von Microsoft Learn zu Foundry gibt es in mehreren Sprachen, und die Seite der Prüfung nennt als Erfahrungsfeld Python. Dieser Kurs schreibt .NET (der Stack der Nordlicht Reisen GmbH und unser eigener); eigenen Python-Code gibt es hier nicht. Gelesen werden muss Python trotzdem: Wer ein Beispiel einer neuen Funktion sucht, findet es oft zuerst dort, und wer die Prüfung ablegt, sollte es lesen können. Diese Lektion zeigt, wie man ein Python-Beispiel liest, welche Zeile in C# welcher entspricht (nach den Lektionen 2.1 bis 2.4) und welche zwei Eigenheiten verwirren.

Vorwissen. Die C#-Seite der Muster steht in den Lektionen 2.1 (Projekt-Client, Responses), 2.3 (Agentenversion, Gespräch) und 2.4 (Werkzeuge); den IChatClient lehrt „KI-Track, Lektion T2.1“. Diese Lektion wiederholt sie nicht, sie ordnet sie zu.

Zu den Ausschnitten. Python steht hier nur als Lesefutter: kurze Ausschnitte in eigenen Worten und mit eigenen Namen, die das Muster der Learn-Beispiele zeigen. Sie sind nicht von Microsoft übernommen und wurden nicht ausgeführt. Die Namen der Aufrufe stammen aus den Learn-Beispielen, die Namen der Variablen sind unsere.

Die Anforderung

Alle Zahlen sind Annahmen der Aufgabe.

Nr. Anforderung Art Messgröße Gewicht Quelle
0.3-01 Ein Python-Beispiel von Learn mit höchstens 15 Zeilen wird in eigenen Worten erklärt. F 3 Teile benannt: Importe, Verbindung, Aufruf Muss Prüfungsliste
0.3-02 Zu jedem Muster des Kurses ist die C#-Zeile bekannt. F 10 Zuordnungen Muss Lektionen 2.1 bis 2.4
0.3-03 Asynchrone und Wörterbuch-Schreibweise werden erkannt. Q 2 von 2 Eigenheiten benannt Soll Lesbarkeit
0.3-04 Eigener Code bleibt .NET. R 0 neue Dateien in Python Muss Stack des Kurses

So liest man ein Beispiel

Ein Beispiel von Learn hat fast immer drei Teile. Importe nennen die Pakete: azure.identity (Anmeldung) und azure.ai.projects (Foundry-Projekt). Verbindung: ein Client, gebaut aus Projekt-Endpunkt und einer Anmeldung. Aufruf: eine Methode am Client oder an einem daraus abgeleiteten Client. Beim Lesen hilft die Reihenfolge der Fragen: Welcher Client ist es? Woher kommt der Name des Modells oder Agenten, aus einer Konfiguration oder als Text im Beispiel? Was kommt zurück, und welches Feld wird gelesen?

python
projekt = AIProjectClient(endpoint=ENDPOINT, credential=DefaultAzureCredential())
openai = projekt.get_openai_client()
antwort = openai.responses.create(model="chat", input="Hallo")
print(antwort.output_text)

Lesefutter 1. Verbindung, ein Client für Modellaufrufe, ein Aufruf, ein Feld. Im Beispiel von Learn steht ein Modellname als Text; im Kurs steht dort der Name der Aufgabe aus der Konfiguration (R-08). Die Seite der Prüfung sagt nicht, wie ihre Aufgaben Code zeigen: offen.

python
definition = PromptAgentDefinition(model="chat", instructions="Du hilfst bei Reisen.")
agent = projekt.agents.create_version(
    agent_name="reiseassistent", definition=definition
)
openai = projekt.get_openai_client(agent_name=agent.name)
gespraech = openai.conversations.create()

Lesefutter 2. Ein Agent entsteht als Version; ein zweiter Client ist an den Agenten gebunden; ein Gespräch entsteht am Client. Danach wird openai.responses.create(conversation=gespraech.id, input="…") gerufen, wie in Lektion 2.3.

Python und C# nebeneinander

Die rechte Spalte ist der Code aus den genannten Lektionen (Azure.AI.Projects 2.0.1, Azure.AI.Projects.Agents 2.0.0, Azure.AI.Extensions.OpenAI 2.0.0 und Azure.Identity 1.21.0, wo die Lektion nichts anderes sagt).

Muster Python in Beispielen von Learn C# in diesem Kurs Lektion
Projekt-Client AIProjectClient(endpoint=…, credential=DefaultAzureCredential()) new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential()) 2.1
Endpunkt aus Umgebung os.environ["NAME"] Environment.GetEnvironmentVariable("NAME") ?? throw … 2.1
Client für ein Modell projekt.get_openai_client(), model=… im Aufruf ProjectOpenAIClient.GetProjectResponsesClientForModel("chat") 2.1
Aufruf, Textfeld openai.responses.create(input=…), .output_text CreateResponseAsync("…"), GetOutputText() 2.1
Agentendefinition PromptAgentDefinition(model=…, instructions=…) new DeclarativeAgentDefinition("chat") { Instructions = … } 2.3
Version anlegen projekt.agents.create_version(agent_name=…, definition=…) AgentAdministrationClient.CreateAgentVersionAsync(name, options: new(definition)) 2.3
Client am Agenten projekt.get_openai_client(agent_name=…) GetProjectResponsesClientForAgent(defaultAgent: name, …) 2.3
Gespräch openai.conversations.create() GetProjectConversationsClient().CreateProjectConversationAsync() 2.3
Version löschen projekt.agents.delete_version(agent_name=…, agent_version=…) DeleteAgentVersionAsync(name, version) 2.3
Werkzeuge tools=[…] in der Definition Tools = { … } (Sammlungsinitialisierer) 2.3, 2.4
Ausgabeposten lesen for item in response.output: und item.type == "function_call" foreach (ResponseItem item in response.OutputItems) und item is FunctionCallResponseItem 2.4
Aufräumen im Block with … as x: using alle

Dazu die Schreibweisen: snake_case wird PascalCase, benannte Argumente (name=wert) werden benannte Argumente oder Objektinitialisierer, None wird null, Wörterbücher und Listen werden Typen und Sammlungsausdrücke, f"…{x}" wird $"…{x}". Nicht abgebildet in den Lektionen 2.1 bis 2.4 ist der Strom von Ereignissen, den Python-Beispiele mit stream=True lesen; für Antworten im Strom nimmt der Kurs den IChatClient des KI-Tracks (Lektion T2.2). Ob es für die Ereignisse des Responses-Clients in .NET einen gleichwertigen Weg gibt, ist offen.

Zwei Eigenheiten, die verwirren

Asynchron. Eine Funktion mit async def liefert beim Aufruf noch kein Ergebnis, sondern ein wartendes Objekt; erst await holt es. Dazu kommen async with (Client öffnen und schließen) und async for. In C# heißt das async Task und await, und der Kompiler warnt, wenn man das Warten vergisst; in Python bemerkt man es erst später. Die gelesenen Learn-Seiten zeigen für azure-ai-projects die synchrone Form; ob und wie eine asynchrone Fassung des Pakets genutzt wird, ist offen.

python
async def hole(client, text):
    async with client as sitzung:
        return await sitzung.lade(text)  # erfundener Aufruf, nur das Muster

Lesefutter 3. Das Muster der asynchronen Azure-SDKs, ohne echten Dienst.

Wörterbuch-Optionen. Wo C# einen Typ verlangt, genügt in Python oft ein Wörterbuch: input=[{"role": "user", "content": "…"}]. Für Felder, die der Client nicht kennt, gibt es extra_body. Frühere Beispiele von Learn gaben so den Agenten an; neuere binden ihn beim Client (get_openai_client(agent_name=…)). Wer ältere Beispiele liest, sieht beides.

python
ref = {"agent_reference": {"name": "reiseassistent", "type": "agent_reference"}}
antwort = openai.responses.create(
    input=[{"role": "user", "content": "Hallo"}],
    extra_body=ref,
)

Lesefutter 4. Zwei Wörterbücher: das Gespräch als Liste und der Verweis auf den Agenten. Ein Tippfehler in einem Schlüssel fällt erst zur Laufzeit auf; in C# ist es ein Typ (AgentReference, Lektion 2.3).

Entscheidung: lesen, übertragen oder ersetzen

  1. Hat die Seite einen C#-Reiter, gilt er: Er stammt von Microsoft und passt zum Paket. Seine Version steht oft dabei; sie stimmt nicht immer mit der Reihe überein, die dieser Kurs nutzt (Lektion 2.1: 2.x stabil, Vorschau nur, wo Learn sie verlangt).
  2. Hat sie keinen, wird das Python-Beispiel mit der Tabelle übertragen und die C#-Fassung übersetzt (Übung 1).
  3. Fehlt auch das Muster in der Tabelle, zählt die REST-Schnittstelle der Seite, falls vorhanden.

Kippt, wenn Typnamen der Vorschau abweichen (Lektion 2.1 nennt Beispiele): Dann gilt der Name der installierten Paketversion, nicht der der Tabelle.

Stand der Prüfung

Der C#-Block der Lösung wurde auf .NET 10 mit Azure.AI.Projects 2.0.1, Azure.AI.Projects.Agents 2.0.0, Azure.AI.Extensions.OpenAI 2.0.0 und Azure.Identity 1.21.0 übersetzt. Übersetzen beweist nur Namen und Signaturen. Der Code wurde nicht gegen Azure ausgeführt. Die Python-Ausschnitte (Lesefutter) wurden weder ausgeführt noch übersetzt; sie zeigen Muster, keine geprüften Programme. Die Zuordnung der Tabelle stützt sich auf die Beispiele von Learn und auf den Code der Lektionen 2.1 bis 2.4. Offen: Ereignisse im Strom in .NET, asynchrone Form des Python-Pakets.

Übung 1

Übertragen Sie dieses Lesefutter nach C# (eine Datei-App) und übersetzen Sie sie:

python
definition = PromptAgentDefinition(model="chat", instructions="Prüfe den Entwurf.")
agent = projekt.agents.create_version(agent_name="pruefer", definition=definition)
openai = projekt.get_openai_client(agent_name=agent.name)
print(openai.responses.create(input="Entwurf: …").output_text)
projekt.agents.delete_version(agent_name=agent.name, agent_version=agent.version)

Lösung 1

C#
using Azure.AI.Extensions.OpenAI;
using Azure.AI.Projects;
using Azure.AI.Projects.Agents;
using Azure.Identity;
using OpenAI.Responses;

#pragma warning disable OPENAI001

string endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
    ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT fehlt.");
AIProjectClient projekt = new(new Uri(endpoint), new DefaultAzureCredential());

DeclarativeAgentDefinition definition = new("chat")
{
    Instructions = "Prüfe den Entwurf.",
};
ProjectsAgentVersion agent = await projekt.AgentAdministrationClient
    .CreateAgentVersionAsync("pruefer", options: new(definition));
ProjectResponsesClient openai = projekt.ProjectOpenAIClient
    .GetProjectResponsesClientForAgent(defaultAgent: agent.Name);
ResponseResult antwort = await openai.CreateResponseAsync("Entwurf: …");
Console.WriteLine(antwort.GetOutputText());
await projekt.AgentAdministrationClient
    .DeleteAgentVersionAsync(agent.Name, agent.Version);

Fünf Zeilen Python werden zu einem kleinen Programm: Die Typen müssen genannt werden, die Aufrufe warten mit await, der Endpunkt kommt aus der Umgebung. Der Name chat ist hier wie im Lesefutter ein Text; in der Anwendung käme er aus der Konfiguration (R-08).

Übung 2

Lesen Sie ohne Ausführen und beantworten Sie: (a) Welche Zeile wartet auf das Netz? (b) Was liefert ein Aufruf von frage(...), wenn man das Warten vergisst? (c) Wie sieht ein Element der Liste input in C# aus?

python
async def frage(client, text):
    async with client:
        nachricht = [{"role": "user", "content": text}]
        antwort = await client.responses.create(input=nachricht)
        return antwort.output_text

Lösung 2

(a) Die Zeile mit await. (b) Ein wartendes Objekt statt eines Textes; erst await frage(...) (oder ein Start der Ereignisschleife) liefert den Text. In C# wäre frage ein async Task<string>, und das vergessene await fiele dem Kompiler auf. (c) ResponseItem.CreateUserMessageItem(text), wie in Lektion 2.4; die Liste ist dort eine List<ResponseItem>.

Typische Denkfehler

  • „Das Beispiel ist Python, also nicht für uns.“ Die Muster sind dieselben; die Tabelle übersetzt sie.
  • „Der Modellname im Beispiel ist unser Deployment.“ Er steht als Text im Beispiel; bei uns heißt das Deployment nach der Aufgabe, das Modell steht in der Konfiguration.
  • „extra_body ist ein Kernfeature.“ Es ist ein Ausweg für Felder, die der Client nicht kennt; neuere Beispiele binden den Agenten beim Client.
  • „Die Prüfung verlangt, Python zu schreiben.“ Die Seite nennt Erfahrung mit Python; wie sie Code zeigt, steht dort nicht. Üben müssen Sie es selbst.

Weiterlesen

  • https://learn.microsoft.com/azure/foundry/quickstarts/get-started-code
  • https://learn.microsoft.com/azure/foundry/agents/quickstarts/prompt-agent
  • https://learn.microsoft.com/azure/foundry/how-to/develop/sdk-overview
  • https://learn.microsoft.com/azure/foundry/agents/concepts/runtime-components
  • https://learn.microsoft.com/credentials/certifications/resources/study-guides/ai-103