feat(observability): đo token cho cả 6 lời gọi NLU, bỏ cờ partial
with_structured_output vứt mất AIMessage gốc — nơi duy nhất mang usage_metadata — nên 6/7 điểm gọi model của ai-core trước đây vô hình với hệ thống đo token: chỉ synthesize được ghi nhận, payload gửi đi phải gắn cờ partial=true.
Thêm helper invoke_structured bọc include_raw=True và tự bóc {raw, parsed}, nên mỗi call site chỉ đổi một dòng và chữ ký hàm nghiệp vụ giữ nguyên. Parse hỏng thì helper ném lại parsing_error thay vì trả None: giữ đúng hợp đồng cũ, tránh None lọt xuống xa nguyên nhân.
INSTRUMENTED_PURPOSES nay đủ 7 nhãn và IS_PARTIAL=False, kèm test chặn việc thêm điểm gọi model mới mà quên khai nhãn — bên nhận đọc hai trường này để biết phạm vi số liệu, khai thiếu thì họ hiểu sai mà không có gì báo động.
Đo thật một lượt query_understand: 837 token trước đây không ai nhìn thấy.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com