Poniższy post jest kontynuacją pierwszej części. W poniższym przypadku, spróbujemy połączyć utworzony wcześniej serwer MCP z naszym modelem AI. Celem naszego klienta, będzie przyjmowanie poleceń w języku naturalnym, otrzymanych od administratora serwerowni. Nasz system będzie w stanie sprawdzić czy:
- serwer odpowiada na ping
- serwer odpowiada na porcie http
Wstępna uwaga:
Z racji że cały nasz kod będzie asynchroniczny, zalecam opakowanie go oraz uruchomienie, po przez asyncio.
async def main():
...
if __name__ == "__main__":
asyncio.run(main())
1. Importy
W naszym projekcie, wykorzystamy biblioteki:
- asyncio - do uruchamiania asynchronicznego naszych workerów (dzięki temu będziemy w stanie dużo bardziej efektywnie przetwarzać dużo większą ilość naszych zapytań równolegle),
- json - do konwersji otrzymanej listy narzędzi z naszego serwera MCP, na format przyjazny Ollamie,
- ollama - jako silnik hostujący naszego LLM-a,
- fastmcp - jako interfejs do porozumiewania się z serwerem MCP i uruchamiania dostępnych na nim narzędzi,
- rich - do upiększenia naszych wyników (przydatne w debugowaniu),
Właściwy kod importów:
import asyncio
import json
import ollama
from fastmcp import Client
from rich import print
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
2. Połączenie z MCP
Na początku tworzymy klienta, który następnie połączy się do naszego serwera (z pierwszej części tutorialu):
client = Client("http://127.0.0.1:8000/mcp")
Pobieramy za pomocą niego, listę dostępnych narzędzi. Niestety z racji że zarówno biblioteka fastmcp, jak i Ollama posiada odmienny format. Będziemy musieli skonwertować naszą listę prostą pętlą for:
async with client:
tools = await client.list_tools()
ollama_tools = []
for t in tools:
ollama_tools.append(
{
"type": "function",
"function": {
"name": t.name,
"description": t.description or "",
"parameters": t.inputSchema,
},
}
)
nice_print("Lista pobranych narzędzi", ollama_tools)
Otrzymując tym samym, finalnie listę narzędzi z naszego serwera w formacie JSON:
[
{
"type": "function",
"function": {
"name": "ping",
"description": "Sends an ICMP echo (ping) request to the specified host and returns its network status.\n\nArgs:\n host (str): The target hostname or IP address to check (e.g., \"8.8.8.8\" or
\"example.com\").\n\nReturns:\n str: One of the following status values:\n - `\"online\"` – if the host responded successfully to the ping request\n - `\"offline\"` – if the host did not respond or a
timeout/error occurred\n - `\"invalid\"` – if the input parameter is missing or invalid",
"parameters": {
"properties": {
"host": {
"type": "string"
}
},
"required": [
"host"
],
"type": "object"
}
}
},
{
"type": "function",
"function": {
"name": "check_port_80",
"description": "Checks whether TCP port 80 on the given host is open.\n\nArgs:\n host (str): The target hostname or IP.\n\nReturns:\n str: \"<host> - port 80 open\" or \"<host> - port 80 closed\"",
"parameters": {
"properties": {
"host": {
"type": "string"
}
},
"required": [
"host"
],
"type": "object"
}
}
}
]
2. Definiujemy własciwy prompt
Następnie zdefiniujmy nasz prompt który wyślemy do Ollamy. Będzie miał on za zadanie sprawdzić czy w naszej sieci są hosty i jeśli tak, to jego zadaniem będzie sprawdzenie czy jest na nich uruchomiony serwer WWW. Ważne aby przekazać nasze intencje w sposób jak najbardziej klarowny i konkretny sposób:
USER_PROMPT = """
Podaj tylko adresy IP hostów, które odpowiadają na ping z listy: 192.168.0.40, 195.181.211.253.
W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80.
"""
messages = [{"role": "user", "content": USER_PROMPT}]
3. Wywoływanie LLM-a aż otrzymamy właściwą odpowiedź
Docelowo, nasz program ma się zakończyć, dopiero w momencie gdy LLM stwierdzi że nie potrzebuje już wykonać żadnych dodatkowych kroków. Może np. podzielić swoją pracę na mniejsze partie i w miarę potrzeb pytać o kolejne porcje danych. Dlatego cały nasz właściwy program uruchomimy w pętli nieskończonej:
while True:Czas na pierwsze wywołanie. Sprawdźmy czy nasz model będzie potrzebował jakiś danych
response = ollama.chat(
model=MODEL,
messages=messages,
tools=ollama_tools,
)
msg = response["message"]
nice_print("Otrzymana odpowiedz modelu", msg)
Otrzymujemy:
Message(
role='assistant',
content='',
thinking=None,
images=None,
tool_name=None,
tool_calls=[
ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'})),
ToolCall(function=Function(name='check_port_80', arguments={'host': '192.168.0.40'})),
ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'})),
ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'}))
]
)
Możemy zaraz po tym dodać warunek kończący, gdy lista wymaganych funkcji do uruchomienia będzie pusta:
if "tool_calls" not in msg:
break
Z racji że jest to pierwsze wywołanie, sprawdźmy jeszcze raz narzędzia które mamy uruchomić:
tool_calls = msg["tool_calls"]
nice_print("Wywołanie narzędzia", tool_calls)
Wynik:
[ │
ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'})), │
ToolCall(function=Function(name='check_port_80', arguments={'host': '192.168.0.40'})), │
ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'})), │
ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'})) │
]
Teraz aby nasz model pamiętał swoją odpowiedź, dopiszmy do naszej rozmowy otrzymaną listę i wykonajmy każde z nich. Oczywiście również dołączając ich wyniki do całej rozmowy:
messages.append(
{"role": "assistant", "content": "", "tool_calls": tool_calls}
)
for call in tool_calls:
name = call["function"]["name"]
args = call["function"].get("arguments", {})
tool_result = await client.call_tool(name, args)
messages.append(
{
"role": "tool",
"name": name,
"content": json.dumps({"result": tool_result.data}),
}
)
Pętla się zamyka. Teraz pytanie, czy nasz llm otrzymując pełną konwersację jako kontekst, poprosi o kolejne dane? Czy może na podstawie historii wywołań, uzna że są już wystarczające. W drugim przypadku, pozostanie nam wyjście z pętli (przez warunek z wywołaniem break).
Otrzymamy wówczas naszą finalną odpowiedź po przez wywołanie:
nice_print("Finalna odpowiedź", response["message"]["content"])
Która da wynik:
Podsumowując, tylko jeden host (195.181.211.253) odpowiada na ping i ma otwarty port 80.
Adresy IP hostów, które odpowiadają na ping:
* 195.181.211.253
Host, który nie odpowiada na ping:
192.168.0.40
Pełny kod:
import asyncio
import json
import ollama
from fastmcp import Client
from rich import print
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
MCP_URL = "http://127.0.0.1:8000/mcp"
MODEL = "llama3.1:8b"
# MODEL="gpt-oss:20b"
USER_PROMPT = """
Podaj tylko adresy IP hostów, które odpowiadają na ping z listy: 192.168.0.40, 195.181.211.253.
W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80.
"""
def nice_print(title: str, values: str) -> None:
console = Console()
with console.capture() as capture:
console.print(values)
str_output = capture.get()
text = Text.from_ansi(str_output)
print(Panel(text, title=title, padding=2))
async def main():
client = Client(MCP_URL)
async with client:
tools = await client.list_tools()
ollama_tools = []
for t in tools:
ollama_tools.append(
{
"type": "function",
"function": {
"name": t.name,
"description": t.description or "",
"parameters": t.inputSchema,
},
}
)
nice_print("Lista pobranych narzędzi", ollama_tools)
messages = [{"role": "user", "content": USER_PROMPT}]
while True:
response = ollama.chat(
model=MODEL,
messages=messages,
tools=ollama_tools,
)
msg = response["message"]
nice_print("Otrzymana odpowiedz modelu", msg)
if "tool_calls" not in msg:
break
tool_calls = msg["tool_calls"]
nice_print("Wywołanie narzędzia", tool_calls)
messages.append(
{"role": "assistant", "content": "", "tool_calls": tool_calls}
)
for call in tool_calls:
name = call["function"]["name"]
args = call["function"].get("arguments", {})
tool_result = await client.call_tool(name, args)
messages.append(
{
"role": "tool",
"name": name,
"content": json.dumps({"result": tool_result.data}),
}
)
nice_print("Pełna historia czatu", messages)
nice_print("Finalna odpowiedź", response["message"]["content"])
if __name__ == "__main__":
asyncio.run(main())
Czas na ciekawostke
Z racji że samo zapytanie było złożone i wymagało najpierw sprawdzenia czy hosty odpowiadają na ping. Dopiero później (już tylko dla odpowiadających), miało sprawdzić czy odpowiada port 80. Musiałem na realnym przykładzie sprawdzić dwa najpopularniejsze darmowe modele offline (llama3.1:8b oraz gpt-oss:20b), jak sobie z tym poradzą. Co ciekawe, oba modele zachowały się całkowicie inaczej.
- llama3.1:8b - wykonała wszystkie narzędzia za pierwszym razem, nie zwracając uwagi na warunek
- gpt-oss:20b - wykonał narzędzia pojedynczo, respektując nasze założenia
Drugie podejście ma dużą zaletę, model wypełnił w pełni nasze instrukcje. Dzięki temu nie utraciliśmy naszego warunku ograniczającego listę. Wadą zaś było to że, przez to: wysłaliśmy dużo więcej zapytań do naszego LLM-a i zużyliśmy więcej mocy obliczeniowej.
Jeśli chciałbyś głębiej zbadać temat, poniżej zostawiam pełne zrzuty odpowiedzi obu modeli.
Gpt-oss:20b
╭─────────────────────────────────────────────────────────────────────────────────────────── Lista pobranych narzędzi ────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ │
│ { │
│ 'type': 'function', │
│ 'function': { │
│ 'name': 'ping', │
│ 'description': 'Sends an ICMP echo (ping) request to the specified host and returns its network status.\n\nArgs:\n host (str): The target hostname or IP address to check (e.g., "8.8.8.8" or │
│ "example.com").\n\nReturns:\n str: One of the following status values:\n - `"online"` – if the host responded successfully to the ping request\n - `"offline"` – if the host did not respond │
│ or a │
│ timeout/error occurred\n - `"invalid"` – if the input parameter is missing or invalid', │
│ 'parameters': {'properties': {'host': {'type': 'string'}}, 'required': ['host'], 'type': 'object'} │
│ } │
│ }, │
│ { │
│ 'type': 'function', │
│ 'function': { │
│ 'name': 'check_port_80', │
│ 'description': 'Checks whether TCP port 80 on the given host is open.\n\nArgs:\n host (str): The target hostname or IP.\n\nReturns:\n str: "<host> - port 80 open" or "<host> - port 80 │
│ closed"', │
│ 'parameters': {'properties': {'host': {'type': 'string'}}, 'required': ['host'], 'type': 'object'} │
│ } │
│ } │
│ ] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message( │
│ role='assistant', │
│ content='', │
│ thinking='The user wants only IP addresses that respond to ping from the list. They provide two IPs: 192.168.0.40, 195.181.211.253. They want to ping them and if any responds, check port 80 as well. We │
│ need │
│ to use functions ping and check_port_80. The instructions: "Podaj tylko adresy IP hostów, które odpowiadają na ping z listy". That is: "Give only IP addresses of hosts that respond to ping from the list". │
│ Then │
│ "W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80." Means "In case one responds to ping, then check if it also responds to port 80."\n\nThus the output should be IP │
│ addresses │
│ that respond to ping, and maybe indicate port 80 status. Possibly something like:\n\n192.168.0.40 - port 80 open\nor\n192.168.0.40 - port 80 closed\n\nBut user says: "Podaj tylko adresy IP hostów" which │
│ might │
│ mean just IP addresses. But they also want to check port 80 for those that responded. So likely output should be like:\n\n192.168.0.40 - port 80 open\n\nIf host does not respond to ping, nothing │
│ printed.\n\nAlso │
│ they might want just IP addresses. But "W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80." So maybe they want the IP addresses plus port 80 status. The phrase "Podaj tylko │
│ adresy IP hostów, które odpowiadają na ping z listy:" means "Give only the IP addresses of hosts that respond to ping from the list". So we output the IP addresses only. But then "In case one responds to │
│ ping, │
│ then check if it responds also to port 80." Might want to print the port 80 status. But instruction might be ambiguous.\n\nLet\'s interpret: We need to give only IP addresses of hosts that responded to │
│ ping. But │
│ we also need to check if they respond to port 80. But maybe the output should include port 80 status too? The user didn\'t specify output format precisely, but likely they want IP addresses of responsive │
│ hosts │
│ and for those also check port 80. Could be two lines per host: IP address and port status.\n\nBut "Podaj tylko adresy IP hostów" implies no extra text. So maybe they want only IP addresses printed, no port │
│ 80. │
│ But the second sentence: "W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80." This might be just a request to also check port 80. But not necessarily ask to include the │
│ port 80 │
│ status in output.\n\nProbably best to output IP addresses that responded, and after that maybe include port 80 status in parentheses or separate. But the phrase "Podaj tylko adresy IP hostów" might be a │
│ direct │
│ instruction to output only IP addresses. I\'d err on that side.\n\nThus we should produce a list of IP addresses that respond to ping. Then also maybe add a note that port 80 status. But to stay consistent │
│ with │
│ "Podaj tylko adresy IP hostów", I\'d output only IP addresses, no port status. But then what about port 80? Might need to perform check but not output. That seems odd. I\'d maybe output IP addresses │
│ followed by │
│ port 80 status in a separate line.\n\nBut the instruction is ambiguous. Could interpret as: "Give only the IP addresses of the hosts that respond to ping from the list. If one responds to ping, also check │
│ if it │
│ responds on port 80." The output maybe just the IP addresses. The check for port 80 might be internal but not part of output. But that doesn\'t satisfy user? They likely want to know whether port 80 is │
│ open.\n\nThus I\'d output like:\n\n192.168.0.40 - port 80 open\n\nBut no other IPs.\n\nThus I\'d output:\n\n192.168.0.40 - port 80 open\n\nIf port 80 closed, they\'d see closed. If host didn\'t respond to │
│ ping, │
│ nothing. That meets "give only IP addresses of hosts that respond" but also shows port 80 status.\n\nLet\'s do that.\n\nNow we need to call ping for each IP. Then for each that is online, call │
│ check_port_80. │
│ Let\'s do calls.\n\n', │
│ images=None, │
│ tool_name=None, │
│ tool_calls=[ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'}))] │
│ ) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────── Wywołanie narzędzia ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'}))] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message(role='assistant', content='', thinking='Now we need to ping 195.181.211.253.', images=None, tool_name=None, tool_calls=[ToolCall(function=Function(name='ping', arguments={'host': │
│ '195.181.211.253'}))]) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────── Wywołanie narzędzia ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'}))] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message( │
│ role='assistant', │
│ content='', │
│ thinking='The user asked: "Podaj tylko adresy IP hostów, które odpowiadają na ping z listy: 192.168.0.40, 195.181.211.253. W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na │
│ port │
│ 80."\n\nWe have ping results: 192.168.0.40 offline; 195.181.211.253 online. Now need to check port 80 on 195.181.211.253. Let\'s call check_port_80.', │
│ images=None, │
│ tool_name=None, │
│ tool_calls=[ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'}))] │
│ ) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────── Wywołanie narzędzia ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'}))] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message( │
│ role='assistant', │
│ content='195.181.211.253', │
│ thinking='We have results: 192.168.0.40 offline, 195.181.211.253 online, port 80 open. The user wants only IP addresses of hosts that respond to ping. So we should output "195.181.211.253". Possibly │
│ also │
│ mention port 80? They said "W przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port 80." That implies we should check port 80 for those that respond, but the output should be only │
│ IP │
│ addresses of hosts that respond. Possibly include port status? The instruction "Podaj tylko adresy IP hostów, które odpowiadają na ping z listy" suggests output only IP addresses. So final answer: │
│ 195.181.211.253.', │
│ images=None, │
│ tool_name=None, │
│ tool_calls=None │
│ ) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────── Pełna historia czatu ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ │
│ { │
│ 'role': 'user', │
│ 'content': '\nPodaj tylko adresy IP hostów, które odpowiadają na ping z listy: 192.168.0.40, 195.181.211.253. \nW przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port │
│ 80.\n' │
│ }, │
│ {'role': 'assistant', 'content': '', 'tool_calls': [ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'}))]}, │
│ {'role': 'tool', 'name': 'ping', 'content': '{"result": "192.168.0.40 - offline"}'}, │
│ {'role': 'assistant', 'content': '', 'tool_calls': [ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'}))]}, │
│ {'role': 'tool', 'name': 'ping', 'content': '{"result": "195.181.211.253 - online"}'}, │
│ {'role': 'assistant', 'content': '', 'tool_calls': [ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'}))]}, │
│ {'role': 'tool', 'name': 'check_port_80', 'content': '{"result": "195.181.211.253 - port 80 open"}'} │
│ ] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────── Finalna odpowiedź ───────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ 195.181.211.253 │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Llama3.1_8b
╭─────────────────────────────────────────────────────────────────────────────────────────── Lista pobranych narzędzi ────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ │
│ { │
│ 'type': 'function', │
│ 'function': { │
│ 'name': 'ping', │
│ 'description': 'Sends an ICMP echo (ping) request to the specified host and returns its network status.\n\nArgs:\n host (str): The target hostname or IP address to check (e.g., "8.8.8.8" or │
│ "example.com").\n\nReturns:\n str: One of the following status values:\n - `"online"` – if the host responded successfully to the ping request\n - `"offline"` – if the host did not respond │
│ or a │
│ timeout/error occurred\n - `"invalid"` – if the input parameter is missing or invalid', │
│ 'parameters': {'properties': {'host': {'type': 'string'}}, 'required': ['host'], 'type': 'object'} │
│ } │
│ }, │
│ { │
│ 'type': 'function', │
│ 'function': { │
│ 'name': 'check_port_80', │
│ 'description': 'Checks whether TCP port 80 on the given host is open.\n\nArgs:\n host (str): The target hostname or IP.\n\nReturns:\n str: "<host> - port 80 open" or "<host> - port 80 │
│ closed"', │
│ 'parameters': {'properties': {'host': {'type': 'string'}}, 'required': ['host'], 'type': 'object'} │
│ } │
│ } │
│ ] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message( │
│ role='assistant', │
│ content='', │
│ thinking=None, │
│ images=None, │
│ tool_name=None, │
│ tool_calls=[ │
│ ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'})) │
│ ] │
│ ) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────── Wywołanie narzędzia ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ │
│ ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'})) │
│ ] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────── Otrzymana odpowiedz modelu ───────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Message( │
│ role='assistant', │
│ content='Podsumowując, tylko jeden host (195.181.211.253) odpowiada na ping i ma otwarty port 80.\n\nAdresy IP hostów, które odpowiadają na ping:\n\n* 195.181.211.253\n\nHost, który nie odpowiada na │
│ ping:\n192.168.0.40', │
│ thinking=None, │
│ images=None, │
│ tool_name=None, │
│ tool_calls=None │
│ ) │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────── Pełna historia czatu ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ [ │
│ { │
│ 'role': 'user', │
│ 'content': '\nPodaj tylko adresy IP hostów, które odpowiadają na ping z listy: 192.168.0.40, 195.181.211.253. \nW przypadku gdy któryś odpowie na ping to sprawdz czy odpowiada również na port │
│ 80.\n' │
│ }, │
│ { │
│ 'role': 'assistant', │
│ 'content': '', │
│ 'tool_calls': [ │
│ ToolCall(function=Function(name='ping', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '192.168.0.40'})), │
│ ToolCall(function=Function(name='ping', arguments={'host': '195.181.211.253'})), │
│ ToolCall(function=Function(name='check_port_80', arguments={'host': '195.181.211.253'})) │
│ ] │
│ }, │
│ {'role': 'tool', 'name': 'ping', 'content': '{"result": "192.168.0.40 - offline"}'}, │
│ {'role': 'tool', 'name': 'check_port_80', 'content': '{"result": "192.168.0.40 - port 80 closed"}'}, │
│ {'role': 'tool', 'name': 'ping', 'content': '{"result": "195.181.211.253 - online"}'}, │
│ {'role': 'tool', 'name': 'check_port_80', 'content': '{"result": "195.181.211.253 - port 80 open"}'} │
│ ] │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────── Finalna odpowiedź ───────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ │
│ Podsumowując, tylko jeden host (195.181.211.253) odpowiada na ping i ma otwarty port 80. │
│ │
│ Adresy IP hostów, które odpowiadają na ping: │
│ │
│ * 195.181.211.253 │
│ │
│ Host, który nie odpowiada na ping: │
│ 192.168.0.40 │
│ │
│ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Komentarze