{"product_id":"vượt-giới-hạn-max_tokens-kỹ-thuật-tiếp-nối-response-dai","title":"Vượt giới hạn max_tokens — Kỹ thuật tiếp nối response dài","description":"\n\u003cp\u003eClaude có context window 200k tokens nhưng \u003ccode\u003emax_tokens\u003c\/code\u003e trong mỗi API call bị giới hạn (thường 4.096 đến 8.192). Khi bạn yêu cầu viết một bài viết dài, generate code phức tạp, hay tạo báo cáo chi tiết, response có thể bị cắt giữa chừng. Bài này hướng dẫn kỹ thuật \u003cstrong\u003econtinuation loop\u003c\/strong\u003e để nhận toàn bộ output.\u003c\/p\u003e\n\n\u003ch2\u003eVấn đề: stop_reason = \"max_tokens\"\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport anthropic\n\nclient = anthropic.Anthropic()\n\nresponse = client.messages.create(\n    model=\"claude-opus-4-5\",\n    max_tokens=100,  # Cố ý đặt thấp để demo\n    messages=[{\n        \"role\": \"user\",\n        \"content\": \"Viết một bài viết 1000 từ về lịch sử Việt Nam.\"\n    }],\n)\n\nprint(f\"stop_reason: {response.stop_reason}\")\nprint(f\"Content: {response.content[0].text[:200]}...\")\n\n# Output:\n# stop_reason: max_tokens  ← Response bị cắt!\n# Content: Việt Nam là một quốc gia có lịch sử hàng nghìn năm...\n\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003cp\u003eKhi \u003ccode\u003estop_reason == \"max_tokens\"\u003c\/code\u003e, có nghĩa là Claude chưa hoàn thành — response bị cắt cứng tại giới hạn token. Khác với \u003ccode\u003estop_reason == \"end_turn\"\u003c\/code\u003e nghĩa là Claude đã hoàn thành tự nhiên.\u003c\/p\u003e\n\n\u003ch2\u003eGiải pháp: Continuation Loop\u003c\/h2\u003e\n\n\u003cp\u003eÝ tưởng: khi phát hiện \u003ccode\u003emax_tokens\u003c\/code\u003e, append response cũ vào messages và gọi API lại, yêu cầu Claude tiếp tục từ chỗ dở:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003edef generate_with_continuation(\n    messages: list,\n    system_prompt: str = \"\",\n    max_tokens_per_call: int = 4096,\n    max_continuations: int = 10,\n    model: str = \"claude-opus-4-5\",\n) -\u0026gt; dict:\n    \"\"\"\n    Generate response dài bằng cách tự động tiếp tục khi bị cắt.\n\n    Returns:\n        dict với keys: full_text, total_tokens, continuation_count, completed\n    \"\"\"\n    full_text = \"\"\n    total_input_tokens = 0\n    total_output_tokens = 0\n    continuation_count = 0\n    current_messages = messages.copy()\n\n    while continuation_count \u0026lt;= max_continuations:\n        response = client.messages.create(\n            model=model,\n            max_tokens=max_tokens_per_call,\n            system=system_prompt,\n            messages=current_messages,\n        )\n\n        # Cộng dồn token usage\n        total_input_tokens += response.usage.input_tokens\n        total_output_tokens += response.usage.output_tokens\n\n        # Lấy text từ response\n        chunk = response.content[0].text\n        full_text += chunk\n\n        print(f\"  Chunk {continuation_count + 1}: {len(chunk)} chars, \"\n              f\"stop_reason={response.stop_reason}\")\n\n        # Kiểm tra điều kiện dừng\n        if response.stop_reason == \"end_turn\":\n            # Claude hoàn thành tự nhiên\n            print(f\"  Completed after {continuation_count} continuations!\")\n            return {\n                \"full_text\": full_text,\n                \"total_input_tokens\": total_input_tokens,\n                \"total_output_tokens\": total_output_tokens,\n                \"continuation_count\": continuation_count,\n                \"completed\": True,\n            }\n\n        elif response.stop_reason == \"max_tokens\":\n            # Bị cắt — cần tiếp tục\n            continuation_count += 1\n\n            if continuation_count \u0026gt; max_continuations:\n                print(f\"  Reached max continuations ({max_continuations})\")\n                break\n\n            # Thêm response vừa nhận vào messages\n            current_messages.append({\n                \"role\": \"assistant\",\n                \"content\": chunk\n            })\n\n            # Thêm lời nhắc tiếp tục\n            current_messages.append({\n                \"role\": \"user\",\n                \"content\": \"Tiếp tục.\"\n            })\n\n        else:\n            # stop_reason khác (stop_sequence, v.v.)\n            print(f\"  Stopped with reason: {response.stop_reason}\")\n            break\n\n    return {\n        \"full_text\": full_text,\n        \"total_input_tokens\": total_input_tokens,\n        \"total_output_tokens\": total_output_tokens,\n        \"continuation_count\": continuation_count,\n        \"completed\": False,\n    }\n\n# Test\nprint(\"Generating long article...\")\nresult = generate_with_continuation(\n    messages=[{\n        \"role\": \"user\",\n        \"content\": \"Viết bài viết đầy đủ về lịch sử lập trình từ 1940 đến nay, bao gồm các ngôn ngữ lập trình quan trọng và milestone lớn.\"\n    }],\n    system_prompt=\"Viết văn tiếng Việt rõ ràng, súc tích. QUAN TRỌNG: Không dùng dấu chấm lửng (...) khi kết thúc câu. Hoàn thành đầy đủ mọi ý tưởng.\",\n    max_tokens_per_call=2048,\n    max_continuations=5,\n)\n\nprint(f\"\n=== KẾT QUẢ ===\")\nprint(f\"Hoàn thành: {result['completed']}\")\nprint(f\"Số lần tiếp tục: {result['continuation_count']}\")\nprint(f\"Tổng output: {len(result['full_text'])} chars\")\nprint(f\"Tổng tokens: {result['total_input_tokens']} in + {result['total_output_tokens']} out\")\nprint(f\"\nNội dung:\n{result['full_text'][:500]}...\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eVấn đề: Seamless Continuation\u003c\/h2\u003e\n\n\u003cp\u003eKỹ thuật \"Tiếp tục.\" đơn giản đôi khi tạo ra seam (chỗ nối lộ liễu). Claude có thể thêm \"Như tôi đã đề cập...\" hoặc bắt đầu đoạn mới không tự nhiên. Cải thiện:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003edef smart_continuation(\n    messages: list,\n    system_prompt: str = \"\",\n    max_tokens_per_call: int = 4096,\n    max_continuations: int = 10,\n    model: str = \"claude-opus-4-5\",\n) -\u0026gt; dict:\n    \"\"\"\n    Continuation thông minh hơn: prefill với ký tự cuối để seamless.\n    \"\"\"\n    full_text = \"\"\n    total_input_tokens = 0\n    total_output_tokens = 0\n    continuation_count = 0\n    current_messages = messages.copy()\n\n    while continuation_count \u0026lt;= max_continuations:\n        response = client.messages.create(\n            model=model,\n            max_tokens=max_tokens_per_call,\n            system=system_prompt,\n            messages=current_messages,\n        )\n\n        total_input_tokens += response.usage.input_tokens\n        total_output_tokens += response.usage.output_tokens\n        chunk = response.content[0].text\n        full_text += chunk\n\n        if response.stop_reason != \"max_tokens\":\n            return {\n                \"full_text\": full_text,\n                \"total_input_tokens\": total_input_tokens,\n                \"total_output_tokens\": total_output_tokens,\n                \"continuation_count\": continuation_count,\n                \"completed\": response.stop_reason == \"end_turn\",\n            }\n\n        continuation_count += 1\n        if continuation_count \u0026gt; max_continuations:\n            break\n\n        # Lấy vài từ cuối để continuity\n        last_words = chunk[-50:] if len(chunk) \u0026gt; 50 else chunk\n\n        # Thêm vào messages và prefill với đoạn cuối\n        current_messages.append({\"role\": \"assistant\", \"content\": chunk})\n        current_messages.append({\n            \"role\": \"user\",\n            \"content\": \"Tiếp tục viết từ chỗ vừa dừng, không cần nhắc lại nội dung trước.\"\n        })\n\n        print(f\"  Continuation {continuation_count} (last: '...{last_words.strip()}')\")\n\n    return {\n        \"full_text\": full_text,\n        \"total_input_tokens\": total_input_tokens,\n        \"total_output_tokens\": total_output_tokens,\n        \"continuation_count\": continuation_count,\n        \"completed\": False,\n    }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eStreaming với Continuation\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003edef stream_with_continuation(\n    messages: list,\n    system_prompt: str = \"\",\n    max_tokens_per_call: int = 4096,\n    max_continuations: int = 10,\n) -\u0026gt; str:\n    \"\"\"Stream response với auto-continuation.\"\"\"\n    full_text = \"\"\n    current_messages = messages.copy()\n    continuation_count = 0\n\n    while continuation_count \u0026lt;= max_continuations:\n        chunk_text = \"\"\n\n        with client.messages.stream(\n            model=\"claude-opus-4-5\",\n            max_tokens=max_tokens_per_call,\n            system=system_prompt,\n            messages=current_messages,\n        ) as stream:\n            for text in stream.text_stream:\n                print(text, end=\"\", flush=True)\n                chunk_text += text\n\n            final_message = stream.get_final_message()\n            stop_reason = final_message.stop_reason\n\n        full_text += chunk_text\n\n        if stop_reason != \"max_tokens\":\n            break\n\n        continuation_count += 1\n        if continuation_count \u0026gt; max_continuations:\n            break\n\n        current_messages.append({\"role\": \"assistant\", \"content\": chunk_text})\n        current_messages.append({\n            \"role\": \"user\",\n            \"content\": \"Tiếp tục.\"\n        })\n        print()  # Newline giữa các chunks khi streaming\n\n    print()  # Final newline\n    return full_text\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eXử lý code generation dài\u003c\/h2\u003e\n\n\u003cpre\u003e\u003ccode\u003edef generate_long_code(spec: str, language: str = \"Python\") -\u0026gt; str:\n    \"\"\"\n    Generate code dài với continuation, đảm bảo syntax đúng.\n    \"\"\"\n    system = f\"\"\"Bạn là senior {language} developer.\nViết code đầy đủ, production-ready, có comments.\nQUAN TRỌNG: Khi bị interrupt giữa chừng, hãy hoàn thành block code hiện tại trước.\nKhông để code bị cắt giữa function hay class.\"\"\"\n\n    result = generate_with_continuation(\n        messages=[{\n            \"role\": \"user\",\n            \"content\": f\"Viết {language} code đầy đủ cho: {spec}\"\n        }],\n        system_prompt=system,\n        max_tokens_per_call=4096,\n        max_continuations=8,\n    )\n\n    # Validate code đủ không\n    code = result[\"full_text\"]\n\n    # Kiểm tra các dấu hiệu code bị cắt\n    incomplete_markers = [\"...\", \"# TODO: Continue\", \"# ...\"]\n    has_incomplete = any(m in code for m in incomplete_markers)\n\n    if not result[\"completed\"]:\n        print(\"WARNING: Code có thể chưa hoàn chỉnh\")\n    if has_incomplete:\n        print(\"WARNING: Phát hiện dấu hiệu code bị cắt\")\n\n    return code\n\n# Generate REST API\napi_code = generate_long_code(\n    \"REST API cho e-commerce với FastAPI: endpoints cho products, orders, users. Bao gồm models, routers, và database operations.\",\n    language=\"Python\"\n)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eTips quan trọng\u003c\/h2\u003e\n\n\u003ch3\u003eTính chi phí continuation\u003c\/h3\u003e\n\n\u003cp\u003eMỗi lần continuation, bạn phải gửi lại toàn bộ history (input tokens tăng dần):\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003edef estimate_continuation_cost(\n    initial_input_tokens: int,\n    avg_output_per_call: int,\n    n_continuations: int,\n    input_price_per_mtok: float = 3.0,\n    output_price_per_mtok: float = 15.0,\n) -\u0026gt; dict:\n    \"\"\"Ước tính chi phí cho n continuations.\"\"\"\n    total_input = 0\n    total_output = 0\n\n    for i in range(n_continuations + 1):\n        # Input tăng dần vì phải gửi lại history\n        input_tokens = initial_input_tokens + i * avg_output_per_call\n        total_input += input_tokens\n        total_output += avg_output_per_call\n\n    cost_input = (total_input \/ 1_000_000) * input_price_per_mtok\n    cost_output = (total_output \/ 1_000_000) * output_price_per_mtok\n\n    return {\n        \"total_input_tokens\": total_input,\n        \"total_output_tokens\": total_output,\n        \"estimated_cost_usd\": cost_input + cost_output,\n    }\n\n# Ví dụ: 5 continuations, mỗi call output 2000 tokens\ncost = estimate_continuation_cost(\n    initial_input_tokens=1000,\n    avg_output_per_call=2000,\n    n_continuations=5,\n)\nprint(f\"Estimated cost: ${cost['estimated_cost_usd']:.4f}\")\nprint(f\"Total tokens: {cost['total_input_tokens'] + cost['total_output_tokens']:,}\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eKhi nào không nên dùng continuation?\u003c\/h3\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eThay vào đó:\u003c\/strong\u003e Dùng \u003ccode\u003emax_tokens\u003c\/code\u003e lớn hơn ngay từ đầu (claude-opus-4-5 hỗ trợ 8.192)\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eChia task:\u003c\/strong\u003e Thay vì viết \"bài 5000 từ\", chia thành 5 sections riêng biệt\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eSummarize thay vì expand:\u003c\/strong\u003e Đôi khi response ngắn hơn mà vẫn đủ thông tin\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eKết hợp với Streaming\u003c\/h2\u003e\n\n\u003cp\u003eĐể UX tốt nhất trong production, kết hợp streaming + continuation:\u003c\/p\u003e\n\n\u003col\u003e\n  \u003cli\u003eStream response trong real-time để user thấy ngay\u003c\/li\u003e\n  \u003cli\u003eDetect \u003ccode\u003emax_tokens\u003c\/code\u003e ở cuối stream\u003c\/li\u003e\n  \u003cli\u003eTự động request continuation (có thể show spinner nhỏ)\u003c\/li\u003e\n  \u003cli\u003eContinue streaming từ continuation response\u003c\/li\u003e\n\u003c\/ol\u003e\n\n\u003cp\u003eKỹ thuật continuation là building block quan trọng cho mọi ứng dụng cần output dài — từ code generators đến report writers. Kết hợp với \u003ca href=\"\/en\/collections\/nang-cao\"\u003ePrompt Caching\u003c\/a\u003e để giảm chi phí input tokens tăng dần trong mỗi continuation call.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47721833169108,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/v_t-gi_i-h_n-max_tokens-k_-thu_t-ti_p-n_i-response-dai.jpg?v=1774521735","url":"https:\/\/claude.vn\/en\/products\/v%c6%b0%e1%bb%a3t-gi%e1%bb%9bi-h%e1%ba%a1n-max_tokens-k%e1%bb%b9-thu%e1%ba%adt-ti%e1%ba%bfp-n%e1%bb%91i-response-dai","provider":"CLAUDE.VN","version":"1.0","type":"link"}