TL;DR
การรั่วไหลของซอร์สโค้ด Claude เปิดเผยฐานโค้ด TypeScript ขนาด 512,000 บรรทัดเมื่อวันที่ 31 มีนาคม 2026 โครงสร้างหลักเป็นลูป while ที่เรียก API, ส่ง tool calls, และส่งผลลัพธ์กลับ คุณสามารถสร้างเวอร์ชันของตัวเองด้วย Python, Anthropic SDK และโค้ดสำหรับลูปหลักประมาณ 200 บรรทัด บทความนี้จะอธิบายโครงสร้างแต่ละส่วนและแนะนำวิธีสร้างเองแบบ step-by-step
บทนำ
เมื่อวันที่ 31 มีนาคม 2026, Anthropic เผลอปล่อยไฟล์ source map ขนาด 59.8 MB ใน @anthropic-ai/claude-code (npm package) ซึ่งทำให้สามารถกู้คืน TypeScript codebase ฉบับเต็มได้ในทันที
ไม่กี่ชั่วโมงหลังการรั่วไหล โค้ดถูกคัดลอกไปทั่ว GitHub หลายแห่ง และนักพัฒนาเริ่มวิเคราะห์แต่ละโมดูล ตั้งแต่ลูปเอเจนต์หลักจนถึงฟีเจอร์ซ่อน เช่น undercover mode และการฉีดเครื่องมือปลอม
ปฏิกิริยามีทั้งด้านวิจารณ์เรื่องความปลอดภัยและความหลงใหลในสถาปัตยกรรมที่ถูกเปิดเผย นักพัฒนาจำนวนมากตั้งคำถาม: "จะสร้างแบบนี้เองได้ไหม?" คำตอบคือ "ได้" โครงสร้างหลักเรียบง่ายและสามารถสร้างใหม่ได้ด้วย Python + Anthropic SDK โดยบทความนี้จะอธิบายทีละส่วน พร้อมโค้ดตัวอย่างที่นำไปใช้ได้ทันที และยังแนะนำวิธีทดสอบ API interaction ของเอเจนต์แบบ custom ด้วย Apidog (ช่วยให้ debug API conversation หลายรอบได้สะดวกกว่า curl มาก)
สิ่งที่การรั่วไหลเปิดเผยเกี่ยวกับสถาปัตยกรรมของ Claude Code
ภาพรวมของฐานโค้ด
Claude Code (โค้ดเนม "Tengu") มีไฟล์ ~1,900 ไฟล์ โครงสร้างโมดูลแบ่งเป็น:
cli/ - UI เทอร์มินัล (React + Ink)
tools/ - เครื่องมือเฉพาะมากกว่า 40 รายการ
core/ - system prompt, สิทธิ์, ค่าคงที่
assistant/ - จัดระเบียบเอเจนต์
services/ - API, การบีบอัด, OAuth, telemetry
UI CLI คือ React app ที่ใช้ Ink (React for terminal) กับ CSS flexbox (Yoga) และ ANSI สำหรับ formatting ทุก interaction เป็น React component ทั้งหมด
สำหรับการทำ DIY agent คุณไม่จำเป็นต้องมี React CLI – ใช้ REPL ธรรมดาก็เพียงพอ
ลูปเอเจนต์หลัก
แกนหลักของ Claude Code เป็น while loop ที่ Anthropic เรียกว่า "nO" กระบวนการคือ:
- ส่งข้อความ (system prompt + tools) ไปยัง Claude API
- รับ response ที่อาจมีข้อความหรือ block
tool_use - dispatch ไปยังฟังก์ชัน tool handler ตามชื่อ
- ผนวกผลลัพธ์เครื่องมือกลับเข้า message list
- ถ้ายังมี tool call ให้วนลูปต่อ
- ถ้าเป็นข้อความธรรมดา ให้ส่งคืน user
ตัวอย่างโค้ด Python (minimal):
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,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return "".join(
block.text for block in response.content
if hasattr(block, "text")
)
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,
})
messages.append({"role": "user", "content": tool_results})
แกนหลักอยู่ที่ ~30 บรรทัด ความซับซ้อนที่เหลือมาจาก tool, สิทธิ์, context, และ memory
การสร้างระบบเครื่องมือ
ทำไมต้องใช้ "เครื่องมือเฉพาะ" แทน bash
Claude Code ใช้ เครื่องมือเฉพาะ เช่น Read, Edit, Grep, Glob แทนที่จะรัน bash ตรง ๆ เนื่องจาก:
- Structured output: เช่น Grep เฉพาะคืนค่าที่ parse ได้
- Security: BashTool จะบล็อก pattern อันตราย, เครื่องมือเฉพาะปลอดภัยกว่า
- ประสิทธิภาพโทเค็น: ตัดทอน output ได้ง่าย, ลด context window เปลือง
ชุดเครื่องมือที่จำเป็น
ตัวอย่างโครงสร้าง TOOLS ที่ควรมี:
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"]
}
},
# ... (write_file, edit_file, run_command, search_code)
]
แมปเครื่องมือกับ handler
โค้ดตัวอย่างสำหรับ dispatch ฟังก์ชัน:
import subprocess
import os
import re
def execute_tool(name: str, params: dict) -> str:
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)
# ... (write_file, edit_file, run_command, search_code)
การจัดการบริบท: ปัญหาที่ยาก
ทำไม context management สำคัญกว่า prompt engineering
Claude Code ลงทุนกับ context compressor (wU2) มากกว่าตัว prompt เอง สองกลยุทธ์ที่ควรมี:
- Auto-compaction: เมื่อใช้ context เกิน ~92% ให้สรุปข้อความเดิม
- CLAUDE.md re-injection: แทรกแนวทางโปรเจกต์ซ้ำทุกเทิร์นเพื่อไม่ให้ context หลุด
ตัวอย่าง compressor แบบง่าย
def maybe_compact(messages: list, system_prompt: str, max_tokens: int = 180000) -> list:
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
summary_response = client.messages.create(
model=MODEL,
max_tokens=4096,
system="Summarize this conversation. ...",
messages=messages,
)
summary_text = summary_response.content[0].text
compacted = [
{"role": "user", "content": f"[สรุปการสนทนา]\n{summary_text}"},
{"role": "assistant", "content": "ฉันมีบริบทจากการสนทนาก่อนหน้านี้แล้ว ..."},
]
compacted.extend(messages[-4:])
return compacted
แทรกแนวทางโปรเจกต์ซ้ำ
def build_system_prompt(project_dir: str) -> str:
base_prompt = """คุณเป็นผู้ช่วยเขียนโค้ด...
ควรอ่านไฟล์ก่อนแก้ไข ..."""
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}"
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# แนวทางที่เก็บ\n{root_context}"
return base_prompt
ระบบหน่วยความจำสามชั้น
ชั้นที่ 1: MEMORY.md (โหลดตลอดเวลา)
- index ขนาดเล็ก (บรรทัดละ <150 ตัวอักษร) โหลดในทุก prompt
- จำกัดที่ 200 บรรทัด / 25KB
- [การตั้งค่าผู้ใช้](memory/user-prefs.md) - ชอบ TypeScript, ใช้การผูกคีย์ Vim
- [ข้อตกลง API](memory/api-conventions.md) - REST + JSON:API
ชั้นที่ 2: ไฟล์หัวข้อ (on demand)
- โหลดเมื่อเกี่ยวข้อง (project conventions, decisions, patterns)
ชั้นที่ 3: บันทึกการสนทนา (grep ค้นหา)
- ไม่โหลดทั้งหมด ใช้ grep หาเฉพาะคีย์
ตัวอย่างระบบ memory
import json
MEMORY_DIR = ".agent/memory"
def load_memory_index() -> str:
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):
os.makedirs(MEMORY_DIR, exist_ok=True)
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}")
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()
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 ใน TOOLS เพื่อให้ agent จดจำความรู้ระหว่าง session
การเพิ่มระบบสิทธิ์
การรั่วไหลเปิดเผย permission mode 5 แบบ: default, auto, bypass, yolo, deny และจัดกลุ่ม risk เป็น LOW, MEDIUM, HIGH
สำหรับ DIY agent สามารถใช้ logic แบบนี้:
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
การพัฒนา agent ที่โต้ตอบกับ Claude API หลายร้อยครั้ง ต้อง debug รอบ conversation แบบ multi-turn และ tool use ได้สะดวก
Apidog ช่วยให้ตรวจสอบ/ทดสอบ API requests ของ agent ได้แบบ visual และ repeatable:
จับภาพและเล่นซ้ำคำขอ API
- เปิด Apidog สร้างโปรเจกต์ใหม่
- นำเข้า endpoint Anthropic Messages API:
POST https://api.anthropic.com/v1/messages - กำหนด request body (system prompt, tools, messages)
- ทดสอบแต่ละรอบด้วยการ replay request ที่จับภาพไว้
ปรับ input หรือ payload ได้ทันทีโดยไม่ต้องรันลูปทั้งหมด
ดีบัก multi-turn conversation
- บันทึก array
messagesเป็น environment variable หลังแต่ละรอบ - เล่นซ้ำจากจุดไหนก็ได้
- เปรียบเทียบผลลัพธ์ tool ระหว่าง run เพื่อวิเคราะห์พฤติกรรม
ตรวจสอบความถูกต้องของ schema เครื่องมือ
นำเข้า JSON schema ของ tools เข้าตัว validator ของ Apidog เพื่อตรวจสอบ schema error ก่อนยิง API
รวบรวมทั้งหมดเข้าด้วยกัน: REPL ที่สมบูรณ์
ตัวอย่าง main REPL agent ที่รวมทุกอย่างไว้ในไฟล์เดียว:
#!/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)
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()
สิ่งที่ต้องเพิ่มต่อไป
หลังจากได้ลูปหลักแล้ว ฟีเจอร์สำคัญที่ควรเพิ่ม คือ:
- เอเจนต์ย่อย (subagent) สำหรับงานขนาน
- การลบข้อมูลซ้ำ จากการอ่านไฟล์ (cache file hash/mtime)
- การตัดเอาต์พุต/สุ่มตัวอย่าง เมื่อ tool คืนข้อมูลใหญ่
- การบีบอัดอัตโนมัติพร้อม reinject ไฟล์ ที่ถูกเข้าถึงล่าสุด
สิ่งที่เราเรียนรู้จากการรั่วไหล
- ลูปหลักของ agent นั้นเรียบง่าย (~30 บรรทัด)
- เครื่องมือเฉพาะดีกว่า bash ในแง่ token density และ security
- Memory และ context management สำคัญมาก
- Harness (ตัวรับ-ส่ง context/tool/memory) คือ "product" จริง ๆ ไม่ใช่ LLM
- ถ้าต้องการ debug API conversation agent แบบ multi-turn หรือ tool use ซับซ้อน ลองใช้ Apidog ฟรี จะช่วยให้ workflow เร็วขึ้นมาก
คำถามที่พบบ่อย
ฉันสามารถใช้รูปแบบจากการรั่วไหลของ Claude Code ได้อย่างถูกกฎหมายหรือไม่?
ได้ รูปแบบสถาปัตยกรรม (while loop + tool dispatch) เป็น standard pattern ที่พบได้ในเอกสาร Anthropic API คุณไม่ควรคัดลอกโค้ดตรง ๆ แต่การสร้างสถาปัตยกรรมแบบเดียวกันด้วยโค้ดของตัวเองถือว่าโอเค
ฉันควรใช้โมเดลใดสำหรับเอเจนต์เขียนโค้ด DIY?
Claude Sonnet 4.6 เหมาะสมสำหรับ balance ด้านความเร็วและความสามารถ งาน complex แนะนำ Opus 4.6 (แต่แพงกว่าและช้ากว่า) งานง่าย ๆ ใช้ Haiku 4.5 ก็ประหยัดสุด
การรันเอเจนต์เขียนโค้ดของคุณเองมีค่าใช้จ่ายเท่าไร?
ค่าใช้จ่ายอยู่ที่ ~1-5 ดอลลาร์ ต่อ session (30-50 รอบ) หากใช้ Claude Sonnet 4.6 ปัจจัยหลักคือ window context – การบีบอัดช่วยลดค่าใช้จ่าย
เหตุใด Claude Code จึงใช้ React สำหรับแอปเทอร์มินัล?
Ink (React for terminal) ช่วย reuse component/state management สำหรับ UI ที่ซับซ้อน สำหรับ DIY ใช้ input()/print() ก็เพียงพอ
คุณสมบัติที่สำคัญที่สุดที่ควรสร้างหลังจากลูปหลักคืออะไร?
ระบบสิทธิ์ (permission) เพื่อป้องกัน agent เขียนทับไฟล์หรือรันคำสั่งอันตรายโดยไม่แจ้งเตือน
Claude Code จัดการข้อผิดพลาดจากการเรียกใช้เครื่องมืออย่างไร?
error จาก tool จะถูกส่งกลับในข้อความ tool_result model ตัดสินใจ retry หรือถาม user เอง ไม่มี error handling logic พิเศษ
ฉันสามารถใช้สิ่งนี้กับโมเดลอื่นที่ไม่ใช่ Claude ได้หรือไม่?
ได้ ถ้า LLM รองรับ function/tool call เช่น GPT-4, Gemini, Llama ปรับแค่รูปแบบ API ที่เรียก
ฉันจะป้องกันไม่ให้เอเจนต์รันคำสั่งที่เป็นอันตรายได้อย่างไร?
เพิ่มรายการ pattern ที่บล็อกไว้ (rm -rf /, mkfs, ฯลฯ) และ require approve สำหรับ run_command ทุกครั้ง แบ่งระดับ risk แล้วแจ้งเตือน user ก่อนรันเครื่องมือเสี่ยง
สนใจ debug และพัฒนา agent API อย่างมีประสิทธิภาพ? ลองใช้ Apidog วันนี้ ฟรี!

Top comments (0)