---
title: "ขั้นตอนและการส่งต่อของเอเจนต์"
description: "แบ่งเอเจนต์ออกเป็นขั้นตอนที่มีคำสั่ง เครื่องมือ และค่าที่เก็บรวบรวมของตนเอง และส่งต่อสายระหว่างเอเจนต์ระหว่างการสนทนา"
---

พรอมป์ต์เดียวทำให้โมเดลได้รับงานทั้งหมดพร้อมกัน **ขั้นตอน** ทำให้โมเดลจัดการแต่ละช่วงของการโทรทีละช่วง: แต่ละขั้นตอนมีคำสั่งของตนเอง เครื่องมือของตนเอง บรรทัดเสริมที่ต้องพูดตามคำทุกคำ และค่าที่ต้องเก็บรวบรวมก่อนดำเนินการต่อ **การส่งต่อ** ช่วยให้เอเจนต์คนอื่นในองค์กรของคุณเข้ามารับช่วงกลางสายได้ โดยมีบันทึกการสนทนาครบถ้วน เพื่อให้ผู้โทรไม่ต้องพูดซ้ำ

เอเจนต์ทุกตัวเริ่มต้นด้วยพรอมป์ต์เดียว ไม่มีสิ่งใดเปลี่ยนแปลงจนกว่าคุณจะเพิ่มขั้นตอนหรือการส่งต่อ

## ขั้นตอนหรือพรอมป์ต์เดียว?

| ใช้ขั้นตอนเมื่อ | ใช้พรอมป์ต์เดียวต่อเมื่อ |
| --- | --- |
| การโทรมีช่วงที่ต้องเกิดขึ้นตามลำดับ: ตรวจสอบก่อน แล้วจึงจอง | การสนทนาไม่มีขอบเขตตายตัว: FAQ แผนกต้อนรับ การสนับสนุนทั่วไป |
| ต้องเก็บค่าหนึ่งก่อนที่เอเจนต์จะดำเนินการต่อ | ไม่จำเป็นต้องเก็บข้อมูลตามลำดับที่กำหนด |
| มีกฎที่ห้ามยืดหยุ่น เช่น "ผู้โทรที่อายุต่ำกว่า 18 ปีให้ติดต่อผู้ปกครอง" | วิจารณญาณของโมเดลเพียงพอสำหรับทุกเส้นทาง |
| ต้องเรียกใช้การค้นหา ณ จุดที่กำหนดในการโทร | สามารถเรียกใช้เครื่องมือได้ทุกเมื่อที่ผู้โทรร้องขอ |
| ส่วนหนึ่งของการโทรเป็นหน้าที่ของเอเจนต์อีกตัว ซึ่งมีพรอมป์ต์และเสียงของตนเอง | บุคลิกเดียวจัดการทุกอย่าง |

ขั้นตอนไม่ใช่ผังโทรศัพท์แบบมีสคริปต์ ภายในขั้นตอน โมเดลจะดำเนินการสนทนาเช่นเดียวกับเมื่อใช้พรอมป์ต์เดียว ขั้นตอนจะกำหนดสิ่งที่โมเดลกำลังดำเนินการ เครื่องมือที่ใช้ได้ และสิ่งที่ต้องมีก่อนออกจากขั้นตอนนั้น

## วิธีสร้างเอเจนต์แบบมีขั้นตอน

เมื่อเอเจนต์มีขั้นตอน `prompt` จะเก็บ **คำสั่งที่ใช้ร่วมกัน**: บุคลิกและกฎที่ใช้กับทุกขั้นตอน แต่ละขั้นตอนจะเพิ่มคำสั่งของตนเองเข้าไป ในทุกเทิร์น โมเดลจะเห็นคำสั่งที่ใช้ร่วมกัน ชื่อและคำสั่งของขั้นตอนปัจจุบัน และค่าที่เก็บรวบรวมไว้จนถึงตอนนั้น

ในตัวสร้าง ให้สลับเอเจนต์จาก **พรอมป์ต์เดียว** เป็น **ขั้นตอน** คุณจะได้รับการ์ดสำหรับแต่ละขั้นตอน ซึ่งมีชื่อ คำสั่ง บรรทัดพูด เครื่องมือ ค่าที่เก็บรวบรวม และการเปลี่ยนผ่าน ตัวเลือกการส่งต่อ และกราฟโฟลว์แบบอ่านอย่างเดียวของขั้นตอน การสลับกลับไปใช้พรอมป์ต์เดียวจะแสดงพรอมป์ต์แบบรวมที่ผสานคำสั่งที่ใช้ร่วมกันและทุกขั้นตอน

ผ่าน API ฟิลด์ `steps` และ `handoffs` อยู่ใน
[เอเจนต์](/api-reference/agents) เช่นเดียวกับฟิลด์เอเจนต์อื่น `PATCH` จะจัดเก็บไว้ในฉบับร่าง และ `POST /v1/agents/{agent_id}/deploy` จะเผยแพร่ให้ใช้งานจริง การส่ง
`"steps": null` จะสลับเอเจนต์กลับไปใช้พรอมป์ต์เดียว การโทรทดสอบและ **สนทนา** จะทำงานกับฉบับร่าง

<Tip>
  [ผู้ช่วยอัตโนมัติ](/th/guides/ask-the-copilot) สามารถสร้างขั้นตอนจากคำอธิบาย
  ("เพิ่มขั้นตอนที่ตรวจสอบวันเกิดก่อนการจอง") และแก้ไขหนึ่งขั้นตอน
  โดยไม่กระทบขั้นตอนอื่น
</Tip>

### ฟิลด์ของขั้นตอน

| ฟิลด์ | ความหมาย |
| --- | --- |
| `id` | ต้องระบุ ใช้อักษรตัวพิมพ์เล็ก ตัวเลข และขีดล่าง โดยต้องเริ่มด้วยตัวอักษร และยาวได้สูงสุด 40 อักขระ ต้องไม่ซ้ำภายในเอเจนต์ |
| `name` | ชื่อที่แสดงซึ่งต้องระบุ ยาว 1 ถึง 80 อักขระ |
| `instructions` | ต้องระบุ ยาวได้สูงสุด 20,000 อักขระ รองรับ[ตัวแปรการโทร](/th/guides/call-variables) `{{var}}` เช่นเดียวกับ `prompt` |
| `say` | บรรทัดเสริมที่ต้องพูดตามคำทุกคำเมื่อเข้าสู่ขั้นตอน ยาวได้สูงสุด 1,000 อักขระ รองรับ `{{var}}` |
| `tools` | `null` ทำให้ขั้นตอนใช้ได้ทุกเครื่องมือที่แนบกับเอเจนต์ รายการจะจำกัดขั้นตอนให้ใช้เฉพาะเครื่องมือเหล่านั้นตามชื่อ การดำเนินการในตัว เช่น `end_call` และ `transfer_call` จะเป็นไปตามการตั้งค่าของเอเจนต์ในทุกขั้นตอน |
| `collect` | ค่าสูงสุด 20 ค่าให้ขั้นตอนเก็บรวบรวม แต่ละค่ามี `name`, `type`, `description` และ `required` (ค่าเริ่มต้นคือ `true`) |
| `on_enter_tool` | ชื่อเครื่องมือเสริม ซึ่งจะถูกเรียกโดยอัตโนมัติเมื่อเข้าสู่ขั้นตอน ดู[ขั้นตอน Webhook](#webhook-steps) |
| `transitions` | รายการตามลำดับของปลายทางถัดไปที่ขั้นตอนสามารถไปได้ ดู[การเปลี่ยนผ่าน](#transitions) |
| `global_when` | ไม่บังคับ ทำให้เข้าถึงขั้นตอนนี้ได้จากทุกขั้นตอน ดู[ขั้นตอนส่วนกลาง](#global-steps) |
| `voice` | รหัสเสียงเสริมสำหรับขั้นตอนนี้ จะถูกละเว้นเมื่อเอเจนต์ใช้การกำหนดค่าเสียงแบบกำหนดเอง |

กราฟเองคือ `{"start_step": "<step id>", "steps": [...]}` การโทรจะเริ่มต้นที่ `start_step`

## การเปลี่ยนผ่าน

การเปลี่ยนผ่านแต่ละรายการมี `to` ซึ่งเป็นรหัสขั้นตอน และมีหนึ่งรายการต่อไปนี้เพียงรายการเดียว:

- **`when`**: เป็นภาษาปกติ และ **โมเดลเป็นผู้ตัดสินใจ** เอเจนต์จะมีเครื่องมือ `go_to_step` ในตัวที่แสดงรายการขั้นตอนที่สามารถย้ายไปได้ โดยใช้ `when` แต่ละรายการเป็นเหตุผลในการเลือก
- **`condition`**: เป็นกฎสำหรับค่าที่เก็บรวบรวม และ **โค้ดเป็นผู้ตัดสินใจ** โมเดลไม่สามารถข้ามหรือพูดเพื่อหลีกเลี่ยงกฎนี้ได้
- **`"always": true`**: ดำเนินการต่อโดยไม่ต้องตรวจสอบ โค้ดจะตัดสินใจก่อน: ทันทีที่เข้าสู่ขั้นตอน หรือเมื่อเก็บค่าที่ต้องใช้ครบแล้ว การเปลี่ยนผ่าน `condition` หรือ `always` รายการแรกที่ตรงกันจะมีผลก่อนที่โมเดลจะเลือก `when` ได้ ดังนั้นให้วาง `always` หลังเงื่อนไขเพื่อใช้เป็นทางเลือกสำรอง หรือวางเพียงรายการเดียวเพื่อเชื่อมขั้นตอนไปยังขั้นตอนถัดไปโดยตรง ขั้นตอนที่มี `always` จะไม่รอการเลือก `when`

`condition` คือ `{"value": <collected name or dotted path>, "op": <op>, "target": <literal>}`:

| `op` | เป็นจริงเมื่อค่า |
| --- | --- |
| `eq`, `ne` | เท่ากับ หรือไม่เท่ากับ `target` |
| `lt`, `lte`, `gt`, `gte` | น้อยกว่า ไม่เกิน มากกว่า หรืออย่างน้อยเท่ากับ `target` |
| `in`, `not_in` | เป็น หรือไม่เป็น หนึ่งในค่าในรายการ `target` |
| `exists`, `missing` | ถูกเก็บรวบรวมแล้ว หรือยังไม่ได้เก็บรวบรวม (ไม่มี `target`) |

`eq`, `ne`, `in` และ `not_in` เปรียบเทียบข้อความโดยไม่คำนึงถึงตัวพิมพ์เล็กใหญ่หลังตัดช่องว่างออก ตัวเลขจะเปรียบเทียบเป็นตัวเลข และวันที่จะเปรียบเทียบเป็นสตริง `YYYY-MM-DD` ค่าที่ไม่เคยถูกเก็บรวบรวมจะทำให้ op ทุกตัวเป็นเท็จ ยกเว้น `missing`

เขียน `when` และ `global_when` เป็นเงื่อนไข เช่น "ผู้โทรต้องการนัดหมาย" ไม่ใช่สิ่งที่ต้องพูด เอเจนต์จะเห็นข้อความเหล่านี้เป็นบันทึกการกำหนดเส้นทางภายใน และได้รับคำสั่งไม่ให้อ่านออกเสียงหรือกล่าวถึงว่ากำลังเปลี่ยนขั้นตอน

เมื่อโมเดลเรียก `go_to_step`:

1. ค่า `required` ทุกค่าของขั้นตอนต้องมีอยู่ หากขาดค่าใด โมเดลจะได้รับแจ้งว่าขาดค่าใดและจะถามผู้โทร
2. ค่าจะถูกจัดเก็บ
3. ระบบจะตรวจสอบการเปลี่ยนผ่าน `condition` และ `always` ตามลำดับ รายการแรกที่ตรงกันจะมีผล
4. มิฉะนั้นเอเจนต์จะย้ายไปยังขั้นตอนที่โมเดลเลือก
5. หากไม่มีรายการใดตรงกันและโมเดลไม่ได้ระบุขั้นตอน เอเจนต์จะอยู่ที่เดิม

การข้ามไปยัง[ขั้นตอนส่วนกลาง](#global-steps)เป็นข้อยกเว้น: ผู้โทรเปลี่ยนหัวข้อ ดังนั้นระบบจะดำเนินการต่อแม้ขั้นตอนปัจจุบันยังมีค่าที่ต้องใช้ไม่ครบ และก่อนตรวจสอบเงื่อนไขของขั้นตอนนั้น

ระบบจะตรวจสอบการเปลี่ยนผ่าน `condition` และ `always` ทุกครั้งที่เข้าสู่ขั้นตอนด้วย ดังนั้นชุดของขั้นตอนที่โค้ดเป็นผู้ตัดสินใจจะทำงานต่อเนื่องในครั้งเดียว การเชื่อมต่อนี้จะหยุด:

- ที่ขั้นตอนที่เคยเข้าชมแล้วในการทำงานครั้งนี้
- หลังจากข้าม 8 ครั้ง
- ที่ขั้นตอนที่ยังมีค่าที่ต้องใช้ให้เก็บรวบรวม เว้นแต่ขั้นตอนนั้นมี `on_enter_tool` (ขั้นตอนเว็บฮุกจะกำหนดเส้นทางตามผลลัพธ์เสมอ)

จะมีการพูดเฉพาะบรรทัด `say` ของขั้นตอนที่การเชื่อมต่อหยุดเท่านั้น

ขั้นตอนที่ไม่มีการเปลี่ยนผ่านเป็นขั้นตอน **สุดท้าย** เอเจนต์จะดำเนินการเสร็จสิ้นที่นั่นและวางสายหรือโอนสายด้วยการทำงานในตัวตามปกติ

## การเก็บรวบรวมค่า

ระบุสิ่งที่ขั้นตอนต้องใช้ใน `collect` โมเดลจะส่งค่าเมื่อเรียก `go_to_step` และค่าจะยังคงพร้อมใช้งานตลอดช่วงที่เหลือของสาย

| `type` | เก็บข้อมูล |
| --- | --- |
| `string` | ข้อความใดก็ได้ |
| `number` | ตัวเลข |
| `boolean` | `true` หรือ `false` |
| `date` | วันที่ในรูปแบบ `YYYY-MM-DD` |

ค่าที่เก็บรวบรวมเป็นเนื้อหาของสาย ค่าจะแสดงในสายถัดจากข้อความถอดเสียง และเป็นไปตามกฎการเก็บรักษา การลบ และการปกปิดข้อมูลเดียวกัน เมื่อปิดการบันทึกสายของเอเจนต์ ค่าที่เก็บรวบรวมจะไม่ถูกจัดเก็บหรือส่งในเว็บฮุก แต่ไทม์ไลน์ของขั้นตอนจะยังคงอยู่ ดู
[ตำแหน่งที่ดูข้อมูลนี้ในสาย](#where-to-see-it-on-a-call)

## บรรทัด say

`say` จะถูกพูดตามที่เขียนไว้ทุกประการเมื่อเอเจนต์เข้าสู่ขั้นตอนนั้น จากนั้นโมเดลจะดำเนินการต่อในขั้นตอนใหม่ภายในการตอบกลับเดียวกัน โดยทราบว่าเพิ่งพูดบรรทัดนั้นไป จึงไม่พูดซ้ำ ใช้ `say` สำหรับคำทักทาย ข้อความเปิดเผยข้อมูลที่จำเป็น และถ้อยคำตายตัว แล้วปล่อยให้โมเดลจัดการส่วนที่เหลือของการตอบกลับ

`say` ของขั้นตอนเริ่มต้นคือประโยคเปิดของเอเจนต์เมื่อเอเจนต์พูดก่อน หลังจากประกาศขอความยินยอม หากผู้โทรพูดก่อน ระบบจะใช้เป็นประโยคเปิดของการตอบกลับครั้งแรกของเอเจนต์ต่อบุคคลเท่านั้น ไม่ใช้กับ IVR หรือข้อความทักทายฝากข้อความเสียง

`say` ของขั้นตอนจะไม่ถูกพูดซ้ำเมื่อสายกลับมายังขั้นตอนนั้นผ่าน `__previous__` หรือการส่งกลับ เขียนให้เป็นไปตามที่ควรฟัง: สะกดทุกอย่างที่เสียงอาจอ่านผิด บรรทัด say รองรับตัวแปรการโทร `{{var}}` แต่ไม่รองรับค่าที่เก็บรวบรวมไว้

## ขั้นตอน Webhook

ตั้งค่า `on_enter_tool` เป็นหนึ่งในเครื่องมือของเอเจนต์ แล้วเครื่องมือจะทำงานทันทีที่เข้าสู่ขั้นตอน โดยไม่ต้องรอโมเดล อาร์กิวเมนต์ของเครื่องมือคือค่าที่เก็บรวบรวมไว้ซึ่งมีชื่อตรงกับชื่อพารามิเตอร์ของเครื่องมือ ผลลัพธ์จะถูกเก็บเป็นค่าที่เก็บรวบรวมชื่อ `<tool>_result` โดยตัดทอนเหลือ 4 KB และจะตรวจสอบทรานซิชัน `condition` และ `always` ทันที ดังนั้นการค้นหาจึงสามารถกำหนดเส้นทางสายตามผลลัพธ์ของตนเองได้

ค่าที่จัดเก็บมีสองฟิลด์:

| ฟิลด์ | เก็บข้อมูล |
| --- | --- |
| `status` | รหัสสถานะ HTTP ที่เอนด์พอยต์ของคุณส่งกลับ |
| `response` | เนื้อหาการตอบกลับ |

กำหนดเส้นทางตามฟิลด์ในเนื้อหาด้วย `lookup_account_result.response.<field>` และตามรหัสสถานะด้วย `lookup_account_result.status`

การค้นหาจะทำงานได้นานสูงสุด 8 วินาที หรือใช้ระยะหมดเวลาของเครื่องมือเองหากสั้นกว่า หากใช้เวลานานกว่า 3 วินาที เอเจนต์จะพูดประโยคสั้น ๆ เพื่อไม่ให้ผู้โทรอยู่ในความเงียบ หากเอนด์พอยต์ของคุณส่งกลับสถานะ 400 ขึ้นไป หรือตอบกลับไม่ทันเวลา เอเจนต์จะแจ้งผู้โทรว่าไม่สามารถตรวจสอบเรื่องนั้นได้ในขณะนี้ และดำเนินการต่อเท่าที่ทำได้

## ขั้นตอนส่วนกลาง

ขั้นตอนที่มี `global_when` สามารถเข้าถึงได้จากทุกขั้นตอนเมื่อคำอธิบายนั้นเป็นจริง โมเดลเป็นผู้ตัดสินใจเช่นเดียวกับ `when` ใช้สำหรับหัวข้อที่ผู้โทรอาจกล่าวถึงได้ทุกเมื่อ เช่น คำถามเกี่ยวกับการเรียกเก็บเงิน คำขอคุยกับบุคคล หรือข้อร้องเรียน

ทรานซิชันที่มี `"to": "__previous__"` ในขั้นตอนส่วนกลางจะส่งสายกลับไปยังขั้นตอนที่ทำงานอยู่ก่อนหน้านั้น เพื่อให้ผู้โทรดำเนินการต่อจากจุดที่ค้างไว้ ขั้นตอนจะกลับมาทำงานต่อ: `on_enter_tool` จะไม่ทำงานซ้ำ `say` จะไม่ถูกพูดซ้ำ และเอเจนต์จะดำเนินการต่อโดยไม่เริ่มใหม่

## การส่งต่อ

`handoffs` แสดงรายการเอเจนต์อื่นในองค์กรของคุณที่สามารถรับสายต่อได้:

```json
[{"agent_id": 413, "when": "The caller asks about billing or a refund"}]
```

เอเจนต์จะได้รับเครื่องมือ `hand_off` ที่มีเอเจนต์และเหตุผลเหล่านั้นมาให้ในตัว เช่นเดียวกับการเปลี่ยนขั้นตอน `when` แต่ละรายการเป็นหมายเหตุสำหรับการกำหนดเส้นทางภายในและจะไม่ถูกอ่านออกเสียง

เมื่อส่งต่อ เอเจนต์จะพูดประโยคสั้นๆ เพื่อบอกผู้โทรว่าจะถูกส่งไปที่ใด เช่น "ได้เลย ฉันจะส่งคุณไปยังทีมเรียกเก็บเงินของเรา" ด้วยเสียงของเอเจนต์เอง จากนั้นพรอมต์ ขั้นตอน เครื่องมือ และเสียงของเอเจนต์ปลายทางจะรับช่วงต่อ และสายจะดำเนินต่อด้วยบันทึกการสนทนาเดิม เอเจนต์ใหม่จะแนะนำตัวโดยสั้นๆ และตอบคำขอล่าสุดของผู้โทร โดยจะไม่ประกาศว่ามีการโอนสาย ฐานความรู้ของเอเจนต์ปลายทางจะพร้อมใช้งานตั้งแต่นั้นเป็นต้นไป

ครั้งแรกที่สายไปถึงเอเจนต์ สายจะเริ่มที่ `start_step` ของเอเจนต์นั้น: `on_enter_tool` ของขั้นตอนจะทำงานและ `say` ของขั้นตอนจะถูกพูด เมื่อสายถูกส่งกลับไปยังเอเจนต์ที่เคยเข้าถึงแล้ว เอเจนต์นั้นจะดำเนินต่อจากขั้นตอนเดิม โดยไม่เรียกใช้ `on_enter_tool` ไม่พูด `say` ซ้ำ และไม่แนะนำตัวอีกครั้ง ค่าที่เก็บรวบรวมจะใช้ร่วมกันโดยเอเจนต์ทุกตัวในสาย

- เอเจนต์ปลายทางต้องอยู่ในองค์กรเดียวกัน และเอเจนต์ไม่สามารถส่งต่อให้ตัวเองได้
- ทุกอย่างในระดับสายจะอยู่กับเอเจนต์ที่รับสาย: ผลิตภัณฑ์ ภาษา ผู้ที่พูดก่อน วอยซ์เมล ประกาศขอความยินยอม และการตั้งค่าสายอื่นๆ ทั้งหมด
- เอเจนต์ปลายทางสามารถส่งต่อไปยังเอเจนต์อื่นและส่งกลับได้ หนึ่งสายสามารถไปถึงเอเจนต์ได้สูงสุด 8 ตัว
- สายทดสอบจะเรียกใช้ฉบับร่างของเอเจนต์ที่คุณทดสอบ เอเจนต์ที่ได้รับการส่งต่อไปจะเรียกใช้เวอร์ชันที่ปรับใช้แล้ว
- หากเอเจนต์สองตัวมีเครื่องมือชื่อเดียวกันแต่มีคำจำกัดความต่างกัน จะเก็บไว้เพียงหนึ่งเครื่องมือสำหรับสายนั้น และเครื่องมือของเอเจนต์ที่รับสายจะมีลำดับความสำคัญเสมอ ตัวสร้างจะแจ้งเตือนเมื่อคุณบันทึก
- เอเจนต์สามารถมีการส่งต่อได้โดยไม่มีขั้นตอน พรอมต์ทั้งหมดของเอเจนต์จะถูกใช้ตามเดิม

`go_to_step` และ `hand_off` เป็นชื่อสงวนสำหรับเอเจนต์ที่มีขั้นตอนหรือการส่งต่อ ดังนั้นเครื่องมือของคุณเองจึงใช้ชื่อเหล่านี้ไม่ได้

## ตำแหน่งที่ดูได้ในสาย

เปิดสายใน **ประวัติการโทร** ไทม์ไลน์ขั้นตอนข้างบันทึกการสนทนาจะแสดงแต่ละขั้นตอนที่สายเข้าสู่และเหตุผล โดยจะแสดงค่าที่เก็บรวบรวมไว้ด้วย

[ออบเจ็กต์สาย](/api-reference/calls#retrieve-a-call) และเว็บฮุกเมื่อสายเสร็จสมบูรณ์ ([`telephony.complete` / `web.complete`](/th/webhooks/call-complete)) มีข้อมูลเดียวกันในรูปแบบ `steps_trace` และ `collected_values` ทั้งสองรายการจะไม่มีในสายที่ไม่มีขั้นตอน

```json
"steps_trace": [
  {"t_ms": 0, "agent": "a412", "step": "greet", "via": "start"},
  {"t_ms": 51230, "agent": "a412", "step": "verify", "via": "model"},
  {"t_ms": 98410, "agent": "a412", "step": "minor", "via": "condition"}
],
"collected_values": {"full_name": "Dana Reyes", "dob": "2010-04-02", "age": 15}
```

`agent` คือ `a` ตามด้วยรหัสเอเจนต์ `via` ระบุสิ่งที่ทำให้สายเปลี่ยนขั้นตอน:

| `via` | ความหมาย |
| --- | --- |
| `start` | สายเริ่มต้นที่ขั้นตอนนี้ |
| `model` | โมเดลเลือกขั้นตอนผ่าน `go_to_step`, `when` หรือ `global_when` |
| `condition` | การเปลี่ยนผ่าน `condition` หรือ `always` ตรงตามเงื่อนไข |
| `on_enter` | การเปลี่ยนผ่านตรงตามผลลัพธ์ของขั้นตอนเว็บฮุก |
| `handoff` | เอเจนต์อื่นรับช่วงต่อ |

## ตำแหน่งที่ขั้นตอนทำงาน

ขั้นตอนทำงานแบบเนทีฟบน **Spark**, **Bolt** และผลิตภัณฑ์ **Storm** ทุกตัว สำหรับสายขาเข้า สายขาออก สายแคมเปญ สายทดสอบ และสายผ่านเว็บและวิดเจ็ต

ในทุกที่อื่น เอเจนต์ยังคงทำงานได้ แต่ไม่มีโครงสร้าง: เอเจนต์จะเรียกใช้พรอมต์แบบรวมที่ผสานคำสั่งที่ใช้ร่วมกันและทุกขั้นตอนตามลำดับ ซึ่งครอบคลุม:

- ผลิตภัณฑ์ Bolt รุ่นทดลอง
- เซสชัน [Realtime](/th/guides/use-with-openai-realtime) รวมถึงเซสชันสำหรับเอเจนต์ที่บันทึกไว้
- การกำหนดค่าแบบอินไลน์ที่ส่งกลับโดยเว็บฮุก [การกำหนดค่าสายแบบไดนามิก](/th/guides/dynamic-call-config) เว็บฮุกที่ส่งกลับ `agent_id` จะใช้ขั้นตอนของเอเจนต์นั้น

## ข้อจำกัด

| ข้อจำกัด | ค่า |
| --- | --- |
| ขั้นตอนต่อเอเจนต์ | 100 |
| ค่าที่เก็บได้ต่อขั้นตอน | 20 |
| ปลายทางการส่งต่อแต่ละเอเจนต์ | 8 |
| เอเจนต์ที่เข้าถึงได้ในการโทรครั้งเดียว | 8 |
| ขนาดของ JSON `steps` | 200 KB |
| `instructions` ของขั้นตอน | 20,000 อักขระ |
| บรรทัด `say` | 1,000 อักขระ |
| ผลลัพธ์ `on_enter_tool` ที่จัดเก็บ | 4 KB |

การบันทึกที่ละเมิดกฎจะส่งกลับ `400` พร้อมพาธของฟิลด์ที่ไม่ถูกต้อง เช่น
`steps.steps[2].transitions[0].to` ชื่อเครื่องมือใน `tools` และ
`on_enter_tool` ต้องเป็นเครื่องมือที่แนบกับเอเจนต์

ไม่รองรับภาษาระดับขั้นตอน ขั้นตอนแบบสคริปต์ที่ข้ามโมเดล และค่าที่เก็บภายในบรรทัด say

## เอเจนต์ที่นำเข้า

การ[นำเข้า](/th/guides/import-agents)จาก Retell, Bland, Vapi หรือ ElevenLabs
จะนำเอเจนต์แบบหลายพรอมต์ โฟลว์ เส้นทาง และเวิร์กโฟลว์มาเป็นขั้นตอน และนำ
squads กับการโอนเอเจนต์มาเป็นการส่งต่อระหว่างเอเจนต์ที่นำเข้า หน้าตรวจสอบจะแสดงทั้งสองส่วนก่อนนำเข้า

## ตัวอย่าง: การรับข้อมูลผู้ป่วยทันตกรรม

สามขั้นตอนตามลำดับ: ทักทาย เก็บรายละเอียดผู้ป่วย จอง

```json
{
  "prompt": "You are Ava, the front desk assistant at Acme Dental. Be warm and brief. Ask one question at a time.",
  "steps": {
    "start_step": "greet",
    "steps": [
      {
        "id": "greet",
        "name": "Greeting",
        "instructions": "Greet the caller and ask how you can help.",
        "say": "Thanks for calling Acme Dental, this is Ava. How can I help you today?",
        "tools": null,
        "collect": [],
        "transitions": [
          {"to": "details", "when": "The caller wants to book an appointment"}
        ]
      },
      {
        "id": "details",
        "name": "Patient details",
        "instructions": "Ask for the caller's full name, date of birth and the reason for the visit. Read the date of birth back and ask the caller to confirm it.",
        "tools": null,
        "collect": [
          {"name": "full_name", "type": "string", "description": "Caller's full name", "required": true},
          {"name": "dob", "type": "date", "description": "Date of birth", "required": true},
          {"name": "reason", "type": "string", "description": "Reason for the visit", "required": true},
          {"name": "new_patient", "type": "boolean", "description": "First visit to the practice", "required": false}
        ],
        "transitions": [
          {"to": "book", "when": "The caller has confirmed their details"}
        ]
      },
      {
        "id": "book",
        "name": "Book",
        "instructions": "Offer the two earliest open slots from book_slot and book the one the caller picks. Confirm the date and time, then say goodbye and end the call.",
        "tools": ["book_slot"],
        "collect": [],
        "transitions": []
      }
    ]
  }
}
```

จัดเตรียมด้วย `PATCH /v1/agents/{agent_id}` ทดลองด้วย **พูดคุย** แล้วปรับใช้
ขั้นตอน `details` ไม่สามารถส่งต่อไปยังการจองได้จนกว่าจะเก็บชื่อ วันเกิด
และเหตุผลครบถ้วน `new_patient` เป็นข้อมูลไม่บังคับ

## ตัวอย่าง: กิ่งที่กำหนดตามอายุ

โมเดลสามารถสนทนากับทุกคนได้ แต่โค้ดเป็นผู้ตัดสินว่าใครจะได้รับการจอง เก็บอายุ
แล้วกำหนดเส้นทางตามอายุก่อนพิจารณาตัวเลือกของโมเดลเอง

```json
{
  "id": "verify",
  "name": "Verify age",
  "instructions": "Ask for the caller's age.",
  "tools": null,
  "collect": [
    {"name": "age", "type": "number", "description": "Caller's age in years", "required": true}
  ],
  "transitions": [
    {"to": "guardian", "condition": {"value": "age", "op": "lt", "target": 18}},
    {"to": "book", "when": "The caller has given their age"}
  ]
}
```

ผู้โทรที่บอกว่าอายุสิบห้าปีจะไปยัง `guardian` ไม่ว่าโมเดลจะตัดสินใจอย่างไร
ทุกคนที่เหลือจะไปยัง `book` ทั้งสองเป็นขั้นตอนปกติที่อยู่ส่วนอื่นของกราฟ

## ตัวอย่าง: ขั้นตอน webhook สำหรับค้นหาบัญชี

ผู้โทรแจ้งหมายเลขบัญชี ระบบจะค้นหาเมื่อเข้าสู่ขั้นตอนถัดไป
และใช้ผลลัพธ์เพื่อเลือกเส้นทาง ไม่ได้แสดงขั้นตอน `not_found`, `collections` และ `support`
เพื่อความกระชับ

```json
[
  {
    "id": "identify",
    "name": "Identify",
    "instructions": "Ask for the caller's account number and read it back digit by digit.",
    "tools": null,
    "collect": [
      {"name": "account_number", "type": "string", "description": "Account number", "required": true}
    ],
    "transitions": [
      {"to": "lookup", "when": "The caller has confirmed their account number"}
    ]
  },
  {
    "id": "lookup",
    "name": "Look up account",
    "instructions": "Tell the caller what you found on their account and ask how you can help.",
    "tools": ["lookup_account"],
    "collect": [],
    "on_enter_tool": "lookup_account",
    "transitions": [
      {"to": "not_found", "condition": {"value": "lookup_account_result.response.found", "op": "eq", "target": false}},
      {"to": "collections", "condition": {"value": "lookup_account_result.response.account_status", "op": "in", "target": ["past_due", "suspended"]}},
      {"to": "support", "when": "The caller needs help with an active account"}
    ]
  }
]
```

`lookup_account` จะได้รับ `account_number` เนื่องจากพารามิเตอร์มีชื่อนี้
หากเนื้อหาการตอบกลับมี `"found": false` สายจะไปที่
`not_found` หาก `account_status` เป็น `past_due` หรือ `suspended` สายจะไปที่
`collections` บัญชีที่ยังใช้งานอยู่จะอยู่ใน `lookup` ซึ่งโมเดลจะให้ความช่วยเหลือและ
ดำเนินการต่อไปยัง `support` หากต้องการส่งการค้นหาที่ล้มเหลวไปยังขั้นตอนเฉพาะของมัน ให้เพิ่ม
เงื่อนไขเกี่ยวกับสถานะก่อน เนื่องจากการตอบกลับข้อผิดพลาดจะเก็บรหัสสถานะไว้ ดังนั้น
`{"value": "lookup_account_result.status", "op": "gte", "target": 400}`
จึงตรวจจับได้ และการหมดเวลาหรือไม่มีคำตอบจะเก็บ `"error"` ไว้ ดังนั้น
`{"value": "lookup_account_result.status", "op": "eq", "target": "error"}`
จึงตรวจจับกรณีนั้นได้

## ตัวอย่าง: จากแผนกต้อนรับไปยังฝ่ายเรียกเก็บเงิน

เอเจนต์สองตัว แผนกต้อนรับ (เอเจนต์ `412`) รับทุกสายและส่งต่อคำถามเกี่ยวกับการเรียกเก็บเงิน
ไปยังเอเจนต์ฝ่ายเรียกเก็บเงิน (เอเจนต์ `413`) ซึ่งมีพรอมต์ เครื่องมือ และเสียงของตนเอง

ที่แผนกต้อนรับ:

```json
{
  "handoffs": [
    {"agent_id": 413, "when": "The caller asks about a bill, a payment or a refund"}
  ]
}
```

ที่เอเจนต์ฝ่ายเรียกเก็บเงิน เพื่อให้สามารถส่งผู้โทรกลับได้:

```json
{
  "handoffs": [
    {"agent_id": 412, "when": "The billing question is resolved and the caller needs something else"}
  ]
}
```

แผนกต้อนรับจะพูดสั้น ๆ เช่น "ขอเชื่อมต่อคุณไปยังทีมเรียกเก็บเงิน"
จากนั้นเอเจนต์ฝ่ายเรียกเก็บเงินจะแนะนำตัวและสนทนาต่อ
จากจุดที่แผนกต้อนรับหยุดไว้ เมื่อฝ่ายเรียกเก็บเงินส่งกลับ แผนกต้อนรับจะดำเนินการต่อ
จากจุดเดิม โดยไม่ทักทายผู้โทรอีกครั้ง ผลิตภัณฑ์ของสาย
ภาษา ประกาศการยินยอม และการตั้งค่าสายอื่น ๆ จะยังคงอยู่กับแผนกต้อนรับ
เนื่องจากเป็นฝ่ายที่รับสาย

หากฝ่ายเรียกเก็บเงินต้องการเพียงคำสั่งของตนเอง ไม่ต้องการเอเจนต์ของตนเอง ให้ใช้
ขั้นตอนส่วนกลางบนแผนกต้อนรับแทน เมื่อคำถามได้รับคำตอบแล้ว สายจะกลับไปยัง
ขั้นตอนที่ผู้โทรอยู่ก่อนหน้า:

```json
{
  "id": "billing",
  "name": "Billing questions",
  "instructions": "Answer questions about bills and payments. Offer to send a payment link by email.",
  "tools": null,
  "collect": [],
  "transitions": [
    {"to": "__previous__", "when": "The billing question is answered"}
  ],
  "global_when": "The caller asks about a bill or payment at any point"
}
```

## ขั้นตอนถัดไป

<CardGroup cols={2}>
  <Card title="คู่มือการเขียนพรอมป์ต์" icon="pen" href="/th/guides/prompting">
    เขียนคำสั่งที่ใช้ร่วมกันและคำสั่งสำหรับขั้นตอนที่ใช้งานได้ดีบนโทรศัพท์
  </Card>
  <Card title="สร้างการผสานรวมเครื่องมือ" icon="code" href="/th/guides/build-tool-integration">
    สร้างเครื่องมือที่ขั้นตอนและขั้นตอน webhook ของคุณเรียกใช้
  </Card>
  <Card title="ทดสอบเอเจนต์ของคุณ" icon="flask" href="/th/guides/test-agents">
    รันสถานการณ์เดิมกับทุกสาขาก่อนปรับใช้
  </Card>
  <Card title="Agents API" icon="robot" href="/api-reference/agents">
    ทุกฟิลด์ของเอเจนต์ แบบร่าง และการปรับใช้
  </Card>
</CardGroup>
