[LangGraph Series] Part 2: Bàn tay con người giữa cỗ máy - Human-In-The-Loop

0 0 0

Người đăng: Ben

Theo Viblo Asia

Introduction

Bối cảnh Human-in-the-loop (HITL) trong LangGraph thật ra khá là đơn giản, chính cái tên dường như đã giúp bạn hình dung phần nào về định nghĩa của nó. Khi chúng ta cần sự tích hợp của con người vào quá trình xử lý của mô hình AI, hay nói cách khác là ta đang cố gắng kiểm soát chúng, hiệu chỉnh và xác nhận kết quả. Điều này khá quan trọng trong các hệ thống sử dụng LangGraph, đặc biệt khi bạn sử dụng retrieval-augmented generation (RAG)

Các case chính được sử dụng trong HITL:

  • Reviewing tool calls: Human sẽ tiến hành review, chỉnh sửa, hoặc là cho phép các tool calls bằng LLM trước khi được execute
  • Validating LLM outputs: Human tiến hành review, chỉnh sửa, hoặc cho phép các nội dung có thể được generated dựa vào LLM
  • Providing context: Giúp cho LLM có thêm đầy đủ input từ human cho việc bổ sung thông tin hoặc conversation trong đoạn hội thoại

Vậy việc Human “xâm nhập” vào loop như thế nào, hãy cùng đi tới phần dưới đây để làm rõ nhé

Interrupt

Hàm interrupt trong LangGraph được áp dụng trong workflow HITL bằng cách ngắng gaph ở 1 node nhất định, đây là nơi mà human sẽ “xâm nhập” vào graph, sau đó thì workflow hoạt động bình thường khi đi qua phần này. interrupt function này sẽ giúp thực hiện tác vụ như xác nhận, chỉnh sửa, và thu thập các input bổ sung và được sử dụng trong conjuction với Command để các node được hoạt động chuyển tiếp trong graph

from langgraph.types import interrupt def human_node(state: State): value = interrupt( # Any JSON serializable value to surface to the human. # For example, a question or a piece of text or a set of keys in the state { "text_to_revise": state["some_text"] } ) # Update the state with the human's input or route the graph based on the input. return { "some_text": value } graph = graph_builder.compile( checkpointer=checkpointer # Required for `interrupt` to work
) # Run the graph until the interrupt
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(some_input, config=thread_config) # Resume the graph with the human's input
graph.invoke(Command(resume=value_from_human), config=thread_config)
Note: 

Khi workflow chạy đến human_node(), nó tạm dừng tại interrupt, yêu cầu con người cung cấp dữ liệu.

Sau khi con người nhập phản hồi, hệ thống tiếp tục chạy workflow từ điểm bị gián đoạn. Để sử dụng interrupt trong đồ thị của bạn, bạn cần:

  • Chỉ định một checkpointer để lưu trạng thái của đồ thị sau mỗi bước.
  • Gọi interrupt() ở vị trí thích hợp. Xem phần Design Patterns để tham khảo ví dụ.
  • Chạy đồ thị với thread ID cho đến khi gặp interrupt.
  • Tiếp tục thực thi bằng cách sử dụng invoke / ainvoke / stream / astream (tham khảo Primitive Command).

Design Patterns

Trong quá trình xây dựng hệ thống xử lý dữ liệu hoặc ra quyết định dựa trên AI, việc tích hợp con người vào quy trình vận hành không chỉ giúp kiểm soát chất lượng mà còn tăng tính linh hoạt của hệ thống. LangGraph cung cấp cơ chế "Human-in-the-Loop" (HITL) để làm điều đó một cách có hệ thống, cho phép luồng xử lý tạm dừng và chờ tương tác từ con người. Có ba mẫu thiết kế (design pattern) phổ biến giúp triển khai HITL hiệu quả trong LangGraph, mỗi mẫu phục vụ một mục đích riêng biệt và có cách triển khai tương ứng.

Approve or Reject (Phê duyệt hoặc Từ chối)

Thường được sử dụng tại những bước quan trọng, nơi hành động kế tiếp có thể gây ảnh hưởng lớn, chẳng hạn như gọi API, gửi email, hoặc ghi dữ liệu vào hệ thống. Tại bước này, graph xử lý sẽ tạm dừng, gửi thông tin đến người dùng để yêu cầu họ đưa ra quyết định có tiếp tục thực hiện hành động hay không. Nếu người dùng phê duyệt, quá trình tiếp tục như bình thường; nếu từ chối, hệ thống có thể rẽ nhánh sang một hành động khác hoặc đơn giản là kết thúc quy trình. Pattern này giúp đảm bảo rằng những hành động "nhạy cảm" luôn được kiểm soát bởi con người, giảm thiểu rủi ro và nâng cao độ tin cậy của hệ thống. image.png

from typing import Literal
from langgraph.types import interrupt, Command def human_approval(state: State) -> Command[Literal["some_node", "another_node"]]: is_approved = interrupt( { "question": "Is this correct?", # Surface the output that should be # reviewed and approved by the human. "llm_output": state["llm_output"] } ) if is_approved: return Command(goto="some_node") else: return Command(goto="another_node") # Add the node to the graph in an appropriate location
# and connect it to the relevant nodes.
graph_builder.add_node("human_approval", human_approval)
graph = graph_builder.compile(checkpointer=checkpointer) # After running the graph and hitting the interrupt, the graph will pause.
# Resume it with either an approval or rejection.
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke(Command(resume=True), config=thread_config)

Review and Edit State

Cho phép con người can thiệp trực tiếp vào trạng thái hiện tại của luồng xử lý. Trong một số tình huống, AI có thể tạo ra thông tin chưa chính xác, thiếu ngữ cảnh hoặc cần được hiệu chỉnh bởi chuyên gia. Với pattern này, graph sẽ dừng lại tại một bước, hiển thị trạng thái hiện tại (ví dụ: kết quả phân tích, nội dung trích xuất, v.v.) và cho phép người dùng chỉnh sửa trước khi tiếp tục. Điều này cực kỳ hữu ích trong các bài toán trích xuất thông tin từ văn bản, tóm tắt báo cáo, hoặc bất kỳ quy trình nào mà dữ liệu đầu ra có thể cần được xác minh và cập nhật bởi con người.

image.png

Xem lại và chỉnh sửa trạng thái của biểu đồ. Điều này hữu ích để sửa lỗi hoặc cập nhật trạng thái bằng thông tin bổ sung. from langgraph.types import interrupt

def human_editing(state: State): ... result = interrupt( # Interrupt information to surface to the client. # Can be any JSON serializable value. { "task": "Review the output from the LLM and make any necessary edits.", "llm_generated_summary": state["llm_generated_summary"] } ) # Update the state with the edited text return { "llm_generated_summary": result["edited_text"] }
# Add the node to the graph in an appropriate location
# and connect it to the relevant nodes.
graph_builder.add_node("human_editing", human_editing)
graph = graph_builder.compile(checkpointer=checkpointer) ... # After running the graph and hitting the interrupt, the graph will pause.
# Resume it with the edited text.
thread_config = {"configurable": {"thread_id": "some_id"}}
graph.invoke( Command(resume={"edited_text": "The edited text"}), config=thread_config
)

Review Tool Calls

Mẫu thiết kế này không nhằm kiểm duyệt hay chỉnh sửa, mà là bổ sung thông tin cần thiết để hệ thống ra quyết định chính xác hơn. Có nhiều tình huống mà AI thiếu dữ liệu để đưa ra câu trả lời tốt, ví dụ như chưa rõ mục tiêu người dùng, không hiểu ngữ cảnh, hoặc cần thêm chi tiết kỹ thuật. Thay vì đoán mò, hệ thống có thể dừng lại, đặt câu hỏi trực tiếp với người dùng và lưu lại phản hồi như một phần của trạng thái. Cách làm này vừa giúp quá trình xử lý minh bạch hơn, vừa tăng khả năng cá nhân hóa hoặc nâng cao độ chính xác trong kết quả đầu ra.

image.png

Tiến hành chỉnh sửa và xem xét lại đầu ra của LLM trước khi execute

def human_review_node(state) -> Command[Literal["call_llm", "run_tool"]]: # This is the value we'll be providing via Command(resume=<human_review>) human_review = interrupt( { "question": "Is this correct?", # Surface tool calls for review "tool_call": tool_call } ) review_action, review_data = human_review # Approve the tool call and continue if review_action == "continue": return Command(goto="run_tool") # Modify the tool call manually and then continue elif review_action == "update": ... updated_msg = get_updated_msg(review_data) # Remember that to modify an existing message you will need # to pass the message with a matching ID. return Command(goto="run_tool", update={"messages": [updated_message]}) # Give natural language feedback, and then pass that back to the agent elif review_action == "feedback": ... feedback_msg = get_feedback_msg(review_data) return Command(goto="call_llm", update={"messages": [feedback_msg]}) 

Tóm lại, ba mẫu thiết kế này – phê duyệt hành động, chỉnh sửa trạng thái và bổ sung dữ liệu – phản ánh các cách tiếp cận linh hoạt và thực tiễn để tích hợp yếu tố con người vào quy trình vận hành của hệ thống AI. Nhờ đó, các luồng xử lý không còn đơn thuần là "tự động hóa khép kín", mà có thể thích ứng với thực tế và yêu cầu giám sát trong những tình huống phức tạp. Khi kết hợp với các thành phần khác trong LangGraph như state management, branching logic, hay callback, các pattern này sẽ mở ra nhiều khả năng thiết kế hệ thống AI có tính tương tác cao và dễ kiểm soát hơn bao giờ hết.

Multi-turn conversation

Một cuộc hội thoại nhiều lượt (multi-turn conversation) trong LangGraph cho phép một tác nhân (agent) tương tác với con người nhiều lần, giúp thu thập thêm thông tin một cách linh hoạt.

Trong một hệ thống gồm nhiều agent, có thể cần con người cung cấp thông tin ở nhiều giai đoạn khác nhau. Điều này được triển khai bằng cách:

  • Tạm dừng (interrupt) → Để chờ phản hồi từ con người trước khi tiếp tục.
  • Cập nhật trạng thái → Để lưu trữ thông tin giữa các lượt hội thoại.
  • Điều hướng theo điều kiện → Chuyển hướng workflow dựa trên phản hồi từ con người.
from langgraph.types import interrupt def human_input(state: State): human_message = interrupt("human_input") return { "messages": [ { "role": "human", "content": human_message } ] } def agent(state: State): # Agent logic ... graph_builder.add_node("human_input", human_input)
graph_builder.add_edge("human_input", "agent")
graph = graph_builder.compile(checkpointer=checkpointer) # After running the graph and hitting the interrupt, the graph will pause.
# Resume it with the human's input.
graph.invoke( Command(resume="hello!"), config=thread_config
)

Tại sao Multi-turn Conversation lại quan trọng?

Trong các hệ thống hội thoại truyền thống (như một số ứng dụng chỉ sử dụng LLM thuần), mỗi câu trả lời thường được xử lý riêng biệt – không có sự lưu trữ hoặc sử dụng lại thông tin từ những lượt trước. Điều này dẫn đến các phản hồi thiếu tự nhiên, hoặc lặp lại thông tin mà người dùng đã cung cấp.

Ví dụ:

user: Tôi muốn đặt bàn tối nay.
assistant: Bạn muốn đặt bàn vào lúc mấy giờ?
user: 7 giờ tối.

(Nếu AI không nhớ câu đầu, nó có thể hỏi lại: "Bạn muốn đặt bàn cho ngày nào?")

Khả năng "ghi nhớ" và "xây dựng ngữ cảnh" chính là điểm mạnh mà LangGraph mang lại thông qua cơ chế graph state và lưu trữ history theo luồng.

Trong LangGraph, toàn bộ quá trình xử lý được chia thành các nodes (các bước) và trạng thái được lưu lại trong graph state. Khi thiết kế hội thoại nhiều lượt, bạn có thể:

  • Lưu trữ lịch sử hội thoại: Dùng một biến trong graph state (ví dụ: messages) để lưu tất cả các lượt trao đổi.
  • Truyền state qua từng bước xử lý: Mỗi node có thể đọc lịch sử, thêm câu mới vào, và đưa ra phản hồi dựa trên toàn bộ ngữ cảnh.
  • Tùy chỉnh logic xử lý cho từng lượt: Bạn có thể phân nhánh xử lý tùy vào trạng thái cuộc hội thoại – ví dụ: nếu chưa có đủ thông tin → hỏi tiếp, nếu đã đủ → thực hiện hành động.

Ưu điểm:

  • Quản lý ngữ cảnh rõ ràng: Không phải phụ thuộc vào prompt engineering để gợi nhớ lịch sử.

  • Dễ kiểm soát logic: Có thể can thiệp tại bất kỳ bước nào (dừng lại, sửa state, yêu cầu input).

  • Kết hợp dễ dàng với HITL: Có thể để con người can thiệp vào bất kỳ lượt hội thoại nào nếu cần.

Multi-turn conversation là một phần thiết yếu trong việc xây dựng assistant thông minh, và với LangGraph, bạn không chỉ tạo được hội thoại "ghi nhớ ngữ cảnh", mà còn dễ dàng mở rộng với các luồng kiểm duyệt (HITL), gọi API, hoặc tự động hóa xử lý khi đã đủ thông tin. Đây là nền tảng vững chắc để tạo ra các agent thực sự hiệu quả, từ chatbot dịch vụ khách hàng cho đến trợ lý nội bộ doanh nghiệp.

Tổng kết

Human-in-the-Loop (HITL) trong LangGraph là một cơ chế mạnh mẽ giúp đưa con người vào quy trình xử lý của mô hình AI, từ đó tăng tính kiểm soát, chính xác và linh hoạt của hệ thống. Bằng cách sử dụng interrupt và Command, LangGraph cho phép workflow tạm dừng tại các node nhất định để chờ tương tác từ con người, sau đó tiếp tục luồng xử lý một cách mượt mà.

Bài viết đã trình bày ba mẫu thiết kế (design patterns) phổ biến để tích hợp HITL:

  • Approve or Reject: Dành cho các bước quan trọng cần sự xác nhận trước khi tiếp tục thực hiện.
  • Review and Edit State Cho phép người dùng sửa đổi trực tiếp output của LLM trước khi tiếp tục xử lý.
  • Review Tool Calls: Hỏi ý kiến người dùng nhằm làm rõ hoặc bổ sung dữ liệu cần thiết cho quyết định tiếp theo.

Việc áp dụng đúng các pattern này không chỉ giúp đảm bảo chất lượng và độ tin cậy của hệ thống AI, mà còn tạo điều kiện cho sự tương tác chủ động từ con người – điều đặc biệt quan trọng trong các ứng dụng sử dụng RAG hoặc các bài toán nhạy cảm đòi hỏi giám sát. Với LangGraph, việc "đưa con người vào vòng lặp" trở nên dễ dàng và có hệ thống hơn bao giờ hết.

📚 Tài liệu tham khảo

📌 LangGraph - Human-in-the-loop

📌 Human-in-the-loop

📌 Making it easier to build human-in-the-loop agents with interrupt

📌 Human-in-the-loop with OpenGPTs and LangGraph

Bình luận

Bài viết tương tự

- vừa được xem lúc

Golang Data Structures and Algorithms - Stack

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 42

- vừa được xem lúc

AWS Certified Solutions Architect Professional - Security - Secrets Manager

Introduction. A quick note about AWS Secrets Manager.

0 0 51

- vừa được xem lúc

Golang Data Structures and Algorithms - Queue

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 53

- vừa được xem lúc

Terraform Series - Bài 17 - Security - Manage Secrets with Vault

Giới thiệu. Chào các bạn tới với series về Terraform, ở bài trước chúng ta đã tìm hiểu về vấn đề security trong Terraform.

0 0 43

- vừa được xem lúc

Golang Data Structures and Algorithms - Linked Lists

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 41

- vừa được xem lúc

AWS Certified Solutions Architect Professional - Security - AWS Certificate Manager

Introduction. A quick note about AWS Certificate Manager.

0 0 35