- หน้าแรก
- Documentation
- เครื่องมือรันไทม์
- เครื่องมือที่กำหนดเอง
เครื่องมือที่กำหนดเอง
เครื่องมือที่กำหนดเองคือฟังก์ชันที่โมเดลสามารถเรียกใช้ได้ ซึ่งเชื่อมต่อเข้ากับกระบวนการประมวลผลเครื่องมือเดียวกันกับเครื่องมือที่มีอยู่ในตัว
เครื่องมือที่กำหนดเองคือโมดูล TypeScript/JavaScript ที่ส่งออก factory โดย factory จะรับ host API (CustomToolAPI) และส่งคืนเครื่องมือหนึ่งชิ้นหรืออาร์เรย์ของเครื่องมือ
สิ่งที่เป็น (และไม่ใช่)
หัวข้อที่มีชื่อว่า “สิ่งที่เป็น (และไม่ใช่)”- เครื่องมือที่กำหนดเอง: สามารถเรียกใช้โดยโมเดลระหว่างรอบการทำงาน (
execute+ TypeBox schema) - ส่วนขยาย: กรอบงานวงจรชีวิต/เหตุการณ์ที่สามารถลงทะเบียนเครื่องมือและสกัดกั้น/ปรับเปลี่ยนเหตุการณ์ได้
- Hook: สคริปต์คำสั่งภายนอกก่อน/หลังการทำงาน
- Skill: แพ็กเกจแนวทาง/บริบทแบบคงที่ ไม่ใช่โค้ดเครื่องมือที่ประมวลผลได้
หากต้องการให้โมเดลเรียกใช้โค้ดโดยตรง ให้ใช้เครื่องมือที่กำหนดเอง
รูปแบบการผสานรวมในโค้ดปัจจุบัน
หัวข้อที่มีชื่อว่า “รูปแบบการผสานรวมในโค้ดปัจจุบัน”มีรูปแบบการผสานรวมที่ใช้งานอยู่สองแบบ:
-
เครื่องมือที่กำหนดเองที่ SDK จัดเตรียมให้ (
options.customTools)- ถูกรวมเข้าเป็นเครื่องมือ agent ผ่าน
CustomToolAdapterหรือ extension wrappers - รวมอยู่ใน active tool set เริ่มต้นเสมอใน SDK bootstrap
- ถูกรวมเข้าเป็นเครื่องมือ agent ผ่าน
-
โมดูลที่ค้นพบจากระบบไฟล์ผ่าน loader API (
discoverAndLoadCustomTools/loadCustomTools)- เปิดเผยเป็น library API ใน
src/extensibility/custom-tools/loader.ts - โค้ดฝั่ง host สามารถเรียกใช้เพื่อค้นพบและโหลดโมดูลเครื่องมือจากพาธของ config/provider/plugin
- เปิดเผยเป็น library API ใน
Model tool call flow
LLM tool call │ ▼Tool registry (built-ins + custom tool adapters) │ ▼CustomTool.execute(toolCallId, params, onUpdate, ctx, signal) │ ├─ onUpdate(...) -> streamed partial result └─ return result -> final tool content/detailsตำแหน่งการค้นพบ (loader API)
หัวข้อที่มีชื่อว่า “ตำแหน่งการค้นพบ (loader API)”discoverAndLoadCustomTools(configuredPaths, cwd, builtInToolNames) รวมจาก:
- Capability providers (
toolCapability) ได้แก่:- Native OMP config (
~/.xcsh/agent/tools,.xcsh/tools) - Claude config (
~/.claude/tools,.claude/tools) - Codex config (
~/.codex/tools,.codex/tools) - Claude marketplace plugin cache provider
- Native OMP config (
- Installed plugin manifests (
~/.xcsh/plugins/node_modules/*ผ่าน plugin loader) - พาธที่กำหนดค่าไว้อย่างชัดเจนที่ส่งให้ loader
พฤติกรรมที่สำคัญ
หัวข้อที่มีชื่อว่า “พฤติกรรมที่สำคัญ”- พาธที่แก้ไขซ้ำกันจะถูกกำจัดออก
- ชื่อเครื่องมือที่ขัดแย้งกันจะถูกปฏิเสธเมื่อซ้ำกับ built-ins และเครื่องมือที่กำหนดเองที่โหลดไว้แล้ว
- ไฟล์
.mdและ.jsonจะถูกค้นพบเป็น tool metadata โดย provider บางส่วน แต่ executable module loader จะปฏิเสธไม่ให้รันเป็นเครื่องมือ - พาธที่กำหนดค่าแบบ relative จะถูกแก้ไขจาก
cwd;~จะถูกขยาย
สัญญาของโมดูล
หัวข้อที่มีชื่อว่า “สัญญาของโมดูล”โมดูลเครื่องมือที่กำหนดเองต้องส่งออกฟังก์ชัน (แนะนำให้ใช้ default export):
import type { CustomToolFactory } from "@f5-sales-demo/xcsh";
const factory: CustomToolFactory = (pi) => ({ name: "repo_stats", label: "Repo Stats", description: "Counts tracked TypeScript files", parameters: pi.typebox.Type.Object({ glob: pi.typebox.Type.Optional(pi.typebox.Type.String({ default: "**/*.ts" })), }),
async execute(toolCallId, params, onUpdate, ctx, signal) { onUpdate?.({ content: [{ type: "text", text: "Scanning files..." }], details: { phase: "scan" }, });
const result = await pi.exec("git", ["ls-files", params.glob ?? "**/*.ts"], { signal, cwd: pi.cwd }); if (result.killed) { throw new Error("Scan was cancelled"); } if (result.code !== 0) { throw new Error(result.stderr || "git ls-files failed"); }
const files = result.stdout.split("\n").filter(Boolean); return { content: [{ type: "text", text: `Found ${files.length} files` }], details: { count: files.length, sample: files.slice(0, 10) }, }; },
onSession(event) { if (event.reason === "shutdown") { // cleanup resources if needed } },});
export default factory;ประเภทที่ส่งคืนจาก factory:
CustomToolCustomTool[]Promise<CustomTool | CustomTool[]>
พื้นผิว API ที่ส่งให้ factories (CustomToolAPI)
หัวข้อที่มีชื่อว่า “พื้นผิว API ที่ส่งให้ factories (CustomToolAPI)”จาก types.ts และ loader.ts:
cwd: working directory ของ hostexec(command, args, options?): ตัวช่วยประมวลผล processui: UI context (อาจเป็น no-op ในโหมด headless)hasUI:falseในกระบวนการที่ไม่โต้ตอบlogger: shared file loggertypebox:@sinclair/typeboxที่ inject มาให้pi: exports ของ@f5-sales-demo/xcshที่ inject มาให้pushPendingAction(action): ลงทะเบียน preview action สำหรับresolvetool ที่ซ่อนอยู่ (docs/resolve-tool-runtime.md)
Loader เริ่มต้นด้วย no-op UI context และต้องการให้โค้ดฝั่ง host เรียก setUIContext(...) เมื่อ UI จริงพร้อมใช้งาน
สัญญาการประมวลผลและการกำหนดประเภท
หัวข้อที่มีชื่อว่า “สัญญาการประมวลผลและการกำหนดประเภท”ลายเซ็นของ CustomTool.execute:
execute(toolCallId, params, onUpdate, ctx, signal)paramsมีการกำหนดประเภทแบบ static จาก TypeBox schema ผ่านStatic<TParams>- การตรวจสอบความถูกต้องของอาร์กิวเมนต์ที่รันไทม์เกิดขึ้นก่อนการประมวลผลใน agent loop
onUpdateส่งผลลัพธ์บางส่วนสำหรับ UI streamingctxรวม session/model state และตัวช่วยabort()signalรับการยกเลิก
CustomToolAdapter เชื่อมต่อสิ่งนี้กับอินเทอร์เฟซเครื่องมือ agent และส่งต่อการเรียกในลำดับอาร์กิวเมนต์ที่ถูกต้อง
วิธีการเปิดเผยเครื่องมือให้โมเดล
หัวข้อที่มีชื่อว่า “วิธีการเปิดเผยเครื่องมือให้โมเดล”- เครื่องมือถูกรวมเข้าเป็น
AgentToolinstances (CustomToolAdapterหรือ extension wrappers) - ถูกแทรกเข้าใน session tool registry ตามชื่อ
- ใน SDK bootstrap เครื่องมือที่กำหนดเองและที่ลงทะเบียนผ่าน extension จะถูกบังคับรวมใน active set เริ่มต้น
- CLI
--toolsในปัจจุบันตรวจสอบเฉพาะชื่อเครื่องมือ built-in; การรวมเครื่องมือที่กำหนดเองจัดการผ่านพาธการค้นพบ/การลงทะเบียนและ SDK options
Rendering hooks
หัวข้อที่มีชื่อว่า “Rendering hooks”Rendering hooks ที่ไม่บังคับ:
renderCall(args, theme)renderResult(result, options, theme, args?)
พฤติกรรมรันไทม์ใน TUI:
- หาก hook มีอยู่ output ของเครื่องมือจะถูก render ภายในคอนเทนเนอร์
Box renderResultรับ{ expanded, isPartial, spinnerFrame? }- ข้อผิดพลาดของ renderer จะถูกจับและบันทึก; UI จะ fallback ไปยังการ render ข้อความเริ่มต้น
การจัดการ session/state
หัวข้อที่มีชื่อว่า “การจัดการ session/state”onSession(event, ctx) ที่ไม่บังคับจะรับเหตุการณ์วงจรชีวิต session รวมถึง:
start,switch,branch,tree,shutdownauto_compaction_start,auto_compaction_endauto_retry_start,auto_retry_endttsr_triggered,todo_reminder
ใช้ ctx.sessionManager เพื่อสร้าง state จาก history ใหม่เมื่อบริบท branch/session เปลี่ยนแปลง
ความล้มเหลวและความหมายของการยกเลิก
หัวข้อที่มีชื่อว่า “ความล้มเหลวและความหมายของการยกเลิก”ความล้มเหลวแบบ synchronous/async
หัวข้อที่มีชื่อว่า “ความล้มเหลวแบบ synchronous/async”- การ throw (หรือ rejected promises) ใน
executeถือเป็นความล้มเหลวของเครื่องมือ - Agent runtime แปลงความล้มเหลวเป็น tool result messages ที่มี
isError: trueและเนื้อหาข้อผิดพลาด - ด้วย extension wrappers,
tool_resulthandlers สามารถเขียน content/details ใหม่ และแม้แต่แทนที่สถานะข้อผิดพลาดได้
การยกเลิก
หัวข้อที่มีชื่อว่า “การยกเลิก”- Agent abort จะแพร่กระจายผ่าน
AbortSignalไปยังexecute - ส่ง
signalต่อไปยังการทำงานของ subprocess (pi.exec(..., { signal })) เพื่อการยกเลิกแบบร่วมมือ ctx.abort()ให้เครื่องมือร้องขอการ abort ของ agent operation ปัจจุบันได้
ข้อผิดพลาดของ onSession
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดของ onSession”- ข้อผิดพลาดของ
onSessionจะถูกจับและบันทึกเป็นคำเตือน โดยจะไม่ทำให้ session หยุดทำงาน
ข้อจำกัดจริงที่ต้องออกแบบรองรับ
หัวข้อที่มีชื่อว่า “ข้อจำกัดจริงที่ต้องออกแบบรองรับ”- ชื่อเครื่องมือต้องไม่ซ้ำกันทั่วทั้ง active registry
- แนะนำให้ใช้ output ที่กำหนดรูปแบบตาม schema แบบ deterministic ใน
detailsสำหรับการ render/สร้าง state ใหม่ - ป้องกันการใช้งาน UI ด้วย
pi.hasUI - ถือว่าไฟล์
.md/.jsonในไดเรกทอรีเครื่องมือเป็น metadata ไม่ใช่โมดูลที่ประมวลผลได้