TL;DR
การรั่วไหลของซอร์สโค้ด Claude ได้เปิดเผยฐานโค้ด TypeScript ขนาด 512,000 บรรทัดเมื่อวันที่ 31 มีนาคม 2026 สถาปัตยกรรมหลักทำงานในรูปแบบลูปแบบ while ที่เรียก API ของ Claude, ส่งคำสั่งเรียกเครื่องมือ (tool calls) และป้อนผลลัพธ์กลับ คุณสามารถสร้างเวอร์ชันของคุณเองได้ด้วย Python, Anthropic SDK และโค้ดประมาณ 200 บรรทัดสำหรับลูปหลัก คู่มือนี้จะอธิบายแต่ละองค์ประกอบและแสดงวิธีสร้างขึ้นมาใหม่
บทนำ
เมื่อวันที่ 31 มีนาคม 2026, Anthropic ได้เผยแพร่ไฟล์ source map ขนาด 59.8 MB ในเวอร์ชัน 2.1.88 ของแพ็คเกจ npm @anthropic-ai/claude-code ของพวกเขา Source map เป็นส่วนประกอบสำหรับการดีบักที่แปลง JavaScript ที่ถูกย่อขนาดกลับไปเป็นซอร์สโค้ดดั้งเดิม เนื่องจากเครื่องมือ build ของ Anthropic (bundler ของ Bun) สร้างไฟล์เหล่านี้โดยค่าเริ่มต้น ทำให้ฐานโค้ด TypeScript ทั้งหมดสามารถกู้คืนได้
ภายในไม่กี่ชั่วโมง นักพัฒนาได้คัดลอกโค้ดไปทั่วที่เก็บ GitHub หลายสิบแห่ง ชุมชนได้ทำการวิเคราะห์แต่ละโมดูลอย่างรวดเร็ว ตั้งแต่ลูปเอเจนต์หลักไปจนถึงคุณสมบัติที่ซ่อนอยู่ เช่น "โหมดลับ" (undercover mode) และการฉีดเครื่องมือปลอม
ปฏิกิริยาแบ่งออกเป็นสองฝ่าย บางคนวิจารณ์แนวปฏิบัติด้านความปลอดภัยของ Anthropic คนอื่นๆ หลงใหลในสถาปัตยกรรม แต่การตอบสนองที่สร้างสรรค์ที่สุดมาจากนักพัฒนาที่ถามว่า: "ฉันสามารถสร้างสิ่งนี้ด้วยตัวเองได้หรือไม่"
คำตอบคือได้ รูปแบบหลักนั้นตรงไปตรงมา คู่มือนี้จะพาคุณไปทำความเข้าใจแต่ละชั้นของสถาปัตยกรรม อธิบายว่าทำไม Anthropic ถึงเลือกสิ่งเหล่านั้น และให้โค้ดที่ใช้งานได้จริงที่คุณสามารถใช้เป็นจุดเริ่มต้นได้ นอกจากนี้ คุณจะได้เรียนรู้วิธีทดสอบการโต้ตอบ API ของเอเจนต์ที่กำหนดเองด้วย Apidog ซึ่งทำให้การดีบักการสนทนา API แบบหลายรอบง่ายกว่าการใช้คำสั่ง curl ดิบๆ มาก
สิ่งที่การรั่วไหลเปิดเผยเกี่ยวกับสถาปัตยกรรมของ Claude Code
ภาพรวมของฐานโค้ด
Claude Code ซึ่งมีชื่อรหัสภายในว่า “Tengu” มีไฟล์ประมาณ 1,900 ไฟล์ การจัดระเบียบโมดูลแบ่งออกเป็นชั้นที่ชัดเจน:
cli/ - ส่วนติดต่อผู้ใช้แบบเทอร์มินัล (React + Ink)
tools/ - การใช้งานเครื่องมือกว่า 40 รายการ
core/ - คำสั่งระบบ (system prompts), สิทธิ์, ค่าคงที่
assistant/ - การจัดระเบียบเอเจนต์
services/ - การเรียก API, การบีบอัด, OAuth, ระบบวัดและส่งข้อมูลทางไกล
CLI นั้นเป็นแอป React ที่แสดงผลผ่าน Ink ซึ่งเป็น React renderer สำหรับเอาต์พุตเทอร์มินัล มันใช้ Yoga (เอนจิ้น CSS flexbox) สำหรับการจัดวางและ ANSI escape codes สำหรับการจัดรูปแบบ ทุกมุมมองการสนทนา พื้นที่ป้อนข้อมูล การแสดงผลการเรียกใช้เครื่องมือ และกล่องโต้ตอบสิทธิ์เป็นคอมโพเนนต์ React ทั้งหมด
สิ่งนี้ถือว่าเกินความจำเป็นสำหรับโปรเจกต์ DIY ส่วนใหญ่ คุณไม่จำเป็นต้องมี UI เทอร์มินัลที่ใช้ React เพื่อสร้างเอเจนต์เขียนโค้ดที่ใช้งานได้ REPL แบบง่ายๆ ก็เพียงพอแล้ว
ลูปเอเจนต์หลัก
หากตัดส่วน UI, ระบบวัดและส่งข้อมูลทางไกล และ feature flags ออกไป แกนหลักของ Claude Code คือลูปแบบ while Anthropic เรียกมันภายในว่า "nO" นี่คือสิ่งที่มันทำ:
- ส่งข้อความไปยัง Claude API (system prompt + การกำหนดเครื่องมือ)
- รับการตอบกลับที่มีข้อความและ/หรือบล็อก
tool_use - ดำเนินการเครื่องมือที่ร้องขอแต่ละรายการผ่านการแมปชื่อ-ไปยัง-ตัวจัดการ (name-to-handler dispatch map)
- ผนวกผลลัพธ์ของเครื่องมือกลับไปยังรายการข้อความ
- หากการตอบกลับมีการเรียกใช้เครื่องมือเพิ่มเติม ให้วนกลับไปขั้นตอนที่ 1
- หากการตอบกลับเป็นข้อความธรรมดาที่ไม่มีการเรียกใช้เครื่องมือ ให้ส่งคืนให้ผู้ใช้
"Turn" คือการเดินทางไปกลับที่สมบูรณ์หนึ่งครั้ง Turn จะดำเนินต่อไปจนกว่า Claude จะสร้างข้อความที่ไม่มีการเรียกใช้เครื่องมือ นั่นคือรูปแบบเอเจนต์ทั้งหมด
นี่คือเวอร์ชัน Python ที่น้อยที่สุดที่จับแกนหลักได้:
import anthropic
client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
def agent_loop(system_prompt: str, tools: list, messages: list) -> str:
"""The core agent loop - keep calling until no more tool use."""
while True:
response = client.messages.create(
model=MODEL,
max_tokens=16384,
system=system_prompt,
tools=tools,
messages=messages,
)
# Add assistant response to conversation
messages.append({"role": "assistant", "content": response.content})
# If the model stopped without requesting tools, we're done
if response.stop_reason != "tool_use":
# Extract the final text
return "".join(
block.text for block in response.content
if hasattr(block, "text")
)
# Execute each tool call and collect results
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
# Feed results back as a user message
messages.append({"role": "user", "content": tool_results})
นั่นคือประมาณ 30 บรรทัด ความซับซ้อนที่เหลือของ Claude Code มาจากเครื่องมือเหล่านั้นเอง ระบบสิทธิ์ การจัดการบริบท และหน่วยความจำ
การสร้างระบบเครื่องมือ
เหตุใดเครื่องมือเฉพาะจึงดีกว่าคำสั่ง bash เพียงคำสั่งเดียว
หนึ่งในการตัดสินใจทางสถาปัตยกรรมที่ชัดเจนที่สุดในการรั่วไหลคือ: Claude Code ใช้เครื่องมือเฉพาะสำหรับการดำเนินการกับไฟล์ แทนที่จะส่งทุกอย่างผ่าน bash
มีเครื่องมือ Read (ไม่ใช่ cat), เครื่องมือ Edit (ไม่ใช่ sed), เครื่องมือ Grep (ไม่ใช่ grep) และเครื่องมือ Glob (ไม่ใช่ find) ระบบพรอมต์บอกโมเดลอย่างชัดเจนให้ใช้สิ่งเหล่านี้แทนคำสั่ง bash ที่เทียบเท่ากัน
ทำไม? มีสามเหตุผล:
- เอาต์พุตที่มีโครงสร้าง เครื่องมือ
Grepเฉพาะจะคืนค่าผลลัพธ์ในรูปแบบที่สอดคล้องกันซึ่งโมเดลสามารถแยกวิเคราะห์ได้ การส่งคำสั่ง bash จะสร้างเอาต์พุตที่ไม่แน่นอนซึ่งโมเดลตีความได้ยาก - ความปลอดภัย
BashToolใน Claude Code บล็อกเครื่องหมายแบ็กทิกและไวยากรณ์ซับเชลล์$()เพื่อป้องกันการโจมตี (injection) เครื่องมือเฉพาะจะหลีกเลี่ยงความเสี่ยงนี้โดยสิ้นเชิง - ประสิทธิภาพของโทเค็น ผลลัพธ์ของเครื่องมือสามารถถูกตัดทอน สุ่มตัวอย่าง หรือย้ายไปยังดิสก์ได้ เอาต์พุต
catขนาดใหญ่จะสิ้นเปลืองโทเค็นในหน้าต่างบริบท
ชุดเครื่องมือที่จำเป็น
จากการรั่วไหล Claude Code เปิดเผยเครื่องมือไม่ถึง 20 รายการโดยค่าเริ่มต้น และมีมากกว่า 60 รายการอยู่เบื้องหลัง feature flags สำหรับเอเจนต์ DIY คุณต้องการห้าอย่าง:
TOOLS = [
{
"name": "read_file",
"description": "อ่านไฟล์จากระบบไฟล์ ส่งคืนเนื้อหาพร้อมหมายเลขบรรทัด",
"input_schema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "พาธสัมบูรณ์ของไฟล์"
},
"offset": {
"type": "integer",
"description": "หมายเลขบรรทัดที่จะเริ่มอ่านจาก (0-indexed)"
},
"limit": {
"type": "integer",
"description": "จำนวนบรรทัดสูงสุดที่จะอ่าน ค่าเริ่มต้นคือ 2000"
}
},
"required": ["file_path"]
}
},
{
"name": "write_file",
"description": "เขียนเนื้อหาลงในไฟล์ สร้างไฟล์หากไม่มีอยู่",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "พาธสัมบูรณ์"},
"content": {"type": "string", "description": "เนื้อหาไฟล์ที่จะเขียน"}
},
"required": ["file_path", "content"]
}
},
{
"name": "edit_file",
"description": "แทนที่สตริงเฉพาะในไฟล์ old_string ต้องไม่ซ้ำกัน",
"input_schema": {
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "พาธสัมบูรณ์"},
"old_string": {"type": "string", "description": "ข้อความที่จะค้นหา"},
"new_string": {"type": "string", "description": "ข้อความสำหรับแทนที่"}
},
"required": ["file_path", "old_string", "new_string"]
}
},
{
"name": "run_command",
"description": "รันคำสั่งเชลล์และส่งคืน stdout/stderr",
"input_schema": {
"type": "object",
"properties": {
"command": {"type": "string", "description": "คำสั่งเชลล์ที่จะรัน"},
"timeout": {"type": "integer", "description": "หมดเวลาในหน่วยวินาที ค่าเริ่มต้น 120"}
},
"required": ["command"]
}
},
{
"name": "search_code",
"description": "ค้นหารูปแบบ regex ในไฟล์ต่างๆ ในไดเรกทอรี",
"input_schema": {
"type": "object",
"properties": {
"pattern": {"type": "string", "description": "รูปแบบ Regex"},
"path": {"type": "string", "description": "ไดเรกทอรีที่จะค้นหา"},
"file_glob": {"type": "string", "description": "ตัวกรองรูปแบบไฟล์ เช่น '*.py'"}
},
"required": ["pattern"]
}
}
]
การส่งต่อตัวจัดการเครื่องมือ
ฟังก์ชันการดำเนินการเครื่องมือแมปชื่อเครื่องมือไปยังฟังก์ชันตัวจัดการ:
import subprocess
import os
import re
def execute_tool(name: str, params: dict) -> str:
"""Dispatch tool calls to their handlers."""
handlers = {
"read_file": handle_read_file,
"write_file": handle_write_file,
"edit_file": handle_edit_file,
"run_command": handle_run_command,
"search_code": handle_search_code,
}
handler = handlers.get(name)
if not handler:
return f"Error: Unknown tool '{name}'"
try:
return handler(params)
except Exception as e:
return f"Error: {str(e)}"
def handle_read_file(params: dict) -> str:
path = params["file_path"]
offset = params.get("offset", 0)
limit = params.get("limit", 2000)
with open(path, "r") as f:
lines = f.readlines()
selected = lines[offset:offset + limit]
numbered = [f"{i + offset + 1}\t{line}" for i, line in enumerate(selected)]
return "".join(numbered)
def handle_write_file(params: dict) -> str:
path = params["file_path"]
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w") as f:
f.write(params["content"])
return f"Successfully wrote to {path}"
def handle_edit_file(params: dict) -> str:
path = params["file_path"]
with open(path, "r") as f:
content = f.read()
old = params["old_string"]
if content.count(old) == 0:
return f"Error: '{old[:50]}...' not found in {path}"
if content.count(old) > 1:
return f"Error: '{old[:50]}...' matches {content.count(old)} locations. Be more specific."
new_content = content.replace(old, params["new_string"], 1)
with open(path, "w") as f:
f.write(new_content)
return f"Successfully edited {path}"
def handle_run_command(params: dict) -> str:
cmd = params["command"]
timeout = params.get("timeout", 120)
# Basic safety: block dangerous patterns
blocked = ["rm -rf /", "mkfs", "> /dev/"]
for pattern in blocked:
if pattern in cmd:
return f"Error: Blocked dangerous command pattern: {pattern}"
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True,
timeout=timeout, cwd=os.getcwd()
)
output = ""
if result.stdout:
output += result.stdout
if result.stderr:
output += f"\nSTDERR:\n{result.stderr}"
if not output.strip():
output = f"Command completed with exit code {result.returncode}"
# Truncate large outputs to save context tokens
if len(output) > 30000:
output = output[:15000] + "\n\n... [truncated] ...\n\n" + output[-15000:]
return output
def handle_search_code(params: dict) -> str:
pattern = params["pattern"]
path = params.get("path", os.getcwd())
file_glob = params.get("file_glob", "")
cmd = ["grep", "-rn", "--include", file_glob, pattern, path] if file_glob else \
["grep", "-rn", pattern, path]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if not result.stdout.strip():
return f"ไม่พบรายการที่ตรงกันสำหรับรูปแบบ: {pattern}"
lines = result.stdout.strip().split("\n")
if len(lines) > 50:
return "\n".join(lines[:50]) + f"\n\n... ({len(lines) - 50} รายการเพิ่มเติม)"
return result.stdout
การจัดการบริบท: ปัญหาที่ยาก
เหตุใดบริบทจึงสำคัญกว่าวิศวกรรมพรอมต์
ซอร์สโค้ดที่รั่วไหลเปิดเผยว่า Claude Code ใช้ความพยายามทางวิศวกรรมไปกับการจัดการบริบทมากกว่าการสร้างพรอมต์ระบบเสียอีก ตัวบีบอัดบริบท (ซึ่งภายในเรียกว่า “wU2”) มีกลยุทธ์ห้าแบบ
สำหรับการสร้าง DIY คุณต้องการสองอย่าง:
การบีบอัดอัตโนมัติ (Auto-compaction) จะทำงานเมื่อการสนทนาเข้าใกล้ขีดจำกัดของหน้าต่างบริบท Claude Code จะทำงานเมื่อใช้ไปประมาณ 92% โดยสำรองบัฟเฟอร์ 13,000 โทเค็นไว้สำหรับสรุป
การแทรก CLAUDE.md ซ้ำ (CLAUDE.md re-injection) ทำให้มั่นใจว่าแนวทางปฏิบัติของโปรเจกต์จะไม่เปลี่ยนแปลงไปในระหว่างเซสชันที่ยาวนาน Claude Code จะแทรกการกำหนดค่าโปรเจกต์ซ้ำในทุกเทิร์น ไม่ใช่แค่ตอนเริ่มต้น นี่คือรูปแบบที่ส่งผลกระทบมากที่สุดในการรักษาเอเจนต์เขียนโค้ดให้อยู่ในแนวทางที่ถูกต้อง
การสร้างตัวบีบอัดอย่างง่าย
def maybe_compact(messages: list, system_prompt: str, max_tokens: int = 180000) -> list:
"""Compact conversation when it gets too long."""
# Rough estimate: 4 chars per token
total_chars = sum(
len(str(m.get("content", ""))) for m in messages
)
estimated_tokens = total_chars // 4
if estimated_tokens < max_tokens * 0.85:
return messages # Not yet at the limit
# Ask the model to summarize the conversation so far
summary_response = client.messages.create(
model=MODEL,
max_tokens=4096,
system="Summarize this conversation. Keep all file paths, decisions made, errors encountered, and current task state. Be specific about what was changed and why.",
messages=messages,
)
summary_text = summary_response.content[0].text
# Replace conversation with summary + recent messages
compacted = [
{"role": "user", "content": f"[สรุปการสนทนา]\n{summary_text}"},
{"role": "assistant", "content": "ฉันมีบริบทจากการสนทนาก่อนหน้านี้ของเราแล้ว ฉันควรทำงานอะไรต่อไป?"},
]
# Keep the last 4 messages for immediate context
compacted.extend(messages[-4:])
return compacted
การแทรกบริบทโปรเจกต์ซ้ำ
Claude Code อ่านไฟล์ .claude/CLAUDE.md และแทรกเข้าไปในทุกๆ รอบ นี่คือวิธีการจำลอง:
def build_system_prompt(project_dir: str) -> str:
"""Build system prompt with project context re-injection."""
base_prompt = """คุณเป็นผู้ช่วยเขียนโค้ดที่ช่วยเหลืองานวิศวกรรมซอฟต์แวร์
คุณสามารถเข้าถึงเครื่องมือสำหรับอ่าน, เขียน, แก้ไขไฟล์, รันคำสั่ง และค้นหาโค้ดได้
ควรอ่านไฟล์เสมอก่อนที่จะแก้ไข เลือกใช้ edit_file แทน write_file สำหรับไฟล์ที่มีอยู่
ตอบกลับให้กระชับ เน้นที่โค้ด ไม่ใช่คำอธิบาย"""
# Look for project guidelines
claude_md_path = os.path.join(project_dir, ".claude", "CLAUDE.md")
if os.path.exists(claude_md_path):
with open(claude_md_path, "r") as f:
project_context = f.read()
base_prompt += f"\n\n# แนวทางโปรเจกต์\n{project_context}"
# Also check for a root CLAUDE.md
root_md = os.path.join(project_dir, "CLAUDE.md")
if os.path.exists(root_md):
with open(root_md, "r") as f:
root_context = f.read()
base_prompt += f"\n\n# แนวทางที่เก็บ (Repository guidelines)\n{root_context}"
return base_prompt
ระบบหน่วยความจำสามชั้น
ซอร์สโค้ดที่รั่วไหลแสดงให้เห็นว่า Claude Code ใช้สถาปัตยกรรมหน่วยความจำสามชั้น นี่เป็นหนึ่งในส่วนที่ถูกประเมินค่าต่ำที่สุดของระบบ
ชั้นที่ 1: MEMORY.md (โหลดตลอดเวลา)
ดัชนีน้ำหนักเบาที่อยู่ในพรอมต์ระบบตลอดเวลา แต่ละรายการมีหนึ่งบรรทัด ไม่เกิน 150 ตัวอักษร ทำหน้าที่เป็นสารบัญที่ชี้ไปยังความรู้ที่ลึกซึ้งยิ่งขึ้น จำกัดที่ 200 บรรทัด / 25KB
- [การตั้งค่าผู้ใช้](memory/user-prefs.md) - ชอบ TypeScript, ใช้การผูกคีย์ Vim
- [ข้อตกลง API](memory/api-conventions.md) - REST พร้อมสเปก JSON:API, snake_case
- [กระบวนการปรับใช้](memory/deploy.md) - ใช้ GitHub Actions, ปรับใช้กับ AWS EKS
ชั้นที่ 2: ไฟล์หัวข้อ (โหลดตามความต้องการ)
ไฟล์ความรู้รายละเอียดจะถูกโหลดเมื่อดัชนีชี้ว่ามีความเกี่ยวข้อง ไฟล์เหล่านี้มีข้อตกลงของโปรเจกต์ การตัดสินใจทางสถาปัตยกรรม และรูปแบบที่เรียนรู้
ชั้นที่ 3: บันทึกการสนทนา (ค้นหา ไม่เคยอ่าน)
บันทึกเซสชันฉบับเต็มที่ไม่เคยถูกโหลดทั้งหมด เอเจนต์จะค้นหา (grep) หาตัวระบุเฉพาะ สิ่งนี้ป้องกันการบวมของบริบทในขณะที่ยังคงสามารถค้นหาได้
การสร้างระบบหน่วยความจำอย่างน้อยที่สุด
import json
MEMORY_DIR = ".agent/memory"
def load_memory_index() -> str:
"""Load the memory index for system prompt injection."""
index_path = os.path.join(MEMORY_DIR, "MEMORY.md")
if os.path.exists(index_path):
with open(index_path, "r") as f:
return f.read()
return ""
def save_memory(key: str, content: str, description: str):
"""Save a memory entry and update the index."""
os.makedirs(MEMORY_DIR, exist_ok=True)
# Write the memory file
filename = f"{key.replace(' ', '-').lower()}.md"
filepath = os.path.join(MEMORY_DIR, filename)
with open(filepath, "w") as f:
f.write(f"---\nname: {key}\ndescription: {description}\n---\n\n{content}")
# Update the index
index_path = os.path.join(MEMORY_DIR, "MEMORY.md")
index_lines = []
if os.path.exists(index_path):
with open(index_path, "r") as f:
index_lines = f.readlines()
# Add or update entry
new_entry = f"- [{key}]({filename}) - {description}\n"
updated = False
for i, line in enumerate(index_lines):
if filename in line:
index_lines[i] = new_entry
updated = True
break
if not updated:
index_lines.append(new_entry)
with open(index_path, "w") as f:
f.writelines(index_lines)
เพิ่มเครื่องมือ save_memory ลงในรายการเครื่องมือของคุณ เพื่อให้เอเจนต์สามารถจัดเก็บความรู้ระหว่างเซสชันได้
การเพิ่มระบบสิทธิ์
การรั่วไหลเผยให้เห็นโหมดการอนุญาตห้าโหมด: default (พรอมต์แบบโต้ตอบ), auto (การอนุมัติแบบ ML), bypass, yolo (อนุมัติทุกอย่าง) และ deny การดำเนินการของเครื่องมือทุกอย่างถูกจัดประเภทเป็นความเสี่ยงต่ำ (LOW), ปานกลาง (MEDIUM) หรือสูง (HIGH)
สำหรับเอเจนต์ DIY ระบบสามชั้นแบบง่ายก็ใช้ได้:
# ระดับความเสี่ยงสำหรับการดำเนินการ
RISK_LEVELS = {
"read_file": "low",
"search_code": "low",
"edit_file": "medium",
"write_file": "medium",
"run_command": "high",
}
def check_permission(tool_name: str, params: dict, auto_approve_low: bool = True) -> bool:
"""ตรวจสอบว่าผู้ใช้อนุมัติการเรียกใช้เครื่องมือนี้หรือไม่"""
risk = RISK_LEVELS.get(tool_name, "high")
if risk == "low" and auto_approve_low:
return True
# แสดงให้ผู้ใช้เห็นว่ากำลังจะเกิดอะไรขึ้น
print(f"\n--- ตรวจสอบสิทธิ์ (ความเสี่ยง {risk.upper()}) ---")
print(f"เครื่องมือ: {tool_name}")
for key, value in params.items():
display = str(value)[:200]
print(f" {key}: {display}")
response = input("อนุญาต? [y/n/always]: ").strip().lower()
if response == "always":
RISK_LEVELS[tool_name] = "low" # อนุมัติเครื่องมือนี้โดยอัตโนมัติสำหรับอนาคต
return True
return response == "y"
การทดสอบการเรียก API ของเอเจนต์ของคุณด้วย Apidog
การสร้างเอเจนต์เขียนโค้ดหมายถึงการเรียก API หลายร้อยครั้งไปยัง Claude การดีบักการโต้ตอบเหล่านี้ โดยเฉพาะการสนทนาแบบหลายรอบที่มีการใช้เครื่องมือ เป็นเรื่องที่ยากลำบากหากใช้บันทึกดิบ

Apidog ช่วยให้คุณตรวจสอบและทดสอบคำขอ API ที่เอเจนต์ของคุณส่งได้อย่างแม่นยำ นี่คือวิธีใช้งานระหว่างการพัฒนา:
จับภาพและเล่นซ้ำคำขอ API
ตั้งค่า Apidog เป็นพร็อกซีเพื่อดักจับการเรียกของเอเจนต์ของคุณไปยัง Anthropic API:
- เปิด Apidog และสร้างโปรเจกต์ใหม่สำหรับเอเจนต์ของคุณ
- นำเข้าปลายทาง (endpoint) Anthropic Messages API:
POST https://api.anthropic.com/v1/messages - ตั้งค่าเนื้อหาคำขอ (request body) ด้วยพรอมต์ระบบ (system prompt), อาร์เรย์เครื่องมือ (tools array) และข้อความ (messages) ของคุณ
- ทดสอบแต่ละรอบ (turn) โดยการเล่นซ้ำคำขอที่ถูกจับภาพด้วยพารามิเตอร์ที่แก้ไขแล้ว
วิธีนี้ช่วยให้คุณแยกแยะรอบการใช้เครื่องมือที่เฉพาะเจาะจงได้โดยไม่ต้องรันลูปเอเจนต์ทั้งหมด เมื่อโมเดลส่งคืนการเรียกใช้เครื่องมือที่ไม่คาดคิดหรือพารามิเตอร์ที่สร้างขึ้นเอง คุณสามารถแก้ไขเนื้อหาคำขอในตัวแก้ไขภาพของ Apidog และส่งซ้ำเพื่อดูว่าอินพุตที่แตกต่างกันเปลี่ยนการตอบสนองอย่างไร
ดีบักการสนทนาแบบหลายรอบ
ส่วนที่ยากที่สุดในการดีบักเอเจนต์คือการจำลองสถานะการสนทนา ตัวแปรสภาพแวดล้อมของ Apidog ช่วยให้คุณบันทึกสแนปช็อตการสนทนาได้:
- บันทึกอาร์เรย์
messagesทั้งหมดเป็นตัวแปรสภาพแวดล้อมหลังจากแต่ละรอบ - เล่นซ้ำจากจุดใดก็ได้ในการสนทนา
- เปรียบเทียบผลลัพธ์เครื่องมือระหว่างการรันเพื่อค้นหาว่าพฤติกรรมแตกต่างกันที่ใด
ตรวจสอบความถูกต้องของสคีมาเครื่องมือ
คำจำกัดความเครื่องมือของคุณ (สคีมา JSON ที่คุณส่งไปยัง API) กำหนดสิ่งที่โมเดลสามารถร้องขอได้ สคีมาที่ผิดรูปทำให้เกิดความล้มเหลวแบบเงียบๆ ซึ่งโมเดลข้ามเครื่องมือหรือส่งพารามิเตอร์ที่ไม่ถูกต้อง
นำเข้าสคีมาเครื่องมือของคุณไปยัง Apidog และใช้ตัวตรวจสอบ JSON Schema เพื่อตรวจจับปัญหาเหล่านั้นก่อนที่จะถึง API ดาวน์โหลด Apidog เพื่อเริ่มต้นการดีบักการโต้ตอบ API ของเอเจนต์ของคุณ
รวบรวมทั้งหมดเข้าด้วยกัน: REPL ที่สมบูรณ์
นี่คือเอเจนต์ฉบับเต็มที่เชื่อมโยงกันเป็น REPL ที่ใช้งานได้จริง:
#!/usr/bin/env python3
"""A minimal Claude Code-style coding agent."""
import anthropic
import os
import sys
client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-6"
PROJECT_DIR = os.getcwd()
def main():
system_prompt = build_system_prompt(PROJECT_DIR)
memory = load_memory_index()
if memory:
system_prompt += f"\n\n# หน่วยความจำ\n{memory}"
messages = []
print("เอเจนต์เขียนโค้ดพร้อมแล้ว พิมพ์ 'quit' เพื่อออก\n")
while True:
user_input = input("> ").strip()
if user_input.lower() in ("quit", "exit"):
break
if not user_input:
continue
messages.append({"role": "user", "content": user_input})
# บีบอัดหากจำเป็น
messages = maybe_compact(messages, system_prompt)
# แทรกบริบทโปรเจกต์ซ้ำ (Claude Code ทำสิ่งนี้ในทุกๆ รอบ)
current_system = build_system_prompt(PROJECT_DIR)
memory = load_memory_index()
if memory:
current_system += f"\n\n# หน่วยความจำ\n{memory}"
# รันลูปเอเจนต์
result = agent_loop(current_system, TOOLS, messages)
print(f"\n{result}\n")
if __name__ == "__main__":
main()
สิ่งนี้จะทำให้คุณได้รับเอเจนต์เขียนโค้ดที่ทำงานได้จริงในโค้ด Python ไม่ถึง 300 บรรทัด มันสามารถอ่านไฟล์ แก้ไขโค้ด รันคำสั่ง ค้นหาโค้ดเบส จัดการบริบท และรักษาหน่วยความจำระหว่างเซสชันได้
สิ่งที่ต้องเพิ่มต่อไป
ซอร์สโค้ดที่รั่วไหลเปิดเผยคุณสมบัติหลายอย่างที่ควรสร้างเมื่อลูปหลักของคุณทำงานได้แล้ว:
เอเจนต์ย่อยสำหรับการทำงานแบบขนาน
Claude Code สร้างเอเจนต์ย่อย (เรียกว่าเอเจนต์ "แยกสาขา") สำหรับงานอิสระ เอเจนต์ย่อยจะได้รับสำเนาของบริบทหลัก ดำเนินการงาน และส่งคืนผลลัพธ์ สิ่งนี้ช่วยหลีกเลี่ยงการทำให้การสนทนาหลักสับสนด้วยงานสำรวจ
รูปแบบคือ: สร้าง agent_loop() ใหม่พร้อมคำอธิบายงานที่มุ่งเน้นและชุดเครื่องมือย่อย ส่งคืนผลลัพธ์เป็นสตริง
การลบข้อมูลซ้ำจากการอ่านไฟล์
Claude Code ติดตามว่ามีการอ่านไฟล์ใดบ้างและเวลาที่แก้ไข หากไฟล์ไม่เปลี่ยนแปลงตั้งแต่การอ่านครั้งล่าสุด มันจะข้ามการอ่านและแจ้งให้โมเดลทราบว่า "ไฟล์ไม่เปลี่ยนแปลงตั้งแต่การอ่านครั้งล่าสุด" ซึ่งช่วยประหยัดโทเค็นในการอ่านซ้ำระหว่างเซสชันที่ยาวนาน
การตัดเอาต์พุตและการสุ่มตัวอย่าง
เมื่อเครื่องมือส่งคืนเอาต์พุตขนาดใหญ่มาก (เช่น ผลลัพธ์ grep มากกว่า 10,000 บรรทัด) Claude Code จะตัดทอนและแจ้งให้โมเดลทราบว่ามีการละเว้นผลลัพธ์ไปเท่าใด หากไม่มีสิ่งนี้ ผลลัพธ์เครื่องมือขนาดใหญ่เพียงรายการเดียวก็สามารถกินพื้นที่หน้าต่างบริบทของคุณทั้งหมดได้
การบีบอัดอัตโนมัติพร้อมการแทรกไฟล์ซ้ำ
ตัวบีบอัดที่รั่วไหลไม่ได้ทิ้งเนื้อหาไฟล์ หลังจากสรุปการสนทนาแล้ว มันจะแทรกเนื้อหาของไฟล์ที่เข้าถึงล่าสุดซ้ำ (สูงสุด 5,000 โทเค็นต่อไฟล์) ซึ่งหมายความว่าโมเดลยังคงมีความรู้ในการทำงานของโค้ดเบสแม้หลังจากการบีบอัด
สิ่งที่เราเรียนรู้จากการรั่วไหล
การรั่วไหลของ Claude Code ยืนยันรูปแบบหลายอย่างที่ชุมชนเอเจนต์ AI ได้ตั้งทฤษฎีไว้:
ลูปหลักนั้นเรียบง่าย รูปแบบเอเจนต์ทั้งหมดสามารถทำได้ใน 30 บรรทัด ความซับซ้อนอยู่ที่เครื่องมือและการจัดการบริบท ไม่ใช่วิศวกรรมพรอมต์
เครื่องมือเฉพาะมีประสิทธิภาพเหนือกว่า bash เครื่องมือที่มีโครงสร้างและสร้างขึ้นมาโดยเฉพาะให้ข้อมูลที่มีความหนาแน่นต่อโทเค็นแก่โมเดลได้ดีกว่าการส่งคำสั่ง bash แบบไปป์
หน่วยความจำต้องการหลายชั้น ดัชนีที่โหลดตลอดเวลา ไฟล์หัวข้อตามต้องการ และบันทึกที่ค้นหาได้เท่านั้น ช่วยรักษาสมดุลระหว่างการเรียกคืนข้อมูลกับต้นทุนบริบท
การจัดการบริบทคือผลิตภัณฑ์ที่แท้จริง การบีบอัดอัตโนมัติ การแทรกแนวทางโปรเจกต์ซ้ำ และการตัดเอาต์พุตคือสิ่งที่ทำให้เซสชันการเขียนโค้ดที่ยาวนานเป็นไปได้
โครงสร้างการทำงาน (harness) คือผลิตภัณฑ์ ไม่ใช่โมเดล โมเดลให้ความฉลาด โครงสร้างการทำงานให้การรับรู้ (การอ่านไฟล์, การค้นหาโค้ด) การกระทำ (การเขียนไฟล์, การรันคำสั่ง) และหน่วยความจำ การสร้างเอเจนต์เขียนโค้ดคือการสร้างโครงสร้างการทำงาน
หากคุณต้องการทดสอบและดีบักการโต้ตอบ API ของเอเจนต์ที่กำหนดเอง รวมถึงการสนทนาการใช้เครื่องมือแบบหลายรอบ สคีมาคำขอที่ซับซ้อน และการตรวจสอบการตอบสนอง ลองใช้ Apidog ฟรี มันช่วยจัดการการดีบัก API เพื่อให้คุณสามารถมุ่งเน้นไปที่ตรรกะของเอเจนต์ได้
คำถามที่พบบ่อย
ฉันสามารถใช้รูปแบบจากการรั่วไหลของ Claude Code ได้อย่างถูกกฎหมายหรือไม่?
การรั่วไหลเปิดเผยรูปแบบสถาปัตยกรรม ไม่ใช่อัลกอริทึมที่เป็นกรรมสิทธิ์ การสร้างเอเจนต์เขียนโค้ดที่ใช้ลูป while พร้อมการส่งต่อเครื่องมือเป็นรูปแบบมาตรฐานที่มีอยู่ในเอกสาร API ของ Anthropic เอง คุณไม่ควรคัดลอกโค้ดของ Anthropic ตามตัวอักษร แต่การสร้างสถาปัตยกรรมขึ้นมาใหม่ด้วยโค้ดของคุณเองถือเป็นแนวปฏิบัติมาตรฐาน
ฉันควรใช้โมเดลใดสำหรับเอเจนต์เขียนโค้ด DIY?
Claude Sonnet 4.6 ให้ความสมดุลที่เหมาะสมระหว่างความเร็วและความสามารถสำหรับงานเขียนโค้ด Claude Opus 4.6 ให้ผลลัพธ์ที่ดีกว่าในการตัดสินใจด้านสถาปัตยกรรมที่ซับซ้อน แต่มีค่าใช้จ่ายสูงกว่าและทำงานช้ากว่า สำหรับการแก้ไขไฟล์และการค้นหาแบบง่ายๆ Claude Haiku 4.5 ก็ใช้ได้และมีค่าใช้จ่ายน้อยกว่า 90%
การรันเอเจนต์เขียนโค้ดของคุณเองมีค่าใช้จ่ายเท่าไร?
เซสชันการเขียนโค้ดทั่วไป (30-50 รอบ) ด้วย Claude Sonnet 4.6 มีค่าใช้จ่าย 1-5 ดอลลาร์สหรัฐฯ ในค่าธรรมเนียม API ปัจจัยหลักที่ทำให้เกิดค่าใช้จ่ายคือขนาดของหน้าต่างบริบท การบีบอัดอย่างเข้มข้นช่วยลดค่าใช้จ่ายได้ ซอร์สโค้ดที่รั่วไหลของ Claude Code แสดงให้เห็นว่ามันกระตุ้นการบีบอัดเมื่อใช้บริบทไปแล้ว 92% เพื่อควบคุมสิ่งนี้
เหตุใด Claude Code จึงใช้ React สำหรับแอปเทอร์มินัล?
Ink (React สำหรับเทอร์มินัล) ช่วยให้ทีมสามารถนำโมเดลคอมโพเนนต์และการจัดการสถานะของ React กลับมาใช้ใหม่สำหรับการโต้ตอบ UI ที่ซับซ้อน เช่น กล่องโต้ตอบสิทธิ์, การสตรีมเอาต์พุต และการแสดงผลการเรียกใช้เครื่องมือ สำหรับโปรเจกต์ DIY การใช้ input() / print() REPL อย่างง่ายก็เพียงพอแล้ว
คุณสมบัติที่สำคัญที่สุดที่ควรสร้างหลังจากลูปหลักคืออะไร?
ระบบสิทธิ์ หากไม่มีระบบนี้ โมเดลสามารถเขียนทับไฟล์และรันคำสั่งโดยพลการได้โดยไม่มีการตรวจสอบจากผู้ใช้ แม้แต่การ "ยืนยันก่อนเขียน/ดำเนินการ" แบบง่ายๆ ก็สามารถป้องกันความเสียหายโดยไม่ตั้งใจส่วนใหญ่ได้
Claude Code จัดการข้อผิดพลาดจากการเรียกใช้เครื่องมืออย่างไร?
ข้อผิดพลาดของเครื่องมือจะถูกส่งคืนเป็นเนื้อหาข้อความในข้อความ tool_result โมเดลจะเห็นข้อผิดพลาดและตัดสินใจว่าจะลองใหม่ ใช้วิธีอื่น หรือสอบถามผู้ใช้ ไม่มีการจัดการข้อผิดพลาดแบบพิเศษ การให้เหตุผลของโมเดลจะจัดการการกู้คืน
ฉันสามารถใช้สิ่งนี้กับโมเดลอื่นที่ไม่ใช่ Claude ได้หรือไม่?
ได้ รูปแบบการใช้เครื่องมือนี้สามารถใช้ได้กับทุกโมเดลที่รองรับการเรียกใช้ฟังก์ชัน: GPT-4, Gemini, Llama และอื่นๆ คุณจะต้องปรับรูปแบบการเรียก API แต่ลูปเอเจนต์ เครื่องมือ และระบบหน่วยความจำนั้นไม่ขึ้นกับโมเดล
ฉันจะป้องกันไม่ให้เอเจนต์รันคำสั่งที่เป็นอันตรายได้อย่างไร?
เริ่มต้นด้วยรายการบล็อกรูปแบบอันตราย (rm -rf /, mkfs, เป็นต้น) และต้องได้รับการอนุมัติอย่างชัดเจนสำหรับการเรียก run_command ทั้งหมด Claude Code จัดประเภทการดำเนินการทุกอย่างเป็นความเสี่ยงต่ำ (LOW), ปานกลาง (MEDIUM) หรือสูง (HIGH) และบล็อกหรือแจ้งเตือนตามการจัดประเภท สร้างสิ่งเดียวกันสำหรับเครื่องมือของคุณ
