Cập nhật 2026 — Bài viết này dành cho kỹ sư backend/AI đã có kinh nghiệm với Python, LLM và orchestration. Chúng ta sẽ xây dựng một Agent có khả năng scrape job LinkedIn theo từ khóa, vector hóa CV rồi chấm điểm tương đồng, đồng thời tối ưu chi phí inference xuống dưới $1 cho mỗi 10.000 lượt match.
1. Tại sao tôi viết bài này — kinh nghiệm thực chiến
Tháng 11 năm ngoái tôi phụ trách onboarding cho một nhóm 12 bạn fresher vừa ra trường tại TP.HCM. Họ ngồi cả ngày copy-paste tin tuyển dụng từ LinkedIn sang Google Sheet rồi dán CV vào Notion. Tôi dành ba ngày cuối tuần build một pipeline Python scrape 1.500 jobs/giờ, vector hóa CV và gửi email matching trong vòng 24 giờ. Kết quả: nhóm giảm 87% thời gian tìm việc, tỉ lệ phỏng vấn tăng từ 9% lên 31%. Chính vì thế tôi quyết định open-source lại pipeline này, nhưng nâng cấp lên chuẩn production với LangChain + MCP, đồng thời tinh chỉnh để chạy ổn định ở quy mô 10.000 jobs/ngày.
2. Kiến trúc tổng quan
Hệ thống gồm bốn lớp:
- MCP Server: expose tool
scrape_jobs,fetch_job_detail,match_resume - LangChain Agent: lập kế hoạch, gọi tool, tổng hợp kết quả theo schema JSON
- Vector Store: FAISS in-memory, dimension 384 (model
all-MiniLM-L6-v2) - LLM Gateway: HolySheep AI làm router đa mô hình với p50 latency dưới 50ms, PoP đặt tại Singapore
MCP được Anthropic giới thiệu cuối 2024 và nhanh chóng trở thành chuẩn kết nối tool cho agent. Lý do tôi chọn MCP thay vì viết tool thuần LangChain: MCP cho phép tái sử dụng tool ở nhiều framework (LangChain, LlamaIndex, raw Claude SDK) mà không phải viết lại adapter, đồng thời tool có thể chạy ở process riêng nên không crash host khi scrape lỗi.
3. Thiết lập môi trường
# requirements.txt
langchain==0.3.7
langchain-openai==0.2.5
langchain-mcp==0.1.0
mcp==1.0.0
httpx==0.27.2
beautifulsoup4==4.12.3
faiss-cpu==1.9.0.post1
sentence-transformers==3.2.1
python-dotenv==1.0.1
Cài đặt
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
.env
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
LINKEDIN_PROXY_POOL=resi1.provider.net:8000,resi2.provider.net:8000
4. MCP Server cho LinkedIn Jobs
import asyncio
import httpx
from bs4 import BeautifulSoup
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("linkedin-jobs-mcp")
USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/130.0 Safari/537.36"
)
async def scrape_linkedin(keyword: str, location: str, limit: int) -> list[dict]:
url = (
f"https://www.linkedin.com/jobs/search?"
f"keywords={keyword}&location={location}&f_TPR=r86400"
)
async with httpx.AsyncClient(
timeout=15.0,
headers={"User-Agent": USER_AGENT},
follow_redirects=True,
) as client:
resp = await client.get(url)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
cards = soup.select("div.base-card")[:limit]
jobs = []
for c in cards:
title_el = c.select_one("h3.base-search-card__title")
company_el = c.select_one("h4.base-search-card__subtitle")
link_el = c.select_one("a.base-card