Lab 08: Python SDK - Tool Choice i Citations

Opis

W tym laboratorium nauczymy się kontrolowania użycia narzędzi przez agenta używając tool_choice parameter oraz ekstrakcji citations z odpowiedzi Bing Search. Będziemy budować kod małymi kawałkami testując różne scenariusze.

Ważne: Użyjemy istniejącego agenta z Bing Search tool (z Lab 06).

⚠️ Citations są OBOWIĄZKOWE dla Bing Grounding

Bing Search ma wymagania prawne (Display Requirements):

  • Musisz wyświetlać źródła (citations) w odpowiedzi
  • Link do źródła + tytuł strony
  • Bing logo (w aplikacjach web)

Nieprzestrzeganie = naruszenie Terms of Service!

Pełna dokumentacja:


Problem: Kontrola narzędzi i citations

Wyzwanie 1: Nieprzewidywalne użycie narzędzi

Agent sam decyduje kiedy użyć Bing Search:

# Pytanie: "Jaka jest stolica Polski?"
# Agent może:
# 1. Odpowiedzieć z własnej wiedzy (bez Bing) → brak citations
# 2. Użyć Bing Search → z citations

Problem: Nie mamy kontroli. Czasem potrzebujemy:

  • Zawsze świeże dane (wymuś Bing)
  • Lub: nigdy nie używaj Bing (tylko wiedza modelu)

Wyzwanie 2: Citations są ukryte w strukturze

Bing zwraca citations, ale są “zagnieżdżone”:

message.content[0].text.annotations[0].url_citation.url
message.content[0].text.annotations[0].url_citation.title

Problem: Trzeba manual extraction. Różne formaty (markdown vs JSON) mają różne struktury.

Use cases

  • Forced fresh data: Wiadomości, pogoda, kursy walut
  • No Bing allowed: Pytania o prywatne dane (compliance)
  • Citations tracking: Legal compliance, source attribution
  • Batch processing: Zbieranie citations z wielu zapytań

Rozwiązanie: tool_choice parameter

Czym jest tool_choice?

tool_choice = kontrola nad wyborem narzędzi przez agenta.

run = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=agent.id,
    tool_choice={"type": "bing_grounding"}  # Wymuś Bing!
)

Opcje:

Wartość Zachowanie
Brak (default) Agent sam decyduje czy użyć narzędzia
{"type": "bing_grounding"} MUSISZ użyć Bing (błąd jeśli nie da się)
{"type": "none"} NIE MOŻESZ użyć żadnego narzędzia

Trade-off:

  • auto (default) = elastic, czasem nieprzewidywalne
  • forced = predictable, ale może być overkill (więcej tokenów)

Przygotowanie środowiska

Krok 1: Sprawdź Agent ID

Użyjemy istniejącego agenta z Bing Search (z Lab 06).

Przejdź do Azure AI Foundry PortalAgents → znajdź agenta z Bing tool (powinien nazywać się HelloWorld) i skopiuj Agent ID.

Sprawdź: Agent ma Bing Grounding tool w sekcji Tools.

Krok 2: Dane z Lab 07

Będziemy używać tych samych danych:

  • PROJECT_ENDPOINT - z Overview
  • AGENT_ID - Agent z Bing
  • ChainedTokenCredential (Service Principal → Azure CLI)

Budowanie skryptu krok po kroku

Zbudujemy skrypt małymi kawałkami testując różne scenariusze tool_choice i citations extraction.

Krok 1: Utworzenie pliku i import

Otwórz Visual Studio Code wpisując code . w terminal będąc w katalogu roboczym z (venv)

Utwórz nowy plik bing_citations.py w katalogu roboczym:

#!/usr/bin/env python3
"""
Tool choice i Citations extraction z Bing Search
"""

from azure.ai.projects import AIProjectClient
from azure.identity import ChainedTokenCredential, EnvironmentCredential, AzureCliCredential
import json

print("✓ Importy załadowane")

Uruchom:

python bing_citations.py

Oczekiwany wynik:

✓ Importy załadowane

Krok 2: Utworzenie klienta i thread

Dodaj kod (zastąp wartości swoimi z Lab 07!):

# Konfiguracja (zastąp swoimi wartościami!)
PROJECT_ENDPOINT = "YOUR_PROJECT_ENDPOINT_HERE"  # Z Lab 07
AGENT_ID = "YOUR_AGENT_WITH_BING_ID_HERE"  # Agent z Bing tool

# Utworzenie klienta
print("\n🔧 Tworzenie klienta...")
credential = ChainedTokenCredential(
    EnvironmentCredential(),
    AzureCliCredential()
)

# (Opcjonalne) Jeżeli masz rozszyfrowywanie ruchu na proxy (SSL interception),
# możesz wyłączyć weryfikowanie certyfikatu:
# from azure.core.pipeline.transport import RequestsTransport
# transport = RequestsTransport(connection_verify=False)

project_client = AIProjectClient(
    endpoint=PROJECT_ENDPOINT,
    credential=credential
    # transport=transport  # Odkomentuj jeżeli używasz proxy
)

# Utworzenie thread
print("🔧 Tworzenie thread...")
thread = project_client.agents.threads.create()

print("✓ Klient i thread gotowe")
print(f"  Thread ID: {thread.id}")

Zastąp wartości swoimi:

  • PROJECT_ENDPOINT - z Lab 07
  • AGENT_ID - Agent z Bing tool (z Lab 06)

Uruchom:

python bing_citations.py

Oczekiwany wynik:

🔧 Tworzenie klienta...
🔧 Tworzenie thread...
✓ Klient i thread gotowe
  Thread ID: thread_xyz789...

Krok 3: Test 1 - Auto mode (agent decyduje)

Dodaj kod:

# Test 1: Auto mode - agent sam decyduje
print("\n" + "=" * 60)
print("TEST 1: AUTO MODE (agent decyduje)")
print("=" * 60)

# Pytanie które agent MOŻE odpowiedzieć bez Bing
question1 = "Jaka jest stolica Polski?"

# Dodaj wiadomość
message1 = project_client.agents.messages.create(
    thread_id=thread.id,
    role="user",
    content=question1
)

# Run BEZ tool_choice (agent sam decyduje)
print(f"\n▶️  Pytanie: {question1}")
print("   Tool choice: AUTO (agent decyduje)")
run1 = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=AGENT_ID
    # Brak tool_choice = auto mode
)

# Odczyt odpowiedzi
messages1 = project_client.agents.messages.list(thread_id=thread.id)
for msg in messages1:
    if msg.role == "assistant" and msg.run_id == run1.id:
        response1 = msg.content[0].text.value
        break

print("\n✓ Odpowiedź agenta:")
print("-" * 60)
print(response1[:300])  # Pierwsze 300 znaków
print("-" * 60)

# Sprawdź czy użyto Bing
run_steps1 = project_client.agents.run_steps.list(
    thread_id=thread.id,
    run_id=run1.id
)

used_bing = False
for step in run_steps1:
    if hasattr(step, 'step_details') and hasattr(step.step_details, 'tool_calls'):
        for tool_call in step.step_details.tool_calls:
            if tool_call.type == "bing_grounding":
                used_bing = True
                break

print(f"\n📊 Użyto Bing Search: {'✓ TAK' if used_bing else '✗ NIE (z własnej wiedzy)'}")
print(f"📊 Tokeny: {run1.usage.total_tokens if run1.usage else 'N/A'}")

Uruchom:

python bing_citations.py

Oczekiwany wynik (może się różnić!):

==========================================================
TEST 1: AUTO MODE (agent decyduje)
==========================================================

▶️  Pytanie: Jaka jest stolica Polski?
   Tool choice: AUTO (agent decyduje)

✓ Odpowiedź agenta:
------------------------------------------------------------
Stolicą Polski jest Warszawa.
------------------------------------------------------------

📊 Użyto Bing Search: ✗ NIE (z własnej wiedzy)
📊 Tokeny: 145

Co się stało:

  • Agent odpowiedział z własnej wiedzy (basic fact)
  • NIE użył Bing Search (nie było potrzeby)
  • Brak citations (odpowiedź z modelu)
  • Niskie zużycie tokenów

Nieprzewidywalność: Czasem agent MOŻE użyć Bing, czasem nie. Zależy od pytania i wewnętrznej heurystyki.


Krok 4: Test 2 - Forced Bing (tool_choice wymusza)

Dodaj kod:

# Test 2: Forced Bing - wymuszenie użycia Bing
print("\n" + "=" * 60)
print("TEST 2: FORCED BING (tool_choice wymusza)")
print("=" * 60)

# TO SAMO pytanie!
question2 = "Jaka jest stolica Polski?"

# Dodaj wiadomość
message2 = project_client.agents.messages.create(
    thread_id=thread.id,
    role="user",
    content=question2
)

# Run Z tool_choice - wymuszamy Bing!
print(f"\n▶️  Pytanie: {question2}")
print("   Tool choice: FORCED Bing")
run2 = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=AGENT_ID,
    tool_choice={"type": "bing_grounding"}  # Wymuś Bing!
)

# Odczyt odpowiedzi
response2 = None
messages2 = project_client.agents.messages.list(thread_id=thread.id)
for msg in messages2:
    if msg.role == "assistant" and msg.run_id == run2.id:
        response2 = msg.content[0].text.value
        break

if response2 is None:
    response2 = "Brak odpowiedzi od agenta."
    print("Brak odpowiedzi od agenta.")

print("\n✓ Odpowiedź agenta:")
print("-" * 60)
print(response2[:400])  # Pierwsze 400 znaków
print("-" * 60)

# Sprawdź czy użyto Bing (powinno być TAK!)
run_steps2 = project_client.agents.run_steps.list(
    thread_id=thread.id,
    run_id=run2.id
)

used_bing2 = False
for step in run_steps2:
    if hasattr(step, 'step_details') and hasattr(step.step_details, 'tool_calls'):
        for tool_call in step.step_details.tool_calls:
            if tool_call.type == "bing_grounding":
                used_bing2 = True
                break

print(f"\n📊 Użyto Bing Search: {'✓ TAK (wymuszono!)' if used_bing2 else ' NIE'}")
print(f"📊 Tokeny: {run2.usage.total_tokens if run2.usage else 'N/A'}")

Uruchom:

python bing_citations.py

Oczekiwany wynik:

==========================================================
TEST 2: FORCED BING (tool_choice wymusza)
==========================================================

▶️  Pytanie: Jaka jest stolica Polski?
   Tool choice: FORCED Bing

✓ Odpowiedź agenta:
------------------------------------------------------------
Stolicą Polski jest Warszawa. Jest to największe miasto w Polsce, położone nad Wisłą [1][2].

[1] https://pl.wikipedia.org/wiki/Warszawa
[2] https://www.britannica.com/place/Warsaw
------------------------------------------------------------

📊 Użyto Bing Search: ✓ TAK (wymuszono!)
📊 Tokeny: 2847

Co się stało:

  • tool_choice={"type": "bing_grounding"} WYMUSIŁO użycie Bing
  • Agent MUSIAŁ wyszukać w internecie (nawet dla basic fact)
  • Odpowiedź zawiera citations ([1][2])
  • Znacznie więcej tokenów (Bing call + processing)

Porównanie:

  • Test 1 (auto): 145 tokenów, brak citations, z własnej wiedzy
  • Test 2 (forced): 2847 tokenów, z citations, z Bing

Krok 5: Citations extraction

Teraz nauczymy się wyciągać citations programmatically.

Dodaj kod:

# Ekstrakcja citations z Test 2
print("\n" + "=" * 60)
print("EKSTRAKCJA CITATIONS")
print("=" * 60)

# Pobierz message z Test 2
messages_with_citations = project_client.agents.messages.list(thread_id=thread.id)
for msg in messages_with_citations:
    if msg.role == "assistant" and msg.run_id == run2.id:
        # Sprawdź czy są annotations (citations)
        text_content = msg.content[0]

        if hasattr(text_content, 'text') and hasattr(text_content.text, 'annotations'):
            annotations = text_content.text.annotations

            if annotations and len(annotations) > 0:
                print(f"\n✓ Znaleziono {len(annotations)} citations:")
                print("-" * 60)

                for i, annotation in enumerate(annotations, 1):
                    # Bing citations są typu url_citation (NIE uri_citation!)
                    if hasattr(annotation, 'url_citation'):
                        citation = annotation.url_citation
                        print(f"\n[{i}] {citation.title}")
                        print(f"    URL: {citation.url}")

                        # Text który został zacytowany
                        if hasattr(annotation, 'text'):
                            print(f"    Ref: {annotation.text}")
            else:
                print("\n✗ Brak citations (agent nie użył Bing)")

        break

print("-" * 60)

Uruchom:

python bing_citations.py

Oczekiwany wynik:

==========================================================
EKSTRAKCJA CITATIONS
==========================================================

✓ Znaleziono 2 citations:
------------------------------------------------------------

[1] Warszawa – Wikipedia, wolna encyklopedia
    URL: https://pl.wikipedia.org/wiki/Warszawa
    Ref: [1]

[2] Warsaw | History, Population, Map, & Facts | Britannica
    URL: https://www.britannica.com/place/Warsaw
    Ref: [2]
------------------------------------------------------------

Co się stało:

  • Citations są w message.content[0].text.annotations
  • Każda citation ma uri_citation.title i uri_citation.uri
  • Możemy programmatically wyciągnąć źródła
  • Display Requirements: Musisz pokazać te linki użytkownikowi!

Krok 6: Porównanie formatów odpowiedzi

Sprawdźmy jak citations wyglądają w formacie JSON.

Dodaj kod:

# Test 3: Citations w JSON format (schema bez format enforcement)
print("\n" + "=" * 60)
print("TEST 3: CITATIONS W JSON (schema only)")
print("=" * 60)

# Pytanie wymagające Bing
question3 = "Jaka jest dzisiejsza pogoda w Warszawie?"

# Dodaj wiadomość
message3 = project_client.agents.messages.create(
    thread_id=thread.id,
    role="user",
    content=question3
)

# Run z Bing + JSON format (schema bez "no markdown")
print(f"\n▶️  Pytanie: {question3}")
print("   Tool choice: FORCED Bing")
print("   Response format: JSON (schema only)")
run3 = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=AGENT_ID,
    tool_choice={"type": "bing_grounding"},
    response_format={"type": "json_object"},
    additional_instructions="""Return ONLY raw JSON object with this exact schema:
{
  "location": "city name",
  "weather": "current weather description",
  "sources": ["list of source URLs"]
}
No explanations, no markdown, no text before JSON. Pure JSON only."""
)

# Odczyt odpowiedzi
response3 = None
messages3 = project_client.agents.messages.list(thread_id=thread.id)
for msg in messages3:
    if msg.role == "assistant" and msg.run_id == run3.id:
        response3 = msg.content[0].text.value
        break
if response3 is None:
    response3 = "Brak odpowiedzi od agenta."
    print("Brak odpowiedzi od agenta.")

print("\n✓ Odpowiedź agenta (JSON):")
print("-" * 60)
print(response3[:400])
print("-" * 60)

# Próba parsowania
print("\n🔍 Próba parsowania JSON:")
try:
    # Strip markdown if present
    json_text = response3
    if response3.strip().startswith("```"):
        import re
        json_match = re.search(r'```json\s*(\{.*\})\s*```', response3, re.DOTALL)
        if json_match:
            json_text = json_match.group(1)

    data3 = json.loads(json_text)
    print("✓ Parse SUCCESS")
    print(json.dumps(data3, indent=2, ensure_ascii=False)[:300])
except json.JSONDecodeError as e:
    print(f"✗ Parse FAILED: {e}")

print(f"\n📊 Tokeny: {run3.usage.total_tokens if run3.usage else 'N/A'}")

Uruchom:

python bing_citations.py

Oczekiwany wynik:

==========================================================
TEST 3: CITATIONS W JSON (schema only)
==========================================================

▶️  Pytanie: Jaka jest dzisiejsza pogoda w Warszawie?
   Tool choice: FORCED Bing
   Response format: JSON (schema only)

✓ Odpowiedź agenta (JSON):
------------------------------------------------------------
```json
{
  "location": "Warszawa",
  "weather": "Pochmurno, 5°C, wiatr północny 15 km/h",
  "sources": [
    "https://weather.com/pl-PL/weather/today/l/Warszawa",
    "https://www.meteo.pl/warszawa"
  ]
}

🔍 Próba parsowania JSON: ✓ Parse SUCCESS { “location”: “Warszawa”, “weather”: “Pochmurno, 5°C, wiatr północny 15 km/h”, “sources”: [ “https://weather.com/pl-PL/weather/today/l/Warszawa”, … }

📊 Tokeny: 3214


**Co się stało:**
- Schema w `additional_instructions` pomogła modelowi structured output
- Model sam dodał `sources` array z URL-ami
- Nadal ```json block (trzeba strip)
- Sources są w JSON, ale nie mamy title (tylko URL)

**Ograniczenie:** Sources w JSON to tylko URLs - brak title jak w native citations!

---

### Krok 7: Porównanie wszystkich testów

Dodaj kod na końcu:

```python
# Porównanie wszystkich testów
print("\n" + "=" * 60)
print("PORÓWNANIE TESTÓW")
print("=" * 60)

print(f"""
Test                     | Tool choice | Format    | Citations | Tokeny
-------------------------|-------------|-----------|-----------|--------
Test 1: Auto mode        | AUTO        | Markdown  | ✗         | {run1.usage.total_tokens if run1.usage else 'N/A':6}
Test 2: Forced Bing      | FORCED      | Markdown  | ✓         | {run2.usage.total_tokens if run2.usage else 'N/A':6}
Test 3: Forced + JSON    | FORCED      | JSON      | ✓ (URLs)  | {run3.usage.total_tokens if run3.usage else 'N/A':6}
""")

print("\nKiedy używać:")
print("  ✓ AUTO: Agent decyduje, oszczędność tokenów, nieprzewidywalne")
print("  ✓ FORCED: Zawsze świeże dane, predictable, więcej tokenów")
print("  ✓ JSON format: Strukturalne dane, ale sources = tylko URLs")

print("\nKluczowa lekcja:")
print("  tool_choice kontroluje użycie narzędzi")
print("  Citations OBOWIĄZKOWE dla Bing (Display Requirements!)")
print("  Native annotations mają title + URL, JSON tylko URL")

# Cleanup
print("\n🧹 Usuwanie thread...")
project_client.agents.threads.delete(thread_id=thread.id)
print("✓ Thread usunięty")

Uruchom:

python bing_citations.py

Oczekiwany wynik:

==========================================================
PORÓWNANIE TESTÓW
==========================================================

Test                     | Tool choice | Format    | Citations | Tokeny
-------------------------|-------------|-----------|-----------|--------
Test 1: Auto mode        | AUTO        | Markdown  | ✗         |    145
Test 2: Forced Bing      | FORCED      | Markdown  | ✓         |   2847
Test 3: Forced + JSON    | FORCED      | JSON      | ✓ (URLs)  |   3214

Kiedy używać:
  ✓ AUTO: Agent decyduje, oszczędność tokenów, nieprzewidywalne
  ✓ FORCED: Zawsze świeże dane, predictable, więcej tokenów
  ✓ JSON format: Strukturalne dane, ale sources = tylko URLs

Kluczowa lekcja:
  tool_choice kontroluje użycie narzędzi
  Citations OBOWIĄZKOWE dla Bing (Display Requirements!)
  Native annotations mają title + URL, JSON tylko URL

🧹 Usuwanie thread...
✓ Thread usunięty

Co się stało:

  • Porównaliśmy 3 podejścia do Bing usage
  • AUTO = cheap, nieprzewidywalne
  • FORCED = expensive, predictable, z citations
  • JSON = strukturalne, ale citations gorsze (tylko URLs)

Trade-off:

  • Koszt (tokeny) vs Predictability (zawsze świeże)
  • Native citations (title+URL) vs JSON sources (tylko URL)

Citations Display Requirements

Wymagania prawne

Bing Grounding ma obowiązkowe wymagania prawne:

You MUST display the following for each Grounding with Bing Search result:

  1. Title of the source page
  2. URL (clickable link)
  3. Bing logo (for web applications)

Source: Bing Grounding Documentation

Jak wyświetlać citations

Przykład (web app):

<div class="citations">
  <img src="bing-logo.png" alt="Powered by Bing" />
  <h4>Sources:</h4>
  <ul>
    <li>
      <a href="https://pl.wikipedia.org/wiki/Warszawa">
        Warszawa – Wikipedia, wolna encyklopedia
      </a>
    </li>
    <li>
      <a href="https://www.britannica.com/place/Warsaw">
        Warsaw | History, Population, Map, & Facts | Britannica
      </a>
    </li>
  </ul>
</div>

Przykład (CLI):

Stolicą Polski jest Warszawa.

Sources (Powered by Bing):
  [1] Warszawa – Wikipedia, wolna encyklopedia
      https://pl.wikipedia.org/wiki/Warszawa

  [2] Warsaw | History, Population, Map, & Facts | Britannica
      https://www.britannica.com/place/Warsaw

Extraction pattern

def extract_citations(message):
    """Extract citations from agent message"""
    citations = []

    if hasattr(message.content[0], 'text') and hasattr(message.content[0].text, 'annotations'):
        for annotation in message.content[0].text.annotations:
            if hasattr(annotation, 'url_citation'):
                citations.append({
                    'title': annotation.url_citation.title,
                    'url': annotation.url_citation.url,
                    'text': annotation.text if hasattr(annotation, 'text') else ''
                })

    return citations

# Usage
citations = extract_citations(message3)
for i, cite in enumerate(citations, 1):
    print(f"[{i}] {cite['title']}")
    print(f"    {cite['url']}")

Troubleshooting

Problem: “Invalid tool_choice type”

Przyczyna: Nieprawidłowa wartość w tool_choice.

Rozwiązanie: Użyj dokładnie {"type": "bing_grounding"}:

tool_choice={"type": "bing_grounding"}  # Correct
# NIE: tool_choice="bing"
# NIE: tool_choice={"bing_grounding": True}

Problem: Run failed z tool_choice

Przyczyna: Agent nie ma Bing tool lub pytanie nie da się odpowiedzieć przez Bing.

Rozwiązanie 1: Sprawdź że agent ma Bing tool:

# W Azure AI Foundry Portal:
# Agents → Twój agent → Tools → sprawdź "Bing Grounding"

Rozwiązanie 2: Pytanie musi być możliwe do wyszukania:

# BAD: "Wymyśl nową bajkę" - nie da się wyszukać!
# GOOD: "Jaka jest pogoda w Warszawie?" - da się wyszukać

Problem: Brak citations mimo forced Bing

Przyczyna: Citations są w annotations, nie w text.

Rozwiązanie: Sprawdź message.content[0].text.annotations:

annotations = message.content[0].text.annotations
if annotations:
    for ann in annotations:
        if hasattr(ann, 'url_citation'):
            print(ann.url_citation.title)
else:
    print("No annotations found")

Problem: JSON format nie ma citations

Przyczyna: JSON format powoduje że model sam formatuje sources (jako URLs w JSON), tracimy native annotations.

Rozwiązanie: Dla pełnych citations (title + URL) używaj markdown format (bez response_format):

# Markdown = native citations z title + URL
run = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=AGENT_ID,
    tool_choice={"type": "bing_grounding"}
    # BEZ response_format!
)
# Potem extract z annotations

Kluczowe koncepty

tool_choice

Co to jest:

  • Parameter w create_and_process()
  • Kontroluje wybór narzędzi przez agenta

Opcje:

# Auto (default) - agent decyduje
run = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=agent.id
)

# Forced - MUSISZ użyć Bing
run = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=agent.id,
    tool_choice={"type": "bing_grounding"}
)

# None - NIE MOŻESZ użyć narzędzi
run = project_client.agents.runs.create_and_process(
    thread_id=thread.id,
    agent_id=agent.id,
    tool_choice={"type": "none"}
)

Use cases:

  • Forced: Wiadomości, pogoda, ceny, aktualne wydarzenia
  • Auto: Oszczędność tokenów, mixed queries
  • None: Compliance (prywatne dane, nie mogą opuścić Azure)

Citations structure

Native annotations (markdown format):

message.content[0].text.annotations[i].url_citation.title  # "Wikipedia - Warszawa"
message.content[0].text.annotations[i].url_citation.url    # "https://pl.wikipedia.org/..."
message.content[0].text.annotations[i].text                # "[1]"

JSON format:

  • Model sam dodaje sources do JSON
  • Tylko URLs (brak title!)
  • Schema w additional_instructions pomaga

Best practice: Dla pełnych citations używaj markdown + native annotations.

Display Requirements

OBOWIĄZKOWE dla Bing:

  1. Title każdego źródła
  2. Clickable URL
  3. Bing logo (web apps)

Nieprzestrzeganie = naruszenie Terms of Service!


Podsumowanie

W tym labie nauczyliśmy się:

tool_choice: Kontrola nad użyciem narzędzi (auto vs forced) ✅ Forced Bing: Zawsze świeże dane (tool_choice={"type": "bing_grounding"}) ✅ Citations extraction: Programmatic access do źródeł ✅ Display Requirements: Prawny obowiązek wyświetlania citations ✅ Format comparison: Markdown (native citations) vs JSON (URLs only) ✅ Trade-offs: Cost vs freshness, native vs JSON citations

Kluczowe wnioski:

  • tool_choice daje predictability (ale kosztuje tokeny)
  • Citations są OBOWIĄZKOWE dla Bing (Display Requirements!)
  • Native annotations (markdown) > JSON sources (tylko URLs)
  • Trade-off: cost (tokeny) vs freshness (Bing)

Best practices:

  • ✓ Używaj forced Bing dla time-sensitive queries
  • ✓ Zawsze ekstraktuj i wyświetlaj citations
  • ✓ Markdown format dla pełnych citations (title + URL)
  • ✓ JSON format tylko jeśli potrzebujesz strukturalnych danych

Next step: Lab 10 - Connected Agents (Multi-agent orchestration)

results matching ""

    No results matching ""