MCPTest
An MCP server that provides AI agents with database tools for querying customer profiles and financial audit data, featuring semantic caching and automatic cache invalidation via webhooks to optimize performance and token usage.
README
สรุประบบ: (MCP + AI Agent)
1. ระบบนี้ทำอะไร
ระบบ MCP ที่คอยคุมการทำงานของ AI เป็นผู้ช่วยตอบคำถามเรื่องข้อมูล
- ผู้ใช้ถาม $\rightarrow$ Agent เลือก tool $\rightarrow$ tool เช็ก cache ก่อน ไม่มีค่อยไป DB จริง $\rightarrow$ ถ้า DB เปลี่ยน ระบบแจ้งเตือนกลับมาล้าง cache ทันที
2. องค์ประกอบหลัก
| ไฟล์ | หน้าที่ |
|---|---|
main.py |
AI Agent — รับคำถาม, ตัดสินใจเรียก tool ผ่าน Gemini |
server.py |
MCP Server — เปิด tools ให้ Agent เรียกใช้ฐานข้อมูล |
semantic_cache.py |
Cache ชั้น 1 — เก็บคำถาม-คำตอบเดิมแบบ embedding |
webhook_cdc.py |
รับแจ้งเตือนจาก Supabase เมื่อข้อมูลเปลี่ยน แล้วล้าง cache |
schema.sql |
โครงสร้าง DB (customers, purchase_ledger, customer_interactions) |
3. ในฐานข้อมูล (DB) เก็บอะไรไว้บ้าง และป้องกันยังไง
DB (PostgreSQL บน Supabase) มี 3 ตารางหลัก:
| ตาราง | เก็บอะไร | การป้องกัน / กลไก |
|---|---|---|
customers |
ข้อมูลโปรไฟล์ลูกค้า (ชื่อ, ข้อมูลติดต่อ ฯลฯ) | แก้ไขได้ปกติ แต่ทุกครั้งที่แก้ trigger จะยิง webhook แจ้งล้าง cache |
purchase_ledger |
ประวัติธุรกรรม/ยอดการเงินของลูกค้า | Append-only — ห้าม UPDATE/DELETE เด็ดขาด ผ่าน DB Trigger ที่บล็อกไว้ตรงๆ ที่ระดับฐานข้อมูล (ต่อให้เจาะเข้ามาตรงๆ ก็แก้ไม่ได้) |
customer_interactions |
ประวัติการพูดคุย/โต้ตอบกับลูกค้า พร้อมเก็บ embedding vector (ใช้ extension pgvector) |
ใช้สำหรับ semantic search ย้อนดูบทสนทนาเก่าที่เกี่ยวข้อง |
Hash Chain (สำหรับ purchase_ledger โดยเฉพาะ):
- แต่ละ record ในตารางนี้จะมีค่า
entry_hashที่คำนวณจากข้อมูลของ record นั้น +prev_hash(hash ของ record ก่อนหน้า) ด้วย SHA-256 - ทำให้ record ทั้งหมดเชื่อมกันเป็น "โซ่" ถ้ามีใครแอบไปแก้ข้อมูลตรงกลางโซ่ (แม้จะพยายามเลี่ยง trigger ก็ตาม) hash ของ record ถัดไปจะไม่ตรงกันทันที ตรวจจับได้ตอน audit
- พูดง่ายๆ: ต่อให้ป้องกันด้วย trigger แล้ว hash chain คือ "เช็คซ้ำอีกชั้น" ว่าข้อมูลไม่ถูกแตะต้องจริงๆ
PROMPT ของ AI
SYSTEM_PROMPT = """
You are an Enterprise CRM & Financial Audit Ledger AI Assistant.
You have access to trusted backend tools that provide customer profiles, live financial transactions, audit logs, and historical interaction text search.
...
"""
4. Workflow
[1] ผู้ใช้พิมพ์คำถามเข้า main.py
│
▼
[2] ระบบแปลงคำถามเป็น embedding (ด้วย Sentence-Transformers, รันในเครื่อง ไม่เสีย token)
│
▼
[3] เช็ค Layer 1 – Semantic Cache ใน Redis
ค้นด้วย key: semantic_cache:customer:{customer_id}:*
เทียบ embedding คำถามใหม่ กับคำถามเก่าที่เคยถามและตอบไปแล้ว (cosine similarity)
│
├───────────────────────────────┐
▼ (similarity ≥ 0.92) ▼ ไม่เจอ / ไม่คล้ายพอ (MISS)
[4a] ส่งคำตอบเดิมกลับทันที [4b] ส่งคำถาม + รายชื่อ tool ที่มีให้ Gemini
จบ flow ตรงนี้ (ไม่เรียก DB, ไม่เรียก DB cache เลย)
│
▼
[5] Gemini ตัดสินใจว่าจะเรียก MCP tool ไหน
(เช่น get_customer_profile, get_customer_financial_summary)
│
▼
[6] Tool ทำงานผ่าน server.py
เช็ค Layer 2 – DB Result Cache ใน Redis ก่อน
key: cache:db:profile:{customer_id}
หรือ cache:db:financial:{customer_id}:{page}:{limit}
│
┌─────┴──────┐
▼ HIT ▼ MISS
[7a] ดึงข้อมูล [7b] Query PostgreSQL จริง
จาก Redis แล้วเก็บผลลัพธ์ลง Redis
(เร็ว ไม่กิน DB) (ตั้ง TTL 24 ชม.)
│ │
└─────┬──────┘
▼
[8] ได้ข้อมูลดิบกลับมาที่ Agent
│
▼
[9] เรียก Gemini อีกครั้ง เพื่อเรียบเรียงข้อมูลดิบ
ให้เป็นคำตอบภาษาคนอ่านง่าย
│
▼
[10] ตอบกลับผู้ใช้
+ บันทึกคำถาม-คำตอบคู่นี้ลง Layer 1 cache
(ไว้ให้คำถามคล้ายๆ กันครั้งหน้า hit ได้)
- Layer 1 เช็คจาก ความหมายของคำถาม
- Layer 2 เช็คจาก ผลลัพธ์ของ DB query ที่เคยทำมาก่อน — เป็นด่านที่สองที่กัน DB ไม่ให้โดนถามซ้ำ
- ทั้งสอง layer แยกกันทำงานคนละหน้าที่ ไม่ใช่ตัวเดียวกัน
5. Webhook + Backend สำหรับล้าง Cache (CDC) — Workflow ละเอียด
[1] มีการเปลี่ยนแปลงข้อมูลจริงในตาราง customers หรือ purchase_ledger
(เช่น เพิ่มธุรกรรมใหม่ / แก้โปรไฟล์ลูกค้า)
│
▼
[2] PostgreSQL Trigger บนตารางนั้นทำงานทันที (ก่อน trigger นี้จะเรียกฟังก์ชัน notify_cache_invalidation() ที่เขียนไว้)
│
▼
[3] ฟังก์ชันเรียก net.http_post(...) (มาจาก extension pg_net) ยิง HTTP POST ออกไปหา backend ภายนอก
│
▼
[4] Backend ที่ deploy แยกไว้บน Render (ไฟล์ webhook_cdc.py, เขียนด้วย FastAPI) รับ request ที่ endpoint POST /api/webhook/db-change
│
▼
[5] Backend ตรวจสอบ header x-supabase-signature เทียบกับ secret ที่ตั้งไว้
→ ถ้าไม่ตรง ปฏิเสธทันที (กัน webhook ปลอมยิงเข้ามาสั่งล้าง cache มั่ว)
│
▼
[6] แกะ payload ดูว่า:
- TG_TABLE_NAME = ตารางไหนที่เปลี่ยน (customers หรือ purchase_ledger)
- ถ้า table = customers → เอา customer_id จาก record.id
- ถ้า table = purchase_ledger → เอา customer_id จาก record.customer_id
│
▼
[7] สั่งลบ key ใน Redis:
- ลบ Layer 1 เสมอ: semantic_cache:customer:{customer_id}:*
(เพราะคำตอบเก่าที่เคย cache ไว้อาจอ้างอิงข้อมูลลูกค้ารายนี้ที่เปลี่ยนไปแล้ว)
- ลบ Layer 2 เฉพาะส่วนที่เกี่ยวกับตารางที่เปลี่ยน:
cache:db:profile:{id} หรือ cache:db:financial:{id}:*
│
▼
[8] ครั้งต่อไปที่มีคนถามถึงลูกค้ารายนี้ → Layer 1 และ Layer 2 จะ miss โดยอัตโนมัติ
→ ระบบไปดึงข้อมูลจาก DB จริง → ได้ข้อมูลล่าสุดเสมอ (Zero-Stale Data)
6. ทำไมเอา Redis มาใช้ (2-Layer Cache)
ปัญหาที่เจอ: ทุกครั้งที่ผู้ใช้ถามคำถาม ถ้าปล่อยให้ระบบวิ่งไปเรียก LLM หรือ query DB ใหม่ทุกครั้ง จะช้าและสิ้นเปลือง โดยเฉพาะกับคำถามที่ถามซ้ำๆ หรือคล้ายเดิม เลยเพิ่ม Redis เข้ามาเป็น cache 2 ชั้น:
- ชั้น 1 (Semantic Cache) — คำถามคล้ายเดิม ตอบกลับเลยไม่ต้องเรียก LLM ใหม่
- ชั้น 2 (DB Result Cache) — ผลลัพธ์จาก DB ที่ไม่ค่อยเปลี่ยน ดึงจาก Redis แทนที่จะยิง query เข้า PostgreSQL ใหม่ทุกครั้ง
7. ปัญหาการใช้ Token (LLM) และวิธีลด
ปัญหา: การเรียก Gemini ทุกครั้งที่มีคำถามเข้ามา ทำให้เสีย token และมี latency สูง โดยเฉพาะเวลาที่มีคนถามคำถามซ้ำๆ หรือคล้ายกัน วิธีที่แก้:
- Semantic Cache (Layer 1) — เช็คก่อนเรียก Gemini ทุกครั้งว่าเคยมีคนถามคำถามที่ "ความหมายคล้ายกัน" ไหม ถ้าคล้าย $\ge$ 0.92 $\rightarrow$ ข้ามการเรียก Gemini ไปเลย
- ใช้ Local Embeddings (Sentence-Transformers) แทนการยิง embedding ผ่าน API ของ Gemini/OpenAI — ขั้นตอนเช็คความคล้ายนี้เลยไม่เสีย token เพิ่มเลย เพราะรันฟรีแบบ offline
- แยก cache ตาม
customer_id(multi-tenant) กันคำตอบของลูกค้ารายหนึ่งไปปนกับอีกราย
[!IMPORTANT] ข้อควรรู้: Layer 2 (DB cache) ช่วยลดแค่ฝั่ง DB load/latency เท่านั้น ไม่ได้ลด token ฝั่ง Gemini เพราะไม่ว่า Layer 2 จะ hit หรือ miss ตัว Agent ก็ยังต้องเรียก Gemini อย่างน้อย 2 ครั้งอยู่ดี (เลือก tool + สรุปคำตอบ) — ตัวเดียวที่ลด token จริงคือ Layer 1
8. ภาพรวมทั้งระบบ (End-to-End)
- ผู้ใช้ถามผ่าน
main.py - เช็ค Semantic Cache ก่อน — คล้ายพอก็ตอบเลย ไม่เรียก Gemini
- ถ้าไม่เจอ $\rightarrow$ Gemini เลือก tool ที่เหมาะสม
- Tool เช็ค DB Cache ก่อนค่อยไป PostgreSQL จริง
- ตอบกลับ + จำคำตอบไว้ใน cache
- ถ้า DB เปลี่ยนเมื่อไหร่ $\rightarrow$ Webhook ยิงมาล้าง cache อัตโนมัติทันที
NOTE
python3 -m venv venv
source venv/bin/activate # (สำหรับ Windows ให้ใช้: venv\Scripts\activate)
pip install -r requirements.txt
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。