LangGraph
Halaman ini menjelaskan cara instrumen LangGraphagen, bagaimana rentang diidentifikasi, dan bagaimana bidang evaluasi diekstraksi. Ini ditutup dengan praktik terbaik untuk penataan LangGraph agen sehingga dapat dievaluasi dengan andal.
Topik
Instrumen agen Anda
Anda dapat menginstrumentasikan LangGraph agen dengan salah satu dari dua pustaka instrumentasi: OpenTelemetry(opentelemetry-instrumentation-langchain) atau OpenInference(openinference-instrumentation-langchain). Amazon Bedrock AgentCore Evaluations mendukung kedua pustaka. Pustaka memancarkan nama lingkup yang berbeda dan menggunakan atribut rentang yang berbeda. Layanan evaluasi mengekstrak nilai yang sama dari masing-masing.
Saat agen Anda berjalan dengan AWS Distro for OpenTelemetry (ADOT), seperti di Amazon Bedrock AgentCore Runtime, Anda tidak perlu menambahkan kode instrumentasi eksplisit. Menambahkan pustaka instrumentasi ke dependensi proyek Anda sudah cukup. ADOT menemukannya saat startup dan mengaktifkannya secara otomatis.
Tambahkan pustaka instrumentasi untuk jalur yang Anda inginkan ke dependensi Anda. Contoh berikut menyematkan versi minimum; gunakan versi terbaru yang tersedia kecuali Anda memiliki alasan untuk menyematkan.
contoh
- OpenTelemetry
-
CATATAN: Gunakan versi 0.55.0 atau yang lebih baru. Versi 0.55.0 menambahkan dukungan untuk konvensi rentang agen OpenTelemetry generatif-AI yang lebih baru yang diandalkan oleh layanan evaluasi.
Tambahkan opentelemetry-instrumentation-langchain ke dependensi Anda. Nama lingkup yang dipancarkan adalah. opentelemetry.instrumentation.langchain
requirements.txt:
opentelemetry-instrumentation-langchain>=0.55.0
pyproject.toml:
[project]
dependencies = [
"opentelemetry-instrumentation-langchain>=0.55.0",
]
- OpenInference
-
Tambahkan openinference-instrumentation-langchain ke dependensi Anda. Nama lingkup yang dipancarkan adalah. openinference.instrumentation.langchain
requirements.txt:
openinference-instrumentation-langchain>=0.1.62
pyproject.toml:
[project]
dependencies = [
"openinference-instrumentation-langchain>=0.1.62",
]
Instrumentasi adalah salah satu langkah dalam mengatur observabilitas. Untuk mengekspor telemetri untuk evaluasi, selesaikan pengaturan lengkap di Siapkan observabilitas.
Bagaimana rentang diidentifikasi
Atribut yang digunakan untuk mengklasifikasikan bentang berbeda antara dua pustaka instrumentasi.
contoh
- OpenTelemetry
-
Pustaka OpenTelemetry instrumentasi mengklasifikasikan rentang menggunakan traceloop.span.kind atribut, dan versi terbaru juga disetel. gen_ai.operation.name
| Jenis rentang |
Mengidentifikasi atribut |
|
Memohon agen
|
traceloop.span.kind= workflow (juga gen_ai.operation.name =invoke_agent)
|
|
Jalankan alat
|
traceloop.span.kind= tool (juga gen_ai.operation.name =execute_tool)
|
|
Inferensi
|
gen_ai.operation.name = chat
|
- OpenInference
-
Pustaka OpenInference instrumentasi mengklasifikasikan rentang menggunakan atribut. openinference.span.kind
| Jenis rentang |
Mengidentifikasi atribut |
|
Memohon agen
|
openinference.span.kind= CHAIN atau AGENT
|
|
Jalankan alat
|
openinference.span.kind = TOOL
|
|
Inferensi
|
openinference.span.kind = LLM
|
Untuk rentang agen pemanggilan, input dan output tidak berisi daftar per pesan yang bersih. Sebaliknya, konten adalah status LangChain grafik serial: string JSON yang membungkus status penuh. Bentuk yang tepat dari status serial ini berbeda antara dua pustaka instrumentasi. Dalam kedua kasus, layanan menguraikannya untuk menemukan prompt pengguna (pesan manusia) dan respons agen (pesan AI).
LangGraph juga membuat serial peran pesan dalam lebih dari satu bentuk. Peran dapat muncul sebagai nilai huruf kecil (human,ai,tool) atau sebagai nama kelas LangChain pesan (HumanMessage,AIMessage,ToolMessage). Layanan mengenali kedua bentuk.
Lokasi konten ini tergantung pada bagaimana telemetri dikumpulkan. Atribut pengidentifikasi (traceloop.span.kindatauopeninference.span.kind) berada pada rentang dalam kedua kasus. Untuk informasi selengkapnya, lihat Rentang, catatan peristiwa, dan sinyal telemetri.
Saat telemetri dibagi, layanan membaca konten dari catatan peristiwa yang berkorelasi dengan setiap rentang:
-
Prompt pengguna dan respons agen: dari catatan peristiwa rentang agen pemanggilan, di body.input danbody.output.
-
Panggilan alat: nama alat dari rentang alat eksekusi. Argumen dan hasil alat berasal dari catatan peristiwa rentang itu, di body.input danbody.output.
Sebagai contoh, lihat Contoh bentang dengan catatan peristiwa.
Ketika telemetri tidak dibagi, konten yang sama tetap pada rentang sebagai atribut. Atribut tergantung pada pustaka instrumentasi:
-
OpenTelemetry:
-
Prompt pengguna dan respons agen: dari gen_ai.task.input dan gen_ai.task.output pada rentang agen pemanggilan.
-
Panggilan alat: nama alat darigen_ai.tool.name, dan argumen dan hasil dari gen_ai.tool.call.arguments dangen_ai.tool.call.result, pada rentang alat eksekusi.
-
OpenInference:
-
Prompt pengguna dan respons agen: dari input.value dan output.value pada rentang agen pemanggilan.
-
Panggilan alat: nama alat daritool.name, dan argumen dan hasil dari input.value danoutput.value, pada rentang alat eksekusi.
Sebagai contoh, lihat Contoh rentang tanpa catatan peristiwa.
Contoh membentang dengan catatan acara
Ketika telemetri dibagi, rentang membawa atribut pengidentifikasi dan konten hidup dalam catatan peristiwa yang berkorelasi. Contoh berikut berasal dari agen LangGraph perencanaan perjalanan yang digunakan di Amazon Bedrock Runtime. AgentCore Agen yang sama ditampilkan di bawah setiap perpustakaan instrumentasi.
Contoh-contoh ini bukan bentang lengkap. Mereka menunjukkan data representatif dari interaksi agen nyata, dengan beberapa bidang dihilangkan dan nilai panjang dipotong untuk keterbacaan.
OpenTelemetry
contoh
- Invoke agent span
-
traceloop.span.kindAtribut (workflow) mengidentifikasi ini sebagai rentang agen pemanggilan; versi pustaka terbaru juga menetapkan gen_ai.operation.name =. invoke_agent
{
"traceId": "6a01eef11066751d68f90def0da1f80a",
"spanId": "ba1833fa7f097041",
"parentSpanId": "836a5ccf9a2186cc",
"name": "travel_agent.workflow",
"kind": "INTERNAL",
"scope": {
"name": "opentelemetry.instrumentation.langchain",
"version": "0.60.0"
},
"startTimeUnixNano": 1778511607308521744,
"endTimeUnixNano": 1778511610930280395,
"durationNano": 3621758651,
"attributes": {
"traceloop.span.kind": "workflow",
"gen_ai.operation.name": "invoke_agent",
"gen_ai.agent.name": "travel_agent",
"gen_ai.provider.name": "langgraph",
"traceloop.workflow.name": "travel_agent",
"session.id": "sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0"
},
"status": {
"code": "OK"
}
}
Catatan peristiwa yang berkorelasi membawa percakapan. Setiap pesan content adalah status LangChain grafik serial. Input membungkus status di bawah inputs kunci. Output membungkusnya di bawah outputs kunci, dengan setiap pesan sebagai objek LangChain konstruktor. Prompt pengguna adalah pesan manusia dan respons agen adalah pesan AI di dalam status serial itu.
{
"spanId": "ba1833fa7f097041",
"traceId": "6a01eef11066751d68f90def0da1f80a",
"scope": {
"name": "opentelemetry.instrumentation.langchain"
},
"body": {
"input": {
"messages": [
{
"content": "{\"inputs\": {\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}, \"tags\": [], \"metadata\": {\"ls_integration\": \"langchain_create_agent\", \"lc_agent_name\": \"travel_agent\", \"thread_id\": \"sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0\"}, \"kwargs\": {\"name\": \"travel_agent\"}}",
"role": "user"
}
]
},
"output": {
"messages": [
{
"content": "{\"outputs\": {\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\", \"type\": \"human\", \"id\": \"12345678-1234-1234-1234-123456789012\"}}, {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"Hello! I'm your travel planning assistant ...\", \"type\": \"ai\"}}]}, \"kwargs\": {\"tags\": []}}",
"role": "assistant"
}
]
}
}
}
- Execute tool span
-
traceloop.span.kindAtribut (tool) mengidentifikasi ini sebagai rentang alat eksekusi; gen_ai.tool.name memegang nama alat dan gen_ai.operation.name =execute_tool.
{
"traceId": "6a01eefa5c52f3d86a35038f35f5ba30",
"spanId": "5b332f3cd15ace04",
"parentSpanId": "922a21edc04eba29",
"name": "execute_tool search_flights",
"kind": "INTERNAL",
"scope": {
"name": "opentelemetry.instrumentation.langchain",
"version": "0.60.0"
},
"startTimeUnixNano": 1778511614892698232,
"endTimeUnixNano": 1778511614893399618,
"durationNano": 701386,
"attributes": {
"traceloop.span.kind": "tool",
"gen_ai.operation.name": "execute_tool",
"gen_ai.tool.name": "search_flights",
"gen_ai.tool.type": "function",
"gen_ai.tool.description": "Search for available flights between cities.",
"gen_ai.provider.name": "langgraph",
"traceloop.workflow.name": "travel_agent",
"session.id": "sea-nyc-trip-2-turns-adot_v17_opentelemetry_0_60_0"
},
"status": {
"code": "OK"
}
}
Catatan peristiwa yang berkorelasi membawa input alat (argumen) dan output (hasil, diserialkan sebagai a LangChain ToolMessage).
{
"spanId": "5b332f3cd15ace04",
"traceId": "6a01eefa5c52f3d86a35038f35f5ba30",
"scope": {
"name": "opentelemetry.instrumentation.langchain"
},
"body": {
"input": {
"messages": [
{ "content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}" }
]
},
"output": {
"messages": [
{
"role": "tool",
"name": "search_flights",
"content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"flights\": [ ... ]}"
}
]
}
}
}
OpenInference
Dengan OpenInference perpustakaan, tipe rentang dibawa dalam openinference.span.kind atribut, dan input dan output agen diserialisasikan dalam catatan peristiwa yang berkorelasi.
contoh
- Invoke agent span
-
openinference.span.kindAtribut (CHAIN, atau AGENT ketika grafik dikompilasi dengan nama) mengidentifikasi ini sebagai rentang agen pemanggilan.
{
"traceId": "6a387ee61078243c1cc455ed45c6c313",
"spanId": "0a7990d804132a9b",
"parentSpanId": "29ae22014173881c",
"name": "LangGraph",
"kind": "INTERNAL",
"scope": {
"name": "openinference.instrumentation.langchain",
"version": "0.1.66"
},
"startTimeUnixNano": 1782087405949310976,
"endTimeUnixNano": 1782087408945828864,
"durationNano": 2996517888,
"attributes": {
"openinference.span.kind": "CHAIN",
"input.mime_type": "application/json",
"output.mime_type": "application/json",
"llm.input_messages.0.message.role": "user",
"session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
},
"status": {
"code": "OK"
}
}
Catatan peristiwa yang berkorelasi membawa percakapan. Prompt pengguna adalah pesan peran manusia dan respons agen adalah AI-role pesan dalam pesan serial.
{
"spanId": "0a7990d804132a9b",
"traceId": "6a387ee61078243c1cc455ed45c6c313",
"scope": {
"name": "openinference.instrumentation.langchain"
},
"body": {
"input": {
"messages": [
{
"role": "user",
"content": "{\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}"
}
]
},
"output": {
"messages": [
{
"content": "{\"messages\": [{\"type\": \"human\", \"data\": {\"content\": \"Hey, how can you help me\", ...}}, {\"type\": \"ai\", \"data\": {\"content\": \"Hello! I'm your travel planning assistant ...\", ...}}]}",
"role": "assistant"
}
]
}
}
}
- Execute tool span
-
openinference.span.kindAtribut (TOOL) mengidentifikasi ini sebagai rentang alat eksekusi; tool.name memegang nama alat.
{
"traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
"spanId": "ab105c12cc40048f",
"parentSpanId": "9b2d4e72760690b4",
"name": "search_flights",
"kind": "INTERNAL",
"scope": {
"name": "openinference.instrumentation.langchain",
"version": "0.1.66"
},
"startTimeUnixNano": 1782087411724620032,
"endTimeUnixNano": 1782087411725306880,
"durationNano": 686848,
"attributes": {
"openinference.span.kind": "TOOL",
"tool.name": "search_flights",
"tool.description": "Search for available flights between cities.",
"input.mime_type": "application/json",
"output.mime_type": "application/json",
"session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
},
"status": {
"code": "OK"
}
}
Catatan peristiwa yang berkorelasi membawa input alat (argumen) dan output (hasil, diserialkan sebagai a LangChain ToolMessage).
{
"spanId": "ab105c12cc40048f",
"traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
"scope": {
"name": "openinference.instrumentation.langchain"
},
"body": {
"input": {
"messages": [
{
"role": "user",
"content": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}"
}
]
},
"output": {
"messages": [
{
"content": "{\"type\": \"tool\", \"data\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"type\": \"tool\", \"name\": \"search_flights\", \"tool_call_id\": \"toolu_bdrk_01LzXXJCfpfuS7Bpf7e1qLMg\", \"status\": \"success\"}}",
"role": "assistant"
}
]
}
}
}
Contoh membentang tanpa catatan peristiwa
Ketika telemetri tidak dibagi, konten yang sama tetap pada atribut rentang dan tidak ada catatan peristiwa terpisah yang dihasilkan. Contoh berikut berasal dari agen LangGraph perencanaan perjalanan. Agen yang sama ditampilkan di bawah setiap perpustakaan instrumentasi.
Contoh-contoh ini bukan bentang lengkap. Mereka menunjukkan data representatif dari interaksi agen nyata, dengan beberapa bidang dihilangkan dan nilai panjang dipotong untuk keterbacaan.
OpenTelemetry
contoh
- Invoke agent span
-
gen_ai.task.inputAtribut menahan prompt pengguna, dan gen_ai.task.output atribut memegang status serial dengan respons agen. Keduanya adalah status LangChain grafik serial.
{
"traceId": "6a4de7b85e61747e6b568a1f4768e89d",
"spanId": "31ea3d5882dac680",
"name": "LangGraph.workflow",
"kind": "INTERNAL",
"scope": {
"name": "opentelemetry.instrumentation.langchain",
"version": "0.62.1"
},
"attributes": {
"traceloop.span.kind": "workflow",
"gen_ai.operation.name": "invoke_agent",
"gen_ai.agent.name": "LangGraph",
"gen_ai.task.input": "{\"inputs\": {\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}, \"tags\": [], \"metadata\": { ... }, \"kwargs\": {\"name\": \"LangGraph\"}}",
"gen_ai.task.output": "{\"outputs\": {\"messages\": [{\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"HumanMessage\"], \"kwargs\": {\"content\": \"Hey, how can you help me\", \"type\": \"human\"}}, {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"AIMessage\"], \"kwargs\": {\"content\": \"Hello! I'm your travel planning assistant ...\", \"type\": \"ai\"}}]}, \"kwargs\": {\"tags\": []}}",
"session.id": "sea-nyc-trip-2-turns-unified"
},
"status": {
"code": "OK"
}
}
- Execute tool span
-
gen_ai.tool.call.argumentsAtribut memegang argumen alat, dan gen_ai.tool.call.result atribut memegang hasil alat, diserialkan sebagai LangChain ToolMessage.
{
"traceId": "6a4de7c376913db82e6f0f336a16731d",
"spanId": "b64c37adefae74f0",
"name": "execute_tool search_flights",
"kind": "INTERNAL",
"scope": {
"name": "opentelemetry.instrumentation.langchain",
"version": "0.62.1"
},
"attributes": {
"traceloop.span.kind": "tool",
"gen_ai.operation.name": "execute_tool",
"gen_ai.tool.name": "search_flights",
"gen_ai.tool.description": "Search for available flights between cities.",
"gen_ai.tool.call.arguments": "{\"input_str\": \"{'origin': 'SEA', 'destination': 'NYC', 'date': '2025-03-15'}\", \"inputs\": {\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}, \"metadata\": { ... }}",
"gen_ai.tool.call.result": "{\"output\": {\"lc\": 1, \"type\": \"constructor\", \"id\": [\"langchain\", \"schema\", \"messages\", \"ToolMessage\"], \"kwargs\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"type\": \"tool\", \"name\": \"search_flights\", \"status\": \"success\"}}}",
"session.id": "sea-nyc-trip-2-turns-unified"
},
"status": {
"code": "OK"
}
}
OpenInference
contoh
- Invoke agent span
-
input.valueAtribut menahan prompt pengguna, dan output.value atribut memegang status serial dengan respons agen.
{
"traceId": "6a387ee61078243c1cc455ed45c6c313",
"spanId": "b8c0b67876b78b91",
"name": "LangGraph",
"kind": "INTERNAL",
"scope": {
"name": "openinference.instrumentation.langchain",
"version": "0.1.66"
},
"attributes": {
"openinference.span.kind": "CHAIN",
"input.value": "{\"messages\": [{\"role\": \"user\", \"content\": \"Hey, how can you help me\"}]}",
"output.value": "{\"messages\": [{\"type\": \"human\", \"data\": {\"content\": \"Hey, how can you help me\"}}, {\"type\": \"ai\", \"data\": {\"content\": \"Hello! I'm your travel planning assistant ...\"}}]}",
"session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
},
"status": {
"code": "OK"
}
}
- Execute tool span
-
input.valueAtribut memegang argumen alat, dan output.value atribut memegang hasil alat, diserialkan sebagai LangChain ToolMessage.
{
"traceId": "6a387ef07b8f4f3732fab45d3c0b51ff",
"spanId": "58752612d9b22ae1",
"name": "search_flights",
"kind": "INTERNAL",
"scope": {
"name": "openinference.instrumentation.langchain",
"version": "0.1.66"
},
"attributes": {
"openinference.span.kind": "TOOL",
"tool.name": "search_flights",
"tool.description": "Search for available flights between cities.",
"input.value": "{\"origin\": \"SEA\", \"destination\": \"NYC\", \"date\": \"2025-03-15\"}",
"output.value": "{\"type\": \"tool\", \"data\": {\"content\": \"{\\\"origin\\\": \\\"SEA\\\", \\\"destination\\\": \\\"NYC\\\", \\\"flights\\\": [ ... ]}\", \"name\": \"search_flights\"}}",
"session.id": "sea-nyc-trip-2-turns-oi-0-1-66"
},
"status": {
"code": "OK"
}
}
Praktik terbaik untuk LangGraph agen
Bagaimana Anda membangun dan memanggil LangGraph agen memengaruhi apa yang muncul dalam telemetrinya, dan oleh karena itu seberapa andal agen tersebut dapat dievaluasi. Praktik berikut membantu memastikan prompt pengguna, respons agen, dan aktivitas alat dapat dipulihkan.
1. Pilih pola konstruksi agen
Ada dua cara umum untuk membangun LangGraph agen:
-
Prebuilt create_agent: cara tercepat untuk memulai. Ini menghasilkan rentang agen pemanggilan tunggal per giliran, dengan percakapan melewati LangGraph loop eksekusi bawaan. Gunakan ini saat Anda menginginkan agen alasan-tindakan standar tanpa alur kontrol khusus.
from langchain.agents import create_agent
agent = create_agent(model=model, tools=[search_flights, book_flight])
-
Kustom StateGraph: memberi Anda kontrol penuh atas node, tepi, dan perutean bersyarat. Setiap eksekusi node menjadi rentangnya sendiri, sehingga jejaknya lebih granular. Gunakan ini saat Anda membutuhkan orkestrasi khusus.
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class State(TypedDict):
messages: list
graph = StateGraph(State)
graph.add_node("generate_response", generate_response)
graph.add_node("tools", run_tools)
graph.add_edge(START, "generate_response")
agent = graph.compile()
Kedua pola dievaluasi dengan cara yang sama; perbedaannya adalah perincian jejak.
2. Gunakan pesan dalam Status grafik Anda (disarankan)
Layanan evaluasi merekonstruksi percakapan dari pesan input dan output agen. Menggunakan messages bidang tidak wajib, tetapi memungkinkan ekstraksi yang paling dapat diandalkan. Untuk kebiasaanStateGraph, simpan percakapan di messages bidang di Negara Bagian Anda:
-
Sertakan messages di Negara Anda (disarankan). Anda dapat menambahkan bidang khusus lainnya (seperti user_id atau metadata). Ketika messages hadir, ekstraksi standar menemukan prompt pengguna dan respons agen secara langsung. Jika tidak messages ada, layanan akan kembali merekonstruksi percakapan dari rentang inferensi individu, yang kurang dapat diandalkan.
-
Tambahkan, jangan ganti. Ikuti LangGraph konvensi menambahkan pesan baru ke daftar daripada menimpa, sehingga riwayat percakapan lengkap dipertahankan.
-
Gunakan jenis LangChain pesan kanonik (HumanMessage,, AIMessageToolMessage,SystemMessage). Instrumentasi membuat serial ini dengan benar, dan layanan mengenali peran mereka.
3. Meneruskan pesan pengguna dalam format yang didukung
Saat Anda memanggil LangGraph agen, Anda menambahkan pesan pengguna ke messages status grafik. LangGraph menerima pesan dalam tiga format yang dapat dipertukarkan, dan AgentCore Evaluasi mendukung semuanya. Masing-masing menghasilkan rentang dan catatan peristiwa yang dapat dibaca oleh layanan.
-
Tuple: (role, content) sepasang:
agent.invoke({"messages": [("user", user_message)]}, config=config)
-
LangChain objek pesan: a HumanMessage (atau kelas pesan lainnya):
from langchain_core.messages import HumanMessage
agent.invoke({"messages": [HumanMessage(content=user_message)]}, config=config)
-
Kamus: {"role", "content"} kamus:
agent.invoke({"messages": [{"role": "user", "content": user_message}]}, config=config)
Ketiga format menghasilkan messages status yang sama, sehingga prompt pengguna dan respons agen diekstraksi secara identik terlepas dari mana yang Anda pilih.