Bài 4 — Tự viết agent loop cho Anthropic tool use
OpenAI Agents SDK giấu vòng lặp đi. Với Anthropic Messages API, bạn tự viết — và đây là kỹ năng quan trọng nhất, vì nó cho bạn quyền kiểm soát tuyệt đối (log, retry, ngắt, giới hạn số vòng).
Sơ đồ vòng lặp
messages = [user question]
lặp tối đa N vòng:
resp = client.messages.create(model, tools, messages, max_tokens)
thêm resp (role=assistant) vào messages
nếu resp.stop_reason != "tool_use":
return text cuối # xong
với mỗi block tool_use trong resp.content:
kết quả = chạy_tool(block.name, block.input)
gom vào 1 message role=user chứa các tool_result (khớp id)
thêm message tool_result vào messages
Chìa khóa: sau mỗi lượt tool_use, bạn gửi một message role=user chứa tất cả tool_result của lượt đó, mỗi cái khớp tool_use_id.
Code đầy đủ
import anthropic, json
client = anthropic.Anthropic()
def chay_tool(name, args):
if name == "tra_cuu_don_hang":
return f"Đơn {args['order_id']}: đang giao"
return f"Lỗi: không có tool {name}"
def run_agent(cau_hoi, tools, max_vong=6):
messages = [{"role": "user", "content": cau_hoi}]
for _ in range(max_vong):
resp = client.messages.create(
model="claude-sonnet-4-5", max_tokens=1024,
tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
return "".join(b.text for b in resp.content if b.type == "text")
ket_qua = []
for block in resp.content:
if block.type == "tool_use":
out = chay_tool(block.name, block.input)
ket_qua.append({
"type": "tool_result",
"tool_use_id": block.id, # PHẢI khớp
"content": out,
})
messages.append({"role": "user", "content": ket_qua})
return "Đã đạt giới hạn số vòng, dừng an toàn."
Các bước triển khai
- Khởi tạo
messagesvới câu hỏi người dùng. - Gọi model, luôn append nguyên
resp.contentvớirole="assistant"— giữ đủ ngữ cảnh cho vòng sau. - Kiểm tra
stop_reason: khác"tool_use"nghĩa là model đã trả lời xong → return. - Duyệt từng block
tool_use, chạy tool tương ứng, thutool_resultkhớpid. - Gửi lại toàn bộ tool_result trong một message
role="user". - Giới hạn
max_vongđể không lặp vô hạn.
Xử lý lỗi tool ngay trong loop
try:
out = chay_tool(block.name, block.input)
except Exception as e:
ket_qua.append({
"type": "tool_result", "tool_use_id": block.id,
"content": f"Lỗi khi chạy tool: {e}",
"is_error": True,
})
continue
Đặt is_error: True để Claude hiểu đây là lỗi và cân nhắc thử cách khác thay vì tin đó là dữ liệu hợp lệ.
Checklist một agent loop đúng
- [ ] Append nguyên
resp.contentvới role=assistant trước khi gửi tool_result. - [ ] Mỗi tool_result khớp đúng tool_use_id của block tương ứng.
- [ ] Gom mọi tool_result của một lượt vào MỘT message role=user.
- [ ] Có
max_vongvà dừng an toàn khi chạm trần. - [ ] Lỗi tool được trả về với is_error=True, không nuốt im lặng.
- [ ] Log tên tool, input, output rút gọn ở mỗi lượt.
Vì sao phải append nguyên resp.content
Một chi tiết dễ bỏ sót nhưng gây lỗi khó hiểu: sau mỗi lượt, bạn phải append nguyên resp.content (danh sách block, gồm cả text lẫn tool_use) vào messages với role="assistant", rồi mới thêm message tool_result. Nếu bạn chỉ nhặt block tool_use hoặc chỉ nhặt text, lịch sử hội thoại gửi lên API sẽ không hợp lệ, vì Anthropic yêu cầu mỗi tool_result phải theo sau một assistant message chứa tool_use tương ứng. Cấu trúc đúng của messages sau một lượt tool là: [user(câu hỏi), assistant(text + tool_use), user(tool_result)]. Giữ đúng thứ tự và đầy đủ nội dung này là điều kiện để vòng sau chạy được.
Khi nào một tool nên trả nhiều tool_use
Trong một lượt, Claude có thể yêu cầu gọi nhiều tool cùng lúc (ví dụ tra 3 đơn hàng song song). Vòng lặp của bạn phải duyệt HẾT các block tool_use trong resp.content, chạy từng cái, và gom tất cả tool_result vào một message role="user" duy nhất. Đừng gửi mỗi kết quả thành một message riêng — điều đó phá vỡ cặp ghép tool_use ↔ tool_result và khiến API báo lỗi hoặc Claude hiểu nhầm ngữ cảnh.
Sai lầm thường gặp
- Quên append
assistantcontent trước khi gửi tool_result → API báo message không hợp lệ. - Gộp sai
tool_use_id→ Claude ghép kết quả nhầm tool. - Trả nhiều tool_result thành nhiều message thay vì một message gộp → cấu trúc hội thoại hỏng.
- Không giới hạn số vòng → agent kẹt lặp, đốt token.
- Nuốt exception im lặng → model tưởng tool thành công, trả lời sai; hãy báo
is_error. - Không log → khi lỗi không biết vòng nào, tool nào gây ra.