开始
作为一名开发者,你是否曾经想过——如果能创建一个AI助手,让它自动判断什么时候该搜索实时数据,什么时候该直接回答问题,那该多酷?今天,我将手把手带你实现这个想法,从零开始构建一个真正会“思考”的智能Agent。
🎯 第一步:明确我们要解决什么问题?
普通的AI对话模型有个明显缺陷:它们要么完全依赖预训练知识(可能过时),要么盲目搜索所有问题(效率低下)。我们需要一个更聪明的方案。
我们要构建的Agent应该具备这样的能力:
- 🧠 智能判断:自动识别问题类型
- 🔍 按需搜索:只在需要时获取实时数据
- 💬 智能回答:结合实时数据和知识库
- 🔄 状态管理:记住对话上下文
🛠️ 第二步:环境准备——搭建开发基础
使用 langGraph 创建 Agent
langGraph 是基于 langChain 的框架,用于构建可控制的 Agent ,是现代开发 Agent 最流行的解决方案之一。
安装 langGraph 必要插件
npm install @langchain/langgraph @langchain/core
使用 Tavily 搜索功能
Tavily 是一个搜索引擎,可以让你的 LLM 和 Agent 有联网搜索的能力
注册登录 把 API key 放在代码库 .env 文件中
TAVILY_API_KEY=xxx
安装 langchain tavily 插件
npm i @langchain/tavily
然后创建一个 agent.js 文件,代码如下
import 'dotenv/config'
import { TavilySearch } from '@langchain/tavily'
// 定义 tools
const agentTools = [
new TavilySearch({
maxResults: 1 // 最多查询 1 个结果
})
]
选择一个大模型
langChain 集成了有很多 LLM 可供选择它默认推荐的是 OpenAI 但是在国内我们没法直接调用它的 API ,所以我当前选择的是 DeepSeek 。
注册登录 DeepSeek 创建一个 API key 并把它放在 .env 文件中。
DEEPSEEK_API_KEY=xxx
安装 langChain deepseek 插件
npm i @langchain/deepseek
然后继续写 agent.js 代码
const llm = new ChatDeepSeek({
model: 'deepseek-chat',
temperature: 0.7, // 控制创造性:0.1-保守,0.7-平衡,1.0-富有创造性
})
🏗️ 第三步:抛出问题——设计思考流程
想象一下:用户问了以下问题:
1、深圳今天天气怎么样?
2、南京明天会下雨吗?
3、你知道南京有什么著名景点吗?
这里有需要联网获取数据之后才能回答的问题(天气),也有大模型可以直接回答的问题(景点)。大家有没有思考过当我们输入这些问题之后,模型背后的处理过程是什么样的?让我们先用文字描述整个思考过程:
用户提问
↓
【决策节点】判断是否为天气问题?
↓
是 → 【搜索节点】获取实时天气数据 → 【回答节点】生成天气报告
↓
否 → 【通用回答节点】直接回答问题
💻 第四步:代码实现——从骨架到血肉
搭建基础骨架(导入和初始化)
import 'dotenv/config'
import { ChatDeepSeek } from '@langchain/deepseek'
import { TavilySearch } from '@langchain/tavily'
import { StateGraph, END, MemorySaver, MessagesAnnotation } from '@langchain/langgraph'
import { HumanMessage, AIMessage } from '@langchain/core/messages'
// 1. 初始化工具和模型
const tavilySearch = new TavilySearch({ maxResults: 1 })
const llm = new ChatDeepSeek({
model: 'deepseek-chat',
temperature: 0.7, // 控制创造性:0.1-保守,0.7-平衡,1.0-富有创造性
})
定义数据容器(状态结构)
const StateAnnotation = {
messages: MessagesAnnotation, // 对话历史记录
step: { reducer: (x, y) => y ?? x }, // 当前执行步骤
searchData: { reducer: (x, y) => y ?? x }, // 搜索到的数据
}
messages 记录对话历史,step 记录当前做到哪一步,searchData 记录搜集到的信息。
创建第一个节点:智能决策器
// 节点1:问题分类器
const startNode = async (state) => {
console.log("🔍 开始分析问题类型...")
const lastMessage = state.messages[state.messages.length - 1]
// 使用LLM判断问题类型
const isWeatherRelated = await llm.invoke([
new HumanMessage(`请判断以下问题是否与天气相关(如天气、气温、降雨等),只需回答"是"或"否":${lastMessage.content}`)
])
const isWeather = isWeatherRelated.content.trim().toLowerCase() === '是'
console.log(`📊 判断结果:${isWeather ? '天气问题' : '普通问题'}`)
// 决定下一步走向
return {
...state,
step: isWeather ? 'search_weather' : 'respond_general',
}
}
创建第二个节点:实时数据搜集器
// 节点2:天气数据搜索
const searchWeatherNode = async (state) => {
console.log("🌤️ 开始搜索实时天气数据...")
const lastMessage = state.messages[state.messages.length - 1]
try {
// 构建搜索查询
const searchQuery = `${lastMessage.content} 天气 实时气温`
console.log(`🔎 搜索关键词:${searchQuery}`)
// 调用Tavily API获取数据
const searchResults = await tavilySearch.invoke({ query: searchQuery })
console.log("✅ 天气数据获取成功")
return {
...state,
searchData: searchResults,
step: 'respond_weather', // 告诉系统下一步该做什么
}
} catch (error) {
console.error("❌ 天气搜索失败:", error)
return {
...state,
searchData: '天气搜索服务暂时不可用',
step: 'respond_weather',
}
}
}
创建第三个节点:天气报告生成器
// 节点3: 生成天气回答(基于Tavily搜索数据)
const respondWeatherNode = async state => {
console.log('📝 基于实时数据生成天气报告...')
// 使用LLM基于搜索数据生成友好回答
const lastMessage = state.messages[state.messages.length - 1]
// 将原始数据转化为友好回答的——这体现了AI的真正价值。
const finalResponse = await llm.invoke([
new HumanMessage(`你是一个天气助手。请基于以下搜索数据,为用户的问题生成一个准确、友好、实用的天气回答。
用户问题:${lastMessage.content}
搜索数据:${state.searchData}
要求:
1. 如果搜索数据中有具体的天气信息,请基于数据回答
2. 如果搜索数据不完整或没有找到信息,请诚实地告知用户
3. 回答要简洁明了,包含温度、天气状况等关键信息
4. 用自然的中文回答`),
])
return {
messages: [...state.messages, new AIMessage(finalResponse.content)], // 保留历史并添加新消息
step: 'end',
}
}
创建第四个节点:通用问题回答器
// 节点4: 生成通用回答(非天气问题,直接使用LLM)
const respondGeneralNode = async state => {
console.log('💭 处理通用知识问题...')
const lastMessage = state.messages[state.messages.length - 1]
// 直接使用LLM回答所有非天气问题
const generalResponse = await llm.invoke([
new HumanMessage(`请回答用户的以下问题,提供准确、有用的信息:
用户问题:${lastMessage.content}
请用中文友好地回答,如果不知道可以诚实地告知。`),
])
return {
messages: [...state.messages, new AIMessage(generalResponse.content)],
step: 'end',
}
}
🔗 第五步:连接所有节点——构建完整工作流
创建流程控制器
// 条件边控制
const shouldContinue = state => {
console.log(`🔄 检查下一步流程,当前步骤:${state.step}`)
return state.step !== 'end' ? state.step : END
}
组装完整的工作流
// 5. 构建状态图
const graph = new StateGraph({ channels: StateAnnotation })
.addNode('start', startNode) // 起始节点
.addNode('search_weather', searchWeatherNode) // 天气搜索节点
.addNode('respond_weather', respondWeatherNode) // 天气响应节点
.addNode('respond_general', respondGeneralNode) // 通用响应节点
.setEntryPoint('start')
.addConditionalEdges('start', shouldContinue)
.addConditionalEdges('search_weather', shouldContinue)
.addConditionalEdges('respond_weather', shouldContinue)
.addConditionalEdges('respond_general', shouldContinue)
关键特点:
- 所有节点都连接到同一个条件判断函数
shouldContinue - 没有明确的边连接,完全依赖条件判断决定流向
┌─────────────┐
│ __start__ │
└──────┬──────┘
↓
┌────────┐
│ start │
└───┬────┘
↓ (shouldContinue判断)
├───→┌───────────────┐ ────┐
│ │ search_weather│ │
│ └───────────────┘ │
│ ↓ (shouldContinue)│
│ │
├───→ ┌───────────────┐ │
│ │ respond_weather │
│ └───────────────┘ │
│ ↓ (shouldContinue) │
│ │
├───→ ┌───────────────┐ │
│ │respond_general│ │
│ └───────────────┘ │
│ ↓ (shouldContinue) │
│ │
└───→ ┌─────────┐ │
│ __end__ │ │
└─────────┘ │
↑ │
└──────────────┘
(循环回到各节点)
启用记忆功能
// 6. 配置记忆存储并编译图
const memory = new MemorySaver()
const agent = graph.compile({
checkpointSaver: memory,
})
console.log('🎉 智能Agent创建成功!')
记忆功能的重要性: 这让Agent能够进行多轮对话,就像人类能记住之前的谈话内容一样。
🧪 第六步:测试我们的智能Agent
创建测试函数
// 7. 运行Agent的函数
async function runWeatherAgent(userInput, threadId = 'default-thread') {
console.log(`\n👤 用户提问:${userInput}`)
console.log('='.repeat(50))
try {
const finalState = await agent.invoke(
{
messages: [new HumanMessage(userInput)],
step: 'start',
},
{ configurable: { thread_id: threadId } }
)
const lastMessage = finalState.messages[finalState.messages.length - 1]
console.log(`🤖 助手回答:${lastMessage.content}`)
console.log('='.repeat(50))
return finalState
} catch (error) {
console.error('❌ 运行出错:', error)
// 出错时使用LLM直接回答
const fallbackResponse = await llm.invoke([new HumanMessage(userInput)])
console.log(`🆘 降级回答:${fallbackResponse.content}`)
return {
messages: [
new HumanMessage(userInput),
new AIMessage(fallbackResponse.content),
],
}
}
}
运行测试
// 8. 测试Agent
;(async () => {
console.log('开始测试智能问答Agent...\n')
await runWeatherAgent('深圳今天天气怎么样?')
await runWeatherAgent('南京明天会下雨吗?')
await runWeatherAgent('你知道南京有什么著名景点吗?')
console.log('测试完成!')
})()
📊 第七步:分析测试结果——看看我们的Agent有多聪明
运行测试后,你应该看到类似这样的输出:
🚀 开始智能Agent测试...
👤 用户提问:深圳今天天气怎么样?
==================================================
🔍 开始分析问题类型...
📊 判断结果:天气问题
🌤️ 开始搜索实时天气数据...
🔎 搜索关键词:深圳今天天气怎么样? 天气 实时气温
✅ 天气数据获取成功
📝 基于实时数据生成天气报告...
🤖 助手回答:根据最新天气数据,深圳今天晴天,气温25-30°C...
==================================================
👤 用户提问:南京明天会下雨吗?
==================================================
🔍 开始分析问题类型...
📊 判断结果:天气问题
🌤️ 开始搜索实时天气数据...
🔎 搜索关键词:南京明天会下雨吗? 天气 实时气温
✅ 天气数据获取成功
📝 基于实时数据生成天气报告...
🤖 助手回答:南京明天预计会有降雨,建议您出门携带雨具...
==================================================
👤 用户提问:南京有什么著名景点?
==================================================
🔍 开始分析问题类型...
📊 判断结果:普通问题
💭 处理通用知识问题...
🤖 助手回答:南京是一座历史悠久、文化底蕴深厚的城市...
==================================================

被折叠的 条评论
为什么被折叠?



