Bạn đã học 7 bài đầu tiên. Kiến thức chắc về:
Sau bài này, bạn sẽ
- Ship một chatbot hoàn chỉnh có thể chạy end-to-end
- Áp dụng pattern multi-turn từ bài 6.7 vào use case thật
- Debug các lỗi phổ biến khi chạy chatbot (role alternating, token limit)
- Đo performance của chatbot: latency, token usage, cost
Mở đầu: Học bằng làm, không học bằng đọc
- Cách gọi API (bài 6.6)
- Multi-turn conversation (bài 6.7)
- Helper functions chuẩn
Bài này là milestone. Bạn không đọc thêm lý thuyết — bạn ship 1 thứ chạy được. Đây là lần đầu tiên trong khóa bạn tạo ra một sản phẩm nhỏ từ kiến thức đã học.
Đề bài
Xây một chatbot "Travel Assistant" trong Jupyter notebook. Yêu cầu:
Tính năng bắt buộc
- Multi-turnnhớ context xuyên suốt phiên chat
- Interactiveuser gõ input, Claude trả lời, lặp lại đến khi user gõ "quit"
- Giới hạn scopeChatbot chỉ trả lời về du lịch (xin gợi ý địa điểm, lịch trình, ẩm thực). Nếu user hỏi off-topic, Claude phải từ chối lịch sự.
- LogIn token usage mỗi turn để theo dõi cost
Tính năng optional (điểm cộng)
- Sliding window history (giữ 10 turn gần nhất)
- Xử lý edge case: input rỗng,
stop_reason=max_tokens - Thống kê cuối session: tổng token, số turn, cost ước tính
Hướng dẫn step-by-step
Bước 1: Setup boilerplate
from dotenv import load_dotenv
load_dotenv()
from anthropic import Anthropic
client = Anthropic()
model = "claude-sonnet-4-6-20260205"
def add_user_message(messages, text):
messages.append({"role": "user", "content": text})
def add_assistant_message(messages, text):
messages.append({"role": "assistant", "content": text})
Bước 2: Viết hàm chat với logging
total_input_tokens = 0
total_output_tokens = 0
def chat(messages):
global total_input_tokens, total_output_tokens
msg = client.messages.create(
model=model,
max_tokens=1000,
messages=messages,
)
# Log token
total_input_tokens += msg.usage.input_tokens
total_output_tokens += msg.usage.output_tokens
print(f" [tokens: in={msg.usage.input_tokens}, out={msg.usage.output_tokens}]")
# Check truncation
if msg.stop_reason == "max_tokens":
print(" ⚠️ Response bị cắt! Cần tăng max_tokens.")
return msg.content[0].text
Bước 3: Viết hàm main loop
def travel_chatbot():
messages = []
# Giới thiệu scope qua prompt đầu
intro = """Bạn là Travel Assistant. Chỉ trả lời câu hỏi về du lịch:
- Gợi ý địa điểm
- Lập lịch trình
- Gợi ý ẩm thực địa phương
- Mẹo du lịch
Nếu user hỏi chủ đề khác (code, finance, relationship...),
hãy lịch sự từ chối và hướng về chủ đề du lịch."""
# Cách "hack": đưa intro vào user message đầu + assistant ack
add_user_message(messages, intro)
add_assistant_message(messages,
"Chào bạn! Tôi là Travel Assistant. Hãy hỏi tôi về du lịch — điểm đến, lịch trình, ẩm thực. Bạn cần tư vấn chuyến đi nào?")
print("🧭 Travel Assistant đã sẵn sàng. Gõ 'quit' để thoát.\n")
print(messages[-1]["content"])
print()
while True:
user_input = input("You: ").strip()
if user_input.lower() in ("quit", "exit", "bye"):
break
if not user_input:
print(" (input rỗng, skip)")
continue
add_user_message(messages, user_input)
try:
answer = chat(messages)
add_assistant_message(messages, answer)
print(f"\nTravel Assistant: {answer}\n")
except Exception as e:
print(f" ❌ Lỗi: {e}")
# Remove user message cuối để giữ history hợp lệ
messages.pop()
# Thống kê cuối
print("\n" + "=" * 50)
print(f"Session ended.")
print(f"Total turns: {(len(messages) - 2) // 2}") # trừ 2 intro message
print(f"Total tokens: input={total_input_tokens}, output={total_output_tokens}")
# Cost estimate (Sonnet 4.6)
cost = (total_input_tokens * 3 + total_output_tokens * 15) / 1_000_000
print(f"Estimated cost: ${cost:.4f}")
Bước 4: Chạy
travel_chatbot()
Bước 5: Test flow
Thử các case:
- "Tôi muốn đi Đà Nẵng 3 ngày, có gợi ý gì?"
- "Nên ăn gì ở đó?" (test memory)
- "Ngân sách khoảng 5 triệu, đủ không?" (test context)
- "Giúp tôi viết Python function" (test scope limit)
- "Quit"
Phiên bản nâng cao (optional)
Sliding window
MAX_HISTORY = 20 # 10 turn × 2 messages
def chat_with_window(messages):
# Giữ 2 message intro + MAX_HISTORY messages gần nhất
if len(messages) > MAX_HISTORY + 2:
trimmed = messages[:2] + messages[-MAX_HISTORY:]
else:
trimmed = messages
msg = client.messages.create(
model=model,
max_tokens=1000,
messages=trimmed,
)
return msg.content[0].text
Validation
def validate_messages(messages):
"""Check role alternating + first=user."""
if not messages:
return
assert messages[0]["role"] == "user", "First message must be user"
for i in range(1, len(messages)):
assert messages[i]["role"] != messages[i-1]["role"], \
f"Role not alternating at index {i}"
Retry logic
import time
from anthropic import RateLimitError, APIError
def chat_with_retry(messages, max_retries=3):
for attempt in range(max_retries):
try:
return chat(messages)
except RateLimitError:
wait = 2 ** attempt # exponential backoff
print(f" Rate limited. Retry in {wait}s...")
time.sleep(wait)
except APIError as e:
if attempt == max_retries - 1:
raise
print(f" API error: {e}. Retrying...")
return None
Case studies tương tự bạn có thể thử sau
Education — Study buddy
Same pattern nhưng scope "giải thích khái niệm học thuật". Tương tác như tutor ở bài 6.7.
Sales — Discovery prep
Scope: gợi ý câu hỏi discovery. Input: thông tin prospect. Output: 10 câu hỏi.
Cooking assistant
Scope: công thức nấu ăn, thay thế nguyên liệu. "Tôi có gì trong tủ lạnh → món gì?"
Fitness coach
Scope: lập plan tập. Track progress qua history.
Mỗi use case đều dùng chính pattern bạn vừa học — chỉ khác intro prompt và scope.
Debug guide — Khi gặp lỗi
Lỗi 1: BadRequestError: messages: first message must use "user" role
- Nguyên nhân
- Có thể bạn append assistant trước user.
- Fix
- Kiểm tra
messages[0]["role"].
Lỗi 2: BadRequestError: messages: all messages must have non-empty content
- Nguyên nhân
- User gõ empty string (enter không có text).
- Fix
- Validate
user_input.strip()trước khi append.
Lỗi 3: AuthenticationError
Nguyên nhân: API key không load.
Fix:
import os
print("Key loaded:", bool(os.getenv("ANTHROPIC_API_KEY")))
Lỗi 4: Claude "quên" mặc dù có history
- Nguyên nhân
- Quên append assistant message sau mỗi turn.
- Fix
- Cẩn thận với flow:
add_user → chat → add_assistant.
Lỗi 5: Response bị cắt
- Nguyên nhân
max_tokensquá thấp.- Fix
- Tăng lên 2000-4000. Check
stop_reason.
Self-review checklist
Trước khi consider "done", check:
Functional
- Chatbot trả lời đúng topic (du lịch)
- Từ chối off-topic lịch sự
- Nhớ context qua nhiều turn
- Quit command hoạt động
Code quality
- 3 helper function tách biệt
- Có exception handling
- In token usage mỗi turn
- Thống kê cuối session
Edge cases
- Empty input không crash
- Long response không bị cắt giữa chừng
- Có thể chat 20+ turn mà không lỗi
Tóm tắt bài học
Những điều cốt lõi
- Đây là milestone đầu tiên — bạn đã ship 1 chatbot production-ready ở mức sơ khai.
- Pattern bạn viết có thể adapt cho mọi use case chatbot: thay intro, thay scope.
- Logging token là thói quen cần duy trì — không có log = không biết cost.
- Validation + retry là 2 layer bảo vệ mà hầu hết app production cần.
- Sliding window đủ cho 80% chatbot casual. Summarization cần cho chatbot dài.
Tài liệu tham khảo
- anthropic-cookbook/basic_chat — nhiều ví dụ chatbot
- Rate limit handling
Bạn đã hoàn thành bài này chưa?Tiến độ lưu trên trình duyệt của bạn.