LangGraph 入门实战(6)
LangGraph å ¥é¨å®æ(6)
åæè½å¡å° 2026-08-14 0 é 读4åéLangGraph 宿ï¼å¹¶è¡è®¡ç®æ 游å叿¶è´¹
æ¬æä½¿ç¨ LangGraph å®ç°ä¸ä¸ªæ 游åå¸éæ©ç¨åºï¼å¤§æ¨¡åå çæåéåå¸ï¼åå¹¶è¡ä¼°ç®æ¯ä¸ªåå¸ä¸å¤©ä¸¤å¤çæ¶è´¹ï¼æåæéé¢ä»é«å°ä½æåºï¼è®©ç¨æ·è¾å ¥ç¼å·éæ©åå¸ã
æµç¨å¦ä¸ï¼
1. ç¯å¢åå¤
æ¬æç¤ºä¾ä½¿ç¨ï¼
Python 3.14.7
LangGraph 1.2.10
langchain-deepseek 1.1.0
å®è£ ä¾èµï¼
python -m pip install -U langgraph langchain-deepseek
é ç½® DeepSeek API Keyï¼
export DEEPSEEK_API_KEY="ä½ ç DeepSeek API Key"
2. å®ä¹ç¶æä¸ç»æåè¾åº
模ååå«ä¼°ç®ä½å®¿ãé¤é¥®ãå¸å 交éåé¨ç¥¨ï¼åç± Python è®¡ç®æ»æ¶è´¹ï¼
class CityExpense(BaseModel):
city: str
hotel: int = Field(ge=0)
food: int = Field(ge=0)
transport: int = Field(ge=0)
tickets: int = Field(ge=0)
class ExpenseItem(TypedDict):
city: str
consumption: int
class OveralState(TypedDict):
topic: str
subjects: list[str]
citys: Annotated[list[ExpenseItem], operator.add]
sorted_citys: list[ExpenseItem]
operator.add æ¯ citys ç reducerãå¤ä¸ªå¹¶è¡èç¹è¿åå叿¶è´¹æ¶ï¼LangGraph ä¼ä½¿ç¨åè¡¨å æ³åå¹¶ç»æï¼é¿å
å¹¶è¡åå
¥å²çªã
3. ä½¿ç¨ Send 卿并è¡
第ä¸ä¸ªèç¹çæåå¸å表åï¼è·¯ç±å½æ°ä¸ºæ¯ä¸ªåå¸å建ä¸ä¸ª Sendï¼
def continue_to_city(state: OveralState):
return [
Send("genrate_city", {"subject": city})
for city in state["subjects"]
]
å¦ææ¨¡åçæå
个åå¸ï¼LangGraph å°±ä¼å¨æå建å
个 genrate_city ä»»å¡ãæ¯ä¸ªä»»å¡åªå¤çä¸ä¸ªåå¸ã
4. 计ç®ä¸æåº
æ»æ¶è´¹ç±ä»£ç å®æå æ³ï¼
consumption = (
response.hotel
+ response.food
+ response.transport
+ response.tickets
)
ææå¹¶è¡ä»»å¡ç»æåï¼æç §æ¶è´¹éé¢éåºæåï¼
sorted_citys = sorted(
state["citys"],
key=lambda item: item["consumption"],
reverse=True,
)
çæçæµç¨å¾å¦ä¸ï¼
5. 宿´ä»£ç
以ä¸ä»£ç çç¥äºæºæä»¶ä¸çè¯¦ç»æ³¨éï¼è¿è¡é»è¾ä¿æä¸è´ï¼
import operator
import os
from typing import Annotated, TypedDict, cast
from langchain_deepseek import ChatDeepSeek
from langgraph.graph import END, START, StateGraph
from langgraph.types import Send
from pydantic import BaseModel, Field, SecretStr
subject_prompt = """
çæä¸ä¸é¢ä¸»é¢ç¸å
³çåå¸ï¼{topic}
åªè¿åä»¥ä¸ JSON æ ¼å¼ï¼ä¸è¦è¾åºå
¶ä»å
容ï¼
{{"subjects": ["åå¸1", "åå¸2", "åå¸3", "åå¸4", "åå¸5", "åå¸6"]}}
"""
city_prompt = """
ä¼°ç®ä¸åæå¹´äººå»{city}æ
游ä¸å¤©ä¸¤å¤çæ»æ¶è´¹ã
åå«ä¼°ç®ä¸¤æä½å®¿ãé¤é¥®ãå¸å
交é忝ç¹é¨ç¥¨ï¼
ä¸å
å«åºåå°å¾è¿è¯¥åå¸ç大交éè´¹ç¨ã
ææè´¹ç¨å¿
é¡»æ¯äººæ°å¸æ´æ°ï¼åä½ä¸ºå
ã
åªè¿åä»¥ä¸ JSON æ ¼å¼ï¼ä¸è¦è¾åºå
¶ä»å
容ï¼
{{"city": "åå¸åç§°", "hotel": 600, "food": 400, "transport": 150, "tickets": 350}}
"""
class Subjects(BaseModel):
subjects: list[str]
class CityExpense(BaseModel):
city: str
hotel: int = Field(ge=0)
food: int = Field(ge=0)
transport: int = Field(ge=0)
tickets: int = Field(ge=0)
class ExpenseItem(TypedDict):
city: str
consumption: int
class OveralState(TypedDict):
topic: str
subjects: list[str]
citys: Annotated[list[ExpenseItem], operator.add]
sorted_citys: list[ExpenseItem]
class CityState(TypedDict):
subject: str
deepseek = ChatDeepSeek(
model="deepseek-v4-flash",
temperature=0,
base_url="https://api.deepseek.com",
api_key=SecretStr(os.environ["DEEPSEEK_API_KEY"]),
)
def genrate_topic(state: OveralState):
prompt = subject_prompt.format(topic=state["topic"])
response = cast(
Subjects,
deepseek.with_structured_output(
Subjects, method="json_mode"
).invoke(prompt),
)
return {"subjects": response.subjects}
def genrate_city(state: CityState):
prompt = city_prompt.format(city=state["subject"])
response = cast(
CityExpense,
deepseek.with_structured_output(
CityExpense, method="json_mode"
).invoke(prompt),
)
consumption = (
response.hotel
+ response.food
+ response.transport
+ response.tickets
)
expense: ExpenseItem = {
"city": response.city,
"consumption": consumption,
}
return {"citys": [expense]}
def continue_to_city(state: OveralState):
return [
Send("genrate_city", {"subject": city})
for city in state["subjects"]
]
def sort_citys(state: OveralState):
return {
"sorted_citys": sorted(
state["citys"],
key=lambda item: item["consumption"],
reverse=True,
)
}
graph_builder = StateGraph(OveralState)
graph_builder.add_node("genrate_topic", genrate_topic)
graph_builder.add_node("genrate_city", genrate_city)
graph_builder.add_node("sort_citys", sort_citys)
graph_builder.add_edge(START, "genrate_topic")
graph_builder.add_conditional_edges(
"genrate_topic", continue_to_city, ["genrate_city"]
)
graph_builder.add_edge("genrate_city", "sort_citys")
graph_builder.add_edge("sort_citys", END)
graph = graph_builder.compile()
initial_state: OveralState = {
"topic": "沿海",
"subjects": [],
"citys": [],
"sorted_citys": [],
}
result = graph.invoke(initial_state)
city_options = result["sorted_citys"]
if not city_options:
raise ValueError("没æçæå¯éæ©çæ
游åå¸")
print("\nå叿¶è´¹æè¡ï¼ä»é«å°ä½ï¼ï¼")
for index, item in enumerate(city_options, start=1):
print(f"{index}. {item['city']}ï¼{item['consumption']} å
")
while True:
choice = input(
f"\nè¯·éæ©åå¸ç¼å·ï¼1-{len(city_options)}ï¼ï¼"
).strip()
if not choice.isdigit():
print("请è¾å
¥ææçæ°åç¼å·ã")
continue
selected_index = int(choice) - 1
if not 0 <= selected_index < len(city_options):
print("ç¼å·è¶
åºèå´ï¼è¯·éæ°éæ©ã")
continue
selected_city = city_options[selected_index]
break
6. è¾åºç¤ºä¾
è¿è¡ï¼
python index.py
è¾åºç¤ºä¾ï¼
å叿¶è´¹æè¡ï¼ä»é«å°ä½ï¼ï¼
1. 䏿µ·ï¼2350 å
2. ä¸äºï¼2200 å
3. æ·±å³ï¼1900 å
4. éå²ï¼1550 å
5. å¦é¨ï¼1450 å
6. 大è¿ï¼1350 å
è¯·éæ©åå¸ç¼å·ï¼1-6ï¼ï¼4
åå¸åéé¢ç±å¤§æ¨¡åä¼°ç®ï¼å æ¤ä¸åæ¶é´è¿è¡å¯è½å¾å°ä¸åç»æãå¹¶è¡ä»»å¡çå®æé¡ºåºä¹ä¸åºå®ï¼ä½ sort_citys ä¼å¨è¾åºåç»ä¸æç
§æ¶è´¹é颿åºã
æ»ç»
è¿ä¸ªæ¡ä¾å±ç¤ºäº LangGraph çä¸ä¸ªå®ç¨è½åï¼ä½¿ç¨ Send æ ¹æ®æ°æ®å¨æå建并è¡ä»»å¡ï¼ä½¿ç¨ reducer èåå¹¶è¡ç»æï¼ä»¥å卿±èèç¹ä¸æ§è¡ç¡®å®æ§çæåºé»è¾ã大模åè´è´£çæåä¼°ç®ï¼Python è´è´£è®¡ç®ãæåºä¸è¾å
¥æ ¡éªï¼èè´£æ´å æ¸
æ°ã
Aitishiku.com