Tóm tắt: Viết tool bằng @tool (langchain-core), model ChatAnthropic, ghép bằng create_react_agent (nhanh) hoặc tự dựng StateGraph + ToolNode + tools_condition (kiểm soát hơn). Gắn checkpointer và thread_id để agent nhớ ngữ cảnh. Khái niệm nền xem LangGraph – khái niệm cốt lõi.
Luồng hoạt động
flowchart LR U[Câu hỏi NOC] --> A["agent: Claude"] A -->|tool_calls| T[ToolNode] T -->|ping| R1[("198.51.100.11")] T -->|show_bgp_summary| R2[("pe1-hni")] T --> A A -->|"không còn tool_calls"| O[Câu trả lời]
Cấu hình
Cài đặt
uv add langgraph langchain-anthropic
export ANTHROPIC_API_KEY='***'Tool
import ipaddress
import subprocess
from langchain_core.tools import tool
INVENTORY = {
"pe1-hni": {"mgmt": "198.51.100.11", "os": "iosxr"},
"pe2-hcm": {"mgmt": "198.51.100.12", "os": "junos"},
}
SHOW_BGP = {"iosxr": "show bgp summary", "junos": "show bgp summary"}
@tool
def ping(target: str, count: int = 3) -> str:
"""Ping một địa chỉ IP (không nhận hostname). Dùng để kiểm tra reachability."""
ip = ipaddress.ip_address(target)
out = subprocess.run(["ping", "-c", str(min(count, 10)), "-W", "1", str(ip)],
capture_output=True, text=True, timeout=30)
return out.stdout[-1500:] or out.stderr
@tool
def show_bgp_summary(device: str) -> str:
"""Chạy 'show bgp summary' (chỉ đọc) trên thiết bị theo tên inventory, vd pe1-hni."""
d = INVENTORY.get(device)
if d is None:
return f"Không có thiết bị {device}. Thiết bị hợp lệ: {', '.join(INVENTORY)}"
return run_show(d["mgmt"], d["os"], SHOW_BGP[d["os"]])
def run_show(host: str, os_name: str, command: str) -> str:
# Gọi netmiko/scrapli/PyEZ ở đây; chỉ cho phép lệnh show
if not command.startswith("show "):
raise ValueError("chỉ cho phép lệnh show")
raise NotImplementedErrorCách 1: prebuilt create_react_agent
from langchain_anthropic import ChatAnthropic
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.prebuilt import create_react_agent
from net_tools import ping, show_bgp_summary
model = ChatAnthropic(model="claude-sonnet-5-5", temperature=0)
agent = create_react_agent(
model,
tools=[ping, show_bgp_summary],
prompt="Bạn là trợ lý NOC. Chỉ dùng tool đọc, trả lời ngắn, nêu rõ lệnh đã chạy.",
checkpointer=InMemorySaver(),
)
config = {"configurable": {"thread_id": "noc-1"}, "recursion_limit": 20}
result = agent.invoke({"messages": [("user", "pe1-hni có ping được không?")]}, config)
print(result["messages"][-1].content)Ở LangGraph 1.x, create_react_agent được đánh dấu deprecated, hướng thay thế là create_agent trong package langchain. Kiểm tra phiên bản bạn cài; Cách 2 bên dưới không phụ thuộc vào thay đổi này.
Cách 2: tự dựng StateGraph
from langchain_anthropic import ChatAnthropic
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from net_tools import ping, show_bgp_summary
tools = [ping, show_bgp_summary]
model = ChatAnthropic(model="claude-sonnet-5-5", temperature=0).bind_tools(tools)
def agent(state: MessagesState) -> dict:
return {"messages": [model.invoke(state["messages"])]}
builder = StateGraph(MessagesState)
builder.add_node("agent", agent)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition) # có tool_calls -> "tools", không -> END
builder.add_edge("tools", "agent")
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "noc-1"}, "recursion_limit": 20}
for chunk in graph.stream({"messages": [("user", "BGP trên pe1-hni có peer nào down?")]},
config, stream_mode="values"):
chunk["messages"][-1].pretty_print()Chạy và hỏi tiếp cùng thread
uv run agent_graph.pyGọi lại graph.invoke(..., config) với cùng thread_id thì agent nhớ thiết bị đang bàn. Muốn duyệt trước khi chạy tool: builder.compile(checkpointer=..., interrupt_before=["tools"]).
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
GraphRecursionError |
Agent lặp agent↔tools quá recursion_limit |
Xem tool trả gì (thường lỗi lặp lại), sửa description; đặt recursion_limit hợp lý |
| Agent không nhớ câu trước | Không có checkpointer hoặc đổi thread_id |
compile(checkpointer=...), giữ nguyên thread_id |
| Model không gọi tool | Quên .bind_tools(tools) ở Cách 2 |
Bind tool cho model trong node agent |
ImportError: create_react_agent hoặc cảnh báo deprecated |
Đổi API giữa các phiên bản | Ghim phiên bản trong pyproject.toml, hoặc dùng Cách 2 |
| Tool lỗi làm crash cả graph | Exception không được bắt | ToolNode mặc định trả lỗi về model; với tool tự viết hãy trả chuỗi lỗi rõ ràng |
Output show quá dài, tốn token |
Trả nguyên output CLI | Lọc/tóm tắt trong tool (chỉ peer down, chỉ dòng liên quan) |
Tham khảo
- LangGraph: agents
- langchain-anthropic
- Netmiko vs Scrapli để hiện thực
run_show.