Chuyển đến nội dung chính

Hướng dẫn API Qwen 3.7 Max: Xây dựng Agent Năng suất Tự Rà soát

Tìm hiểu cách xây dựng một agent nâng cao hiệu suất với Qwen 3.7 Max qua OpenRouter: lập kế hoạch, nghiên cứu, viết và tự rà soát đầu ra trong một ứng dụng Streamlit.
· 15 phút đọc

Khám phá cùng AI

Hầu hết các bản demo AI vẫn trông như chatbot: bạn đặt câu hỏi, mô hình trả lời, rồi tương tác kết thúc. Nhưng công việc năng suất thực sự thường không diễn ra chỉ trong một bước. Một trợ lý hữu ích cần biết chia nhỏ yêu cầu mơ hồ, thu thập ngữ cảnh, so sánh lựa chọn, tạo ra sản phẩm có thể dùng được, rồi tự kiểm tra lại trước khi bàn giao.

Qwen 3.7 Max là một mô hình agentic của đội ngũ Qwen, được thiết kế cho khối lượng công việc lấy agent làm trung tâm, bao gồm lập trình, tác vụ văn phòng và thực thi dài hạn. Đây là mô hình ưu tiên agent, không chỉ để trả lời tốt một prompt, mà còn để duy trì mạch lạc xuyên suốt quy trình dài gồm lập kế hoạch, dùng công cụ, viết và tự hiệu chỉnh. 

Trong hướng dẫn này, chúng ta sẽ xây dựng một agent năng suất gọn nhẹ bằng Qwen 3.7 Max và Streamlit. Ứng dụng nhận một yêu cầu kinh doanh mơ hồ, như chuẩn bị phân tích cạnh tranh về các cơ sở dữ liệu vector, và chuyển nó thành một tệp competitive_analysis.md được trau chuốt.

Kết thúc bài này, bạn sẽ hiểu cách:

  • Xây dựng quy trình agent cố định năm lượt

  • Sử dụng enable_thinking=True và preserve_thinking=True

  • Thêm nghiên cứu song song nhẹ

  • Viết và vá bản thảo markdown

  • Tạo giao diện Streamlit đơn giản cho quy trình

Mã hoàn chỉnh có sẵn trong kho GitHub và. 

Dự án được cố ý giữ nhỏ gọn, với logic chính tách giữa agent.py cho ứng dụng Streamlit và thinking_agent.py cho luồng xử lý backend.

Nếu bạn mới làm quen với AI agentic, tôi khuyên bạn bắt đầu với lộ trình kỹ năng Nền tảng AI Agent của chúng tôi.

Qwen 3.7 Max là gì?

Qwen 3.7 Max là mô hình chủ lực mới của Alibaba, phát hành ngày 20 tháng 5 năm 2026. Mô hình được xây dựng cho các quy trình agentic. Điều này có nghĩa thay vì chỉ thiết kế cho trò chuyện một lượt, nó được tối ưu cho các tác vụ cần lập kế hoạch, dùng công cụ, lập trình, tự động hóa văn phòng, suy luận ngữ cảnh dài và thực thi nhiều bước. 

Đội ngũ Qwen mô tả đây là mô hình cho “kỷ nguyên agent”, mạnh ở các mảng agent lập trình, trợ lý năng suất, quy trình dựa trên MCP và các nhiệm vụ tự động dài hạn.

Điểm thú vị của mô hình này không chỉ là hiệu năng benchmark thô, mà còn là khả năng duy trì nhất quán trong một tác vụ nhiều lượt, mỗi lượt có mục đích khác nhau, bao gồm:

  • lập kế hoạch
  • tổng hợp bằng chứng
  • viết dài
  • tự phản biện
  • chỉnh sửa

Trên X, người dùng nhận thấy mô hình đạt mức tương đương với Opus 4.7 và GPT 5.5 cho các tác vụ lập trình dài hạn.

Bài đăng X so sánh Qwen 3.7 Max với Opus 4.7

Thế mạnh và hiệu năng benchmark của Qwen 3.7 Max

Qwen 3.7 Max thể hiện tốt trên các benchmark về agent và suy luận. Trong đánh giá agent lập trình, điểm số lần lượt là 

  • 69.7 trên Terminal-Bench 2.0-Terminus
  • 60.6 trên SWE-bench Pro
  • 78.3 trên SWE-bench Multilingual
  • 47.2 trên NL2Repo
  • 53.5 trên SciCode

Trong các benchmark agent tổng quát, đạt 

  • 60.8 trên MCP-Mark
  • 76.4 trên MCP-Atlas
  • 67.2 trên CoWorkBench
  • 87.0 trên SpreadSheetBench-v1

Qwen 3.7 max trên các benchmark

Nguồn: Blog dành cho nhà phát triển Qwen

Để bạn hình dung: tất cả các kết quả đó đều vượt DeepSeek V4 Pro; đa số nhỉnh hơn một chút, nhưng với lập trình dài hạn, có khoảng cách đáng kể giữa hai mô hình (47.2 so với 35.5 ở NL2Repo).

Điều này dẫn tới một ví dụ nổi bật từ bản phát hành: lần tối ưu kernel dài hạn của Qwen 3.7 Max. Được báo cáo rằng Qwen 3.7 Max chạy khoảng 35 giờ, thực hiện 1.158 lần gọi công cụ, hoàn tất 432 lần đánh giá kernel, và tạo ra tốc độ trung bình hình học nhanh hơn 10,0 lần so với triển khai tham chiếu Triton. 

Điều này quan trọng vì nó cho thấy mô hình được đánh giá qua cải tiến lặp lại dựa trên công cụ, không chỉ qua các câu hỏi benchmark tĩnh.

Tóm lại, Qwen 3.7 Max rất phù hợp cho bản demo này vì nó được xây dựng cho các quy trình trong đó mô hình cần suy luận, dùng công cụ, giữ ngữ cảnh và cải thiện một sản phẩm qua nhiều lượt.

Xây dựng Agent Năng suất có Bảo tồn Tư duy với Qwen 3.7 Max

Trong dự án này, chúng ta sẽ minh họa ba ý tưởng thực tiễn:

  • Một yêu cầu mơ hồ có thể được chuyển thành quy trình nhiều bước có cấu trúc.
  • Đầu ra công cụ có thể được đưa trở lại quá trình suy luận thay vì dồn vào một prompt dài.
  • Một mô hình có thể tự đọc lại và cải thiện đầu ra của chính nó qua chain-of-thought prompting trước khi bàn giao sản phẩm cuối cùng cho người dùng.

Tổng quan dự án

Chúng ta sẽ xây dựng một ứng dụng đơn giản với một ô nhập và một nút, nơi người dùng nhập một yêu cầu mơ hồ như:

I need to prepare a competitive analysis of the top 3 vector databases
for a team presentation next week. I have no idea where to start.

Demo

Sau đó, agent chạy một quy trình cố định năm lượt:

User prompt
Turn 1: Plan the work
Parallel research for Pinecone, Weaviate, and Qdrant
Turn 2: Summarize research
Turn 3: Build comparison matrix
Turn 4: Write competitive_analysis.md
Turn 5: Self-review and patch the markdown file
Streamlit file preview

Giao diện hiển thị mỗi bước như một thẻ tiến độ. Khi quy trình hoàn tất, các thẻ chuyển từ chờ sang xong. Cuối cùng, người dùng có thể xem từng giai đoạn: lập kế hoạch, nghiên cứu, tổng hợp, bản nháp, tự rà soát và xem trước tệp cuối.

Cách này giúp quy trình của agent trở nên minh bạch thay vì ẩn sau một câu trả lời chat duy nhất.

Kho mã được cố ý giữ nhỏ. Các phần chính gồm:

  • thinking_agent.py: Chứa logic agent backend như gọi mô hình, prompt, bảo tồn tư duy, nghiên cứu, thử lại, ghi tệp và tự rà soát.

  • agent.py: Chứa giao diện Streamlit, gồm bố cục trang, ô prompt, thanh tiến độ, thẻ bước, bộ chọn kết quả, xem trước markdown và nút tải xuống.

  • requirements.txt: Chứa các phụ thuộc runtime cần cho demo

Backend xoay quanh lớp ThinkingAgent chạy chuỗi năm lượt cố định và trả về bản ghi lượt có cấu trúc. UI không cần biết chi tiết cách gọi mô hình, thử lại, hay fanout nghiên cứu. UI chỉ cần render mỗi lượt khi nó đến.

Toàn bộ mã cho hướng dẫn này có trên GitHub.

Lưu ý: Tôi sử dụng OpenRouter để truy cập Qwen 3.7 Max, nhưng bạn cũng có thể dùng mô hình qua Nền tảng API Qwen chính thức. 

Bước 1: Thiết lập môi trường

Trước khi xây dựng agent năng suất Qwen 3.7 Max, ta cần thiết lập môi trường Python nhỏ với Streamlit, client tương thích OpenAI và httpx để tìm kiếm web nhẹ.

Trong hướng dẫn này, chúng ta dùng OpenRouter làm cổng mô hình, nhưng backend được viết theo định dạng API tương thích OpenAI. Nghĩa là cùng cấu trúc này có thể điều chỉnh cho Alibaba Cloud Model Studio hoặc nhà cung cấp tương thích khác.

Trong bước này, chúng ta sẽ:

  • Tạo môi trường ảo

  • Cài đặt phụ thuộc cần thiết

  • Thiết lập OPENROUTER_API_KEY hoặc QWEN_API_KEY

  • Xác nhận có thể khởi chạy ứng dụng bằng Streamlit

Tạo và kích hoạt môi trường ảo:

python3 -m venv .venv
source .venv/bin/activate

Cài đặt phụ thuộc dự án:

pip install -r requirements.txt

Tệp requirements.txt được cố ý giữ nhỏ:

httpx>=0.23.0
openai>=1.30.0
streamlit>=1.45.0

Chúng ta dùng streamlit để xây dựng giao diện web tương tác, openai để gọi Qwen 3.7 Max qua API tương thích OpenAI, và httpx để lấy kết quả tìm kiếm trực tiếp nhẹ cho bước nghiên cứu.

Bước 2: Cấu hình client Qwen 3.7 Max

Backend dùng OpenAI Python SDK vì OpenRouter cung cấp endpoint tương thích OpenAI. Tệp thinking_agent.py định nghĩa vài mặc định:

from openai import OpenAI
DEFAULT_MODEL = "qwen/qwen3.7-max"
DEFAULT_BASE_URL = "https://openrouter.ai/api/v1"
DEFAULT_REFERER = "https://qwen.ai"
DEFAULT_TITLE = "Qwen3.7-Max Productivity Agent"

Sau đó tạo client:

def build_client(
   api_key: str | None = None,
   base_url: str | None = None,
   referer: str = DEFAULT_REFERER,
   title: str = DEFAULT_TITLE,
) -> OpenAI:
   return OpenAI(
       base_url=base_url or DEFAULT_BASE_URL,
       api_key=api_key,
       default_headers={
           "HTTP-Referer": referer,
           "X-Title": title,
       },
   )

Hàm build_client() tập trung toàn bộ cấu hình đặc thù nhà cung cấp. Phần còn lại của ứng dụng không cần biết yêu cầu đi qua OpenRouter, Alibaba Cloud Model Studio hay endpoint tương thích khác.

Khi client đã cấu hình, backend có thể gọi Qwen 3.7 Max bằng định dạng chat completions tiêu chuẩn.

Giờ chúng ta sẽ xây dựng luồng cốt lõi ThinkingAgent.

Bước 3: Quy trình agent năm lượt

Sau khi cấu hình client mô hình, ta có thể định nghĩa luồng backend cốt lõi. Thay vì yêu cầu Qwen 3.7 Max tạo báo cáo đầy đủ trong một lần, ta chia nhiệm vụ thành năm giai đoạn kiểm soát.

  • Lập kế hoạch: Chuyển prompt mơ hồ thành kế hoạch tác vụ có cấu trúc.

  • Nghiên cứu: Thu thập và tóm tắt bằng chứng từ các đoạn trích bên ngoài.

  • Tổng hợp: Xây dựng ma trận so sánh từ nghiên cứu.

  • Bản nháp: Viết báo cáo competitive_analysis.md đầu tiên.

  • Tự rà soát: Rà soát và vá báo cáo trước khi trả tệp cuối.

class ThinkingAgent:
   def run(
       self,
       goal: str,
       preserve_thinking: bool = True,
       data_mode: str = "live",
       on_turn: Callable[[TurnRecord], None] | None = None,
   ) -> ThinkingRunResult:
       history = [{"role": "system", "content": SYSTEM_PROMPT}]
       turns = []
       backend = self._build_search_backend(data_mode)
       turn1 = self._run_turn(
           history=history,
           turn_index=1,
           phase="planning",
           user_prompt=_planning_prompt(goal),
           preserve_thinking=preserve_thinking,
       )
       search_results = self._run_research(backend)
       turn2 = self._run_turn(
           history=history,
           turn_index=2,
           phase="research",
           user_prompt=_research_prompt(goal, search_results),
           preserve_thinking=preserve_thinking,
       )


       turn3 = self._run_turn(
           history=history,
           turn_index=3,
           phase="synthesis",
           user_prompt=_synthesis_prompt(),
           preserve_thinking=preserve_thinking,
       )
       turn4 = self._run_turn(
           history=history,
           turn_index=4,
           phase="draft",
           user_prompt=_draft_prompt(goal),
           preserve_thinking=preserve_thinking,
       )
       turn5 = self._run_turn(
           history=history,
           turn_index=5,
           phase="self_review",
           user_prompt=_self_review_prompt(draft_markdown),
           preserve_thinking=preserve_thinking,
       )

Phương thức run() là vòng điều phối chính. Nó bắt đầu với system prompt, tạo lịch sử hội thoại, xây dựng backend nghiên cứu, rồi thực thi năm lượt lần lượt.

Mỗi lần gọi _run_turn() gắn với một pha cụ thể. Điều này giúp dễ gỡ lỗi vì mỗi lần gọi mô hình có một trách nhiệm rõ ràng. Pha lập kế hoạch thì lập kế hoạch. Pha nghiên cứu diễn giải các đoạn trích bên ngoài. Pha tổng hợp xây dựng cấu trúc. Pha nháp thì viết. Pha tự rà soát cải thiện đầu ra.

Bước nghiên cứu đặt giữa Lượt 1 và Lượt 2 vì mô hình cần hiểu nhiệm vụ trước khi thông tin bên ngoài được đưa vào quy trình.

Ở bước tiếp theo, chúng ta sẽ thêm logic bảo tồn tư duy đặc trưng của Qwen giúp mô hình duy trì tính liên tục xuyên suốt các lượt.

Bước 4: Bảo tồn tư duy qua các lượt

Lý do chính dùng Qwen 3.7 Max trong demo này là hỗ trợ các quy trình bảo tồn tư duy. Agent không xem mỗi lần gọi mô hình là một prompt biệt lập. Thay vào đó, nó có thể giữ lại suy luận từ các lượt trước và truyền ngữ cảnh đó vào các lượt sau.

Trong bước này, chúng ta sẽ:

  • Bật chế độ tư duy của mô hình.
  • Bảo tồn suy luận qua các lượt.
  • Gắn đầu ra trợ lý trở lại lịch sử chung.
response = self.client.chat.completions.create(
   model=self.config.model,
   messages=request_messages,
   extra_body={
       "enable_thinking": True,
       "preserve_thinking": preserve_thinking,
   },
)

Cờ enable_thinking=True cho phép Qwen 3.7 Max tạo nội dung suy luận trong phản hồi. Trong khi cờ preserve_thinking kiểm soát liệu nội dung suy luận đó có được giữ cho các lượt sau hay không.

Sau khi mô hình phản hồi, backend trích xuất cả câu trả lời hiển thị và nội dung suy luận:

message = response.choices[0].message
assistant_content = _normalize_text(getattr(message, "content", ""))
reasoning_content = _extract_reasoning_content(message)
The visible answer is what the app can show to the user. The reasoning content is used internally to help the next turn stay aligned with the earlier plan.
Then the backend appends the assistant message back into the conversation history:
```python
assistant_message = {
   "role": "assistant",
   "content": assistant_content,
}
if preserve_thinking and reasoning_content:
   assistant_message["reasoning_content"] = reasoning_content
history.append(assistant_message)

Nếu ở lượt lập kế hoạch quyết định báo cáo cuối sẽ so sánh cơ sở dữ liệu vector theo mô hình triển khai, khả năng mở rộng, trải nghiệm nhà phát triển, tìm kiếm lai và mức độ sẵn sàng cho doanh nghiệp, các lượt sau có thể tiếp tục dùng các tiêu chí đó. Nếu không có điều này, mỗi lượt có thể bị lệch. Bước tổng hợp có thể chọn tiêu chí khác với bước lập kế hoạch, hoặc bước tự rà soát đánh giá báo cáo theo mục tiêu khác.

Tiếp theo, chúng ta sẽ thêm lớp nghiên cứu cung cấp ngữ cảnh bên ngoài trước khi mô hình viết báo cáo.

Bước 5: Thêm nghiên cứu song song với dữ liệu dự phòng

Trước khi mô hình viết phân tích cạnh tranh, nó cần một số ngữ cảnh bên ngoài. Trong demo này, chúng ta giữ phạm vi nghiên cứu cố định bằng cách so sánh ba cơ sở dữ liệu vector: Pinecone, Weaviate và Qdrant.

Trong bước này, chúng ta sẽ:

  • Xác định các cơ sở dữ liệu mục tiêu.
  • Chạy các cuộc gọi nghiên cứu song song.
  • Rơi về dữ liệu fixture tất định nếu tìm kiếm trực tiếp thất bại.
DATABASES = ("Pinecone", "Weaviate", "Qdrant")

Danh sách mục tiêu được cố định có chủ ý. Điều này giữ demo ổn định và dễ giải thích hơn. Sau này, bạn có thể mở rộng để mô hình tự chọn mục tiêu so sánh.

Tiếp theo, backend khởi chạy các cuộc gọi nghiên cứu song song:

from concurrent.futures import ThreadPoolExecutor, as_completed
with ThreadPoolExecutor(max_workers=3) as executor:
   future_map = {
       executor.submit(backend.search, database, query): database
       for database, query in queries.items()
   }
   for future in as_completed(future_map):
       database = future_map[future]
       results[database] = future.result()

ThreadPoolExecutor khởi động một tìm kiếm cho mỗi cơ sở dữ liệu. Điều này nhanh hơn chạy tuần tự vì nghiên cứu về Pinecone, Weaviate và Qdrant có thể song song.

try:
   results[database] = future.result()
except Exception as exc:
   fallback = FixtureSearchBackend().search(database, queries[database])
   fallback.warning = (
       f"Live search failed for {database} ({exc}). "
       "Fallback demo research was used."
   )
   results[database] = fallback

Nếu tìm kiếm trực tiếp hoạt động, agent dùng các đoạn trích mới. Nếu thất bại, ứng dụng vẫn hoàn tất quy trình bằng các đoạn nghiên cứu tất định dựng sẵn. Nguyên tắc thiết kế quan trọng là suy giảm duyên dáng: agent không nên sập chỉ vì một phụ thuộc bên ngoài lỗi.

Ở bước tiếp theo, chúng ta sẽ đưa kết quả nghiên cứu này vào Qwen 3.7 Max để tổng hợp ma trận so sánh.

Bước 6: Tạo tổng hợp và ma trận so sánh

Khi agent có các đoạn nghiên cứu, nhiệm vụ tiếp theo là tổng hợp. Bước này chuyển thông tin rời rạc thành một so sánh có cấu trúc để hỗ trợ báo cáo cuối.

Trong bước này, chúng ta sẽ:

  • Gửi ngữ cảnh nghiên cứu trở lại Qwen 3.7 Max.
  • Yêu cầu mô hình tạo một so sánh có cấu trúc.
  • Sau đó render đầu ra này trong giao diện Streamlit.
turn3 = self._run_turn(
   history=history,
   turn_index=3,
   phase="synthesis",
   user_prompt=_synthesis_prompt(),
   preserve_thinking=preserve_thinking,
)

Tới lúc mã này chạy, lịch sử hội thoại đã chứa yêu cầu ban đầu, đầu ra lập kế hoạch, tóm tắt nghiên cứu và (tùy chọn) tư duy được bảo tồn từ các lượt trước.

Điều này có nghĩa prompt tổng hợp không cần nhắc lại toàn bộ nhiệm vụ. Nó có thể tập trung vào một việc: chuyển nghiên cứu thành ma trận so sánh sẵn sàng ra quyết định.

Một tổng hợp tốt nên so sánh các lựa chọn theo các chiều như:

  • Mô hình triển khai
  • Khả năng mở rộng
  • Trải nghiệm nhà phát triển
  • Hỗ trợ tìm kiếm lai
  • Mức độ sẵn sàng cho doanh nghiệp
  • Tình huống sử dụng phù hợp nhất

Tiếp nữa, chúng ta sẽ dùng cấu trúc đã tổng hợp này để viết báo cáo Markdown đầu tiên.

Bước 7: Viết báo cáo markdown

Sau lập kế hoạch, nghiên cứu và tổng hợp, agent có đủ ngữ cảnh để viết sản phẩm đầu tiên hoàn chỉnh. Trong demo này, sản phẩm là tệp markdown tên competitive_analysis.md.

Trong bước này, chúng ta sẽ:

  • Yêu cầu Qwen 3.7 Max viết bản nháp báo cáo đầu tiên.
  • Loại bỏ hàng rào code markdown không cần thiết.
  • Lưu bản nháp xuống đĩa.
  • Ghi nhận hành động ghi tệp như một sự kiện công cụ.
turn4 = self._run_turn(
   history=history,
   turn_index=4,
   phase="draft",
   user_prompt=_draft_prompt(goal),
   preserve_thinking=preserve_thinking,
)

Lượt nháp sử dụng toàn bộ ngữ cảnh trước đó, gồm mục tiêu ban đầu của người dùng, kế hoạch, nghiên cứu và ma trận tổng hợp.

Phản hồi của mô hình sau đó được làm sạch và ghi xuống đĩa:

draft_markdown = _strip_markdown_fences(turn4.assistant_content)
report_path = run_output_dir / "competitive_analysis.md"
report_path.write_text(draft_markdown)

Trợ thủ _strip_markdown_fences() hữu ích vì mô hình thường bao bọc đầu ra markdown trong ba dấu gạch ngược. Điều đó ổn trong phản hồi chat, nhưng không lý tưởng khi ghi trực tiếp vào tệp .md.

Backend cũng ghi nhận hành động ghi tệp:

turn4.tool_events.append(
   ToolEvent(
       name="write_file",
       status="completed",
       input_payload={"path": str(report_path)},
       output_payload={
           "description": "Initial draft written to disk.",
       },
   )
)

Sự kiện công cụ này giúp UI cho thấy backend đã tạo tệp thật, không chỉ thêm một phản hồi văn bản. Ở giai đoạn này, ứng dụng đã có một báo cáo có thể dùng. Nhưng chúng ta thêm một lượt mô hình nữa để nâng chất lượng trước khi hiển thị sản phẩm cuối.

Ở bước tiếp theo, ta sẽ yêu cầu Qwen 3.7 Max tự rà soát và vá bản nháp của chính nó.

Bước 8: Tự rà soát và vá báo cáo

Lượt mô hình cuối là bước tự rà soát. Thay vì trao ngay bản nháp đầu, agent đọc lại báo cáo, kiểm tra so với mục tiêu ban đầu và viết một phiên bản cải tiến.

Trong bước này, chúng ta sẽ:

  • Gửi bản nháp markdown trở lại mô hình.
  • Yêu cầu mô hình xác định điểm yếu.
  • Tạo phiên bản đã chỉnh sửa.
  • Ghi đè báo cáo bằng phiên bản cuối đã cải thiện.
turn5 = self._run_turn(
   history=history,
   turn_index=5,
   phase="self_review",
   user_prompt=_self_review_prompt(draft_markdown),
   preserve_thinking=preserve_thinking,
)

Prompt tự rà soát yêu cầu mô hình kiểm tra các vấn đề thực tiễn, như khuyến nghị có rõ ràng không, tiêu chí so sánh có nhất quán không, và báo cáo có hữu ích cho thuyết trình nhóm không.

Đầu ra chỉnh sửa được ghi lại vào cùng tệp:

final_markdown = _strip_markdown_fences(turn5.assistant_content)
report_path.write_text(final_markdown)

Agent cũng có thể ghi nhận sự kiện ghi cuối cùng:

turn5.tool_events.append(
   ToolEvent(
       name="write_file",
       status="completed",
       input_payload={"path": str(report_path)},
       output_payload={
           "description": "Self-reviewed final report written to disk.",
       },
   )
)

Bước này biến ứng dụng từ một trình sinh đơn giản thành một quy trình biên tập nhỏ. Thứ người dùng thấy không phải bản nháp đầu, mà là phiên bản đã rà soát và vá.

Tiếp theo, chúng ta sẽ nối luồng backend này với ứng dụng Streamlit.

Bước 9: Xây dựng giao diện tiến độ Streamlit

Giờ chuyển từ logic backend sang giao diện người dùng. Ứng dụng Streamlit nên cho phép người dùng nhập prompt, chạy quy trình và theo dõi từng giai đoạn hoàn tất.

Lưu ý: Mã này thuộc tệp agent.py vì nó điều khiển những gì người dùng nhìn thấy và tương tác.

Trong bước này, chúng ta sẽ:

  • Cấu hình trang Streamlit.

  • Tạo ô nhập prompt.

  • Thêm nút chạy.

  • Gọi hàm ThinkingAgent.run().

  • Cập nhật UI khi các lượt hoàn tất.

def render_streamlit_app() -> None:
   st.set_page_config(
       page_title="Qwen3.7-Max Demo Agent",
       page_icon=":material/psychology:",
       layout="wide",
       initial_sidebar_state="collapsed",
   )

Ứng dụng dùng bố cục rộng vì UI có thẻ bước, chỉ báo tiến độ và các bảng kết quả.

Vùng nhập liệu đơn giản:

goal = st.text_area(
   "Prompt",
   value=DEFAULT_GOAL,
   height=120,
   label_visibility="collapsed",
)

run_clicked = st.button("Run Demo", use_container_width=True)

Người dùng chỉ cần cung cấp nhiệm vụ. Ứng dụng không yêu cầu họ cấu hình temperature, chế độ tìm kiếm, ID mô hình hay thiết lập suy luận. Những chi tiết đó do backend xử lý.

Khi người dùng bấm nút, agent.py tạo agent backend và chạy nó:

agent = ThinkingAgent(config=config)

result = agent.run(
   goal=goal,
   preserve_thinking=True,
   data_mode="live",
   on_turn=on_turn,
)

Đây là nơi hai tệp gặp nhau. agent.py kích hoạt quy trình, nhưng thinking_agent.py sở hữu hành vi agent thực sự. Callback UI cập nhật trang sau mỗi lượt:

def on_turn(turn: TurnRecord) -> None:
   partial_turns.append(turn)

   progress_placeholder.progress(
       len(partial_turns) / len(phase_order),
       text=f"Completed {_phase_label(turn.phase)}",
   )

   with steps_placeholder.container():
       _render_step_cards(partial_turns, result_ready=False)

Callback nhận một TurnRecord đã hoàn thành và làm mới thanh tiến độ cùng các thẻ bước. Trong lúc thực thi, các thẻ đóng vai trò chỉ báo trạng thái. Sau khi hoàn tất, chúng trở thành các ô xem kết quả có thể nhấp.

Giờ chúng ta sẽ render báo cáo markdown cuối và cung cấp nút tải xuống.

Bước 10: Xem trước và tải xuống báo cáo cuối

Sau khi backend xong, UI cần hiển thị báo cáo cuối. Backend đã tạo tệp markdown, nên agent.py chỉ cần đọc, render và cung cấp nút tải xuống.

Trong bước này, chúng ta sẽ:

  • Kiểm tra tệp báo cáo có tồn tại không.
  • Đọc nội dung markdown cuối.
  • Hiển thị nút tải xuống.
  • Render markdown trong ứng dụng Streamlit
def _render_file_preview(report_path: str) -> None:
   path = Path(report_path)

   if not path.exists():
       st.info("No file was written yet.")
       return
   content = path.read_text()
   st.caption(f"Created file: {path.name}")
   st.download_button(
       "Download competitive_analysis.md",
       data=content,
       file_name=path.name,
       use_container_width=True,
   )
   st.markdown(content)

Hàm bắt đầu bằng kiểm tra tệp tồn tại để tránh ứng dụng sập nếu backend lỗi trước khi ghi báo cáo. Sau đó đọc nội dung markdown và tạo nút tải. Cuối cùng, render markdown trực tiếp trên trang Streamlit.

Đây là sự phân tách trách nhiệm gọn gàng nơi:

  • thinking_agent.py ghi báo cáo

  • agent.py xem trước và tải báo cáo

Ở bước tiếp theo, chúng ta sẽ thêm xử lý lỗi cơ bản để demo hoạt động tốt hơn khi tuyến nhà cung cấp gặp sự cố hoặc giới hạn tốc độ.

Bước 11: Xử lý lỗi và giới hạn tốc độ

Nhà cung cấp mô hình có thể timeout, trả lỗi tạm thời, hoặc giới hạn tốc độ. Nếu ta phơi bày trực tiếp các lỗi thô đó cho người dùng, ứng dụng sẽ kém hoàn thiện.

Bước này chia giữa hai tệp. Logic thử lại thuộc thinking_agent.py vì là phần của cuộc gọi mô hình backend. Thông báo lỗi thuộc agent.py vì nó điều khiển những gì người dùng thấy.

Trong bước này, chúng ta sẽ:

  • Thử lại lỗi tạm thời của nhà cung cấp trong backend.
  • Nhận diện lỗi kiểu giới hạn tốc độ.
  • Hiển thị thông điệp rõ ràng trong UI Streamlit
delays = (1.0, 2.0)

for attempt_index in range(len(delays) + 1):
   try:
       return self.client.chat.completions.create(...)
   except Exception as exc:
       if not _is_retryable_provider_error(exc):
           raise

       if attempt_index >= len(delays):
           raise

       time.sleep(delays[attempt_index])

Backend thử gọi mô hình, bắt lỗi có thể thử lại, chờ ngắn và thử lần nữa. Độ trễ ngắn đủ để làm mượt các bất ổn tạm thời mà không bắt người dùng chờ quá lâu.

def _friendly_rate_limit_message(exc: Any) -> str:
   return (
       "Qwen 3.7 Max is temporarily rate-limited upstream. "
       "Please wait 30 to 60 seconds and try again."
   )

Điều này giúp giữ JSON thô của nhà cung cấp hoặc stack trace ngoài UI. Người dùng nhận được giải thích rõ ràng và bước tiếp theo thực tế.

Với bước cuối này, giờ ta có thể chạy toàn bộ ứng dụng và kiểm thử quy trình end-to-end.

Bước 12: Chạy demo

Khi luồng backend và giao diện Streamlit đã kết nối, ta có thể chạy ứng dụng hoàn chỉnh.

Trong bước này, chúng ta sẽ:

  • Khởi động ứng dụng Streamlit.
  • Nhập prompt năng suất mặc định.
  • Chạy quy trình năm giai đoạn.
  • Xem trước và tải xuống báo cáo markdown cuối

Khởi động ứng dụng:

streamlit run agent.py

Dùng prompt mặc định:

I need to prepare a competitive analysis of the top 3 vector databases
for a team presentation next week. I have no idea where to start.

Khi bạn nhấp Run Demo, ứng dụng sẽ đi qua toàn bộ quy trình:

  • Lập kế hoạch: Tạo kế hoạch có cấu trúc.

  • Nghiên cứu: Thu thập các đoạn trích cho Pinecone, Weaviate và Qdrant.

  • Tổng hợp: Xây dựng ma trận so sánh.

  • Bản nháp: Viết báo cáo markdown đầu tiên.

  • Tự rà soát: Vá báo cáo.

  • Xem trước: Hiển thị tệp competitive_analysis.md cuối.

Báo cáo cuối được lưu tại:

output/showcase_runs/<timestamp>/with_preserve/competitive_analysis.md

Cuối cùng, bạn sẽ thấy phần xem trước báo cáo và nút tải xuống trong giao diện Streamlit.

Kết luận

Trong hướng dẫn này, chúng ta đã xây dựng một agent năng suất gọn nhẹ với Qwen 3.7 Max chạy quy trình cố định, có thể diễn giải từ lập kế hoạch, nghiên cứu, tổng hợp, viết đến tự rà soát. Trong suốt quá trình, nó cho thấy vì sao bảo tồn suy luận qua các lượt hữu ích cho tác vụ agent dài.

Phiên bản hiện tại chỉ dùng Streamlit, các cuộc gọi mô hình tương thích OpenAI, lớp tìm kiếm nhẹ và đầu ra Markdown. Điều đó giúp bạn dễ học, chỉnh sửa và tái sử dụng trong thử nghiệm của riêng mình.

Từ đây có rất nhiều hướng mở rộng tự nhiên:

  • Để mô hình tự chọn mục tiêu so sánh thay vì dùng danh sách cố định

  • thêm so sánh cạnh nhau giữa chạy “có preserve_thinking” và “không có preserve_thinking”

  • Hiển thị mức dùng token và ước tính chi phí trong UI

  • tạo dàn ý slide hoặc biểu đồ kèm báo cáo markdown

  • Thay scraping DuckDuckGo bằng API tìm kiếm vững chắc hơn

Nếu bạn muốn vượt qua quy trình cố định và học cách xây dựng ứng dụng AI đạt chuẩn sản xuất từ prompt engineering, RAG đến hệ thống agentic, lộ trình kỹ năng AI Engineering với LangChain là bước tiếp theo phù hợp.

Câu hỏi thường gặp về Hướng dẫn API Qwen 3.7 Max

Khi nào tôi nên dùng preserve_thinking=True với Qwen 3.7 Max?

preserve_thinking=True giúp các lượt sau vẫn bám sát các quyết định trước trong quy trình nhiều lượt. Trong ví dụ của chúng tôi, nếu lượt lập kế hoạch chọn các tiêu chí so sánh cụ thể, các lượt tổng hợp và tự rà soát có thể tiếp tục dùng cùng lập luận đó thay vì bị lệch.

Qwen 3.7 Max chỉ dành cho agent lập trình thôi sao?

Không. Lập trình là một trong các thế mạnh lớn của nó, nhưng mô hình cũng được định vị cho các agent năng suất, tự động hóa văn phòng, quy trình dựa trên MCP, nhiệm vụ đa ngôn ngữ, suy luận ngữ cảnh dài và dùng công cụ dài hạn.

Ứng dụng demo có dùng tìm kiếm web thật không?

Có. Đường dẫn mặc định hiện tại thử scraping DuckDuckGo trực tiếp. Nếu thất bại, nó rơi về nghiên cứu fixture dựng sẵn.

Mô hình có tạo các subagent trong ứng dụng demo của chúng ta không?

Không. Ứng dụng dùng một phiên mô hình. Phần “song song” diễn ra ở lớp nghiên cứu, nơi backend khởi chạy ba tìm kiếm web đồng thời và đưa kết quả trở lại cùng một phiên mô hình.

Vì sao chúng ta tách agent.py và thinking_agent.py?

agent.py giúp giao diện Streamlit dễ đọc. thinking_agent.py chứa luồng backend, xây dựng prompt, thử lại và logic nghiên cứu để hành vi agent có thể được kiểm thử độc lập với giao diện.

Aashi Dutt

Tác giả

Aashi Dutt

Tôi là Chuyên gia Google Developers trong lĩnh vực ML (Gen AI), Chuyên gia Kaggle 3x và Đại sứ Women Techmakers với hơn 3 năm kinh nghiệm trong ngành công nghệ. Tôi đồng sáng lập một startup công nghệ y tế vào năm 2020 và hiện đang theo học thạc sĩ khoa học máy tính tại Georgia Tech, chuyên sâu về học máy.

Chủ đề

Liên quan