Xây dựng Agentic App với Mastra, CopilotKit và MCP: Góc nhìn từ thực chiến
Hành trình từ một fullstack developer chuyển sang xây dựng hệ thống AI agentic — những gì Mastra, CopilotKit và MCP thực sự giải quyết, và những bẫy bạn cần tránh.
Nghe bài viết này dưới dạng podcastXây dựng Agentic App với Mastra, CopilotKit và MCP: Góc nhìn từ thực chiến
Khi tôi bắt đầu tích hợp AI vào một nền tảng EdTech, tôi nghĩ đây chỉ là "gọi API OpenAI rồi hiển thị kết quả". Sau vài tháng, tôi nhận ra mình đang xây dựng một hệ thống multi-agent với HITL, AG-UI protocol và MCP server. Đây là những gì tôi học được.
Agentic AI là gì và tại sao nó khác với "chỉ gọi LLM"?

Một ứng dụng thông thường gọi LLM theo kiểu request → response: bạn gửi prompt, model trả về text, xong. Đơn giản, dễ debug, dễ test.
Agentic AI hoạt động khác hoàn toàn. Agent có khả năng:
- Lập kế hoạch (planning): chia mục tiêu lớn thành các bước nhỏ
- Sử dụng tool: gọi API, truy vấn database, chạy code
- Phản hồi theo ngữ cảnh: quyết định bước tiếp theo dựa trên kết quả trước
- Tương tác với con người (HITL — Human in the Loop): dừng lại và hỏi khi cần
Sự khác biệt nghe có vẻ nhỏ, nhưng về mặt engineering nó tạo ra một loại complexity hoàn toàn mới: state management, error recovery, observability của một hệ thống phi tuyến tính.
Model Context Protocol (MCP): Ngôn ngữ chung cho AI Tools

Trước khi có MCP, mỗi agent framework đều tự định nghĩa cách tích hợp tool theo cách riêng. Anthropic giới thiệu Model Context Protocol để chuẩn hóa giao tiếp giữa LLM và các external service.
MCP hoạt động như thế nào?
MCP định nghĩa một giao thức client-server đơn giản:
- MCP Server: expose các
tools,resources,promptstheo chuẩn - MCP Client (thường là LLM host): gọi đến server khi model cần dùng tool
- Transport layer: stdio (local) hoặc SSE (remote)
// Ví dụ một MCP Tool đơn giản với FastMCP (Python)
@mcp.tool()
async def search_student_records(student_id: str) -> dict:
"""Search for student learning records by ID"""
records = await db.query(
"SELECT * FROM learning_sessions WHERE student_id = $1",
student_id
)
return {"records": records, "count": len(records)}
Điều quan trọng là LLM tự quyết định khi nào cần gọi tool dựa trên tool description. Bạn không hardcode logic gọi tool nữa — bạn mô tả tool và để model tự suy luận.
MCP và chi phí token
Một điều tôi học được qua thực tế: MCP server là nguồn tốn token ẩn lớn nhất. Khi bạn mount nhiều MCP server, toàn bộ tool definitions được inject vào context của mỗi request. 20 tools × 500 tokens/tool = 10,000 tokens overhead mỗi lần gọi.
Giải pháp: chỉ enable MCP server khi thực sự cần, và tối ưu description của từng tool thật súc tích.
Mastra: Framework Agent cho TypeScript

Nếu bạn là TypeScript developer và muốn build agent, Mastra là lựa chọn đáng xem xét nhất hiện tại.
Điểm mạnh của Mastra
1. Phân biệt rõ Workflow và Agent
Đây là điểm thiết kế tôi đánh giá cao nhất. Mastra có hai primitive riêng biệt:
- Workflow: deterministic, step-by-step, dễ test và debug. Dùng cho luồng có thể dự đoán trước.
- Agent: adaptive, có thể gọi tool và tự quyết định. Dùng khi cần flexibility.
// Mastra Workflow — deterministic pipeline
const ocrWorkflow = createWorkflow({
name: 'ocr-pipeline',
steps: [extractPages, runOCR, validateOutput, saveToDatabase]
});
// Mastra Agent — adaptive
const tutorAgent = new Agent({
name: 'TutorAgent',
model: openai('gpt-4o'),
tools: { searchKnowledge, generateQuiz, checkAnswer },
instructions: `Bạn là gia sư thông minh. Dựa vào điểm yếu của học sinh,
hãy tạo bài tập phù hợp và giải thích từng bước.`
});
2. Tích hợp native với MCP
Mastra cho phép mount MCP server trực tiếp như tool source:
const agent = new Agent({
tools: await mcp.getTools(), // tools từ MCP server
});
3. Memory và RAG tích hợp sẵn
Mastra có built-in support cho vector store (pgvector, Pinecone) để agent có thể recall context từ các session trước. Không cần tự implement retrieval pipeline từ đầu.
Hạn chế cần biết
- Ecosystem còn non trẻ, API thay đổi giữa các minor version
- Documentation tốt nhưng đôi khi chậm cập nhật so với code
- Chưa có production-grade observability out of the box — bạn cần tự setup tracing
CopilotKit: Đưa Agent vào UI
Mastra xử lý logic agent ở backend. Nhưng làm sao để UI phản ứng theo real-time với những gì agent đang làm? Đây là bài toán CopilotKit giải quyết.
AG-UI Protocol
CopilotKit định nghĩa AG-UI (Agent-UI) Protocol — một chuẩn stream events từ agent xuống frontend:
Agent Backend ──stream──▶ AG-UI Events ──▶ React Components
Các event types chính:
TEXT_MESSAGE_CHUNK: agent đang "nói" gìTOOL_CALL_START / TOOL_CALL_END: agent đang dùng tool nàoSTATE_SNAPSHOT: trạng thái hiện tại của agentSTEP_STARTED: bước mới bắt đầu
HITL — Human in the Loop
Đây là tính năng tôi dùng nhiều nhất trong dự án EdTech. Agent tạo quiz, nhưng giáo viên cần review trước khi gửi cho học sinh:
// Backend: Agent tạm dừng và chờ approval
const quizResult = await agent.run(prompt);
// Emit HITL event
yield {
type: 'INTERRUPT',
data: { quizDraft: quizResult, requiresApproval: true }
};
// Frontend: Hiện modal cho giáo viên review
function QuizReviewModal({ draft, onApprove, onReject }) {
return (
<div>
<QuizPreview data={draft} />
<button onClick={onApprove}>✅ Phê duyệt</button>
<button onClick={onReject}>❌ Chỉnh sửa</button>
</div>
);
}
Pattern này cực kỳ powerful: AI làm việc nặng, con người kiểm soát điểm quyết định.
Bức tranh toàn cảnh: Khi ba thứ kết hợp
┌─────────────────────────────────────────┐
│ Frontend (Next.js) │
│ CopilotKit hooks ◄──── AG-UI Stream │
│ Real-time UI updates │
└────────────────┬────────────────────────┘
│ HTTP / WebSocket
┌────────────────▼────────────────────────┐
│ Backend (NestJS) │
│ Mastra Agent / Workflow Engine │
│ ├── MCP Tools (OCR, Search, DB...) │
│ ├── pgvector RAG │
│ └── HITL Gate │
└─────────────────────────────────────────┘
MCP chuẩn hóa cách agent dùng tool.
Mastra orchestrate logic agent và workflow.
CopilotKit đưa trạng thái agent lên UI real-time.
Ba tầng này tách biệt concern rõ ràng và có thể thay thế độc lập — đây là kiến trúc tôi recommend cho bất kỳ agentic app nào nghiêm túc.
Những bẫy phổ biến cần tránh
1. Over-agentify mọi thứ
Không phải mọi feature đều cần agent. Nếu logic có thể dự đoán 100%, dùng Workflow hoặc thậm chí function thông thường. Agent giỏi ở sự linh hoạt, nhưng đi kèm với unpredictability và chi phí.
2. Thiếu observability từ đầu
Agent failure rất khó debug nếu bạn không có trace. Setup logging cho từng tool call, từng step từ ngày đầu. OpenTelemetry + một dashboard đơn giản sẽ cứu bạn rất nhiều lần.
3. Context window là tài nguyên hữu hạn
Mỗi tool definition, mỗi message history đều tốn token. Thiết kế agent với context budget trong đầu: tool descriptions ngắn gọn, conversation history có TTL, và memory summarization khi cần.
4. Bỏ qua testing
Agent là non-deterministic nhưng vẫn cần test được. Dùng mock LLM responses để test tool routing logic, và định nghĩa "acceptable output range" thay vì exact match.
Kết luận
Agentic development đang thay đổi cách chúng ta nghĩ về software architecture. Thay vì code mọi luồng logic, chúng ta đang thiết kế hệ thống mà AI có thể tự điều hướng.
Mastra, CopilotKit và MCP không phải magic — chúng là infrastructure tốt giúp bạn tập trung vào product logic thay vì plumbing. Nhưng như mọi abstraction, bạn cần hiểu layer bên dưới để debug khi mọi thứ đi sai.
Nếu bạn đang bắt đầu hành trình agentic, lời khuyên của tôi là: bắt đầu nhỏ, đo lường kỹ, và thêm complexity chỉ khi thực sự cần.
Bạn đang xây dựng agentic app? Để lại comment, tôi rất muốn nghe về kiến trúc và stack bạn đang dùng.
Chưa có bình luận
Để lại bình luận
Bình luận sẽ được phê duyệt trước khi hiển thị.