为了账号安全,请及时绑定邮箱和手机立即绑定

如何用OpenAI、Langchain和MongoDB打造个性AI助手💡✨

大型语言模型(LLM)如ChatGPT、Gemini和Claude已成为企业的重要工具。每家公司都希望开发出适合自身特定需求或客户需求的定制化AI。在这次探索中,我们将专注于创建一个具备功能调用、将消息存储在数据库以支持多会话对话、并能执行网络搜索和总结结果的个性化助手。为了让其更加有组织并方便未来添加新功能,我们将使用Langchain、Langgraph和LangSmith,这些工具简化了流程并增强了功能。Langchain简化了流式传输和工具调用的过程,并支持你使用多个不同的LLM。Langgraph是一个决策工具,帮助决定使用哪种工具,并让代理自行选择路径。LangSmith是一个观察工具,让你从提问到获得答案的整个流程一览无余。

⭐️ 本指南中提到的完整源代码可在 GitHub (https://github.com/zpillsbury/ai-agent) 上找到

环境配置

.env 文件中添加环境变量,你需要用到你的 openai_keytavily_keymongo_uri,并确保正确设置它们。

📝 笔记:.env 文件

    OPENAPI_KEY=OPENAI_KEY=sk-proj-XXXXXX
    TAVILY_API_KEY=tvly-XXXXXXXXXXXXXXXXXXXXXXXX
    MONGO_URI=mongodb+srvXXXXXXXXXXXXXXXXXXXXXXXXXXX

    LANGCHAIN_TRACING_V2=true
    LANGCHAIN_ENDPOINT=https://api.smith.langchain.com
    LANGCHAIN_API_KEY=lsv2_pt_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxx
    LANGCHAIN_PROJECT=project-name

全屏 退出全屏

在设置中添加 langsmith、OpenAI key、Tavily key 和 Mongo URI。

📝 app/utilities/settings.py

    from pydantic import SecretStr
    from pydantic_settings import BaseSettings, SettingsConfigDict

    class Settings(BaseSettings):
        openai_key: SecretStr
        tavily_api_key: str
        mongo_uri: SecretStr

        # LangSmith
        langchain_tracing_v2: bool = True
        langchain_endpoint: str = "https://api.smith.langchain.com"
        langchain_project: str = "ai-agent"
        langchain_api_key: str

        model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    settings = Settings()

全屏模式 退出全屏

用在设置里找到的密钥来设置OpenAI客户端。

📝 编辑 app/应用主文件
或者更简洁地:编辑主文件

from langchain_openai import ChatOpenAI

from .utilities.settings import settings

# 初始化ChatOpenAI模型,设置API密钥和最大重试次数
llm = ChatOpenAI(
    openai_api_key=settings.openai_key,
    model_name="gpt-4o-mini",
    max_retries=2,
)

点击全屏,点击退出.

Langgraph

设置状态以便数据可以在节点间传递和使用。我们使用大模型为聊天机器人的消息记录添加消息。来自langgraph的add_messages函数会将从节点返回的任何消息追加到现有消息记录中。

查看 app/main.py 这个文件

    from typing import Annotated, TypedDict

    from langchain_core.messages import BaseMessage
    from langgraph.graph import END, START, StateGraph
    from langgraph.graph.message import add_messages
    from langgraph.graph.state import CompiledStateGraph

    class State(TypedDict):
        # 消息列表,由 add_messages 函数处理
        messages: Annotated[list[BaseMessage], add_messages]

切换到全屏 退出全屏

节点是图中的点,在langgraph中节点用函数表示。创建一个新的名为 chatbot 的函数,该函数通过调用 llm.ainvoke 向OpenAI发送当前存储的消息。OpenAI将返回一个新的AI消息。然后返回包含AI消息的新状态更新。这将通过 add_messages 辅助函数添加到现有消息中。

在 langgraph 中,方法有两种版本:同步和异步。如果您需要使用异步版本,只需在方法名前加上一个字母 a 即可。在这种情况下,我们选择使用 ainvoke 这个异步方法而不是同步方法 invoke

📝 这是主应用程序文件
这里主要是...

(保持与源代码格式一致,此处为空行)

    async def chatbot(state: State) -> State:
        """
        聊天功能
        """
        response_message = await llm.ainvoke(state["messages"])

        return {"messages": [响应消息]}

全屏, 退出

使用 add_node 函数将功能添加到图中。你需要用边将节点连接起来,在查看图的图片时,这些边就是每个节点之间的线。第一条边总是从 START 开始。使用 add_edgeSTART 与聊天机器人节点连接起来。然后用 graph_builder.compile 编译图,编译完成后才能使用该图。

📝 app/main.py (这里查看主要文件)

    async def get_graph() -> CompiledStateGraph:
        """
        获取状态图
        """
        graph_builder = StateGraph(State)  # 状态图构建器

        graph_builder.add_node("chatbot", chatbot)  # 添加节点“聊天机器人”

        graph_builder.add_edge(START, "chatbot")  # 添加从开始到聊天机器人的边
        graph_builder.add_edge("chatbot", END)  # 添加从聊天机器人到结束的边

        graph = graph_builder.compile()  # 编译状态图

        return graph

点击进入全屏 点击退出全屏

现在的图表看起来像这样:

图

创建一个新的异步函数 run_graph,它使用 graph.astream(如果是同步操作,则用 graph.stream)。它将首先插入系统提示,然后插入用户的问题作为当前状态。将其发送到 openAI,后者将返回 AI 消息,你的函数会将其追加到状态中。在每次图流循环中,我们可以通过事件变量访问当前状态。从 value 中提取最后一条消息的内容并返回给用户。

给AI添加系统提示,就是给它提供信息。这样能让AI助手在查找信息时更准确。大模型只训练到某一个时间段,因此它更可能找到的是旧文章而不是新文章。

📝 查看主程序文件:
app/main.py

    从 datetime 模块导入 datetime 和 timezone
    从 langchain_core.messages 导入 BaseMessage, HumanMessage, SystemMessage

    now = datetime.now(timezone.utc)

    system_prompt = f"""
    你是一个帮助用户的智能助手。

    当前日期和时间: {now}
    """

    async def run_graph(question: str) -> None:
        """
        运行图表
        """
        async for event in graph.astream(
            {
                "messages": [
                    SystemMessage(content=system_prompt),
                    HumanMessage(content=question),
                ]
            }
        ):
            for value in event.values():
                print(value["messages"][-1].content)

        # return None  # 可以省略此行,因为在异步函数中返回 None 是常见的做法。

全屏 退出全屏

创建一个新的异步函数 main,此函数将接收用户输入,并根据输入添加一个 while 循环,当用户输入 (quit, exit 或 q) 时停止聊天会话。该函数将使用 run_graph 函数处理用户输入,并按照之前所述的过程进行操作。

📝 修改app/main.py文件来编辑吧
或者让我们开始处理app/main.py这个文件

    async def 主() -> None:
        """
        AI 代理
        """
        while True:
            问题 = input("q: ")
            if 问题.lower() in ["退出", "结束", "q"]:
                print("再见!")
                break

            await 运行(问题)

        # return None (removed as per expert suggestion)

    if __name__ == "__main__":
        anyio.run(主())

全屏模式 退出全屏

工具呼叫

你将在 .env.settings 文件中使用 tavily_api_key。通过使用 TavilySearchResults,你的 AI 助手可以搜索信息。我们将它们放入 tools 中,这样你可以方便地添加更多工具。然后设置 llm_with_tools = llm.bind_tools,这将给你的 AI 助手提供工具列表。接下来,将所有曾使用 llm 的地方改为 llm_with_tools

📝 app/main.py # 主程序文件

    从 langchain_community.tools.tavily_search 导入 TavilySearchResults 作为 TavilySearchResults

    web_search = TavilySearchResults(max_results=2)
    tools = [web_search]

    llm = ChatOpenAI(
        openai_api_key=settings.openai_key,
        model_name="gpt-4o-mini",
        max_retries=2,
    )

    llm_with_tools = llm.bind_tools(tools)

    async def chatbot(state: State) -> State:
        """
        处理聊天消息并返回回复
        """
        response_message = await llm_with_tools.ainvoke(state["messages"])

        return {"messages": [response_message]}

全屏模式 退出全屏

接下来添加一个 tool_node,这个节点用于运行工具并发送AI消息。接下来添加 add_conditional_edge,这将允许AI助手根据需要选择通往多个不同节点的边。它会先检查是否需要调用工具,如果不需调用工具,则会将其发送到 END 节点。

📝 app/main.py 文件 (主要的入口文件)

    async def get_graph() -> CompiledStateGraph:
        """
        获取图谱
        """
        graph_builder = StateGraph(State)  # 状态图(State)

        graph_builder.add_node("chatbot", chatbot)

        tool_node = ToolNode(tools=tools)
        graph_builder.add_node("tools", tool_node)  # 添加工具节点

        graph_builder.add_edge(START, "chatbot")

        graph_builder.add_conditional_edges("chatbot", tools_condition)  # 添加条件边以处理工具条件
        graph_builder.add_edge("tools", "chatbot")

        graph = graph_builder.compile()

        # 返回编译后的图谱
        return graph

进入全屏 退出全屏

保存聊天记录

你将使用从 .env.settings 文件中获取的 mongo_uri。然后在顶部添加你的 async_mongodb_client。设置一个检查点,将内存数据保存到 MongoDB 中。在 get_graph 函数内部添加这些内容,因为这需要在一个异步函数中运行。

📝 app/main.py — 这是主程序文件,这里写着主要代码呢

    from typing import Annotated, Any, TypedDict
    from langchain_core.runnables import RunnableConfig
    from langgraph.checkpoint.mongodb.aio import AsyncMongoDBSaver
    from motor.motor_asyncio import AsyncIOMotorClient

    async_mongodb_client: AsyncIOMotorClient[Any] = AsyncIOMotorClient(
        settings.mongo_uri.get_secret_value()
    )

    async def get_graph() -> CompiledStateGraph:
        """
        获取状态图
        """
        checkpointer = AsyncMongoDBSaver(
            client=async_mongodb_client,
            db_name="ai",
            checkpoint_collection_name="checkpoints",
            writes_collection_name="checkpoint_writes",
        )

        graph_builder = StateGraph(State)

        graph_builder.add_node("chatbot", chatbot)

        tool_node = ToolNode(tools=tools)
        graph_builder.add_node("tools", tool_node)

        graph_builder.add_edge(START, "chatbot")

        graph_builder.add_conditional_edges("chatbot", tools_condition)
        graph_builder.add_edge("tools", "chatbot")

        graph = graph_builder.compile(checkpointer=checkpointer)

        return graph

进入全屏 退出全屏

现在使用 MongoDB 来配置你的 thread_id,以便存储不同聊天历史中的消息。这样设置可以让助手记住多轮会话中的对话,并且允许你通过使用不同的 thread_id 来创建独立的聊天记录。

这是主程序文件:app/main.py。

    从 langchain_core.runnables 导入 RunnableConfig

    async def run_graph(config: RunnableConfig, question: str) -> None:
        """
        运行图
        """
        graph = await get_graph()

        async for event in graph.astream(
            {
                "messages": [
                    SystemMessage(content=system_prompt),
                    HumanMessage(content=question),
                ]
            },
            config=config,
            stream_mode="values",
        ):
            event["messages"][-1].pretty_print()

        return None

    async def main() -> None:
        """
        AI助手
        """
        config = RunnableConfig(configurable={"thread_id": 1})

        while True:
            question = input("q: ")
            if question.lower() in ["quit", "exit", "q"]:
                print("拜拜了!")
                break

            await run_graph(config=config, question=question)

        return None

点这里进入全屏,点这里退出全屏

你可以运行脚本并查看输出,因为我们让它用了当前日期,所以你可以看到它拿到了最新的团队信息。

    $ python3 -m app.main

    q: 卡罗莱纳黑豹队目前的战绩是什么?
    ================================ 人类消息 =================================

    卡罗莱纳黑豹队目前的战绩是什么?
    ================================== AI 消息 ==================================
    工具调用:
      tavily_search_results_json (call_apvK6LYyrTMRunPcoO8enCqD)
     调用ID:call_apvK6LYyrTMRunPcoO8enCqD
      参数:
        query: 卡罗莱纳黑豹队目前的战绩
    ================================= 工具消息 =================================
    名称:tavily_search_results_json

    [{"url": "https://www.footballdb.com/teams/nfl/carolina-panthers/results", "content": "查看2024年卡罗莱纳黑豹队的比赛日程、结果和分数,包括常规赛、季前赛和季后赛。... 2024年战绩:3胜11负。12月29日,2024年1:00 PM;卡罗莱纳(3-11)--坦帕湾(8-6)--最近一场比赛:TB 26 @ CAR 23(2024年12月1日)"}, {"url": "https://champsorchumps.us/team/nfl/carolina-panthers/2024", "content": "卡罗莱纳黑豹队目前战绩为3胜9负。2024年黑豹队主场战绩为2胜5负,客场战绩为1胜4负。2024年卡罗莱纳黑豹队所在的是哪个分区?2024年卡罗莱纳黑豹队在NFC南部分区比赛中。"}]
    ================================== AI 消息 ==================================

    截至12月2024,卡罗莱纳黑豹队的战绩为**3胜11负**(3-11),目前排名第4。

全屏模式 退出全屏模式

你可以在langsmith项目中一步一步查看运行。

Langsmith 迹踪图 [图片]

这是获取一个能够自主决定是否需要工具调用的AI代理的基础知识,该代理可以自行决定是否在网络上查找信息或获取资料。此外,它还可以为每个对话存储信息,使你的助手能够记住之前的对话。由于我们使用的是LangChain,将来很容易添加其他功能,如流式处理、多模型支持和其他大型语言模型。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消