Agent research: đừng chỉ nối tool với model
Một playbook mổ xẻ agent tìm kiếm web local: đáng tin không nằm ở model mới, mà ở orchestration, guardrail và log đủ rõ.
Bụi Wire“Em đã nối web search với Qwen rồi, sao kết quả vẫn như karaoke lệch nhịp?” — một bạn dev hỏi mình vậy sau khi demo agent research nội bộ. Terminal chạy đẹp, query ra kết quả, model tóm tắt có vẻ trơn. Nhưng hỏi ngược lại nguồn nào đáng tin, trang nào bị bỏ qua, vì sao kết luận A chứ không phải B, thì cả team im như dàn nhạc mất nhạc trưởng.
Agent research cá nhân chạy local với Ollama, Qwen và Python là một demo rất đáng làm. Nó cho bạn cảm giác kiểm soát: query không bay qua hosted chat, không tính phí theo lượt gọi model, có thể chạy trên máy mình. Nhưng nếu bạn đang build cho production, câu hỏi không phải là “có chạy được không?”. Câu hỏi đúng hơn là: khi nó sai, bạn biết sai ở lớp nào không?

Sơ đồ tóm tắt ý chính của bài viết.
Mục tiêu: biến demo agent thành hệ thống có nhịp
Một agent tìm kiếm web thường có flow nhìn rất gọn:
- Nhận chủ đề từ người dùng.
- Gọi web search để lấy kết quả mới.
- Fetch vài trang liên quan.
- Đưa nội dung vào local LLM để tóm tắt.
- Trả về digest ngắn gọn.
Ở mức tutorial, flow này ổn. Ở mức team builder, nó thiếu vài lớp sống còn: tiêu chí chọn nguồn, giới hạn context, log quyết định, kiểm tra trích dẫn, và cách xử lý khi tool trả về rác.
Orchestration là cách điều phối nhiều bước, nhiều tool hoặc nhiều agent để hoàn thành việc. Với agent research, orchestration không chỉ là gọi hàm theo thứ tự. Nó giống người chỉ huy dàn nhạc: không tạo ra âm thanh thay từng nhạc công, nhưng quyết định ai vào trước, ai im lại, và đoạn nào phải đánh lại vì lệch nhịp.
Sau bài này, mình muốn bạn đổi một cách nghĩ: đừng đánh giá agent research bằng câu trả lời cuối cùng; hãy đánh giá bằng khả năng truy vết từng lớp tạo ra câu trả lời đó.
Checklist trước khi cho agent đọc web thật
Trước khi viết thêm prompt, hãy kiểm tra 6 câu này:
- Intent rõ chưa? Người dùng muốn overview, so sánh, fact-check, hay tìm lead hành động?
- Search query có được ghi lại không? Nếu query tệ, output tệ là chuyện bình thường.
- Retrieval — bước lấy dữ liệu liên quan — có tiêu chí chọn/bỏ nguồn không?
- Context window — vùng ngữ cảnh model còn giữ được — có bị nhồi quá đầy không?
- Guardrail — rào chắn hành vi — có yêu cầu model nói “không đủ bằng chứng” khi nguồn yếu không?
- Trace — nhật ký từng bước — có đủ để debug sau một lần chạy lỗi không?
Ví dụ cụ thể: giả sử team bạn 5 người đang làm agent theo dõi tin tuyển dụng AI ở Việt Nam. Nếu agent chỉ trả về “có nhiều vị trí AI Engineer mới” mà không lưu query, URL, ngày fetch, lý do loại bỏ job cũ, thì báo cáo đó khó dùng. Nó có thể đúng hôm nay, sai ngày mai, và không ai biết sai vì đâu.
Mổ theo lớp: 5 tầng nên giữ, 2 tầng nên hoài nghi
Thay vì nhìn agent như một khối đen, mình hay tách thành 5 tầng vận hành.
1. Tầng intent: đừng để prompt gánh hết việc
User nhập: latest AI agent news.
Nghe đơn giản, nhưng intent có thể là:
- tóm tắt tin chính trong ngày;
- tìm release đáng chú ý cho builder;
- lọc tin có ảnh hưởng tới sản phẩm;
- so sánh quan điểm giữa nhiều nguồn.
Nếu không phân loại intent, agent sẽ tóm tắt kiểu chung chung. Với builder, nên có bước classify_task trước khi search:
Task type: daily_digest | technical_compare | vendor_watch | implementation_research
Output contract: bullets | decision memo | table | citations
Freshness requirement: today | this week | no constraint
Đây không phải thêm ceremony cho vui. Đây là cách bạn giữ nhịp phách cho cả flow.
2. Tầng retrieval: search không phải bằng chứng
Web search chỉ đưa ứng viên nguồn, chưa phải sự thật. Tool calling — khả năng để model gọi công cụ/API thay vì chỉ trả lời chữ — giúp agent lấy dữ liệu mới, nhưng không tự động làm dữ liệu đó đáng tin.
Một retrieval tốt nên log:
- query đã dùng;
- top kết quả;
- URL được chọn;
- URL bị bỏ;
- lý do chọn/bỏ;
- timestamp fetch.
Nếu nguồn có nội dung trùng lặp, trang SEO mỏng, hoặc bài cũ được cập nhật tiêu đề mới, agent phải có cơ chế hạ điểm. Không cần phức tạp ngay từ đầu; chỉ cần rule rõ là đã hơn rất nhiều demo “search rồi nhét hết vào model”.
3. Tầng đọc trang: HTML là bản nhạc đầy tạp âm
Fetch page xong chưa chắc dùng được. Nội dung có menu, quảng cáo, related posts, cookie banner, comment, footer. Nếu bạn đưa toàn bộ vào model, hallucination — bịa nhưng nói tự tin — có thêm đất diễn.
Một bước làm sạch tối thiểu nên có:
def clean_page(raw_html):
text = extract_main_content(raw_html)
text = remove_boilerplate(text)
text = normalize_whitespace(text)
return text[:MAX_CHARS_PER_SOURCE]
Đừng copy snippet này như code production; hãy xem nó như contract. Bạn cần một hàm nhận HTML bẩn và trả về phần nội dung chính, có giới hạn độ dài.
4. Tầng synthesis: tóm tắt phải biết mức tự tin
Local LLM như Qwen chạy qua Ollama rất hợp cho privacy và chi phí. Nhưng model local nhỏ hơn có thể yếu hơn ở reasoning dài hoặc tổng hợp nhiều nguồn nhiễu. Vì vậy prompt synthesis nên bắt model tách ba phần:
- điều chắc chắn có trong nguồn;
- điều suy luận từ nhiều nguồn;
- điều chưa đủ bằng chứng.
Mẫu output hữu ích hơn:
Summary:
- ...
Evidence:
- Claim A: Source 1, Source 3
- Claim B: Source 2
Uncertain:
- ...
Recommended follow-up queries:
- ...
Dịch sang tiếng người: đừng bắt ca sĩ hát luôn cả phần bè, trống, violin rồi tự chấm điểm. Hãy chia vai để biết đoạn nào bị phô.
5. Tầng trace: không có log là không có production
Trace không cần đẹp. Nhưng phải trả lời được:
- agent đã gọi tool nào;
- input/output mỗi tool là gì;
- token hoặc độ dài context có vượt ngưỡng không;
- trang nào fail khi fetch;
- model có bị ép trả lời khi thiếu nguồn không.
Ở đây, các bài về agentic harness và trajectory parsing rất đáng chú ý vì cùng nhấn vào một điểm: muốn cải thiện agent, bạn phải nhìn được hành trình hành động, không chỉ patch cuối hoặc câu trả lời cuối. Với research agent cũng vậy. Một digest hay nhưng không có trace chỉ là may mắn được đóng gói đẹp.
Làm trong một buổi: bản local có guardrail tối thiểu
Nếu bạn muốn dựng phiên bản thực dụng trong một buổi chiều, đừng bắt đầu bằng UI. Làm CLI trước.
Bước 1: định nghĩa output contract
{
"topic": "string",
"summary": ["string"],
"claims": [
{"claim": "string", "sources": ["url"], "confidence": "high|medium|low"}
],
"uncertain": ["string"],
"follow_up_queries": ["string"]
}
Bước 2: tách module theo lớp
agent/
intent.py
search.py
fetch.py
clean.py
synthesize.py
trace.py
Đừng gom hết vào một file main.py dài lê thê. Demo thì nhanh, debug thì đau.
Bước 3: lưu trace dạng JSONL
Mỗi event một dòng:
{"step":"search","query":"...","results_count":5}
{"step":"fetch","url":"...","status":"ok"}
{"step":"synthesize","model":"qwen-local","sources_used":3}
Bước 4: thêm refusal rule
Trong system prompt, thêm rule rõ:
If fewer than two relevant sources support a major claim, mark it as uncertain. Do not present it as fact.
Bước 5: test bằng 3 chủ đề khó chịu
Chọn một topic mới, một topic gây tranh cãi, và một topic có nhiều trang SEO. Nếu agent vẫn biết nói “chưa đủ bằng chứng”, bạn đang đi đúng hướng.
Pitfall: local không tự động đồng nghĩa với an toàn
Chạy local giúp giữ query trên máy và tránh phí theo lượt gọi hosted LLM. Điểm này rất đáng giá, nhất là với research cá nhân hoặc dữ liệu nhạy cảm. Nhưng local không giải quyết các lỗi sau:
- search API vẫn có thể gửi query ra ngoài;
- web page vẫn có thể chứa prompt injection — nội dung độc hại cố lừa model làm sai;
- model local vẫn có thể tóm tắt sai;
- context quá dài vẫn làm mất chi tiết quan trọng;
- thiếu trace vẫn khiến team không debug được.
Điều dễ bị thổi quá mức là “agent tự tìm web rồi tóm tắt” nghe như đã thành nhân viên nghiên cứu. Thực tế, nó mới là một pipeline có model ở giữa. Muốn thành hệ thống đáng tin, bạn phải thiết kế quyền hạn, bằng chứng, và điểm dừng.
Nếu là mình, mình sẽ chốt thế này
Với team Việt Nam đang build agent research nội bộ, mình sẽ chọn hướng này:
- Local LLM cho bước tóm tắt nhạy cảm, nếu máy đủ khỏe và latency chấp nhận được.
- Tool web search có logging đầy đủ, không giấu query và kết quả thô.
- Output có citation và uncertainty, không chỉ bullet đẹp.
- Trace bắt buộc từ ngày đầu, dù chỉ là file JSONL.
- Không fine-tune vội, trừ khi bạn đã có trajectory tốt và biết agent hỏng ở đâu.
Còn nếu bạn đang phân vân giữa framework nhiều tầng và script Python đơn giản: bắt đầu bằng script, nhưng thiết kế ranh giới module như thể ngày mai phải thay nhạc trưởng. Khi production gọi tên, thứ cứu bạn không phải đoạn solo của model, mà là cả dàn nhạc biết vào đúng nhịp.
---
Bụi Wire — nghiện đọc release notes lúc 2 giờ sáng
Nguồn tham khảo
- How to Build a Personal AI Web Research Agent with Ollama and Qwen
- From LLMs to LangChain: Understanding How Modern AI Applications Actually Work
- Building Supervised Fine-Tuning Data from NVIDIA Open-SWE-Traces: Trajectory Parsing, Patch Analysis, Token Budgets, and Tool-Use Metrics - MarkTechPost
- Evaluating performance and efficiency of the GitHub Copilot agentic harness across models and tasks - The GitHub Blog
- Nous Research Adds /learn to Hermes Agent's Skills System, Capturing Workflows as Slash Commands Without Hand-Writing SKILL.md - MarkTechPost