ä»é¶åŒå§ïŒåœ»åºææ¡ AI Agent çæå»ºä¹é ------ 让 AI ä»ãåªèœè¯Žãè¿åå°ãèœæèãèœè¡åšãèœè®°å¿ã ïŒðè¥äœ ç¬¬äžæ¬¡å¬è¯ŽLangChainïŒå¯ä»¥æµè§æåŸææç« ïŒå¿«éæå»ºè®€ç¥äœç³»ïŒ
ææ¯æ ïŒLangChain v0.3 | LangGraph v0.2 | Node.js 18+
ð 代ç ä»åº & è¿è¡ç¯å¢
ðŠ æ¬æææä»£ç 瀺äŸåå·²äžäŒ GitHubïŒè¯·æŸå¿é£çšïŒ
GitHub ä»åºå°å
ð github.com/Lidh1023/no...
è¿è¡ç¯å¢èŠæ±
bash
# ç¯å¢èŠæ±
Node.js >= 18.0.0
npm >= 9.0.0
# å
é代ç
git clone https://github.com/Lidh1023/nodejs-basic.git
cd nodejs-basic/langchain-learning
# å®è£
äŸèµ
npm install
# é
眮ç¯å¢åéïŒå建 .env æä»¶ïŒ
DEEPSEEK_API_KEY=your_api_key_here
# è¿è¡ç€ºäŸ
node src/agent.js
äŸèµå çæ¬
| å å | çæ¬ | 诎æ |
|---|---|---|
@langchain/langgraph |
^0.2.x | LangGraph æ žå¿åº |
@langchain/core |
^0.3.x | LangChain æ žå¿ç»ä»¶ |
@langchain/deepseek |
^0.1.x | DeepSeek æš¡åæ¯æ |
zod |
^3.x | åæ°æ ¡éª |
dotenv |
^16.x | ç¯å¢åé管ç |
ð¡ Tips: æå代ç åïŒåªéé 眮奜 API KeyïŒå³å¯çŽæ¥è¿è¡ææç€ºäŸä»£ç ïŒ
ð ååšåé¢
äœ æ¯åŠæ³è¿è¿äºé®é¢ïŒ
- ð€ 䞺ä»ä¹ ChatGPT åªèœè倩ïŒäžèœåž®æçæ£åäºïŒ
- ð€ åŠäœè®© AI èªå·±å³å®ä»ä¹æ¶åæ¥èµæãä»ä¹æ¶å计ç®ïŒ
- ð€ AI Agent å°åºæ¯ä»ä¹ïŒåæ®éç AI å¯¹è¯æä»ä¹åºå«ïŒ
- ð€ LangChain Agent å LangGraph å°åºæ¯ä»ä¹å ³ç³»ïŒ
æ¬æå°åžŠäœ 圻åºçè§£ Agent çæŠå¿µãåçåå®ç°ïŒ
è¯»å®æ¬æïŒäœ å°èœå€ïŒ
- â çè§£ LangChain äž LangGraph çå ³ç³»
- â ææ¡ Agent çæ¬èŽšåå·¥äœåç
- â ææ¡ ReAct æš¡åŒïŒæšç + è¡åšïŒ
- â äœ¿çš LangGraph æå»ºèªå·±ç Agent
- â 䞺 Agent æ·»å è®°å¿åèœïŒå®ç°å€èœ®å¯¹è¯
- â çè§£ Agent äž Tools çåäœå ³ç³»
ð æç« 倧纲
yaml
ð Part 1: åºç¡æŠå¿µ
âââ LangChain äž LangGraph çå
³ç³»
âââ ä»ä¹æ¯ Agent
âââ Agent vs æ®é LLM
ð Part 2: ReAct æš¡åŒ
âââ ReAct æš¡åŒè¯Šè§£
âââ ReAct çæ žå¿åŸªç¯
ð§© Part 3: Agent æ žå¿ç»ä»¶
âââ ç»ä»¶æ¶æåŸ
âââ LLMïŒå€§èïŒ
âââ ToolsïŒå·¥å
·ïŒ
âââ StateïŒç¶æïŒ
âââ GraphïŒæµçšåŸïŒ
ð ïž Part 4: æå»º Agent
âââ ç¯å¢åå€
âââ 宿Žä»£ç 瀺äŸ
âââ Agent çå·¥äœæµçš
ðŸ Part 5: è®°å¿åèœ
âââ 䞺ä»ä¹éèŠè®°å¿
âââ MemorySaver 诊解
âââ thread_id çº¿çšæ è¯
ð¯ Part 6: 宿æ¡äŸ
âââ å€èœ®å¯¹è¯æºåšäºº
âââ 垊记å¿ç Agent
âââ æºèœå®¢æ Agent
â
Part 7: æäœ³å®è·µ
â Part 8: åžžè§é®é¢ FAQ
ð Part 9: è¿é¶äž»é¢
Part 1: åºç¡æŠå¿µ
LangChain äž LangGraph çå ³ç³»
ð¡ è¿æ¯åŸå€ååŠè çå°æç¹ïŒè®©æä»¬å æè¿äžªé®é¢ææž æ¥
äžå¥è¯åç
Agent æ¯äžäžªéçšæŠå¿µïŒäœåš LangChain çæäžïŒæå»ºå€æ Agent çèœåäž»èŠç± LangGraph æäŸã
䞀è çå®äœåºå«
scss
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â LangChain çæç³»ç» â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â LangChain â â
â â â â
â â ðŠ åºç¡ç»ä»¶åº â â
â â ⢠LLMïŒå€§è¯èšæš¡åïŒ â â
â â ⢠PromptïŒæç€ºè¯æš¡æ¿ïŒ â â
â â ⢠ToolsïŒå·¥å
·å®ä¹ïŒ â â
â â ⢠MemoryïŒè®°å¿ç»ä»¶ïŒ â â
â â ⢠ç®åç»åïŒpipe()ãRunnableSequence â â
â â ⢠䞻èŠå€çïŒçº¿æ§æµçš â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â â
â ⌠â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â LangGraph â â
â â â â
â â ð å·¥äœæµçŒææ¡æ¶ â â
â â â¢ ç¶æç®¡çïŒStateGraphïŒ â â
â â â¢ å€ææµçšïŒåæ¯ã埪ç¯ãå¹¶è¡ â â
â â ⢠Agent æš¡åŒïŒReActïŒ â â
â â â¢ æ£æ¥ç¹/è®°å¿ïŒCheckpointerïŒ â â
â â ⢠䞻èŠå€çïŒé线æ§ãæç¶æç倿æµçš â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
诊ç»å¯¹æ¯
| å±é¢ | LangChain | LangGraph |
|---|---|---|
| å®äœ | åºç¡ç»ä»¶åº | å·¥äœæµçŒææ¡æ¶ |
| æäŸ | LLMãPromptãToolsãMemory | ç¶æç®¡çã埪ç¯ãæ¡ä»¶åæ¯ |
| æµçš | çº¿æ§æµçš | 倿çéçº¿æ§æµçš |
| Agent | æ©ææç®å Agent API | æšèç Agent æå»ºæ¹åŒ |
| éçšåºæ¯ | ç®åç Prompt â LLM â Parser | æ¡ä»¶åæ¯ã埪ç¯è¿ä»£ã倿 Agent |
é å䜿çšç€ºäŸ
javascript
// LangChain ç»ä»¶
import { ChatDeepSeek } from "@langchain/deepseek";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { DynamicStructuredTool } from "@langchain/core/tools";
// LangGraph çŒæ
import { StateGraph, Annotation } from "@langchain/langgraph";
// ç»å䜿çš
const llm = new ChatDeepSeek({ model: "deepseek-chat" });
const tools = [
new DynamicStructuredTool({
/* ... */
}),
];
const llmWithTools = llm.bindTools(tools); // LangChain èœå
const graph = new StateGraph(State) // LangGraph çŒæ
.addNode("agent", async (state) => {
const response = await llmWithTools.invoke(state.messages);
return { messages: [response] };
});
éæ©æå
| åºæ¯ | éæ© |
|---|---|
| ç®åç Prompt â LLM â Parser | LangChain pipe() |
| RAG æ£çŽ¢é®ç | LangChain |
| 倿¥éª€çææµçš | 䞀è éœå¯ |
| æ¡ä»¶åæ¯é»èŸ | LangGraph |
| 埪ç¯/è¿ä»£ä»»å¡ | LangGraph |
| 倿 Agent | LangGraph |
| å€ Agent åäœ | LangGraph |
䞺ä»ä¹ Agent èŠçš LangGraphïŒ
Agent ç ReAct æš¡åŒéèŠïŒ
- â 埪ç¯è¿ä»£ - LangGraph åçæ¯æ
- â æ¡ä»¶åæ¯ - æ ¹æ®å·¥å ·è°çšç»æå³å®äžäžæ¥
- â ç¶æç®¡ç - èªåšç®¡ç对è¯åå²åäžéŽç¶æ
è¿äºèœå LangChain çç®å pipe() éŸä»¥å®ç°ïŒäœ LangGraph åçæ¯æã
ð¡ æ»ç» ïŒAgent çæŠå¿µåç»ä»¶ ïŒåŠ ToolsïŒæ¥èª LangChainïŒäœæå»ºåçŒæ Agent çèœåäž»èŠç± LangGraph æäŸãç°åšå®æ¹æšèäœ¿çš LangGraph æ¥æå»ºç产级ç Agentã
ä»ä¹æ¯ Agent
ð€ äžå¥è¯è§£é
AgentïŒæºèœä»£çïŒæ¯èœå€èªäž»æèãå³çå¹¶æ§è¡ä»»å¡ç AI ç³»ç»ã
å®äžä» èœ"诎"ïŒçæææ¬ïŒïŒè¿èœ"å"ïŒè°çšå·¥å ·æ§è¡æäœïŒã
圢象æ¯å»
æ³è±¡äœ éäºäžäžªå©çïŒ
| ç±»å | è¡äžºç¹ç¹ |
|---|---|
| æ®é AI | åªäŒåçé®é¢ïŒ"å€©æ°æäžç¥éïŒäœ èªå·±æ¥å§" |
| AI Agent | äŒäž»åšè¡åšïŒ"æåž®äœ æ¥äžäž... å京ä»å€© 15°C" |
Agent = æææèç AI
Agent çæ žå¿èœå
scss
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Agent çæ žå¿èœå â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â ð§ æšçèœå (Reasoning) â
â çè§£çšæ·æåŸïŒå解任å¡ïŒå¶å®è®¡å â
â â
â ð§ å·¥å
·äœ¿çš (Tool Use) â
â è°çšå€éšå·¥å
·ïŒæçŽ¢ã计ç®ãAPIãæ°æ®åºç â
â â
â ð èªäž»åŸªç¯ (Autonomous Loop) â
â æè â è¡åš â è§å¯ â åæèïŒçŽå°å®æä»»å¡ â
â â
â ð è®°å¿èœå (Memory) â
â è®°äœå¯¹è¯åå²åäžäžæïŒä¿æè¿èŽ¯æ§ â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
Agent vs æ®é LLM
å ³é®åºå«
| 对æ¯ç»ŽåºŠ | æ®é LLM | Agent |
|---|---|---|
| èœå蟹ç | åªèœçæææ¬ | å¯ä»¥è°çšå·¥å ·ãæ§è¡æäœ |
| ä¿¡æ¯æ¥æº | ä» éè®ç»æ°æ® | å¯è·å宿¶ä¿¡æ¯ïŒæçŽ¢ãAPIïŒ |
| 任塿§è¡ | äžé®äžçïŒè¢«åšååº | èªäž»è§åã倿¥æ§è¡ |
| å³çæ¹åŒ | æ | æ ¹æ®æ åµèªäž»å³å®äžäžæ¥ |
| å€æä»»å¡ | éŸä»¥å€ç | å¯åè§£å¹¶éæ¥å®æ |
å®é 对æ¯ç€ºäŸ
åºæ¯ïŒçšæ·é®ãåž®æè®¢äžåŒ æå€©å»äžæµ·çæºç¥šã
æ®é LLM çåç
markdown
æ±æïŒææ æ³åž®æšè®¢æºç¥šãæšå¯ä»¥éè¿ä»¥äžæ¹åŒé¢è®¢ïŒ
1. è®¿é®æºçšãå»åªå¿ççœç«
2. äžèœœèªç©ºå
¬åž App
3. æšæèªç©ºå
¬åžå®¢æçµè¯
...
Agent çè¡äžº
ini
[æè] çšæ·æ³è®¢æºç¥šïŒæéèŠïŒ
1. 确讀åºåå°
2. æçŽ¢èªç
3. éæ©åéçèªç
4. 宿é¢è®¢
[è¡åš 1] è°çš search_flights(from="å京", to="äžæµ·", date="æå€©")
[è§å¯ 1] æŸå° 15 䞪èªçïŒæäœä»· Â¥550
[è¡åš 2] è°çš get_flight_details(flight_id="CA1234")
[è§å¯ 2] CA1234ïŒ08:00-10:30ïŒÂ¥680
[æè] éèŠçšæ·ç¡®è®€èªçéæ©...
[åç] ææŸå°äº 15 䞪èªçïŒæšè以äžé项ïŒ
- CA1234ïŒ08:00-10:30ïŒÂ¥680
- MU5678ïŒ10:00-12:30ïŒÂ¥550
è¯·é®æšæ³é¢è®¢åªäžªïŒ
Part 2: ReAct æš¡åŒ
ReAct æš¡åŒè¯Šè§£
ä»ä¹æ¯ ReActïŒ
ReAct = Reasoning + ActingïŒæšç + è¡åšïŒ
è¿æ¯æå»º Agent ææµè¡çèåŒïŒæºèª 2022 幎ç论æãReAct: Synergizing Reasoning and Acting in Language Modelsãã
ReAct çæ žå¿åŸªç¯
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â ReAct åŸªç¯æµçš â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â âââââââââââ â
â â Thought â æèïŒåæåœåç¶æïŒå³å®äžäžæ¥ â
â ââââââ¬âââââ â
â â â
â ⌠â
â âââââââââââ â
â â Action â è¡åšïŒè°çšå·¥å
·æ§è¡æäœ â
â ââââââ¬âââââ â
â â â
â ⌠â
â âââââââââââââââ â
â â Observation â è§å¯ïŒè·åå·¥å
·è¿åçç»æ â
â ââââââââ¬âââââââ â
â â â
â ⌠â
â ââââââââââââââââ â
â â 忬¡æè â æ¯åŠéèŠç»§ç»ïŒ â
â ââââââââ¬ââââââââ â
â â â
â âââââââŽââââââ â
â â â â
â ⌠⌠â
â ç»§ç»åŸªç¯ çææç»çæ¡ â
â â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
å ·äœç€ºäŸ
çšæ·é®ïŒå京æ¯äžæµ·å·å€å°åºŠïŒ
scss
[Thought 1]
çšæ·æ³ç¥éæž©åºŠå·®ïŒæéèŠïŒ
1. æ¥è¯¢å京倩æ°
2. æ¥è¯¢äžæµ·å€©æ°
3. è®¡ç®æž©åºŠå·®
[Action 1]
è°çš get_weather(city="å京")
[Observation 1]
{"city": "å京", "temperature": "5°C", "condition": "æŽå€©"}
[Thought 2]
å·²è·åå京枩床 5°CïŒç°åšéèŠæ¥äžæµ·
[Action 2]
è°çš get_weather(city="äžæµ·")
[Observation 2]
{"city": "äžæµ·", "temperature": "12°C", "condition": "å€äº"}
[Thought 3]
å京 5°CïŒäžæµ· 12°CïŒéèŠè®¡ç®å·®åŒ
[Action 3]
è°çš calculator(expression="12 - 5")
[Observation 3]
12 - 5 = 7
[Thought 4]
计ç®å®æïŒäžæµ·æ¯åäº¬é« 7 床ïŒå¯ä»¥åçäº
[Final Answer]
ç®ååäº¬æ°æž© 5°CïŒäžæµ·æ°æž© 12°CïŒäžæµ·æ¯åäº¬é« 7°Cã
å京ä»å€©äŒæ¯èŸå·ïŒåºéšè®°åŸå€ç©¿è¡£æïŒ
䞺ä»ä¹ ReAct ææïŒ
| äŒå¿ | 诎æ |
|---|---|
| å¯è§£éæ§ | æ¯äžæ¥æèéœå¯è§ïŒäŸ¿äºè°è¯åçè§£ |
| åç¡®æ§ | éè¿å·¥å ·è·åç宿°æ®ïŒé¿å LLM å¹»è§ |
| çµæŽ»æ§ | 坿 ¹æ®äžéŽç»æåšæè°æŽçç¥ |
| 坿©å±æ§ | åªéæ·»å æ°å·¥å ·å³å¯æ©å± Agent èœå |
Part 3: Agent æ žå¿ç»ä»¶
ç»ä»¶æ¶æåŸ
yaml
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Agent æ¶æ â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ð§ LLMïŒå€§èïŒ â â
â â ⢠çè§£çšæ·æåŸ â â
â â ⢠æšçåå³ç â â
â â ⢠çæåç â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â â
â ⌠â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ð§ ToolsïŒå·¥å
·ïŒ â â
â â âââââââââââ âââââââââââ âââââââââââ âââââââââââ â â
â â â å€©æ° â â æçŽ¢ â â è®¡ç® â â æ°æ®åº â ... â â
â â âââââââââââ âââââââââââ âââââââââââ âââââââââââ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â â
â ⌠â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ð StateïŒç¶æïŒ â â
â â ⢠messages: 对è¯åå² â â
â â ⢠å
¶ä»äžäžæä¿¡æ¯ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â â
â ⌠â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â ð GraphïŒæµçšåŸïŒ â â
â â å®ä¹èç¹ä¹éŽçæµèœ¬é»èŸ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
1. LLMïŒå€§èïŒ
LLM æ¯ Agent çæ žå¿ïŒèŽèŽ£ïŒ
- çè§£ïŒè§£æçšæ·æåŸ
- æšçïŒåæé®é¢ïŒå¶å®çç¥
- å³çïŒéæ©åéçå·¥å ·
- çæïŒäº§åºæç»åç
javascript
import { ChatDeepSeek } from "@langchain/deepseek";
const llm = new ChatDeepSeek({
model: "deepseek-chat",
temperature: 0, // å·¥å
·è°çšå»ºè®®äœæž©åºŠ
});
2. ToolsïŒå·¥å ·ïŒ
å·¥å ·æ¯ Agent ç"åæ"ïŒæ©å± Agent çèœå蟹çïŒ
javascript
import { DynamicStructuredTool } from "@langchain/core/tools";
import { z } from "zod";
// 瀺äŸïŒå€©æ°æ¥è¯¢å·¥å
·
const weatherTool = new DynamicStructuredTool({
name: "get_weather",
description: "è·åååžå€©æ°ä¿¡æ¯ãåœçšæ·è¯¢é®å€©æ°æ¶äœ¿çšã",
schema: z.object({
city: z.string().describe("ååžåç§°"),
}),
func: async ({ city }) => {
// è°çšå€©æ° API
return JSON.stringify({ city, temp: "15°C", condition: "æŽå€©" });
},
});
3. StateïŒç¶æïŒ
ç¶æä¿å Agent ç"è®°å¿"ïŒ
javascript
import { Annotation } from "@langchain/langgraph";
const AgentState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next], // çŽ¯å æ¶æ¯
default: () => [],
}),
});
æ¶æ¯ç±»åïŒ
| ç±»å | 诎æ |
|---|---|
HumanMessage |
çšæ·åéçæ¶æ¯ |
AIMessage |
AI çåå€ïŒå¯èœå å« tool_callsïŒ |
ToolMessage |
å·¥å ·æ§è¡çç»æ |
SystemMessage |
ç³»ç»æä»€ |
4. GraphïŒæµçšåŸïŒ
äœ¿çš LangGraph å®ä¹ Agent çæ§è¡æµçšïŒ
javascript
import { StateGraph, START, END } from "@langchain/langgraph";
const graph = new StateGraph(AgentState)
.addNode("agent", agentNode) // æèèç¹
.addNode("tools", toolNode) // å·¥å
·èç¹
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldCallTools, {
tools: "tools",
end: END,
})
.addEdge("tools", "agent"); // å
³é®ïŒåŸªç¯ïŒ
Part 4: æå»º Agent
ç¯å¢åå€
bash
# å®è£
äŸèµ
npm install @langchain/langgraph @langchain/core @langchain/deepseek zod dotenv
javascript
// .env æä»¶
DEEPSEEK_API_KEY = your_api_key;
宿Žä»£ç 瀺äŸ
javascript
/**
* äœ¿çš LangGraph æå»º ReAct Agent
*/
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { ChatDeepSeek } from "@langchain/deepseek";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { HumanMessage } from "@langchain/core/messages";
import { z } from "zod";
import "dotenv/config";
// 1. å®ä¹å·¥å
·
const tools = [
new DynamicStructuredTool({
name: "get_weather",
description: "è·åååžå€©æ°",
schema: z.object({ city: z.string() }),
func: async ({ city }) => {
const data = { å京: "15°C æŽ", äžæµ·: "20°C å€äº" };
return data[city] || "22°C æŽå€©";
},
}),
new DynamicStructuredTool({
name: "calculator",
description: "æ°åŠè®¡ç®",
schema: z.object({ expression: z.string() }),
func: async ({ expression }) => {
return String(Function(`"use strict"; return (${expression})`)());
},
}),
];
// 2. åå§å LLM å¹¶ç»å®å·¥å
·
const llm = new ChatDeepSeek({ model: "deepseek-chat", temperature: 0 });
const llmWithTools = llm.bindTools(tools);
// 3. å®ä¹ç¶æ
const AgentState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next],
default: () => [],
}),
});
// 4. å®ä¹èç¹
async function agentNode(state) {
const response = await llmWithTools.invoke(state.messages);
return { messages: [response] };
}
const toolNode = new ToolNode(tools);
// 5. å®ä¹è·¯ç±
function shouldCallTools(state) {
const last = state.messages[state.messages.length - 1];
return last.tool_calls?.length > 0 ? "tools" : "end";
}
// 6. æå»ºåŸ
const graph = new StateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldCallTools, {
tools: "tools",
end: END,
})
.addEdge("tools", "agent");
// 7. çŒè¯å¹¶äœ¿çš
const agent = graph.compile();
const result = await agent.invoke({
messages: [new HumanMessage("åäº¬å€©æ°æä¹æ ·ïŒ")],
});
console.log(result.messages[result.messages.length - 1].content);
代ç è§£æ
æ¥éª€ 1-2ïŒå®ä¹å·¥å ·å¹¶ç»å®
javascript
// å建工å
·
const tools = [weatherTool, calculatorTool];
// ç»å®å° LLMïŒå
³é®ïŒïŒ
const llmWithTools = llm.bindTools(tools);
bindTools() åè¯ LLM æåªäºå·¥å
·å¯çšïŒLLM äŒïŒ
- åšéèŠæ¶è¿å
tool_calls - å å«å·¥å ·åç§°ååæ°
æ¥éª€ 3ïŒå®ä¹ç¶æ
javascript
const AgentState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next], // 环å ïŒ
default: () => [],
}),
});
䞺ä»ä¹çšçŽ¯å æš¡åŒïŒ
- 对è¯éèŠä¿çåå²
- LLM éèŠå®æŽäžäžæ
- å·¥å ·ç»æéèŠè¿œå
æ¥éª€ 4ïŒå®ä¹èç¹
javascript
// Agent èç¹ïŒè°çš LLM åå³ç
async function agentNode(state) {
const response = await llmWithTools.invoke(state.messages);
return { messages: [response] };
}
// Tool èç¹ïŒæ§è¡å·¥å
·ïŒäœ¿çšå
眮 ToolNodeïŒ
const toolNode = new ToolNode(tools);
ToolNode èªåšå€çïŒ
- è§£æ
tool_calls - æ§è¡å¯¹åºå·¥å ·
- å
è£
ç»æäžº
ToolMessage
æ¥éª€ 5ïŒå®ä¹è·¯ç±
javascript
function shouldCallTools(state) {
const last = state.messages[state.messages.length - 1];
// æ tool_calls â 廿§è¡å·¥å
·
// 没æ â ç»æ
return last.tool_calls?.length > 0 ? "tools" : "end";
}
æ¥éª€ 6ïŒæå»ºåŸ
javascript
const graph = new StateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addEdge(START, "agent") // å
¥å£
.addConditionalEdges("agent", shouldCallTools, {
tools: "tools",
end: END,
})
.addEdge("tools", "agent"); // 埪ç¯å agentïŒ
å
³é®ïŒtools â agent 圢æåŸªç¯ïŒ
è¿è®© Agent å¯ä»¥å€æ¬¡è°çšå·¥å ·ïŒçŽå°å®æä»»å¡ã
Agent çå·¥äœæµçš
æµçšåŸ
scss
çšæ·èŸå
¥
â
âŒ
ââââââââââââââââââ
â START â
âââââââââ¬âââââââââ
â
âŒ
ââââââââââââââââââ
â Agent èç¹ âââââââââââââââââ
â (LLM æè) â â
âââââââââ¬âââââââââ â
â â
⌠â
ââââââââââââââââââ â
â è·¯ç±å€æ â â
â æ tool_calls? â â
âââââââââ¬âââââââââ â
â â
ââââââââââââŽâââââââââââ â
â Yes â No â
⌠⌠â
ââââââââââââââ ââââââââââââââ â
â Tools èç¹ â â END â â
â (æ§è¡å·¥å
·) â â èŸåºç»æ â â
âââââââ¬âââââââ ââââââââââââââ â
â â
ââââââââââââââââââââââââââââââââââââââ
å·¥å
·ç»æè¿å
æ¶æ¯æµèœ¬ç€ºäŸ
以ãåäº¬å€©æ°æä¹æ ·ïŒã䞺äŸïŒ
javascript
// åå§ç¶æ
messages: [
HumanMessage("åäº¬å€©æ°æä¹æ ·ïŒ")
]
// Agent èç¹æ§è¡å
messages: [
HumanMessage("åäº¬å€©æ°æä¹æ ·ïŒ"),
AIMessage({
content: "",
tool_calls: [{ name: "get_weather", args: { city: "å京" } }]
})
]
// Tools èç¹æ§è¡å
messages: [
HumanMessage("åäº¬å€©æ°æä¹æ ·ïŒ"),
AIMessage({ tool_calls: [...] }),
ToolMessage({ content: '{"city":"å京","temp":"15°C"}' })
]
// Agent èç¹å次æ§è¡åïŒæç»ïŒ
messages: [
HumanMessage("åäº¬å€©æ°æä¹æ ·ïŒ"),
AIMessage({ tool_calls: [...] }),
ToolMessage({ content: '...' }),
AIMessage({ content: "å京ä»å€©å€©æ°æŽæïŒæ°æž©15°CïŒéååºè¡ïŒ" })
]
Part 5: è®°å¿åèœ
䞺ä»ä¹éèŠè®°å¿
没æè®°å¿çé®é¢
javascript
// â æ²¡æè®°å¿ç对è¯ïŒæ¯æ¬¡éœæ¯å
šæ°çïŒ
// ç¬¬äžæ¬¡å¯¹è¯
await app.invoke({ messages: [new HumanMessage("æå«å°æ")] });
// AI: "äœ å¥œå°æïŒ"
// ç¬¬äºæ¬¡å¯¹è¯ïŒAI å®å
šäžè®°åŸïŒïŒ
await app.invoke({ messages: [new HumanMessage("æå«ä»ä¹ååïŒ")] });
// AI: "æ±æïŒæäžç¥éäœ çååïŒäœ è¿æ²¡åè¯æå¢" â å°Žå°¬ïŒ
æè®°å¿åçææ
javascript
// â
æè®°å¿ç对è¯ïŒè¿èޝèªç¶ïŒ
// ç¬¬äžæ¬¡å¯¹è¯
await app.invoke(
{ messages: [new HumanMessage("æå«å°æ")] },
{ configurable: { thread_id: "user_001" } }
);
// AI: "äœ å¥œå°æïŒ"
// ç¬¬äºæ¬¡å¯¹è¯ïŒåäžäžª thread_idïŒAI è®°åŸïŒïŒ
await app.invoke(
{ messages: [new HumanMessage("æå«ä»ä¹ååïŒ")] },
{ configurable: { thread_id: "user_001" } }
);
// AI: "äœ å«å°æåïŒ" â å®çŸïŒ
è®°å¿åèœçåºçšåºæ¯
| åºæ¯ | æè¿° | 䞺ä»ä¹éèŠè®°å¿ |
|---|---|---|
| å®¢ææºåšäºº | å€ççšæ·é®é¢ | è®°äœçšæ·èº«ä»œãä¹åçé®é¢ãå€çè¿åºŠ |
| 䞪人å©ç | æ¥çšç®¡çãä»»å¡è·èžª | è®°äœçšæ·å奜ãåŸ åäºé¡¹ãäžäžæ |
| æè²èŸ 富 | äžå¯¹äžæåŠ | è®°äœåŠä¹ è¿åºŠãè匱ç¹ãåŠä¹ 飿 Œ |
| æžžæ NPC | 亀äºåŒå¯¹è¯ | è®°äœå§æ è¿å±ãç©å®¶éæ©ãå ³ç³»ç¶æ |
| å¿çåšè¯¢ | éªäŒŽå¯¹è¯ | è®°äœçšæ·æ åµãåå²å¯¹è¯ãæ 绪åå |
MemorySaver 诊解
ä»ä¹æ¯ MemorySaver
MemorySaver = 让 AI è®°äœå¯¹è¯åå²çå·¥å ·
æ³è±¡äžäžïŒäœ åæåè倩ïŒ
- â æ²¡æ MemorySaverïŒæ¯å¥è¯ AI éœåœäœæ°å¯¹è¯ïŒå®å šäžè®°åŸä¹å诎äºä»ä¹
- â æ MemorySaverïŒAI èœè®°äœäœ 诎è¿çæ¯äžå¥è¯ïŒåçäººäžæ ·è¿èޝ坹è¯
圢象æ¯å»
less
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
â ð MemorySaver å°±åäžæ¬ãå¯¹è¯æ¥è®°ã â
â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â å¯¹è¯æ¥è®° (thread_id: "user_001") â â
â â âââââââââââââââââââââââââââââââââââââââââââââââââ â â
â â [蜮次 1] çšæ·: äœ å¥œïŒæå«å°æ â â
â â AI: äœ å¥œå°æïŒæä»ä¹å¯ä»¥åž®äœ çïŒ â â
â â âââââââââââââââââââââââââââââââââââââââââââââââââ â â
â â [蜮次 2] çšæ·: æå欢çŒçš â â
â â AI: çŒçšåŸæ£ïŒå°æïŒäœ äž»èŠçšä»ä¹è¯èšå¢ïŒ â â
â â âââââââââââââââââââââââââââââââââââââââââââââââââ â â
â â [蜮次 3] çšæ·: æå«ä»ä¹ååïŒ â â
â â AI: äœ å«å°æåïŒïŒå 䞺æè®°åŸïŒïŒ â â
â âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ â
â â
â ð å
³é®ç¹ïŒAI èœåå¿èµ·ä¹å诎ç"å°æ"å"忬¢çŒçš" â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
æ žå¿æŠå¿µ
ð¹ CheckpointerïŒæ£æ¥ç¹åšïŒ
éä¿è§£éïŒå°±åæžžæéçãåæ¡£åèœã
- æžžæåæ¡£ïŒä¿åæžžæè¿åºŠïŒäžæ¬¡å¯ä»¥ç»§ç»ç©
- CheckpointerïŒä¿å对è¯ç¶æïŒäžæ¬¡å¯ä»¥ç»§ç»è
css
æžžæåæ¡£ LangGraph Checkpointer
âââââââââ âââââââââââââââââââââ
åæ¡£äœçœ®: 第3å
³Bosså åæ¡£äœçœ®: 第5蜮对è¯å
è§è²ç级: 50级 æ¶æ¯åå²: [msg1, msg2, ...]
èå
ç©å: [å, çŸ, è¯æ°Ž] ç¶ææ°æ®: { topic: "çŒçš", ... }
ð¹ MemorySaver
éä¿è§£éïŒå°åæ¡£ä¿ååšãå åãéçåæ¡£åš
- äŒç¹ïŒé床快ã䜿çšç®å
- 猺ç¹ïŒçšåºå ³éååæ¡£äž¢å€±ïŒå°±å没ææè®°å¿å¡çæžžææºïŒ
javascript
import { MemorySaver } from "@langchain/langgraph";
const memory = new MemorySaver(); // å建äžäžª"å
ååæ¡£åš"
åºæ¬äœ¿çšæ¥éª€
javascript
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph"; // 1ïžâ£ 富å
¥
// 2ïžâ£ å建 MemorySaver å®äŸ
const memory = new MemorySaver();
// 3ïžâ£ å®ä¹ç¶æ
const ChatState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next],
default: () => [],
}),
});
// 4ïžâ£ æå»ºåŸ
const graph = new StateGraph(ChatState)
.addNode("chat", chatNode)
.addEdge(START, "chat")
.addEdge("chat", END);
// 5ïžâ£ çŒè¯æ¶äŒ å
¥ checkpointer
const app = graph.compile({
checkpointer: memory, // å
³é®ïŒæ·»å è®°å¿åèœ
});
// 6ïžâ£ è°çšæ¶æå® thread_id
const result = await app.invoke(
{ messages: [new HumanMessage("äœ å¥œ")] },
{ configurable: { thread_id: "my_thread_123" } } // å
³é®ïŒæå®å¯¹è¯çº¿çš
);
ð¡ å ³é®çè§£ ïŒ
compile()äžäŒcheckpointeræ¶ïŒåºç𿲡æè®°å¿åèœïŒäŒ äºææïŒ
thread_id çº¿çšæ è¯
ä»ä¹æ¯ thread_id
thread_id æ¯å¯¹è¯çã身仜è¯å·ãïŒçšæ¥åºåäžåç对è¯ã
php
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â thread_id çäœçš â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â æ³è±¡äžäžªå®¢æç³»ç»ïŒåæ¶æå¡å€äžªçšæ·ïŒ â
â â
â çšæ· A (thread_id: "user_a_session") â
â âââ "æèŠé莧" â AIè®°äœ â
â âââ "订åå·æ¯123" â AIå
³èå°éèŽ§è¯·æ± â
â â
â çšæ· B (thread_id: "user_b_session") â
â âââ "æšèäžæ¬Ÿææº" â AIè®°äœ â
â âââ "é¢ç®3000" â AIç»åæšèéæ± â
â â
â äž€äžªçšæ·ç对è¯å®å
šç¬ç«ïŒäºäžå¹²æ°ïŒ â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
thread_id çåœå建议
javascript
// â
奜ç thread_id åœå
{
thread_id: "user_12345";
} // çšæ·ID
{
thread_id: "session_abc123";
} // äŒè¯ID
{
thread_id: "user_12345_chat_1";
} // çšæ·ID + 对è¯åºå·
{
thread_id: "order_inquiry_67890";
} // äžå¡ç±»å + ID
// â äžå¥œç thread_id åœå
{
thread_id: "1";
} // 倪ç®åïŒå®¹æå²çª
{
thread_id: "test";
} // äžå
·äœ
{
thread_id: "";
} // 空å笊䞲
å€çšæ·åºæ¯ç€ºäŸ
javascript
const memory = new MemorySaver();
const app = graph.compile({ checkpointer: memory });
// çšæ· A ç对è¯
async function chatWithUserA() {
const config = { configurable: { thread_id: "user_a" } };
await app.invoke({ messages: [new HumanMessage("ææ¯çšæ·A")] }, config);
await app.invoke({ messages: [new HumanMessage("æå欢Python")] }, config);
// çšæ· A ç对è¯åå²åªå
å«çšæ· A 诎çè¯
}
// çšæ· B ç对è¯
async function chatWithUserB() {
const config = { configurable: { thread_id: "user_b" } };
await app.invoke({ messages: [new HumanMessage("ææ¯çšæ·B")] }, config);
await app.invoke({ messages: [new HumanMessage("æå欢Java")] }, config);
// çšæ· B ç对è¯åå²åªå
å«çšæ· B 诎çè¯
}
// äž€äžªçšæ·ç对è¯äºäžåœ±å
await chatWithUserA();
await chatWithUserB();
Part 6: 宿æ¡äŸ
æ¡äŸ 1ïŒå€èœ®å¯¹è¯æºåšäºº
éæ±åæ
æå»ºäžäžªèœå€ïŒ
- è®°äœçšæ·åå
- è®°äœå¯¹è¯åå²
- åºäºäžäžæåçé®é¢
宿Žä»£ç
javascript
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph";
import { ChatDeepSeek } from "@langchain/deepseek";
import {
HumanMessage,
AIMessage,
SystemMessage,
} from "@langchain/core/messages";
import "dotenv/config";
// 1. å建ç»ä»¶
const memory = new MemorySaver();
const llm = new ChatDeepSeek({ model: "deepseek-chat", temperature: 0.7 });
// 2. å®ä¹ç¶æ
const ChatbotState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next],
default: () => [],
}),
});
// 3. å®ä¹è倩èç¹
async function chatNode(state) {
// æ·»å ç³»ç»æç€ºïŒè®© AI ç¥éèŠè®°äœäžäžæïŒ
const systemPrompt = new SystemMessage(
"äœ æ¯äžäžªå奜ç婿ã请记äœçšæ·åšå¯¹è¯äžæå°çä¿¡æ¯ïŒåŠååãå奜çïŒïŒ" +
"å¹¶åšåç»å¯¹è¯äžèªç¶å°äœ¿çšè¿äºä¿¡æ¯ãä¿æå¯¹è¯è¿èޝæ§ã"
);
// ç»åæ¶æ¯ïŒç³»ç»æç€º + å岿¶æ¯
const messagesWithSystem = [systemPrompt, ...state.messages];
const response = await llm.invoke(messagesWithSystem);
return { messages: [response] };
}
// 4. æå»ºåŸ
const chatbot = new StateGraph(ChatbotState)
.addNode("chat", chatNode)
.addEdge(START, "chat")
.addEdge("chat", END)
.compile({ checkpointer: memory });
// 5. 对è¯åœæ°
async function chat(threadId, userMessage) {
const config = { configurable: { thread_id: threadId } };
const result = await chatbot.invoke(
{ messages: [new HumanMessage(userMessage)] },
config
);
// è·åæåäžæ¡ AI åå€
const aiResponse = result.messages[result.messages.length - 1];
return aiResponse.content;
}
// 6. æµè¯å€èœ®å¯¹è¯
async function main() {
const threadId = "demo_conversation";
console.log("ð€ åŒå§å€èœ®å¯¹è¯æŒç€º\n");
console.log("=".repeat(50));
// 第äžèœ®
console.log("ð€ çšæ·: äœ å¥œïŒæå«å°æ");
let response = await chat(threadId, "äœ å¥œïŒæå«å°æ");
console.log(`ð€ AI: ${response}\n`);
// 第äºèœ®
console.log("ð€ çšæ·: æå欢çŒçšïŒç¹å«æ¯ JavaScript");
response = await chat(threadId, "æå欢çŒçšïŒç¹å«æ¯ JavaScript");
console.log(`ð€ AI: ${response}\n`);
// 第äžèœ®ïŒæµè¯è®°å¿ïŒ
console.log("ð€ çšæ·: æå«ä»ä¹ååïŒæåæ¬¢ä»ä¹ïŒ");
response = await chat(threadId, "æå«ä»ä¹ååïŒæåæ¬¢ä»ä¹ïŒ");
console.log(`ð€ AI: ${response}\n`);
console.log("=".repeat(50));
console.log("â
æŒç€ºå®æïŒAI æåè®°äœäºçšæ·ä¿¡æ¯ïŒ");
}
main();
æ§è¡ææ
yaml
ð€ åŒå§å€èœ®å¯¹è¯æŒç€º
==================================================
ð€ çšæ·: äœ å¥œïŒæå«å°æ
ð€ AI: äœ å¥œå°æïŒåŸé«å
Žè®€è¯äœ ïŒæä»ä¹å¯ä»¥åž®äœ çåïŒ
ð€ çšæ·: æå欢çŒçšïŒç¹å«æ¯ JavaScript
ð€ AI: 倪æ£äºå°æïŒJavaScript æ¯äžªåŸå¥œçéæ©ïŒ
äœ æ¯åå端åŒåè¿æ¯å
šæ åŒåå¢ïŒ
ð€ çšæ·: æå«ä»ä¹ååïŒæåæ¬¢ä»ä¹ïŒ
ð€ AI: äœ å«å°æïŒäœ 忬¢çŒçšïŒç¹å«æ¯ JavaScriptïŒ
==================================================
â
æŒç€ºå®æïŒAI æåè®°äœäºçšæ·ä¿¡æ¯ïŒ
æ¡äŸ 2ïŒåžŠè®°å¿ç Agent
éæ±åæ
æå»ºäžäžªèœå€ïŒ
- è®°äœå¯¹è¯åå²
- 䜿çšå·¥å ·ïŒè®¡ç®åšãå€©æ°æ¥è¯¢ïŒ
- å€èœ®äº€äºå®æå€æä»»å¡
æ žå¿ä»£ç ç»æ
javascript
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { MemorySaver } from "@langchain/langgraph";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { ChatDeepSeek } from "@langchain/deepseek";
import { DynamicStructuredTool } from "@langchain/core/tools";
import { HumanMessage } from "@langchain/core/messages";
import { z } from "zod";
import "dotenv/config";
// 1. å建记å¿ååš
const memory = new MemorySaver();
// 2. å®ä¹å·¥å
·
const tools = [
new DynamicStructuredTool({
name: "calculator",
description: "æ°åŠè®¡ç®",
schema: z.object({ expression: z.string() }),
func: async ({ expression }) => {
return String(eval(expression)); // 泚æïŒç产ç¯å¢éèŠå®å
šå€ç
},
}),
// ... å
¶ä»å·¥å
·
];
// 3. ç»å®å·¥å
·å° LLM
const llm = new ChatDeepSeek({ model: "deepseek-chat", temperature: 0 });
const llmWithTools = llm.bindTools(tools);
// 4. å®ä¹ç¶æ
const AgentState = Annotation.Root({
messages: Annotation({
reducer: (prev, next) => [...prev, ...next],
default: () => [],
}),
});
// 5. å®ä¹èç¹
async function agentNode(state) {
const response = await llmWithTools.invoke(state.messages);
return { messages: [response] };
}
const toolNode = new ToolNode(tools);
// 6. è·¯ç±åœæ°
function shouldCallTools(state) {
const lastMessage = state.messages[state.messages.length - 1];
return lastMessage.tool_calls?.length > 0 ? "tools" : "end";
}
// 7. æå»ºåŸå¹¶æ·»å è®°å¿
const agent = new StateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addEdge(START, "agent")
.addConditionalEdges("agent", shouldCallTools, {
tools: "tools",
end: END,
})
.addEdge("tools", "agent")
.compile({ checkpointer: memory }); // å
³é®ïŒæ·»å è®°å¿ïŒ
// 8. 䜿çš
const config = { configurable: { thread_id: "agent_session_1" } };
// 第äžèœ®ïŒè®¡ç®
await agent.invoke(
{ messages: [new HumanMessage("åž®æè®¡ç® 100 * 5")] },
config
);
// 第äºèœ®ïŒåºäºäžäžæç»§ç»ïŒè®°åŸäžæ¬¡ç®äº 500ïŒ
await agent.invoke({ messages: [new HumanMessage("åä¹ä»¥ 2")] }, config);
è®°å¿åš Agent äžçäœçš
css
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â 垊记å¿ç Agent æ§è¡æµçš â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â 第äžèœ®å¯¹è¯ (thread_id: "session_1") â
â ââââââââââââââââââââââââââââââââââââââ â
â çšæ·: "åž®æè®¡ç® 100 * 5" â
â â â
â Agent å³å®è°çš calculator â
â â â
â Tool è¿å: "500" â
â â â
â AI åå€: "100 à 5 = 500" â
â â â
â ðŸ ä¿åå° memoryïŒå
å«å®æŽå¯¹è¯ïŒ â
â â
â 第äºèœ®å¯¹è¯ (åäžäžª thread_id) â
â ââââââââââââââââââââââââââââââââââââââ â
â çšæ·: "åä¹ä»¥ 2" â
â â â
â ð ä» memory 读ååå²ïŒç¥éäžæ¬¡ç®äº 500ïŒ â
â â â
â Agent çè§£äžäžæïŒè°çš calculator("500 * 2") â
â â â
â AI åå€: "500 à 2 = 1000" â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
æ¡äŸ 3ïŒæºèœå®¢æ Agent
åºæ¯
çµå客æïŒèœæ¥è¯¢è®¢åãå€ç鿬Ÿ
javascript
// è®¢åæ¥è¯¢å·¥å
·
const orderTool = new DynamicStructuredTool({
name: "query_order",
description: "æ¥è¯¢è®¢åç¶æ",
schema: z.object({
order_id: z.string().describe("订åå·"),
}),
func: async ({ order_id }) => {
// è°çšè®¢åç³»ç»
return JSON.stringify({
order_id,
status: "å·²å莧",
shipping: "顺䞰快é",
expected: "æå€©é蟟",
});
},
});
// 鿬Ÿç³è¯·å·¥å
·
const refundTool = new DynamicStructuredTool({
name: "request_refund",
description: "ç³è¯·é欟",
schema: z.object({
order_id: z.string(),
reason: z.string(),
}),
func: async ({ order_id, reason }) => {
// è°çšé欟系ç»
return JSON.stringify({
refund_id: "RF" + Date.now(),
status: "å®¡æ žäž",
message: "鿬Ÿç³è¯·å·²æäº€ïŒé¢è®¡1-3äžªå·¥äœæ¥å€ç",
});
},
});
// æå»ºå®¢æ Agent
const customerServiceAgent = new StateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", new ToolNode([orderTool, refundTool]))
// ... å
¶ä»é
眮
.compile({ checkpointer: memory });
对è¯ç€ºäŸ
css
çšæ·ïŒæç订å 12345 ä»ä¹æ¶åå°ïŒ
Agent:
[æè] çšæ·æ¥è¯¢è®¢åç¶æïŒéèŠè°çšè®¢åæ¥è¯¢å·¥å
·
[è¡åš] query_order(order_id="12345")
[è§å¯] {"status":"å·²å莧","shipping":"顺䞰","expected":"æå€©é蟟"}
[åç] æšç订å 12345 å·²ç»å莧åŠïŒç±é¡ºäž°å¿«éé
éïŒé¢è®¡æå€©é蟟ã
è¯·ä¿æææºç
éïŒå¿«éå°å¥äŒæåèç³»æš~
Part 7: æäœ³å®è·µ
â 1. å·¥å ·è®Ÿè®¡åå
javascript
// â
奜çå·¥å
·è®Ÿè®¡
const goodTool = {
// åç§°ïŒç®æŽãæè¿°æ§
name: "search_products",
// æè¿°ïŒè¯Šç»ãå
å«äœ¿çšåºæ¯
description:
"æçŽ¢ååä¿¡æ¯ã" +
"åœçšæ·è¯¢é®ååä»·æ Œãåºåãè§æ Œæ¶äœ¿çšã" +
"è¿ååååç§°ãä»·æ Œãåºåæ°éã",
// åæ°ïŒæ¯äžªéœææž
æ°ç describe
schema: z.object({
keyword: z.string().describe("æçŽ¢å
³é®è¯ïŒåŠåååç§°"),
category: z.string().optional().describe("ååç±»å«ïŒåŠ'çµå产å'"),
max_results: z.number().optional().describe("æå€§è¿åæ°éïŒé»è®€10"),
}),
// è¿åïŒç»æå JSON
func: async (params) => {
const results = await searchProducts(params);
return JSON.stringify(results);
},
};
â 2. æ§å¶ Agent è¡äžº
javascript
// äœ¿çš SystemMessage è®Ÿå® Agent 人讟åè¡äžºè§è
const messages = [
new SystemMessage(`
äœ æ¯äžäžªäžäžç客æå©æã
è§åïŒ
1. å§ç»ä¿æç€Œè²åäžäž
2. åŠæäžç¡®å®ïŒè¯·äœ¿çšæçޢ工å
·æ¥è¯¢
3. æ¶å鿬ŸçæææäœïŒå
ç¡®è®€çšæ·èº«ä»œ
4. åŠæé®é¢è¶
åºèœåèåŽïŒå»ºè®®èœ¬äººå·¥å®¢æ
`),
new HumanMessage(userQuestion),
];
â 3. éå¶åå åº
javascript
// 讟眮åççéå¶
const agent = graph.compile({
checkpointer: memory,
recursionLimit: 15, // æå€§åŸªç¯æ¬¡æ°
});
// è¶
æ¶å€ç
const result = await Promise.race([
agent.invoke(input),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("è¶
æ¶")), 30000)
),
]);
â 4. æ¶æ¯ç¶æäœ¿çšçޝå reducer
javascript
// â
æ£ç¡®ïŒäœ¿çšçޝå reducer
messages: Annotation({
reducer: (prev, next) => [...prev, ...next], // 环å
default: () => [],
}),
// â é误ïŒäœ¿çšæ¿æ¢ reducerïŒäŒäž¢å€±åå²ïŒ
messages: Annotation({
reducer: (prev, next) => next, // æ¿æ¢
default: () => [],
}),
â 5. éå¶æ¶æ¯åå²é¿åºŠ
javascript
// åœæ¶æ¯è¿å€æ¶ïŒå¯èœè¶
åº LLM äžäžæéå¶
// è§£å³æ¹æ¡ïŒåšèç¹äžè£åªåå²
async function chatNode(state) {
// åªä¿çæè¿ 20 æ¡æ¶æ¯
const recentMessages = state.messages.slice(-20);
const response = await llm.invoke(recentMessages);
return { messages: [response] };
}
â 6. æ¥å¿åçæ§
javascript
// è®°åœ Agent çæ¯äžæ¥æäœ
async function agentNodeWithLogging(state) {
const startTime = Date.now();
console.log(`[${new Date().toISOString()}] Agent åŒå§æè`);
console.log(`èŸå
¥æ¶æ¯æ°: ${state.messages.length}`);
const response = await llmWithTools.invoke(state.messages);
console.log(`èæ¶: ${Date.now() - startTime}ms`);
console.log(`æ¯åŠè°çšå·¥å
·: ${response.tool_calls?.length > 0}`);
return { messages: [response] };
}
Part 8: åžžè§é®é¢ FAQ
Q1: Agent é·å ¥æ»åŸªç¯æä¹åïŒ
é®é¢ïŒAgent äžæè°çšå·¥å ·ïŒæ æ³åæ¢
è§£å³æ¹æ¡ïŒ
javascript
// æ¹æ³ 1ïŒé嶿倧è¿ä»£æ¬¡æ°
const agent = graph.compile({
recursionLimit: 10, // æå€ 10 次埪ç¯
});
// æ¹æ³ 2ïŒåšè·¯ç±åœæ°äžæ£æ¥
function shouldCallTools(state) {
if (state.messages.length > 20) {
console.log("èŠåïŒèŸŸå°æå€§æ¶æ¯æ°ïŒåŒºå¶ç»æ");
return "end";
}
// ... æ£åžžé»èŸ
}
Q2: Agent ééäºå·¥å ·æä¹åïŒ
é®é¢ïŒAgent è°çšäºäžçžå ³çå·¥å ·
è§£å³æ¹æ¡ïŒ
javascript
// 1. äŒåå·¥å
·æè¿°
const weatherTool = {
name: "get_weather",
// â äžå¥œçæè¿°
description: "æ¥è¯¢å€©æ°",
// â
奜çæè¿°
description:
"è·åæå®ååžç倩æ°ä¿¡æ¯ïŒå
æ¬æž©åºŠã倩æ°ç¶åµãé£åçã" +
"åœçšæ·è¯¢é®å€©æ°ãæž©åºŠãæ¯åŠäžéšãæ¯åŠéèŠåžŠäŒæ¶äœ¿çšæ€å·¥å
·ã",
};
// 2. äœ¿çš SystemMessage ç» Agent æç¡®æä»€
const messages = [
new SystemMessage(
"äœ æ¯äžäžªæºèœå©æã请ä»ç»åæçšæ·é®é¢ïŒéæ©æåéçå·¥å
·ã" +
"åŠæé®é¢äžéèŠå·¥å
·ïŒçŽæ¥åçå³å¯ã"
),
new HumanMessage("çšæ·é®é¢..."),
];
Q3: åŠäœè°è¯ AgentïŒ
javascript
// 1. åšèç¹äžæ·»å æ¥å¿
async function agentNode(state) {
console.log("=== Agent èç¹ ===");
console.log("åœåæ¶æ¯æ°:", state.messages.length);
console.log("æåäžæ¡æ¶æ¯:", state.messages.slice(-1));
const response = await llmWithTools.invoke(state.messages);
console.log("LLM è¿å:", response);
console.log("tool_calls:", response.tool_calls);
return { messages: [response] };
}
// 2. äœ¿çš LangGraph çæµåŒèŸåºæ¥çäžéŽç¶æ
const stream = await agent.stream({
messages: [new HumanMessage("é®é¢")],
});
for await (const chunk of stream) {
console.log("Chunk:", JSON.stringify(chunk, null, 2));
}
Q4: 䞺ä»ä¹ AI 没æè®°äœæè¯Žçè¯ïŒ
å¯èœåå ïŒ
- æ²¡ææ·»å checkpointer
javascript
// â å¿è®°æ·»å checkpointer
const app = graph.compile();
// â
æ£ç¡®
const app = graph.compile({ checkpointer: memory });
- thread_id äžäžèŽ
javascript
// â æ¯æ¬¡çšäžåç thread_id
await app.invoke(input, { configurable: { thread_id: "thread_1" } });
await app.invoke(input, { configurable: { thread_id: "thread_2" } }); // æ°çº¿çšïŒ
// â
䜿çšçžåç thread_id
const threadId = "my_conversation";
await app.invoke(input, { configurable: { thread_id: threadId } });
await app.invoke(input, { configurable: { thread_id: threadId } });
- æ¶æ¯ reducer 讟眮é误
javascript
// â äœ¿çšæ¿æ¢ reducer
reducer: (prev, next) => next; // æ¯æ¬¡éœæ¿æ¢ïŒäž¢å€±åå²
// â
䜿çšçޝå reducer
reducer: (prev, next) => [...prev, ...next]; // ä¿çåå²
Q5: çšåºéå¯åè®°å¿äž¢å€±äºïŒ
è§£çïŒMemorySaver ååšåšå åäžïŒçšåºç»æåæ°æ®å°±æ²¡äºãåŠéæä¹ åïŒè¯·äœ¿çš SqliteSaver æå ¶ä»æä¹ å Checkpointerã
åžžè§ Checkpointer 对æ¯
| ç±»å | ååšäœçœ® | æä¹ å | éçšåºæ¯ |
|---|---|---|---|
| MemorySaver | å å | â | åŒåæµè¯ |
| SqliteSaver | SQLite æä»¶ | â | æ¬å°åºçš |
| PostgresSaver | PostgreSQL | â | ç产ç¯å¢ |
| RedisSaver | Redis | â | 髿§èœåºæ¯ |
| MongoSaver | MongoDB | â | ææ¡£åååš |
Q6: å€äžªçšæ·äŒå ±äº«è®°å¿åïŒ
äžäŒïŒåªèŠäœ¿çšäžåç thread_idïŒæ¯äžªçšæ·çå¯¹è¯æ¯å®å šé犻çã
javascript
// çšæ· A åçšæ· B 䜿çšäžåç thread_id
const userAConfig = { configurable: { thread_id: "user_a" } };
const userBConfig = { configurable: { thread_id: "user_b" } };
// 䞀è
ç对è¯åå²äºäžåœ±å
Part 9: è¿é¶äž»é¢
1. å€ Agent åäœ
javascript
// äž» Agent è°åºŠå€äžªå Agent
const researchAgent = buildResearchAgent();
const writerAgent = buildWriterAgent();
const reviewerAgent = buildReviewerAgent();
// å·¥äœæµïŒç ç©¶ â åäœ â å®¡æ ž
const workflow = new StateGraph(State)
.addNode("research", researchAgent)
.addNode("write", writerAgent)
.addNode("review", reviewerAgent)
.addEdge(START, "research")
.addEdge("research", "write")
.addEdge("write", "review")
.addEdge("review", END);
2. 人工ä»å ¥ïŒHuman-in-the-LoopïŒ
javascript
// åšå
³é®æ¥éª€çåŸ
人工确讀
const graph = new StateGraph(AgentState)
.addNode("agent", agentNode)
.addNode("tools", toolNode)
.addNode("human_review", async (state) => {
// çåŸ
人工确讀
console.log("请确讀æ¯åŠæ§è¡ä»¥äžæäœ...");
// å®é
åºçšäžïŒè¿éäŒçåŸ
çšæ·èŸå
¥
return { messages: [new HumanMessage("已确讀")] };
})
.addConditionalEdges(
"agent",
(state) => {
// æææäœéèŠäººå·¥ç¡®è®€
const last = state.messages.slice(-1)[0];
if (isSensitiveOperation(last.tool_calls)) {
return "human_review";
}
return "tools";
},
{
human_review: "human_review",
tools: "tools",
end: END,
}
);
3. é误æ¢å€
javascript
// å·¥å
·å€±èŽ¥æ¶çå€ç
async function toolNodeWithRetry(state) {
const toolNode = new ToolNode(tools);
try {
return await toolNode.invoke(state);
} catch (error) {
console.log("å·¥å
·è°çšå€±èŽ¥ïŒå°è¯éè¯...");
// è¿åé误信æ¯ç» AgentïŒè®©å®å³å®äžäžæ¥
return {
messages: [
new ToolMessage({
content: `å·¥å
·è°çšå€±èŽ¥: ${error.message}ã请å°è¯å
¶ä»æ¹æ³ã`,
tool_call_id: state.messages.slice(-1)[0].tool_calls[0].id,
}),
],
};
}
}
4. å¹¶è¡å·¥å ·è°çš
javascript
// LangGraph ç ToolNode èªå𿝿并è¡è°çš
// åœ AI è¿åå€äžª tool_calls æ¶ïŒäŒå¹¶è¡æ§è¡
// AI è¿åïŒ
{
tool_calls: [
{ name: "get_weather", args: { city: "å京" } },
{ name: "get_weather", args: { city: "äžæµ·" } },
{ name: "get_time", args: {} },
];
}
// ToolNode äŒå¹¶è¡æ§è¡è¿ 3 䞪工å
·è°çš
5. ç¶ææ¥çäžè°è¯
javascript
// äœ¿çš getState æ¹æ³æ¥çåœåç¶æ
const state = await app.getState({ configurable: { thread_id: "my_thread" } });
console.log("åœåç¶æ:", state.values);
console.log("æ¶æ¯æ°é:", state.values.messages.length);
// è·åææåå²ç¶æïŒæ£æ¥ç¹ïŒ
const history = app.getStateHistory({
configurable: { thread_id: "my_thread" },
});
for await (const checkpoint of history) {
console.log("æ¶éŽ:", checkpoint.createdAt);
console.log("æ¶æ¯æ°:", checkpoint.values.messages.length);
console.log("---");
}
æ»ç»
ð¯ æ žå¿èŠç¹
-
LangChain vs LangGraph
- LangChain æäŸåºç¡ç»ä»¶ïŒLLMãToolsãPromptïŒ
- LangGraph æäŸçŒæèœåïŒåŸªç¯ã忝ãç¶æç®¡çïŒ
- æå»ºå€æ Agent æšèäœ¿çš LangGraph
-
Agent = LLM + Tools + èªäž»å³ç
- LLM èŽèŽ£æèåæšç
- Tools æäŸæ§è¡èœå
- åŸªç¯æºå¶å®ç°èªäž»å³ç
-
ReAct æš¡åŒæ¯ Agent çæ žå¿
- Thought â Action â Observation â Thought...
- 埪ç¯çŽå°å®æä»»å¡
-
MemorySaver 让 Agent æ¥æè®°å¿
- checkpointer ä¿å对è¯ç¶æ
- thread_id åºåäžå对è¯
- æ¶æ¯äœ¿çšçޝå reducer
-
å·¥å ·è®Ÿè®¡å³å® Agent èœå
- æž æ°çæè¿°
- åççåæ°
- çš³å®çè¿åæ ŒåŒ
ð¡ è®°äœè¿å¥è¯
Agent ç区倧äžåšäºå®æå€èªæïŒèåšäºå®èœçšå·¥å ·åå€å°äºã
å·¥å ·è¶äž°å¯ãè¶å¯é ïŒAgent çèœåå°±è¶åŒºã
åŠä¹ è·¯åŸå»ºè®®
ð é å¥å®ææä»¶
ð ä»åºå°åïŒgithub.com/Lidh1023/no...
| æä»¶ | å 容 | é¢è®¡çšæ¶ | GitHub éŸæ¥ |
|---|---|---|---|
src/agent.js |
Agent å ¥éš Demo | 30 åé | ð æ¥ç代ç |
src/langgraph/06-react-agent.js |
ReAct Agent 诊解 | 30 åé | ð æ¥ç代ç |
src/langgraph/07-memory-basic.js |
MemorySaver åºç¡ | 20 åé | ð æ¥ç代ç |
src/langgraph/08-memory-chatbot.js |
å€èœ®å¯¹è¯æºåšäºº | 30 åé | ð æ¥ç代ç |
src/langgraph/09-memory-agent.js |
垊记å¿ç Agent | 30 åé | ð æ¥ç代ç |
src/tools-demo.js |
Tools 䜿çšè¯Šè§£ | 20 åé | ð æ¥ç代ç |
ð¯ åŠä¹ 路线
é¶æ®µ 1ïŒçè§£æŠå¿µïŒ1 å°æ¶ïŒ
- â é è¯»æ¬ææ¡£ïŒçè§£ AgentãReActãMemorySaver çæŠå¿µ
- â
è¿è¡
src/agent.jsïŒè§å¯ Agent çå·¥äœè¿çš - â å°è¯ä¿®æ¹é®é¢ïŒè§å¯ Agent çäžåè¡äžº
é¶æ®µ 2ïŒåšæå®è·µïŒ2 å°æ¶ïŒ
- ð¥ æ·»å æ°çå·¥å ·ïŒåŠç¿»è¯ãæ°æ®åºæ¥è¯¢ïŒ
- ð¥ å®ç°äžäžªç¹å®åºæ¯ç AgentïŒåŠå®¢æã婿ïŒ
- ð¥ å°è¯å€ç倿ç倿¥éª€ä»»å¡
é¶æ®µ 3ïŒè¿é¶ææ¡ïŒ3+ å°æ¶ïŒ
- ð åŠä¹ MemorySaver å®ç°å¯¹è¯è®°å¿
- ð å®ç°äººå·¥ä»å ¥çå®¡æ žæµçš
- ð æ¢çŽ¢å€ Agent åäœ
åèèµæº
宿¹ææ¡£
- LangGraph 宿¹ææ¡£ : langchain-ai.github.io/langgraphjs...
- LangChain Tools ææ¡£ : js.langchain.com/docs/concep...
- ReAct 论æ : arxiv.org/abs/2210.03...
çžå ³æç«
- ãReAct: Synergizing Reasoning and Acting in Language Modelsã
- ãTool Learning with Foundation Modelsã
äœè å¯è¯
Agent æ¯ AI åºçšçæªæ¥ãçè§£äº AgentïŒäœ å°±ææ¡äºè®© AI "åäº"èäžä» ä» æ¯"诎è¯"çèœåã
ä»ç®åç ReAct Agent åŒå§ïŒéæ¥æ¢çŽ¢æŽå€æçåºæ¯ãè®°äœïŒ
- å çè§£åçïŒåå åèœ
- ä»ç®ååºæ¯åŒå§ïŒéæ¥å¢å å€æåºŠ
- å€è°è¯ãå€è§å¯ Agent çè¡äžº
åŠæäœ å®æäºæ¬æçšçææç»ä¹ ïŒæåäœ ïŒäœ å·²ç»å ·å€äºæå»ºç产级 AI Agent çåºç¡èœåã
Happy Coding! ð
ð¡ åŠä¹ 建议: å çè§£ LangChain å LangGraph çå ³ç³»ïŒåä»ç®åç Agent åŒå§ïŒéæ¥æ·»å è®°å¿åèœãæ¯äžæ¥éœè§å¯ AI æ¯åŠäœ"æè"å"è¡åš"çïŒ