Menangani Pemanggilan Alat dalam Respons yang Dialirkan
Uraikan respons yang dialirkan dan berisi argumen pemanggilan fungsi yang tiba token demi token, tampung pecahan JSON, lalu jalankan alat hanya setelah pemanggilan selesai.
Menangani Pemanggilan Alat dalam Respons yang Dialirkan adalah pelajaran AI Engineering Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Engineering Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Engineering Academy mencakup 4 pelajaran total.
Pemanggilan Alat Tiba dengan Cara Berbeda dalam Stream
Saat LLM memutuskan untuk memanggil suatu fungsi, struktur respons berubah. Alih-alih string content, delta berisi larik tool_calls. Namun, dalam respons streaming, argumen pemanggilan fungsi tiba token demi token sebagai string JSON parsial—Anda tidak menerima objek JSON lengkap dalam satu potongan. Anda harus menyangga fragmen-fragmen ini dan menyusun kembali JSON lengkap sebelum dapat mengurainya dan menjalankan pemanggilan alat.
# In a non-streaming response, tool call is complete:
# choice.message.tool_calls[0].function.arguments = '{"city": "Paris"}'
# In a streaming response, arguments arrive in pieces:
# chunk 1: delta.tool_calls[0].function.arguments = '{'
# chunk 2: delta.tool_calls[0].function.arguments = '"city"'
# chunk 3: delta.tool_calls[0].function.arguments = ': "'
# chunk 4: delta.tool_calls[0].function.arguments = 'Paris'
# chunk 5: delta.tool_calls[0].function.arguments = '"}'
# You must concatenate these before JSON.parse can workMendeteksi Pemanggilan Alat dalam Stream
Periksa finish_reason pada setiap potongan untuk mengetahui kapan harus mengharapkan panggilan alat. Jika finish_reason bernilai 'tool_calls', model telah memutuskan untuk memanggil suatu fungsi dan pengaliran akan berakhir. Jika finish_reason bernilai 'stop', model menghasilkan respons teks biasa. Selama pengaliran, periksa apakah chunk.choices[0].delta.tool_calls bukan None untuk mengidentifikasi fragmen argumen panggilan alat.
async def detect_stream_type(messages, tools):
stream = await async_client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
tools=tools,
stream=True,
)
response_type = 'text'
async for chunk in stream:
choice = chunk.choices[0]
if choice.delta.tool_calls: # tool call fragment arriving
response_type = 'tool_call'
if choice.finish_reason == 'tool_calls':
print('Model wants to call a function')
elif choice.finish_reason == 'stop':
print('Normal text response')
return response_typeMenyimpan Sementara Argumen Panggilan Alat
Gunakan kamus dengan indeks panggilan alat sebagai kunci untuk mengumpulkan fragmen argumen dari setiap potongan. Indeks tersebut mengidentifikasi panggilan alat yang sedang dialirkan—model dapat memanggil beberapa fungsi dalam satu respons. Untuk setiap potongan dengan delta tool_calls yang tidak bernilai null, tambahkan fragmen argumen ke entri penyangga yang sesuai berdasarkan indeks panggilan alat.
from collections import defaultdict
async def collect_streamed_tool_calls(messages, tools):
stream = await async_client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
tools=tools,
stream=True,
)
tool_call_buffers = defaultdict(lambda: {'name': '', 'id': '', 'arguments': ''})
text_buffer = ''
async for chunk in stream:
delta = chunk.choices[0].delta
if delta.content: # text content
text_buffer += delta.content
if delta.tool_calls:
for tc in delta.tool_calls:
idx = tc.index
if tc.id:
tool_call_buffers[idx]['id'] = tc.id
if tc.function.name:
tool_call_buffers[idx]['name'] += tc.function.name
if tc.function.arguments:
tool_call_buffers[idx]['arguments'] += tc.function.arguments
return text_buffer, dict(tool_call_buffers)Mengurai dan Menjalankan Panggilan Alat
Setelah pengaliran berakhir dan Anda telah memperoleh string argumen yang lengkap, uraikan masing-masing string dengan json.loads dan teruskan ke fungsi Python yang sesuai. Jalankan panggilan alat sesuai urutan permintaannya (atau secara paralel jika tidak saling bergantung), lalu format hasilnya sebagai pesan respons alat untuk dikirim kembali dalam panggilan API lanjutan.
import json
# Example tool registry
tools_registry = {
'get_weather': lambda city, unit='celsius': {'temp': 22, 'desc': 'sunny', 'city': city},
'search_docs': lambda query, top_k=3: [{'title': 'Doc 1', 'snippet': 'Relevant info...'}],
}
def execute_tool_calls(tool_call_buffers: dict) -> list[dict]:
tool_messages = []
for idx in sorted(tool_call_buffers.keys()):
tc = tool_call_buffers[idx]
func_name = tc['name']
args = json.loads(tc['arguments'])
if func_name in tools_registry:
result = tools_registry[func_name](**args)
else:
result = {'error': f'Unknown function: {func_name}'}
tool_messages.append({
'role': 'tool',
'tool_call_id': tc['id'],
'content': json.dumps(result),
})
return tool_messagesSiklus Panggilan Alat dengan Pengaliran Lengkap
Alur panggilan alat dengan pengaliran lengkap memerlukan siklus percakapan beberapa giliran. Permintaan pertama mungkin mengembalikan panggilan alat; Anda menjalankannya dan menambahkan hasilnya ke riwayat pesan; permintaan kedua mengembalikan jawaban teks akhir. Siklus ini dapat berulang beberapa kali jika model memilih untuk melakukan panggilan alat tambahan berdasarkan hasil panggilan sebelumnya.
async def streaming_agent_loop(initial_messages, tools):
messages = list(initial_messages)
max_iterations = 5
for iteration in range(max_iterations):
text, tool_calls = await collect_streamed_tool_calls(messages, tools)
if tool_calls:
# Append assistant message with tool calls
assistant_msg = {
'role': 'assistant',
'content': text or None,
'tool_calls': [
{'id': tc['id'], 'type': 'function',
'function': {'name': tc['name'], 'arguments': tc['arguments']}}
for tc in tool_calls.values()
]
}
messages.append(assistant_msg)
# Execute tools and append results
tool_results = execute_tool_calls(tool_calls)
messages.extend(tool_results)
else:
# No more tool calls — final text response
print('Final answer:', text)
return text
return 'Max iterations reached'Mengalirkan Teks Sambil Menyimpan Panggilan Alat
Dalam praktiknya, Anda ingin segera mengalirkan teks ke klien sambil menyimpan sementara argumen panggilan alat. Hal ini memerlukan pembedaan antara potongan yang membawa content (segera dialirkan) dan potongan yang membawa tool_calls (disimpan untuk dijalankan nanti). Alat hanya dapat dijalankan dan proses dilanjutkan setelah pengaliran berakhir dan panggilan alat selesai.
async def stream_with_tools(messages, tools):
stream = await async_client.chat.completions.create(
model='gpt-4o-mini', messages=messages, tools=tools, stream=True
)
tool_buffers = defaultdict(lambda: {'name': '', 'id': '', 'arguments': ''})
text_parts = []
async for chunk in stream:
delta = chunk.choices[0].delta
finish = chunk.choices[0].finish_reason
if delta.content:
text_parts.append(delta.content)
yield ('text', delta.content) # stream to client immediately
if delta.tool_calls:
for tc in delta.tool_calls:
if tc.id: tool_buffers[tc.index]['id'] = tc.id
if tc.function.name: tool_buffers[tc.index]['name'] += tc.function.name
if tc.function.arguments: tool_buffers[tc.index]['arguments'] += tc.function.arguments
if finish == 'tool_calls':
yield ('tool_calls', dict(tool_buffers)) # signal tool execution neededEksekusi Panggilan Alat secara Paralel
Saat model mengembalikan beberapa panggilan alat secara bersamaan (fitur yang disebut pemanggilan fungsi paralel), jalankan semuanya secara paralel dengan asyncio.gather, bukan secara berurutan. Eksekusi berurutan menambah latensi yang tidak perlu—jika model memanggil API cuaca dan pencarian basis data secara bersamaan, tidak ada alasan untuk menunggu salah satunya selesai sebelum memulai yang lain.
import asyncio
async def execute_tool_calls_parallel(tool_call_buffers: dict) -> list[dict]:
async def execute_one(idx, tc):
func_name = tc['name']
args = json.loads(tc['arguments'])
if func_name in async_tools_registry:
result = await async_tools_registry[func_name](**args)
else:
result = {'error': f'Unknown function: {func_name}'}
return {
'role': 'tool',
'tool_call_id': tc['id'],
'content': json.dumps(result),
}
tasks = [execute_one(idx, tc) for idx, tc in sorted(tool_call_buffers.items())]
return await asyncio.gather(*tasks)Mengalirkan Jawaban Akhir Setelah Menggunakan Alat
Setelah menjalankan panggilan alat dan menambahkan hasilnya ke riwayat pesan, buat permintaan kedua dengan pengaliran untuk mendapatkan jawaban akhir model. Alirkan respons ini langsung ke klien. Pola dua permintaan ini (permintaan awal dengan panggilan alat + permintaan lanjutan dengan hasil alat) merupakan siklus giliran agen standar, dan kedua permintaan dapat mengalirkan teksnya ke antarmuka pengguna.
async def full_tool_calling_stream(question: str, tools: list):
messages = [{'role': 'user', 'content': question}]
# First request: may produce tool calls
tool_buffers = {}
text1 = ''
async for event_type, data in stream_with_tools(messages, tools):
if event_type == 'text':
text1 += data
yield data # stream partial text if any
elif event_type == 'tool_calls':
tool_buffers = data
if tool_buffers:
# Execute tools, then get final streaming answer
tool_results = await execute_tool_calls_parallel(tool_buffers)
messages += [{ # assistant tool call message
'role': 'assistant',
'tool_calls': [
{'id': tc['id'], 'type': 'function',
'function': {'name': tc['name'], 'arguments': tc['arguments']}}
for tc in tool_buffers.values()
]
}] + tool_results
# Second request: final answer streams directly
async for token in token_stream(messages): # from earlier lesson
yield tokenMenampilkan Kemajuan Panggilan Alat di Antarmuka Pengguna
Pengguna seharusnya dapat melihat apa yang sedang dilakukan agen saat menunggu panggilan alat selesai. Sebelum menjalankan alat, alirkan peristiwa status ke klien yang menunjukkan fungsi mana yang dipanggil dan argumen yang digunakan. Setelah eksekusi selesai, alirkan status penyelesaian. Transparansi ini sangat meningkatkan persepsi kecepatan respons dan membantu pengguna menelusuri penggunaan alat yang tidak terduga.
import json
async def stream_with_progress(question, tools):
messages = [{'role': 'user', 'content': question}]
tool_buffers = {}
async for event_type, data in stream_with_tools(messages, tools):
if event_type == 'tool_calls':
tool_buffers = data
for tc in tool_buffers.values():
args = json.loads(tc['arguments'])
yield f'data: {json.dumps({"type": "tool_start", "function": tc["name"], "args": args})}\n\n'
result = tools_registry.get(tc['name'], lambda **kw: {})(** args)
yield f'data: {json.dumps({"type": "tool_done", "function": tc["name"]})}\n\n'
# Then stream final answer...Penanganan Kesalahan dalam Pengaliran Panggilan Alat
Eksekusi alat dapat gagal—API mengembalikan kesalahan, fungsi menimbulkan pengecualian, dan penguraian JSON gagal. Selalu tangkap pengecualian saat menjalankan alat dan kembalikan respons kesalahan terstruktur kepada model. Model kemudian dapat memutuskan untuk mencoba lagi dengan argumen berbeda, memanggil alat cadangan, atau menjelaskan kepada pengguna bahwa tindakan yang diminta gagal. Jangan pernah membiarkan pengecualian alat yang tidak tertangkap menghentikan siklus pengaliran.
def safe_execute_tool(func_name: str, args: dict) -> str:
try:
if func_name not in tools_registry:
return json.dumps({'error': f'Function {func_name!r} not found'})
result = tools_registry[func_name](**args)
return json.dumps(result)
except TypeError as e:
return json.dumps({'error': f'Invalid arguments: {str(e)}'})
except Exception as e:
return json.dumps({'error': f'Execution failed: {str(e)}'})
# Tool result message with error handled
tool_message = {
'role': 'tool',
'tool_call_id': tc['id'],
'content': safe_execute_tool(tc['name'], json.loads(tc['arguments'])),
}Membandingkan Pengaliran dan Tanpa Pengaliran untuk Agen
Untuk aplikasi berbasis agen, pengaliran menambah kompleksitas, tetapi memberikan manfaat besar bagi pengalaman pengguna. Tanpa pengaliran, pengguna tidak melihat apa pun selama siklus pemanggilan alat yang terdiri dari beberapa langkah dan mungkin memerlukan waktu 10–30 detik. Dengan pengaliran, pengguna dapat melihat teks perantara, pemberitahuan panggilan alat, dan jawaban akhir yang muncul token demi token. Kompleksitas kode tambahan biasanya sepadan untuk aplikasi interaktif, tetapi agen latar belakang yang berjalan secara mandiri dapat menggunakan mode tanpa pengaliran agar kodenya lebih sederhana.
Pemeriksaan Singkat
Uji pemahaman Anda tentang panggilan alat dalam respons yang dialirkan dari pelajaran ini.
Rangkuman Pelajaran
Dalam pelajaran ini Anda mempelajari bahwa argumen panggilan alat tiba sebagai fragmen JSON dalam respons yang dialirkan dan harus disimpan sementara berdasarkan indeks panggilan alat sebelum diuraikan, jalankan alat setelah pengaliran selesai lalu buat permintaan kedua dengan pengaliran untuk mendapatkan jawaban akhir, dan eksekusi paralel dengan asyncio.gather meminimalkan latensi saat model memanggil beberapa fungsi secara bersamaan. Selalu tangkap kesalahan eksekusi alat agar siklus agen tidak terhenti. Selanjutnya, kita akan menerapkan penyimpanan respons sementara untuk mengurangi biaya API.
Belajar Python dengan tutor AI — gratis
Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.
- Kursus
- 30
- Pelajaran
- 120
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Menangani Pemanggilan Alat dalam Respons yang Dialirkan” gratis?
Ya — teks lengkap “Menangani Pemanggilan Alat dalam Respons yang Dialirkan” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Engineering Academy, upgrade ke CoddyKit PRO. Kursus AI Engineering Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Menangani Pemanggilan Alat dalam Respons yang Dialirkan”?
Uraikan respons yang dialirkan dan berisi argumen pemanggilan fungsi yang tiba token demi token, tampung pecahan JSON, lalu jalankan alat hanya setelah pemanggilan selesai. Kamu berlatih AI Engineering Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai AI Engineering Academy?
Tidak diperlukan pengalaman sebelumnya. AI Engineering Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.
Berapa lama pelajaran “Menangani Pemanggilan Alat dalam Respons yang Dialirkan” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Engineering Academy ini?
Ya. Setiap pelajaran AI Engineering Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Memahami Pengaliran Token
- Mengonsumsi Aliran dengan Python SDK
- Pengaliran di FastAPI dengan Peristiwa yang Dikirim Server
- Menangani Pemanggilan Alat dalam Respons yang Dialirkan