ข้ามไปยังเนื้อหา

เครื่องมือเอเจนต์ในตัว

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

เครื่องมือทุกตัวที่นี่จะถูกลงทะเบียนก็ต่อเมื่อทรัพยากรที่รองรับมันถูกต่อไว้สำหรับรันนั้น — ผู้ดูแลเซิร์ฟเวอร์ภาษาหรือดีบัก พาร์เซอร์ tree-sitter โรงงานเคอร์เนล พอร์ตคำถาม รายชื่อวอร์กเกอร์ vibe การตรวจหาว่ามีเซิร์ฟเวอร์ภาษาหรืออะแดปเตอร์ดีบักสำหรับเช็กเอาต์นี้หรือไม่เป็นการตรวจแยกต่างหากที่เกิดทีหลัง (status / adapters): เครื่องมือเหล่านี้ยังคงอยู่บนพื้นผิวเพื่อให้เอเจนต์ถามได้ว่าทำไมถึงใช้งานอะไรไม่ได้ แทนที่จะเดา

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

ระดับ ความหมาย เครื่องมือที่นี่
read ไม่มีผลข้างเคียง lsp, ast_grep, ask_user และ vibe_* ทั้งห้าตัว
write เปลี่ยนแปลงไฟล์ lsp_rename, ast_edit, resolve
exec เริ่มหรือควบคุมโปรเซส debug, eval

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

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

การกระทำ อาร์กิวเมนต์ สิ่งที่คืนกลับ
diagnostics file (หรือ "*") ข้อผิดพลาดและคำเตือน; "*" รันตัวตรวจสอบของโปรเจกต์แทน
definition file, line, symbol ตำแหน่งที่สัญลักษณ์ถูกประกาศ
type_definition file, line, symbol ตำแหน่งที่ไทป์ของมันถูกประกาศ
implementation file, line, symbol การนำไปใช้จริง
references file, line, symbol ทุกจุดที่ถูกใช้ จำกัดที่ 50
hover file, line, symbol ลายเซ็นและคอมเมนต์เอกสาร
symbols query หรือ file ค้นหาในเวิร์กสเปซด้วย query (จำกัดที่ 100); สัญลักษณ์ในเอกสารด้วย file (จำกัดที่ 200)
code_actions file, line, symbol การแก้แบบด่วนที่เซิร์ฟเวอร์เสนอ
status — เซิร์ฟเวอร์ตัวใดกำลังทำงาน และสำหรับรากใด
reload — รีสตาร์ตเซิร์ฟเวอร์สำหรับรากนี้

ผลตอบแทนไม่ใช่ตัวเครื่องมือ write, edit และ apply_patch ถูกห่อไว้: หลังการเขียน ไฟล์จะถูกซิงก์และบันทึกผ่านเซิร์ฟเวอร์ภาษา และมีเพียงผลวินิจฉัยที่เพิ่งเกิดขึ้นใหม่เท่านั้นที่ถูกพับรวมเข้าไปในผลลัพธ์ของเครื่องมือ เอเจนต์จึงรู้ว่าตัวเองทำให้บิลด์พังในจังหวะที่มันพัง โดยไม่ต้องนึกขึ้นมาได้ว่าต้องถาม

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

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

อาร์กิวเมนต์ ความหมาย
file ตำแหน่งที่สัญลักษณ์อยู่
line บรรทัดที่นับจาก 1
symbol สตริงย่อยที่ระบุตัวมันบนบรรทัดนั้น
new_name ชื่อที่จะเรียกมัน

หาโค้ดด้วยรูปร่างแทนที่จะด้วยข้อความ $NAME จับคู่กับโหนดใดก็ได้ $$$NAME จับคู่กับโหนดพี่น้องที่ต่อเนื่องกัน และ $_ เป็นไวลด์การ์ดที่ไม่ผูกอะไรเลย

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

ast_grep(pattern: "if ($X != null) $X.dispose()", language: "dart")

จะจับคู่กับโค้ดป้องกันที่ dispose สิ่งที่มันเพิ่งตรวจสอบ และจะไม่จับคู่กับอันที่ dispose อย่างอื่น

อาร์กิวเมนต์ ความหมาย
pattern เศษโค้ดที่มีเมทาวาริเอเบิล
path ไฟล์หรือไดเรกทอรีที่จะค้นหา; ค่าเริ่มต้นคือทั้งเวิร์กสเปซ
language id ของไวยากรณ์ที่จัดส่งมา (dart, python, rust, c, …) จะถูกอนุมานเมื่อ path ชี้ไปที่ไฟล์
max_results ค่าเริ่มต้น 50

การจับคู่แบบเดียวกัน บวกกับเทมเพลต rewrite ที่เมทาวาริเอเบิลถูกแทนกลับเข้าไป ไม่มีอะไรถูกเขียนลงไป การเปลี่ยนแปลงถูกพักไว้ และผลลัพธ์ระบุ edit_id

ast_edit(pattern: "dispose($X)", rewrite: "$X.dispose()", language: "dart")
→ (proposed) 12 replacements in 4 files … call resolve with edit_id "edit_1"
อาร์กิวเมนต์ ความหมาย
pattern สิ่งที่จะจับคู่
rewrite สิ่งที่จะใส่แทน โดยมี $NAME ถูกแทนที่
path ขอบเขต; ค่าเริ่มต้นคือทั้งเวิร์กสเปซ
language id ของภาษา

resolve — คอมมิตหรือทิ้งการเปลี่ยนแปลงที่พักไว้

หัวข้อที่มีชื่อว่า “resolve — คอมมิตหรือทิ้งการเปลี่ยนแปลงที่พักไว้”
อาร์กิวเมนต์ ความหมาย
edit_id จากผลลัพธ์ (proposed)
action accept หรือ discard

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


ลงทะเบียนเมื่อการรันมีผู้ดูแลเซสชันดีบัก ไดเรกทอรีทำงาน และ id ของบทสนทนา ว่าอะแดปเตอร์จะเริ่มทำงานได้จริงหรือไม่นั้นถูกตรวจตอนเรียกใช้ ด้วยกฎส่วนตัดกันเดียวกับเซิร์ฟเวอร์ภาษา (มาร์กเกอร์ที่ราก และไบนารี) ระดับ exec; ไม่ปลอดภัยเมื่อรันขนานกัน (เซสชันคือสถานะที่อยู่ข้ามการเรียก ดังนั้น continue ที่แซง stack จะอ่านเฟรมที่ขยับไปแล้ว)

หนึ่งเซสชันต่อหนึ่งบทสนทนา พร้อม TTL แบบตายตัว (30 นาที) launch ครั้งที่สองจะถูกปฏิเสธแทนที่จะไปแทนที่ครั้งแรกอย่างเงียบ ๆ

อ็อป อาร์กิวเมนต์ หมายเหตุ
adapters — อะแดปเตอร์ตัวใดใช้ได้ที่นี่ และทำไมถึงไม่
status — มีเซสชันกำลังทำงานอยู่หรือไม่ และมันหยุดอยู่ที่ไหน
breakpoints file, lines[] lines ว่างเปล่าจะล้างไฟล์ ตั้งไว้ก่อนเปิดโปรแกรมได้
launch program, args[] เปิดโปรแกรมโดยหยุดที่เบรกพอยต์ของคุณ
attach port เชื่อมต่อกับโปรแกรมที่กำลังรันอยู่แล้ว
continue thread_id? รันไปจนถึงจุดหยุดถัดไป
pause thread_id? หยุดมันตรงที่มันอยู่
step_over / step_in / step_out thread_id? เดินไปหนึ่งขั้น
threads — id และชื่อของเธรด
stack thread_id? เฟรม จำกัดที่ 30
scopes frame_id สโคปบนเฟรมหนึ่ง แต่ละอันมี reference
variables reference สมาชิกลูก จำกัดที่ 50 ค่าจำกัดที่ 400 ตัวอักษร
evaluate expression, frame_id? ประเมินในสโคปของเฟรมนั้นเอง
output — stdout/stderr ของโปรแกรมที่ถูกดีบัก แบบมีขอบเขต
terminate — จบเซสชันและฆ่าอะแดปเตอร์
Id คำสั่ง มาร์กเกอร์ที่ราก
dart dart debug_adapter pubspec.yaml
debugpy python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt
lldb lldb-dap Cargo.toml, CMakeLists.txt
delve dlv dap go.mod

debugpy เลือก .venv/bin หรือ venv/bin ภายในโปรเจกต์เหนือ $PATH: ไพทอนที่มาจาก $PATH เป็นคนละสภาพแวดล้อม และการแอตแทชจากตัวที่ผิดจะล้มเหลวในลักษณะที่อ่านดูเหมือนโค้ดพัง


เซลล์ Python หรือ JavaScript ที่ตัวแปร อิมพอร์ต และข้อมูลที่โหลดไว้อยู่ต่อระหว่างการเรียก ระดับ exec; ไม่ปลอดภัยเมื่อรันขนานกัน

อาร์กิวเมนต์ ความหมาย
code เซลล์
description จำเป็น 5–10 คำสำหรับป้ายในทรานสคริปต์
language python (ค่าเริ่มต้น) หรือ javascript
reset ทิ้งอินเทอร์พรีเตอร์และเริ่มใหม่ที่สะอาดก่อน
  • นิพจน์สุดท้ายจะถูกสะท้อนกลับแบบที่โน้ตบุ๊กทำ
  • display() ทำงาน ดาต้าเฟรมของ pandas เรนเดอร์เป็นตาราง และภาพของ matplotlib กลับมาเป็นรูปภาพที่ทรานสคริปต์แสดง
  • สำนวนของ IPython ที่คนพิมพ์กันโดยไม่ทันคิด — %pip, %cd, !cmd, %%bash, %%timeit — ถูกเขียนใหม่เป็นโค้ดธรรมดา %pip ยังไล่ sys.modules ออกด้วย import ครั้งถัดไปจึงเห็นสิ่งที่เพิ่งติดตั้งไป
contents = tool("read", {"path": "lib/auth.dart"})

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

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


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

อาร์กิวเมนต์ ความหมาย
question สิ่งที่จะถาม
context ไม่บังคับ: ทำไมคุณถึงถาม และอะไรขึ้นอยู่กับคำตอบ
options[] ตัวเลือก แต่ละอันเป็น {label, description?}
allow_free_text รับคำตอบที่พิมพ์เองหรือไม่
multi_select เลือกได้หลายตัวเลือกหรือไม่

การข้ามถือว่าสำเร็จ: ไปต่อบนสมมติฐานที่ระบุไว้และอย่าถามอีก การหมดเวลาเป็นข้อผิดพลาดที่มีคำสั่งเดียวกัน พรอมป์ต์ขออนุญาต (“ฉันทำสิ่งนี้ได้ไหม?”) เป็นด่านแยกต่างหากและเรนเดอร์เป็นการ์ดแบบเดียวกันในบทสนทนา

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


ลงทะเบียนเฉพาะขณะโหมด vibeเปิดอยู่ ทั้งห้าตัวเป็นระดับ read: ด่านควรอยู่ที่เครื่องมือของวอร์กเกอร์ ซึ่งการแก้ไขเกิดขึ้นจริงตรงนั้น

เครื่องมือ อาร์กิวเมนต์ หมายเหตุ
vibe_spawn brief, label, tier? คืนค่าทันที; วอร์กเกอร์ทำงานต่อไป
vibe_send worker_id, message ต่อท้ายบรีฟแล้วรันใหม่
vibe_wait worker_id, timeout_seconds? บล็อกรอจนกว่ามันจะจบ
vibe_kill worker_id หยุดมัน
vibe_list — ระดับ สถานะ เวลาที่ผ่านไป และผลลัพธ์ล่าสุด

tier คือ fast (งานเชิงกลไกที่มีสเปกชัดเจน) หรือ good (การออกแบบ การใช้ดุลพินิจ หรือการรีวิวผลลัพธ์ของวอร์กเกอร์ระดับ fast)