DEV Community

Cover image for เพิ่มหน่วยความจำถาวรให้ AI Agent จำได้เหมือนเมื่อวาน
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

เพิ่มหน่วยความจำถาวรให้ AI Agent จำได้เหมือนเมื่อวาน

สรุปย่อ (TL;DR)

เพิ่มหน่วยความจำถาวรให้เอเจนต์ AI ใน 4 ขั้นตอน: (1) ตั้งค่าเซิร์ฟเวอร์หน่วยความจำ MCP ด้วยเครื่องมือ remember, recall, search และ rollback, (2) เพิ่มคำสั่งหน่วยความจำลงในพรอมต์ของเอเจนต์, (3) กำหนดค่า ~/.claude/settings.json สำหรับ Claude Code หรือ .cursor/mcp.json สำหรับ Cursor, (4) ใช้รูปแบบหน่วยความจำสำหรับการบันทึกการตัดสินใจ, การส่งต่องานระหว่างเอเจนต์ และจุดตรวจสอบเซสชัน เอเจนต์จะเก็บรักษาบริบทข้ามเซสชัน — ไม่ต้องคัดลอกและวางการสนทนาก่อนหน้าอีกต่อไป

ทดลองใช้ Apidog วันนี้

แก้ปัญหา “ฉันจำเรื่องเมื่อวานไม่ได้” เพิ่มหน่วยความจำถาวรให้เอเจนต์ AI โดยใช้โปรโตคอล MCP แล้วพวกเขาจะจดจำการตัดสินใจ สิ่งที่ส่งมอบได้ และบริบทจากเซสชันก่อนหน้า

คุณรู้จักวิธีปฏิบัตินี้ดี:

วันแรก: "สร้างระบบยืนยันตัวตนผู้ใช้"
เอเจนต์: [สร้าง JWT auth, สร้างตาราง users, implements refresh tokens]

วันที่สอง: "ทำต่อจากเมื่อวาน"
เอเจนต์: "ฉันไม่มีบริบทจากเซสชันก่อนหน้า คุณช่วยวางสิ่งที่เราทำไปแล้วได้ไหม?"
Enter fullscreen mode Exit fullscreen mode

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

หน่วยความจำถาวรแก้ไขปัญหานี้ ด้วยหน่วยความจำ MCP (Model Context Protocol) เอเจนต์จะจัดเก็บการตัดสินใจโดยอัตโนมัติและเรียกคืนเมื่อจำเป็น ไม่ต้องคัดลอกวาง ไม่ต้องอธิบายซ้ำ

ในบทช่วยสอนนี้ คุณจะได้ตั้งค่าหน่วยความจำ MCP สำหรับเอเจนต์ AI คุณจะได้เรียนรู้วิธีจัดเก็บการตัดสินใจจากเซสชันของ Backend Architect, เรียกคืนบริบทเมื่อเปลี่ยนไปใช้ Database Optimizer และส่งมอบสิ่งที่ทำสำเร็จแล้วให้ Frontend Developer — ทั้งหมดนี้โดยไม่สูญเสียบริบท รูปแบบหน่วยความจำเดียวกันนี้ใช้ได้ไม่ว่าคุณจะสร้าง API ด้วยการรวม Apidog หรือจัดการสปรินต์การพัฒนาหลายวัน

หน่วยความจำ MCP คืออะไร?

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

เครื่องมือหลัก 4 อย่างของ MCP:

เครื่องมือ วัตถุประสงค์ ตัวอย่าง
remember จัดเก็บข้อมูลพร้อมแท็ก บันทึก “users table with UUID, bcrypt”
recall ค้นหาด้วยคีย์เวิร์ดหรือแท็ก ค้นหา “auth decisions”
rollback คืนค่ากลับสู่สถานะก่อนหน้า ยกเลิกการเปลี่ยนแปลงสคีมาที่ไม่ดี
search ค้นหาข้ามเซสชัน “Backend Architect ตัดสินใจอะไรไปบ้าง?”
┌─────────────────┐         ┌──────────────────┐         ┌─────────────┐
│  AI Agent       │         │  MCP Memory      │         │  Storage    │
│  (Claude Code)  │◄───────►│  Server          │◄───────►│  (SQLite)   │
└─────────────────┘   JSON  └──────────────────┘  I/O    └─────────────┘
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 1: ตั้งค่าเซิร์ฟเวอร์หน่วยความจำ MCP

คุณต้องมีเซิร์ฟเวอร์ MCP ที่เปิดเผยเครื่องมือหน่วยความจำนี้ สามารถเลือกใช้ได้ทั้งแบบโฮสต์หรือแบบ local

วิธีที่ 1: ใช้เซิร์ฟเวอร์หน่วยความจำแบบติดตั้ง

npm install -g @example/mcp-memory-server
Enter fullscreen mode Exit fullscreen mode

วิธีที่ 2: สร้างเซิร์ฟเวอร์ภายในเครื่องอย่างง่าย

สร้างไฟล์ memory-server.js ดังนี้:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

const MEMORY_FILE = path.join(process.env.HOME, ".mcp-memory", "memories.json");

const server = new McpServer({
  name: "memory",
  version: "1.0.0"
});

async function initMemory() {
  await fs.mkdir(path.dirname(MEMORY_FILE), { recursive: true });
  try {
    await fs.access(MEMORY_FILE);
  } catch {
    await fs.writeFile(MEMORY_FILE, JSON.stringify([]));
  }
}

// Tool: remember
server.tool(
  "remember",
  {
    content: z.string().describe("Information to store"),
    tags: z.array(z.string()).describe("Tags for retrieval (e.g., ['backend', 'auth'])"),
    agent: z.string().optional().describe("Agent name for tagging")
  },
  async ({ content, tags, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const memory = {
      id: Date.now().toString(),
      content,
      tags,
      agent,
      timestamp: new Date().toISOString()
    };
    memories.push(memory);
    await fs.writeFile(MEMORY_FILE, JSON.stringify(memories, null, 2));
    return { content: [{ type: "text", text: `Stored memory with tags: ${tags.join(", ")}` }] };
  }
);

// Tool: recall
server.tool(
  "recall",
  {
    query: z.string().describe("Search query or tag to find"),
    agent: z.string().optional().describe("Filter by agent name")
  },
  async ({ query, agent }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories.filter(m => {
      const matchesQuery = m.content.toLowerCase().includes(query.toLowerCase()) ||
                          m.tags.some(t => t.toLowerCase().includes(query.toLowerCase()));
      const matchesAgent = !agent || m.agent === agent;
      return matchesQuery && matchesAgent;
    });
    return {
      content: [{
        type: "text",
        text: results.length === 0
          ? "No memories found"
          : results.map(m => `[${m.timestamp}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: search
server.tool(
  "search",
  {
    tags: z.array(z.string()).describe("Tags to search for"),
    limit: z.number().optional().default(10)
  },
  async ({ tags, limit }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const results = memories
      .filter(m => tags.some(t => m.tags.includes(t)))
      .slice(0, limit);
    return {
      content: [{
        type: "text",
        text: results.map(m => `[${m.agent || "unknown"}] ${m.content}`).join("\n\n")
      }]
    };
  }
);

// Tool: rollback
server.tool(
  "rollback",
  {
    agent: z.string().describe("Agent name to rollback"),
    timestamp: z.string().describe("Rollback to this timestamp")
  },
  async ({ agent, timestamp }) => {
    await initMemory();
    const memories = JSON.parse(await fs.readFile(MEMORY_FILE, "utf-8"));
    const rolledBack = memories.filter(m =>
      m.agent !== agent || new Date(m.timestamp) <= new Date(timestamp)
    );
    await fs.writeFile(MEMORY_FILE, JSON.stringify(rolledBack, null, 2));
    return {
      content: [{
        type: "text",
        text: `Rolled back ${agent} to ${timestamp}`
      }]
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);
Enter fullscreen mode Exit fullscreen mode

เรียกใช้เซิร์ฟเวอร์:

node memory-server.js
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 2: เพิ่มคำสั่งหน่วยความจำไปยังเอเจนต์ใดก็ได้

ไม่ต้องแก้ไขไฟล์เอเจนต์ ให้เพิ่มคำสั่งหน่วยความจำในพรอมต์ของคุณโดยตรง:

คุณสามารถเข้าถึงเครื่องมือหน่วยความจำ MCP ได้: remember, recall, search, rollback

ปฏิบัติตามโปรโตคอลหน่วยความจำเหล่านี้:

**เมื่อเริ่มเซสชัน:**
1. recall(query="ecommerce-api", agent="Backend Architect")
2. ตรวจสอบรายการที่รอดำเนินการจากเซสชันล่าสุด

**เมื่อทำงานเสร็จ:**
1. remember(content="Created users table with UUID primary key, bcrypt password hashing, JWT auth with refresh tokens", tags=["ecommerce-api", "database", "auth"], agent="Backend Architect")
2. ระบุการตัดสินใจที่ทำไปแล้วและรายการที่รอดำเนินการ

**เมื่อส่งต่องาน:**
1. remember(content="API endpoints: /auth/login, /auth/register, /products, /orders. Auth flow: JWT access token (15min) + refresh token (7 days). Known issue: rate limiting not yet implemented", tags=["ecommerce-api", "handoff"], agent="Backend Architect", for="Frontend Developer")

**เมื่อ rollback:**
1. ค้นหาสถานะที่ทราบว่าดีที่สุดล่าสุด
2. ใช้ rollback เพื่อกู้คืน
Enter fullscreen mode Exit fullscreen mode

ตัวอย่างการใช้งานจริง:

คุณ: "เปิดใช้งานโหมด Backend Architect ออกแบบระบบผู้ใช้สำหรับ e-commerce API ของเรา"

[เอเจนต์ออกแบบระบบ, สร้างสคีมา, implements auth]

เอเจนต์: "ระบบผู้ใช้เสร็จสมบูรณ์ กำลังจัดเก็บหน่วยความจำ..."
→ เรียกใช้: remember("users table with UUID, bcrypt, JWT + refresh tokens", tags: ["ecommerce-api", "auth", "database"])

[เซสชันสิ้นสุด]

[วันถัดไป]

คุณ: "ทำต่อจากเมื่อวาน"

เอเจนต์: "กำลังเรียกคืนบริบท..."
→ เรียกใช้: recall(query="ecommerce-api")
→ คืนค่า: "users table with UUID, bcrypt, JWT + refresh tokens"

เอเจนต์: "เมื่อวานฉันออกแบบตารางผู้ใช้ด้วย UUID primary keys, implements bcrypt password hashing, และตั้งค่า JWT authentication พร้อม refresh tokens ขั้นตอนต่อไป: สคีมาแคตตาล็อกสินค้า พร้อมทำงานต่อ"
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 3: กำหนดค่าสำหรับ Claude Code

เพิ่มเซิร์ฟเวอร์หน่วยความจำลงในการกำหนดค่า MCP ของคุณ

แก้ไข ~/.claude/settings.json:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"],
      "env": {
        "HOME": "/Users/your-username"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

รีสตาร์ท Claude Code แล้วเครื่องมือหน่วยความจำควรพร้อมใช้งาน

ทดสอบ:

ใช้เครื่องมือ remember เพื่อจัดเก็บ: "Test memory for ecommerce project"
แท็ก: ["test", "ecommerce-api"]
Enter fullscreen mode Exit fullscreen mode
ใช้เครื่องมือ recall เพื่อค้นหาหน่วยความจำเกี่ยวกับ "test"
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 4: กำหนดค่าสำหรับ Cursor

สร้าง .cursor/mcp.json ในโปรเจกต์ของคุณ:

{
  "mcpServers": {
    "memory": {
      "command": "node",
      "args": ["/absolute/path/to/memory-server.js"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

ทดสอบ:

@memory remember "Starting ecommerce API project with PostgreSQL"
แท็ก: ["ecommerce-api", "setup"]
Enter fullscreen mode Exit fullscreen mode
@memory recall query="ecommerce"
Enter fullscreen mode Exit fullscreen mode

รูปแบบหน่วยความจำสำหรับเวิร์กโฟลว์จริง

รูปแบบที่ 1: การบันทึกการตัดสินใจ

ทุกครั้งที่มีการตัดสินใจทางเทคนิค ให้บันทึกไว้:

remember({
  content: "Chose PostgreSQL over MySQL for: (1) JSONB support for flexible product attributes, (2) better full-text search, (3) UUID native support",
  tags: ["ecommerce-api", "database", "decision"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

ค้นหาย้อนหลัง:

recall(query="PostgreSQL MySQL decision")
Enter fullscreen mode Exit fullscreen mode

รูปแบบที่ 2: การส่งต่องานระหว่างเอเจนต์

ทิ้งบันทึกการส่งต่องาน:

remember({
  content: "Backend complete. Endpoints: POST /auth/login, POST /auth/register, GET /products, POST /orders. Auth: JWT 15min access + 7 day refresh. Pending: rate limiting, email verification. Frontend needs: login form, product list, cart, checkout.",
  tags: ["ecommerce-api", "handoff", "backend-complete"],
  agent: "Backend Architect",
  for: "Frontend Developer"
})
Enter fullscreen mode Exit fullscreen mode

Frontend Developer:

recall(query="handoff", agent="Backend Architect")
Enter fullscreen mode Exit fullscreen mode

รูปแบบที่ 3: จุดตรวจสอบเซสชัน

เมื่อสิ้นสุดเซสชัน:

remember({
  content: "Session complete. Done: users table, auth endpoints, product schema. Next session: order system, payment webhook. Blockers: waiting for Stripe API keys.",
  tags: ["ecommerce-api", "checkpoint", "session-1"],
  agent: "Backend Architect"
})
Enter fullscreen mode Exit fullscreen mode

เซสชันถัดไป:

recall(query="checkpoint session-1")
Enter fullscreen mode Exit fullscreen mode

รูปแบบที่ 4: การติดตามข้อผิดพลาด (Bug Tracking)

เมื่อพบข้อผิดพลาด:

remember({
  content: "BUG: Refresh token not expiring after logout. Token stored in memory, not persisted. Fix: move to Redis with TTL.",
  tags: ["ecommerce-api", "bug", "auth"],
  agent: "Code Reviewer",
  severity: "high"
})
Enter fullscreen mode Exit fullscreen mode

ค้นหาข้อผิดพลาด:

search(tags=["bug", "ecommerce-api"])
Enter fullscreen mode Exit fullscreen mode

การแก้ไขปัญหา

หน่วยความจำไม่คงอยู่:

  • ตรวจสอบพาธไฟล์หน่วยความจำ (~/.mcp-memory/memories.json)
  • ตรวจสอบว่าเซิร์ฟเวอร์ MCP กำลังทำงาน
  • ตรวจสอบว่า Claude Code หรือ Cursor มีการกำหนดค่า MCP

ผลลัพธ์ recall มากเกินไป:

  • เพิ่มแท็กที่เฉพาะเจาะจง
  • กรองด้วยชื่อเอเจนต์
  • ใช้ประโยคที่ตรงตัวในเครื่องหมายคำพูด

ไฟล์หน่วยความจำใหญ่ขึ้น:

  • เก็บถาวรหน่วยความจำเก่า
  • ใช้ rollback เพื่อล้างโปรเจกต์ที่เสร็จสมบูรณ์
  • เพิ่มวันหมดอายุใน schema

สิ่งที่คุณสร้าง

ส่วนประกอบ วัตถุประสงค์
MCP Memory Server จัดเก็บ/เรียกคืนข้อมูลข้ามเซสชัน
เครื่องมือ remember บันทึกการตัดสินใจ, สิ่งที่ส่งมอบได้, การส่งต่องาน
เครื่องมือ recall ค้นหาบริบทจากเซสชันก่อนหน้า
เครื่องมือ search สืบค้นด้วยแท็กจากหน่วยความจำทั้งหมด
เครื่องมือ rollback คืนค่ากลับสู่สถานะก่อนหน้าเมื่อจำเป็น
รูปแบบหน่วยความจำ การบันทึกการตัดสินใจ, การส่งต่องาน, จุดตรวจสอบ, bug tracking

ขั้นตอนต่อไป

ขยายเซิร์ฟเวอร์หน่วยความจำ:

  • เพิ่ม semantic search ด้วย embeddings
  • เพิ่มการหมดอายุอัตโนมัติหลัง 30 วัน
  • เพิ่มการสรุป/ย่อเซสชันอัตโนมัติ

สร้างหน่วยความจำทีม:

  • แชร์เซิร์ฟเวอร์หน่วยความจำกลางกับทีม
  • แท็กหน่วยความจำตามโปรเจกต์และนักพัฒนา
  • สร้าง onboarding สำหรับสมาชิกใหม่

ผสานรวมกับเครื่องมือ:

  • บันทึก git commits อัตโนมัติเป็นหน่วยความจำ
  • ซิงค์กับ Jira, Linear
  • ส่งออกหน่วยความจำไปยังเอกสาร

การแก้ไขปัญหาทั่วไป

หน่วยความจำไม่คงอยู่ระหว่างเซสชัน:

  • ตรวจสอบว่าเซิร์ฟเวอร์ MCP ทำงานก่อนเปิด Claude Code
  • ตรวจสอบไฟล์: ls -la ~/.mcp-memory/memories.json
  • ตรวจสอบ permission: chmod 644 ~/.mcp-memory/memories.json
  • พาธใน ~/.claude/settings.json ต้องถูกต้อง

recall ส่งคืนว่างเปล่า:

  • ตรวจสอบว่าคำค้นหาตรงกับแท็ก (case sensitive)
  • ลองใช้คำค้นหาที่กว้างขึ้น หรือ search พร้อมแท็กเฉพาะ
  • ตรวจสอบหน่วยความจำถูกจัดเก็บจริงหรือไม่: cat ~/.mcp-memory/memories.json
  • ตรวจสอบว่าตัวกรอง agent ตรงกัน

ไฟล์หน่วยความจำใหญ่เกินไป:

  • ใช้การเก็บถาวรอัตโนมัติสำหรับข้อมูลเก่า
  • เพิ่มเครื่องมือ prune สำหรับลบข้อมูลตามช่วงวันที่
  • แยกไฟล์ตามโปรเจกต์/วันที่
  • เปลี่ยนไปใช้ SQLite สำหรับข้อมูลขนาดใหญ่

เซิร์ฟเวอร์ไม่เริ่มต้น:

  • ตรวจสอบ Node.js version: node --version (ควร 18+)
  • ติดตั้ง dependencies: npm install @modelcontextprotocol/sdk zod
  • ตรวจสอบโค้ด syntax
  • เรียกใช้ตรง ๆ เพื่อตรวจสอบ error: node memory-server.js

เอเจนต์หลายตัวเขียนทับกัน:

  • ใส่ฟิลด์ agent เสมอเมื่อ remember
  • ใช้แท็กเฉพาะแต่ละโปรเจกต์ เช่น ["project-x", "backend", "auth"]
  • recall ด้วย agent filter
  • แยกไฟล์หน่วยความจำแต่ละโปรเจกต์

ข้อควรพิจารณาด้านความปลอดภัยของเซิร์ฟเวอร์หน่วยความจำ

การจัดเก็บ API Key:

ถ้าหน่วยความจำมีข้อมูลละเอียดอ่อน ให้เข้ารหัสข้อมูล:

import crypto from 'crypto';

const ENCRYPTION_KEY = process.env.MEMORY_ENCRYPTION_KEY;
const ALGORITHM = 'aes-256-gcm';

function encrypt(text) {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY), iv);
  const encrypted = cipher.update(text, 'utf8', 'hex');
  return {
    encryptedData: encrypted + cipher.final('hex'),
    iv: iv.toString('hex'),
    authTag: cipher.getAuthTag().toString('hex')
  };
}

function decrypt(encrypted) {
  const decipher = crypto.createDecipheriv(
    ALGORITHM,
    Buffer.from(ENCRYPTION_KEY),
    Buffer.from(encrypted.iv, 'hex')
  );
  decipher.setAuthTag(Buffer.from(encrypted.authTag, 'hex'));
  return decipher.update(encrypted.encryptedData, 'hex', 'utf8') + decipher.final('utf8');
}
Enter fullscreen mode Exit fullscreen mode

การควบคุมการเข้าถึง:

  • ใช้ API key สำหรับการเรียกเครื่องมือ
  • ใช้ namespace เฉพาะผู้ใช้
  • log ทุก action เพื่อ audit
  • จำกัดอัตราการร้องขอ per user

ตอนนี้เอเจนต์ AI ของคุณมีหน่วยความจำถาวรแล้ว

พวกเขาจดจำเรื่องเมื่อวาน พวกเขาเรียกคืนการตัดสินใจ พวกเขาส่งต่อบริบทโดยไม่ต้องคัดลอกวาง

หมดปัญหา “ฉันไม่มีบริบทจากเซสชันก่อนหน้า” หรือเสียเวลาซ้ำซ้อน

นี่คือพลังของ MCP Memory — สมุดบันทึกที่ใช้ร่วมกันสำหรับเอเจนต์ของคุณสำหรับโปรเจกต์ที่ต่อเนื่องหลายวัน

คำถามที่พบบ่อย

หน่วยความจำ MCP คืออะไร?

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

ตั้งค่าหน่วยความจำถาวรใน Claude Code อย่างไร?

ติดตั้ง MCP memory server แล้วเพิ่ม config ใน ~/.claude/settings.json จากนั้นรีสตาร์ท Claude Code

เอเจนต์ AI ไหนใช้ MCP memory ได้บ้าง?

Claude Code, Cursor, Windsurf — หรือเอเจนต์ใดก็ได้ที่รองรับ MCP client

รูปแบบที่ดีที่สุดสำหรับ handoff คือ?

ใช้ remember กับแท็ก ["handoff", "project-name"] และให้รายละเอียดงานที่เสร็จ รายการที่เหลือ เอเจนต์ถัดไป recall ด้วย recall(query="handoff")

MCP server เก็บหน่วยความจำได้เยอะแค่ไหน?

ขึ้นกับการใช้งาน เซิร์ฟเวอร์อ้างอิงใช้ไฟล์ JSON ขนาดไม่จำกัด แต่แนะนำจัดการ retention หรือใช้ database สำหรับงานใหญ่

ทีมแชร์ memory server ได้ไหม?

ได้ ตั้ง server กลาง เชื่อมต่อไคลเอนต์ทีมทั้งหมด และแท็กข้อมูลตามโปรเจกต์/นักพัฒนา

ถ้า recall ได้ผลลัพธ์เยอะเกินไป?

เพิ่มแท็กเฉพาะ, กรองด้วย agent, ใช้ประโยคตรงตัว หรือเพิ่ม semantic search ด้วย embeddings

Top comments (0)