在前三篇文章中,我们实现了 AI 组件渲染、流式 JSON 解析和多工具并发。然而,所有这些交互依然局限在模型的"离线知识库"中…
你是否遇到过这样的场景:
- 用户问:“今天北京天气怎么样?”
- AI 回答:“根据我的训练数据,北京春季平均气温 10-20°C…”
- 用户心里想:“我要的是今天的实时天气,不是历史数据!”
今天我们来解决这个问题,从离线知识库升级到实时感知!
😱 回顾问题:离线知识库的致命缺陷
在前三篇文章中,我们实现了完美的 AI 组件渲染系统,但存在一些致命问题:
❌ 数据时效性缺失
用户: "今天北京天气怎么样?" ↓ AI 搜索训练数据... ↓ [AI 回答] "根据我的训练数据,北京春季平均气温 10-20°C..." ↓ 用户: 😡 我要的是今天!不是历史数据!问题:AI 的知识截止于训练时间,无法获取实时信息。
❌ 幻觉风险
用户: "马斯克最近发推了吗?" ↓ AI 可能回答: "马斯克昨天发推说..." ↓ 实际上:马斯克根本没发这条推! ↓ 用户: 被误导 ❌问题:AI 会"幻觉"出不存在的信息,误导用户。
🔥 网页端 vs SDK:本质区别深度解析
很多开发者习惯了在网页端使用 DeepSeek 的联网功能,但在开发 AI 应用时,使用import { ChatDeepSeek } from '@langchain/deepseek'拥有完全不同的维度!
🎯 对比表格
| 维度 | 网页端 DeepSeek | LangChain SDK |
|---|---|---|
| 决策透明度 | ❌ 黑盒,看不到思考过程 | ✅ 可观察完整逻辑链路 |
| 交互闭环 | ❌ 搜索结果只能看 | ✅ 数据可直接注入下游组件 |
| 环境控制 | ❌ 系统自动注入,开发者不可控 | ✅ 开发者主动注入,完全可控 |
| 数据复用 | ❌ 搜索结果无法传递 | ✅ 可传递给后续工具 |
| 自定义控制 | ❌ 受限于网页功能 | ✅ 完全可控的执行逻辑 |
💡 深度解析:三大核心差异
1️⃣ 决策透明度:黑盒 vs 白盒
网页端(黑盒):
用户输入 ↓ [黑盒] AI 内部处理(看不见) ↓ 输出答案SDK(白盒):
用户输入 ↓ AI 思考(可观察) → 决定调用工具 ↓ 工具执行(可拦截) → 返回结果 ↓ AI 分析结果 → 生成最终答案 ↓ 输出答案 + 完整链路在 SDK 中,我们可以看到 AI 从"思考"到"决定调用工具"的完整逻辑链路,这对于调试和优化至关重要。
2️⃣ 交互闭环:只读 vs 可写
网页端(只读):
AI 搜索 → 显示结果 → 用户只能看 ↓ 无法将搜索到的数据(如经纬度)传递给其他组件SDK(可写):
AI 搜索 → 获取经纬度 → 注入地图组件 → 渲染地图 ↓ 数据在工具间流动,形成完整的交互闭环类比:
- 网页端像"看报纸":只能获取信息,无法利用
- SDK 像"指挥官":获取信息后,可以指挥后续行动
3️⃣ 环境感应:静态 vs 动态
网页端(静态):
// 系统自动注入当前时间,但开发者无法控制// 开发者无法自定义注入的上下文格式// 用户位置等信息由浏览器获取,开发者难以精确控制SDK(动态):
// 开发者可以主动、精确地注入上下文constcurrentTime=newDate().toLocaleString('zh-CN',{year:'numeric',month:'long',day:'numeric',weekday:'long',hour:'2-digit',minute:'2-digit'})// 可以动态注入用户地理位置constuserLocation={lat:39.9,lng:116.4,city:'北京',country:'中国'}// 可以注入业务相关的上下文constbusinessContext={userRole:'VIP用户',accountStatus:'active'}通过代码,我们可以动态告知模型当前的精确时间、用户地理位置等上下文,这是静态 Web 界面难以实现的自动化体验。
🎨 实时搜索集成:引入 TavilySearch
要让 AI 能够检索实时天气或新闻,我们需要引入TavilySearch。它是专为 LLM 设计的搜索引擎,能够过滤掉网页广告,只返回干净的文本摘要。
📦 工具定义
import{TavilySearch}from'@langchain/community/tools/tavily_search'constrawTools=[// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 实时搜索工具// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━newTavilySearch({tavilyApiKey:TAVILY_API_KEY,// 🎯 关键:动态注入当前日期,防止 AI 获取到过期的快照description:`【必备工具】用于获取实时信息。搜索时请务必带上当前日期(${newDate().toLocaleDateString()}),以确保获取的是最新结果。`}),// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// UI 渲染工具// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━tool(async(args)=>`地图路线已生成`,{name:'render_map_itinerary',schema:MapItinerarySchema,description:'【必须调用】当用户询问地点、路线规划时使用。'}),]🔍 为什么不需要tool()包裹?
这是一个经常被问到的问题!
TavilySearch:
// ❌ 不需要 tool() 包裹newTavilySearch({...})自定义渲染函数:
// ✅ 必须用 tool() 包裹tool(async(args)=>`地图路线已生成`,{...})原因分析:
| 类型 | 类继承 | 内置能力 | 是否需要 tool() |
|---|---|---|---|
| TavilySearch | 继承LangChain BaseTool | 已有name、description、schema | ❌ 不需要 |
| 自定义函数 | 纯 JS 函数 | 无任何元数据 | ✅ 必须用 |
技术细节:
TavilySearch本身就是一个已经扩展了LangChain BaseTool基类的类实例- 它内部已经内置了模型所需的
name、description和输入schema - 我们自定义的渲染函数只是一个纯 JS 函数,必须通过
tool()装饰器进行包装,才能被识别为合法的 Agent 工具
🛡️ 规则前置:用 System Prompt 强制模型"认清现实"
要让 AI 在不知道答案时主动搜索,必须通过System Prompt建立强硬的执行准则。
🎯 强约束系统提示词
constSYSTEM_PROMPT=`当前时间:${newDate().toLocaleString()}## 核心准则 1. 禁止幻觉:你本身不具备联网能力,严禁凭空编造气温、新闻或地点坐标。 2. 搜索前置:涉及任何实时性、位置详情的问题,必须先调用 'tavily_search_results_json'。 3. 数据去噪:如果搜索结果日期陈旧(如当前是3月,搜到的是1月数据),必须忽略该结果并告知用户无法获取实时数据。`在实际测试中,我们发现搜索工具可能会返回过时的网页。例如:
当前时间: 2026年3月12日 搜索结果: 2026年1月15日的数据 ← 过期了! ❌💡 优化的反幻觉策略
| 策略 | 实现方式 | 作用 |
|---|---|---|
| 动态上下文注入 | 每次调用时实时计算new Date() | 确保 AI 知道当前时间 |
| 置信度校准 | 在工具描述中明确日期偏差限制 | 让 AI 主动过滤过期数据 |
| 结果导向 | AI 对比数据逻辑,不合理则报错 | 避免渲染错误数据 |
具体实现:
// 1. 动态上下文注入constSYSTEM_PROMPT=`当前时间:${newDate().toLocaleString()}`// 2. 置信度校准newTavilySearch({description:`如果搜索结果中的日期与当前日期偏差超过 3 天,请视为失效数据。`})// 3. 结果导向if(result.temperature<-50||result.temperature>60){thrownewError('温度数据异常,可能是幻觉')}⚙️ 核心逻辑:invoke 深度解析
在 Agent 架构中,最优雅的设计是多态执行。无论 AI 决定是去搜索还是去渲染,我们的结算代码应该是一致的。
💻 完整代码实现
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 结算阶段:真正执行工具并回传数据// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━if(toolCalls.length>0){consttoolMessages=awaitPromise.all(toolCalls.map(async(call)=>{constid=call.id!;// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 第一步:动态寻找工具实例// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━consttoolInstance=rawTools.find(t=>t.name===call.name);letresult;try{if(toolInstance){// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 【核心点】调用 invoke 触发执行// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// - 对于 Tavily:invoke 内部运行的是 _call 方法,执行真正的 HTTP 请求// - 对于 自定义 tool:invoke 会自动调用你传入的异步函数// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━result=awaittoolInstance.invoke(call.args);}else{result="Error: Tool not found";}}catch(error){result=`执行失败:${error.message}`;}// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 第二步:更新插件状态(如果是 UI 渲染工具)// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━finalPlugins[id]={...messages[messages.length-1]?.pluginMap?.[id],isPartial:false,argsParsed:call.args,};// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 第三步:将真实的执行结果封装为 ToolMessage// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━returnnewToolMessage({tool_call_id:id,content:typeofresult==='string'?result:JSON.stringify(result),});}));// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━// 第四步:将结果推入上下文,开启下一轮迭代// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━currentMessages.push(...toolMessages);}优势:这种设计让我们的 while 循环变得极其简洁:
- 模型说要用什么工具
- 代码就 invoke 什么工具
- 结果再喂给模型
完全统一的执行逻辑!
🎊 核心技术点总结
- 实时搜索数据时效性缺失
new TavilySearch({ ... })| - 动态上下文注入AI 不知道当前环境
new Date().toLocaleString()| - 多态 invoke统一执行逻辑
toolInstance.invoke(call.args)|
🎯 完整闭环架构
感知(AI 决策) → 检索(搜索工具) → 校验(反幻觉) → 展现(UI 组件) ↓ ↓ ↓ ↓ 识别意图 获取实时数据 验证数据合理性 渲染可视化组件💡 常见问题
Q1: 为什么不直接用网页端的 DeepSeek?
A: 网页端适合个人使用,但 SDK 提供了:
- 完整的控制权
- 数据复用能力
- 自定义执行逻辑
- 更高的可定制性
Q2: TavilySearch 是免费的吗?
A: Tavily 提供免费额度(每月 1000 次搜索),对于原型开发足够。生产环境可能需要付费升级。
Q3: 如何确保搜索结果的时效性?
A: 三重保障:
- 动态注入当前日期
- System Prompt 强制 AI 检查日期
- 代码层校验数据合理性
Q4: 搜索失败怎么办?
A: 优雅降级:
try{result=awaittoolInstance.invoke(call.args);}catch(error){result="搜索失败,请稍后重试";}Q5: 可以使用其他搜索引擎吗?
A: 可以!LangChain 支持多个搜索引擎:
TavilySearch(推荐,专为 LLM 设计)SerpAPIGoogleSearchAPI- 自定义搜索引擎
📚 推荐资源
- Tavily Search 文档
- LangChain Tools
- LangChain Agents
- DeepSeek API 文档
🎉 总结
至此我们的 AI 助手正式拥有了"感官"!通过toolInstance.invoke的统一分发,我们构建了一个感知 → 检索 → 校验 → 展现的完整闭环。
- TavilySearch- 专为 LLM 设计的实时搜索引擎
- 动态上下文注入- 让 AI 知道当前环境
- 多态 invoke- 统一的执行逻辑
- 反幻觉策略- 确保数据准确性
现在才算真正赋予了 AI“感官”!
AI 不再是一个只能"背诵知识"的书呆子,它是一个能够感知现实世界的智能助手。