随着人工智能能力的不断提升,工程师们正在探索构建智能应用的新途径。在本文中,我们将介绍如何使用尖端的开源工具和框架来构建一个AI搜索代理的概念验证。我们将利用LangGraph来编排工作流,使用LangChain来集成大型语言模型,使用Ollama运行开源模型,如Llama3.1,并使用Next.js来构建全栈混合web应用。让我们开始吧!
搭建环境要开始,你需要一个 JavaScript/TypeScript 开发环境。这里提到的是我们将会用到的关键技术。下面是一些我们将用到的关键技术:
确保你已经安装了 Node.js(LTS 版本 v20 或更高版本),然后创建一个 Next.js 项目。
npx create-next-app@latest poc-ai cd poc-ai npm i
运行以下命令来创建一个新的Next.js项目并安装依赖项.
安装一下必需的依赖项。
npm i @langchain/community @langchain/core @langchain/langgraph @langchain/ollama nanoid zod
// 该命令用于安装指定的npm包,包括@langchain社区、核心、langgraph、ollama、nanoid和zod。
项目结构:poc-ai/ ├── src/ │ ├── ai/ │ │ ├── agents/ │ │ │ └── index.ts │ │ ├── llm-model/ │ │ │ ├── index.ts │ │ │ └── ollama.ts │ │ └── tools/ │ │ ├── index.ts │ │ └── searchTool.ts │ ├── app/ │ │ └── api/ │ │ └── ai/ │ │ └── route.ts │ └── ... ├── .env.local ├── .eslintrc.json ├── .gitignore ├── next-env.d.ts ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── README.md (读我(README)) ├── tailwind.config.ts └── tsconfig.json实现AI代理程序:
我们来看看关键部分:
LLM模型配置(使用Ollama,与Llama 3.1):
此代码使用Ollama(一个特定的平台或工具)设置我们的语言模型为,并将其与自定义工具集成。
// src/ai/llm-model/ollama.ts import { ChatOllama } from "@langchain/ollama"; const 模型名称 = "llama3.1"; export const llm = new ChatOllama({ model: 模型名称, 温度: 0, });
// src/ai/llm-model/index.ts import { 工具 } from "../tools"; import { llm } from "./ollama"; export const 模型 = llm.bindTools(工具);
LangGraph Agent(Langchain配置):
这建立了我们的LangGraph工作流,定义了智能体的决策方式和工具使用。
// src/ai/index.ts import { MemorySaver, StateGraph } from "@langchain/langgraph"; import { callModel, shouldContinue, StateAnnotation } from "./agents"; import { toolNode } from "./tools"; import { HumanMessage } from "@langchain/core/messages"; // 定义一个新的图 export const workflow = new StateGraph(StateAnnotation) .addNode("agent", callModel) .addNode("tools", toolNode) .addEdge("__start__", "agent") .addConditionalEdges("agent", shouldContinue) .addEdge("tools", "agent"); // 初始化内存以在图运行之间持久化状态 const checkpointer = new MemorySaver(); export const startRunnable = async (query: string, thread_id: string) => { // 最终,我们编译它, // 这将它编译成一个 LangChain 可执行对象,同时传递了内存。 // 注意,我们在编译图时(可选)传递了内存。 const app = workflow.compile({ checkpointer }); // 使用这个可执行 const finalState = await app.invoke( { messages: [new HumanMessage(query)], }, { configurable: { thread_id: thread_id } } ); // 返回最终状态的最后一条消息的内容。 return finalState.messages[finalState.messages.length - 1].content; };
// src/ai/agents/index.ts import { AIMessage, BaseMessage, SystemMessage, } from "@langchain/core/messages"; import { Annotation } from "@langchain/langgraph"; import { model } from "../llm-model"; export const StateAnnotation = Annotation.Root({ messages: Annotation<BaseMessage[]>({ reducer: (x, y) => x.concat(y), }), }); // 定义一个函数来判断是否继续执行 // 我们可以通过 `StateAnnotation.State` 来提取状态类型 export function shouldContinue(state: typeof StateAnnotation.State) { const messages = state.messages; const lastMessage = messages[messages.length - 1] as AIMessage; // 如果LLM调用了一个工具,则转向"tools"节点 if (lastMessage.tool_calls?.length) { return "tools"; } // 否则,我们停止(回复给用户) return "__end__"; } // 定义一个函数来调用模型 export async function callModel(state: typeof StateAnnotation.State) { const messages = state.messages; const response = await model.invoke([ new SystemMessage(`你是一名记者,正在传达新闻信息。 请根据以下请求总结句子。 请求: 1. 用第一句话总结文章,保持在5行或更少。 2. 用项目符号列出主要要点,并使用印地语。 3. 不要翻译任何技术术语。 4. 不要包含任何不必要的信息。 你有工具可以在互联网上搜索和获取数据,然后进行总结。 摘要: `), ...messages, ]); // 我们返回一个列表,因为这将被添加到现有的消息列表中 return { messages: [response] }; }
自定义搜索工具(使用Tavily的搜索工具):
我们使用自定义工具Tavily搜索API来获取现实世界数据。
// src/ai/tools/searchTool.ts import { TavilySearchResults } from "@langchain/community/tools/tavily_search"; // 搜索工具,用于从TavilySearchResults获取最多10条结果 export const searchTool = new TavilySearchResults({ maxResults: 10, });
// src/ai/工具/index.ts 文件 import { ToolNode } from "@langchain/langgraph/prebuilt"; import { searchTool } from "./searchTool"; export const 工具列表 = [searchTool]; export const toolNode = new ToolNode(工具列表);
API 路由创建 —Next.js (应用路由):
这设置了我们的Next.js API路由,来处理收到的请求,并与我们的AI代理进行互动。
// src/app/api/ai/route.ts import { startRunnable } from "@/ai"; import { nanoid } from "nanoid"; import { z } from "zod"; const InputBodySchema = z.object({ message: z.string().min(1), chatId: z.string().optional(), }); type InputBodyType = z.infer<typeof InputBodySchema>; export async function POST(req: Request) { const { message, chatId }: InputBodyType = await req.json(); const result = InputBodySchema.safeParse({ message, chatId }); if (!result.success) { return Response.json({ data: null, error: result?.error }, { status: 500 }); } const res = await startRunnable(message, chatId ?? nanoid()); return Response.json({ data: JSON.stringify(res) }); }
配置环境变量设置:在根目录下创建一个 .env.local
文件并在该文件中添加您的 API 密钥:
LANGCHAIN_TRACING_V2=true # 这是语言链跟踪的环境变量 (This is the environment variable for LangChain tracing) LANGCHAIN_ENDPOINT="https://api.smith.langchain.com" LANGCHAIN_API_KEY="your_langchain_api_key" # your_langchain_api_key (你的LangChain API密钥) LANGCHAIN_PROJECT="your_project_name" TAVILY_API_KEY="our_tavily_api_key" LANGCHAIN_CALLBACKS_BACKGROUND=true # 背景回调开关 (background callback toggle)利用LangSmith提升可观测性
LangSmith 提供了强大的追踪和调试能力,以帮助我们追踪和调试 LangChain 应用程序。从这些截图中,我们可以看到 LangSmith 如何可视化我们代理的决策流程、工具使用情况及其整体性能。
这里是一个查看 Langsmith POC 的链接。
使用 LangSmith 的关键优势:启动应用
使用英文“Ollama”和“Llama”,启动你的 Ollama 服务器,并使用 Llama 3.1 模型
运行一下 llama3.1
运行你的 Next.js 开发服务器:npm run dev
:
向 http://localhost:3000/api/ai
发送一个 POST 请求到,其中请求体包含一个包含 message
字段的 JSON 数据。
这里我用的是VS Code中的REST客户端。
有人问:关于西孟加拉的抗议活动,发生了什么事?
回复:{ "data": "\"本文是一句话总结:\n\n在印度西孟加拉邦,一名在公立医院实习的医生遭到强奸并遇害后,数千名抗议者堵塞了铁路轨道,使巴士停止运行,并呼喊口号。\n\n主要要点摘要(印地语):\n\n1. 印度西孟加拉邦公立医院的一名住院医生遭到强奸和杀害。\n2. 数千名抗议者走上街头。\n3. 抗议者堵塞了铁路轨道并使巴士停止运行。\n4. 人们呼喊口号,要求正义。\n5. 这一事件引发了对当地法律和秩序状况的质疑。\"" }结语
我们尝试使用LangGraph、LangChain和开源LLM来构建AI驱动的搜索代理。我们看到了这些工具可以如何结合来创建强大且灵活的AI应用。通过利用LangSmith的可观测性,我们可以轻松地调试和优化AI工作流程。
概念验证展示了这些技术在创建智能应用和情境理解方面的潜力。在继续探索并使用这些工具时,请记得要考虑到人工智能系统的伦理问题和潜在的偏见。
喜欢这些内容吗?
如果你想支持我,可以请我喝杯咖啡吧!你的支持让我能继续创作更多有用的内容。
在这里请我喝杯咖啡。☕💻
如果你对这个项目感兴趣,或者想讨论人工智能工程的话题,可以在LinkedIn: Manoj Mukherjee上与我联系。
我一直都在寻找机会扩展我的AI职业生涯。如果您有兴趣合作或讨论潜在的机会,请随时联系我。我愿意分享更多关于这个项目的信息,并探讨我们如何一起突破AI技术的界限。
祝大家编码愉快,期待能与各位AI爱好者和专业人士互动!
照片由Hanny Naibaho在Unsplash发布。