Trung cấpHướng dẫnClaude APINguồn: Anthropic

SubQuestion Engine — Phân tách câu hỏi phức tạp tự động

Nghe bài viết
00:00

Điểm nổi bật

Nhấn để đến mục tương ứng

  1. 1 Bước thực hành then chốt trong subquestion engine hoạt động như thế nào?: Decomposition — Claude phân tích câu hỏi gốc, chia thành N sub-questions Routing — Mỗi sub-question được assign đến query engine phù hợp Parallel Query — Tất cả sub-questions được query đồng thời Synthesis — nắm vững điều này giúp bạn triển khai nhanh hơn và giảm thiểu lỗi thường gặp.
  2. 2 Về tạo indexes cho nhiều tài liệu, thực tế cho thấy # Báo cáo Q1 2024 q1_docs Documenttext"""BÁO CÁO Q1 2024 Công ty A: - Doanh thu: 5.2 tỷ đồng tăng 23% so với Q1 2023 - Chi phí vận hành: 3.1 tỷ đồng - Lợi nhuận ròng: 1 — đây là con dao hai lưỡi nếu không hiểu rõ giới hạn và điều kiện áp dụng của nó.
  3. 3 Dữ liệu từ chạy complex queries cho thấy: # Query phức tạp: cần nhiều sub-questions complex_queries "So sánh doanh thu và lợi nhuận của Công ty A giữa Q1 và Q2 2024 — những con số này phản ánh mức độ cải thiện thực tế mà người dùng có thể kỳ vọng.
  4. 4 Khi triển khai ví dụ output subquestion decomposition, điều cốt lõi là Khi hỏi "So sánh doanh thu và lợi nhuận Q1 và Q2?", engine sẽ tự động tạo: SubQuestion 1: Doanh thu Công ty A trong Q1 2024? -&gt Engine: q1_report -&gt Answer: 5 — hiểu đúng nguyên lý này giúp bạn tránh sai lầm phổ biến và đạt kết quả tốt hơn ngay từ đầu.
  5. 5 Góc nhìn thực tế về performance: async vs sync: # Sync tuần tự start time.time sync_engine SubQuestionQueryEngine.from_defaults query_engine_toolsq1_tool, q2_tool, industry_tool, use_asyncFalse, # Tuần tự verboseFalse r1 sync_engine.query"So sánh Công ty A và B trong H1 2024" sync_time time.time - start # Async song song start time.time async_engine SubQuestionQueryEngine.from_defaults query_engine_t — hiệu quả phụ thuộc nhiều vào cách triển khai và ngữ cảnh sử dụng cụ thể.
computer program screengrab

Câu hỏi phức tạp như "So sánh doanh thu, chi phí và lợi nhuận của công ty A và B trong Q1 và Q2 2024" thực ra là nhiều câu hỏi nhỏ hơn. SubQuestion Query Engine của LlamaIndex tự động phân tách câu hỏi phức tạp thành sub-questions, query mỗi câu song song, rồi tổng hợp thành câu trả lời hoàn chỉnh.

Kết hợp với Claude, đây là kỹ thuật mạnh mẽ để xử lý câu hỏi phân tích phức tạp trên nhiều tài liệu.

SubQuestion Engine hoạt động như thế nào?

  1. Decomposition — Claude phân tích câu hỏi gốc, chia thành N sub-questions
  2. Routing — Mỗi sub-question được assign đến query engine phù hợp
  3. Parallel Query — Tất cả sub-questions được query đồng thời
  4. Synthesis — Claude tổng hợp tất cả sub-answers thành câu trả lời cuối

Ví dụ: "So sánh doanh thu Q1 và Q2?" sẽ được chia thành: (1) "Doanh thu Q1 là bao nhiêu?" và (2) "Doanh thu Q2 là bao nhiêu?" — query song song rồi tổng hợp.

Cài đặt

pip install llama-index llama-index-llms-anthropic llama-index-embeddings-voyageai nest_asyncio
import os
import asyncio
import nest_asyncio
nest_asyncio.apply()  # Cho phép nested event loops

from llama_index.core import Settings, VectorStoreIndex, Document
from llama_index.llms.anthropic import Anthropic
from llama_index.embeddings.voyageai import VoyageEmbedding
from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.tools import QueryEngineTool

Settings.llm = Anthropic(model="claude-opus-4-5", max_tokens=2048)
Settings.embed_model = VoyageEmbedding(
    model_name="voyage-3",
    voyage_api_key=os.environ.get("VOYAGE_API_KEY")
)

print("SubQuestion Engine ready")

Tạo Indexes cho nhiều tài liệu

# Báo cáo Q1 2024
q1_docs = [
    Document(text="""BÁO CÁO Q1 2024
    Công ty A:
    - Doanh thu: 5.2 tỷ đồng (tăng 23% so với Q1 2023)
    - Chi phí vận hành: 3.1 tỷ đồng
    - Lợi nhuận ròng: 1.8 tỷ đồng (biên lợi nhuận 34.6%)
    - Nhân viên: 87 người
    - Khách hàng mới: 234

    Công ty B:
    - Doanh thu: 4.8 tỷ đồng (tăng 18% so với Q1 2023)
    - Chi phí vận hành: 3.4 tỷ đồng
    - Lợi nhuận ròng: 1.1 tỷ đồng (biên lợi nhuận 22.9%)
    - Nhân viên: 95 người
    - Khách hàng mới: 198"""),
    Document(text="""PHÂN TÍCH THỊ TRƯỜNG Q1 2024
    Thị phần: Công ty A 28%, Công ty B 26%, Others 46%.
    Tăng trưởng ngành: 15% YoY.
    Top sản phẩm A: Enterprise Plan (45% doanh thu).
    Top sản phẩm B: Pro Bundle (38% doanh thu).""")
]

# Báo cáo Q2 2024
q2_docs = [
    Document(text="""BÁO CÁO Q2 2024
    Công ty A:
    - Doanh thu: 6.1 tỷ đồng (tăng 17.3% so với Q1 2024)
    - Chi phí vận hành: 3.5 tỷ đồng
    - Lợi nhuận ròng: 2.2 tỷ đồng (biên lợi nhuận 36.1%)
    - Nhân viên: 102 người (tuyển thêm 15)
    - Khách hàng mới: 312

    Công ty B:
    - Doanh thu: 5.3 tỷ đồng (tăng 10.4% so với Q1 2024)
    - Chi phí vận hành: 3.7 tỷ đồng
    - Lợi nhuận ròng: 1.3 tỷ đồng (biên lợi nhuận 24.5%)
    - Nhân viên: 108 người
    - Khách hàng mới: 221"""),
    Document(text="""PHÂN TÍCH THỊ TRƯỜNG Q2 2024
    Thị phần: Công ty A 31% (+3%), Công ty B 25% (-1%), Others 44%.
    Tăng trưởng ngành: 18% YoY (tăng tốc so với Q1).
    A mở rộng sang thị trường miền Trung.
    B ra mắt sản phẩm mới: Team Edition.""")
]

# Dữ liệu ngành
industry_docs = [
    Document(text="""TỔNG QUAN NGÀNH 2024
    Thị trường SaaS Việt Nam dự kiến đạt 1.2 tỷ USD năm 2024.
    Tăng trưởng CAGR 5 năm: 22%.
    Churn rate trung bình ngành: 6-8%.
    CAC benchmark: 15-25 triệu đồng.
    LTV:CAC ratio tốt: trên 3:1.
    Top concerns: Security (67%), Integration (54%), Cost (48%).""")
]

# Tạo indexes
q1_index = VectorStoreIndex.from_documents(q1_docs)
q2_index = VectorStoreIndex.from_documents(q2_docs)
industry_index = VectorStoreIndex.from_documents(industry_docs)

print("All indexes created")

Tạo SubQuestion Engine

from llama_index.core.question_gen import LLMQuestionGenerator
from llama_index.core.question_gen.prompts import build_tools_text

# Tạo query engine tools
q1_tool = QueryEngineTool.from_defaults(
    query_engine=q1_index.as_query_engine(similarity_top_k=3),
    name="q1_report",
    description="Báo cáo kinh doanh Q1 2024 của Công ty A và Công ty B. "
                "Chứa: doanh thu, chi phí, lợi nhuận, nhân viên, khách hàng Q1."
)

q2_tool = QueryEngineTool.from_defaults(
    query_engine=q2_index.as_query_engine(similarity_top_k=3),
    name="q2_report",
    description="Báo cáo kinh doanh Q2 2024 của Công ty A và Công ty B. "
                "Chứa: doanh thu, chi phí, lợi nhuận, nhân viên, khách hàng Q2."
)

industry_tool = QueryEngineTool.from_defaults(
    query_engine=industry_index.as_query_engine(similarity_top_k=3),
    name="industry_data",
    description="Dữ liệu và benchmark ngành SaaS Việt Nam 2024. "
                "Chứa: thị phần, tăng trưởng ngành, benchmarks về CAC, LTV, churn."
)

# Tạo SubQuestion Engine
subquestion_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[q1_tool, q2_tool, industry_tool],
    llm=Settings.llm,
    use_async=True,    # Query sub-questions song song
    verbose=True       # In ra sub-questions và kết quả
)

print("SubQuestion Engine created!")

Chạy Complex Queries

# Query phức tạp: cần nhiều sub-questions
complex_queries = [
    "So sánh doanh thu và lợi nhuận của Công ty A giữa Q1 và Q2 2024. Tăng trưởng như thế nào?",
    "Công ty nào hoạt động hiệu quả hơn trong H1 2024? Dựa trên doanh thu, biên lợi nhuận và tăng trưởng.",
    "So với benchmark ngành, CAC và churn rate của Công ty A và B như thế nào?"
]

for query in complex_queries:
    print(f"
{'='*60}")
    print(f"QUERY: {query}")
    print("="*60)

    response = subquestion_engine.query(query)
    print(f"
FINAL ANSWER:
{response.response}")

Ví dụ output SubQuestion decomposition

Khi hỏi "So sánh doanh thu và lợi nhuận Q1 và Q2?", engine sẽ tự động tạo:

[SubQuestion 1]: Doanh thu Công ty A trong Q1 2024?
  -> Engine: q1_report
  -> Answer: 5.2 tỷ đồng

[SubQuestion 2]: Doanh thu Công ty A trong Q2 2024?
  -> Engine: q2_report
  -> Answer: 6.1 tỷ đồng

[SubQuestion 3]: Lợi nhuận Công ty A Q1 2024?
  -> Engine: q1_report
  -> Answer: 1.8 tỷ (biên 34.6%)

[SubQuestion 4]: Lợi nhuận Công ty A Q2 2024?
  -> Engine: q2_report
  -> Answer: 2.2 tỷ (biên 36.1%)

[Synthesis]: Doanh thu tăng 17.3% từ Q1 sang Q2 (5.2 -> 6.1 tỷ).
Lợi nhuận tăng 22.2% (1.8 -> 2.2 tỷ). Biên lợi nhuận cải thiện
từ 34.6% lên 36.1%, cho thấy hiệu quả vận hành tốt hơn...

Custom Question Generator

Tùy chỉnh cách Claude phân tách câu hỏi:

from llama_index.core.prompts import PromptTemplate

custom_decompose_prompt = PromptTemplate(
    """Bạn là AI analyst chuyên về business intelligence.
Cho câu hỏi sau: {query_str}

Và các data sources sau:
{tools_str}

Hãy phân tách câu hỏi thành các sub-questions cụ thể cần trả lời.
Mỗi sub-question phải:
1. Rõ ràng, có thể trả lời độc lập
2. Được assign đến đúng data source
3. Kết hợp lại sẽ trả lời đầy đủ câu hỏi gốc

Format: JSON list of {{"sub_question": "...", "tool_name": "..."}}
"""
)

# Custom question generator
custom_question_gen = LLMQuestionGenerator.from_defaults(
    llm=Settings.llm,
    prompt=custom_decompose_prompt
)

custom_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[q1_tool, q2_tool, industry_tool],
    question_gen=custom_question_gen,
    use_async=True,
    verbose=False
)

Performance: Async vs Sync

import time

# Sync (tuần tự)
start = time.time()
sync_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[q1_tool, q2_tool, industry_tool],
    use_async=False,  # Tuần tự
    verbose=False
)
r1 = sync_engine.query("So sánh Công ty A và B trong H1 2024")
sync_time = time.time() - start

# Async (song song)
start = time.time()
async_engine = SubQuestionQueryEngine.from_defaults(
    query_engine_tools=[q1_tool, q2_tool, industry_tool],
    use_async=True,   # Song song
    verbose=False
)
r2 = async_engine.query("So sánh Công ty A và B trong H1 2024")
async_time = time.time() - start

print(f"Sync time:  {sync_time:.2f}s")
print(f"Async time: {async_time:.2f}s")
print(f"Speedup: {sync_time/async_time:.1f}x")

Kết hợp với Router Engine

Kết hợp SubQuestion Engine và Router Engine cho maximum flexibility:

from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.selectors import LLMSingleSelector

# SubQuestion engine xử lý complex queries
subq_tool = QueryEngineTool.from_defaults(
    query_engine=subquestion_engine,
    name="complex_analysis",
    description="Phân tích phức tạp cần so sánh nhiều metrics, thời kỳ, hoặc công ty. "
                "Dùng cho câu hỏi 'so sánh...', 'phân tích tổng hợp...', 'trend của...'."
)

# Simple query engine cho câu hỏi đơn giản
simple_tool = QueryEngineTool.from_defaults(
    query_engine=q1_index.as_query_engine(),
    name="simple_lookup",
    description="Tra cứu nhanh thông tin cụ thể trong Q1 2024. "
                "Dùng cho câu hỏi đơn giản về một số liệu hoặc fact."
)

# Router quyết định: simple lookup hay complex analysis
combined_engine = RouterQueryEngine(
    selector=LLMSingleSelector.from_defaults(),
    query_engine_tools=[simple_tool, subq_tool],
    verbose=True
)

combined_engine.query("Doanh thu Công ty A Q1?")  # -> Simple lookup
combined_engine.query("So sánh hiệu quả A và B H1 2024")  # -> SubQuestion

Kết luận

SubQuestion Engine là kỹ thuật mạnh mẽ nhất trong LlamaIndex cho complex analytical queries. Thay vì dựa vào một prompt dài với tất cả context, engine phân chia và chinh phục — tìm đúng thông tin cho từng phần của câu hỏi rồi tổng hợp thông minh.

Sử dụng use_async=True để query sub-questions song song, giảm latency đáng kể khi có nhiều sub-questions. Kết hợp với Router Engine để tự động chọn strategy phù hợp cho mỗi câu hỏi.

Bước tiếp theo: Xem lại Router Query Engine để kết hợp hai kỹ thuật này, hoặc khám phá Multi-Document Agent cho kiến trúc agent linh hoạt hơn.


Bài viết liên quan

Tính năng liên quan:SubQuestionQuery DecompositionLlamaIndexParallel Query

Bai viet co huu ich khong?

Bản quyền thuộc về tác giả. Vui lòng dẫn nguồn khi chia sẻ.

Bình luận (0)
Ảnh đại diện
Đăng nhập để bình luận...
Đăng nhập để bình luận
  • Đang tải bình luận...

Đăng ký nhận bản tin

Nhận bài viết hay nhất về sản phẩm và vận hành, gửi thẳng vào hộp thư của bạn.

Bảo mật thông tin. Hủy đăng ký bất cứ lúc nào. Chính sách bảo mật.