LangChain入门初体验-实现简单智能体

35 阅读9分钟

第1天:环境搭建与LangChain入门

第一天我们将快速搭建开发环境,并深入理解LangChain的核心概念,最终实现一个能回答“现在几点了”的智能体(Agent)。通过这个简单的实践,你将掌握LangChain的基本工作流。


1. LangChain核心概念

LangChain是一个用于构建大语言模型(LLM)应用的框架。它抽象出几个关键组件,让开发者可以灵活组合,构建复杂的AI应用。

1.1 Chain(链)

Chain 是LangChain中最基础的组合单元。它将多个组件(如LLM、Prompt模板、输出解析器等)串联成一个处理流程。例如,一个简单的Chain可能包含:

  • 一个Prompt模板:将用户输入格式化成适合LLM的指令。
  • 一个LLM:生成回复。
  • 一个输出解析器:将LLM的输出整理成需要的格式。

在Agent的上下文中,Chain通常作为Agent内部处理逻辑的一部分,但Agent本身是更高级的抽象,它包含了决策和工具调用的循环。

1.2 Tool(工具)

Tool 是Agent可以调用的外部功能。每个工具通常包含:

  • 名称(name):唯一标识。
  • 描述(description):告诉LLM该工具的功能和何时使用,是LLM选择工具的关键依据。
  • 函数(func):实际执行的逻辑,可以是一个Python函数。

例如,一个“时间工具”可以返回当前时间;一个“计算器工具”可以执行数学运算。工具使得Agent能够突破LLM的知识局限,获取实时信息或执行具体操作。

1.3 Agent(智能体)

Agent 是一个由LLM驱动的决策者。它接收用户输入,结合可用工具的描述,决定采取什么行动(比如调用某个工具或直接回答)。Agent的核心是“思考-行动-观察”循环,这个循环通常基于ReAct模式(下文详述)。

LangChain提供了多种Agent类型,如zero-shot-react-descriptionconversational-react-description等。不同类型的Agent在提示词和记忆处理上有所区别。

1.4 Executor(执行器)

Executor(也称为AgentExecutor)负责驱动Agent的循环。它接收Agent和工具列表,然后:

  1. 将用户输入传递给Agent。
  2. Agent返回一个行动指令(比如调用哪个工具、输入什么参数)。
  3. Executor执行该工具,获得观察结果。
  4. 将观察结果返回给Agent,让Agent继续思考。
  5. 重复直到Agent输出最终答案或达到最大迭代次数。

Executor还处理错误、限制循环次数、格式化中间步骤等。


2. ReAct模式原理

ReAct(Reason + Act)是一种提示词工程模式,由Shunyu Yao等人提出。它让LLM在生成最终答案之前,先进行推理(Reason)并采取行动(Act),然后基于观察结果继续推理。这种模式模仿了人类解决问题的方式:先思考需要什么信息,然后通过行动(如查询、计算)获取信息,再根据新信息继续推理,最终得出结论。

在LangChain的Agent中,ReAct模式的典型流程如下:

  1. 思考(Thought):LLM分析当前情况,决定下一步需要做什么。
  2. 行动(Action):LLM输出一个工具调用指令,格式通常为 Action: tool_name\nAction Input: tool_input
  3. 观察(Observation):Executor执行工具,并将结果返回给LLM,格式为 Observation: tool_output
  4. 重复:LLM基于观察结果继续思考,可能再次调用工具,直到它认为可以给出最终答案。
  5. 最终答案(Final Answer):当LLM确定已经获得足够信息时,它会输出 Final Answer: ...

这种循环让LLM能够动态获取外部信息,大大增强了其处理复杂任务的能力。


3. 如何自定义Tool

在LangChain中,自定义工具非常灵活。最常用的两种方式:

3.1 使用 @tool 装饰器(推荐)

from langchain.tools import tool

@tool
def get_current_time(format: str = "%Y-%m-%d %H:%M:%S") -> str:
    """返回当前时间的字符串,格式可指定。当用户询问时间时使用此工具。"""
    from datetime import datetime
    return datetime.now().strftime(format)

装饰器会自动将函数转换为Tool对象,函数名作为工具名,文档字符串作为工具描述。

3.2 继承 BaseTool

from langchain.tools import BaseTool
from datetime import datetime

class TimeTool(BaseTool):
    name = "get_current_time"
    description = "返回当前时间的字符串,格式为'%Y-%m-%d %H:%M:%S'。当用户询问时间时使用此工具。"

    def _run(self, tool_input: str = "") -> str:
        # tool_input 可以用于接收格式参数,这里简单处理
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    async def _arun(self, tool_input: str = "") -> str:
        # 异步版本,如果不需要可简单调用 _run
        return self._run(tool_input)

这种方式适合需要更复杂配置的场景。

注意事项

  • 工具的描述要清晰准确,以便LLM正确选择。
  • 工具的函数参数应与LLM可能传递的输入匹配,必要时可以使用argparsepydantic模型定义参数结构。

4. 实践任务:搭建一个“时间查询”Agent

现在我们将动手实践,完成第一个Agent。

4.1 环境安装

首先,创建项目目录并安装必要的Python包。建议使用Python 3.9+。

# 创建虚拟环境(可选)
python -m venv langchain-env
source langchain-env/bin/activate  # Windows: langchain-env\Scripts\activate

# 安装核心库
pip install langchain==0.3.21 langchain-openai==0.3.6 python-dotenv

如果你使用OpenAI,需要获取API Key并设置为环境变量。在项目根目录创建.env文件:

OPENAI_API_KEY=你的API密钥

如果使用DeepseekAI,配置文件内容如下:

DEEPSEEK_API_KEY = "sk-************************"
DEEPSEEK_API_BASE_URL = "https://api.deepseek.com/v1"

以下使用deepseekAI和ollama本地模型两种方式。

4.2 编写自定义时间工具

创建一个Python文件,比如time_agent.py,首先导入所需模块:

import os
from dotenv import load_dotenv
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

load_dotenv()  # 加载环境变量

定义时间工具:

@tool
def get_current_time() -> str:
    """获取当前日期和时间。当用户询问时间或日期时使用此工具。"""
    from datetime import datetime
    now = datetime.now()
    return now.strftime("%Y年%m月%d日 %H:%M:%S")

4.3 创建Agent

我们需要一个LLM实例、工具列表和一个提示词模板。

# 初始化LLM
llm = ChatOpenAI(model="deepseek-chat",
                 openai_api_key = os.getenv("DEEPSEEK_API_KEY"),
                 openai_api_base = os.getenv("DEEPSEEK_API_BASE_URL"),
                 temperature=0) # temperature=0使输出更确定

# 工具列表
tools = [get_current_time]

# ReAct 提示词模板(LangChain已内置,但我们可以自定义以理解结构)
# 这里使用 LangChain 的标准模板,它包含了 "Answer:" 等占位符
prompt = PromptTemplate.from_template(
    """你是一个智能助手,可以调用工具来获取信息。
尽可能以有帮助的方式回答问题。

你有这些工具可用:
{tools}

工具名称:{tool_names}

使用以下格式:
Question: 用户输入的问题
Thought: 你下一步应该做什么
Action: 要使用的工具名称,必须是 [{tool_names}] 之一
Action Input: 工具的输入参数
Observation: 工具返回的结果
... (这个 Thought/Action/Action Input/Observation 可以重复多次)
Thought: 我现在知道最终答案
Final Answer: 对用户的最终回答

开始!

Question: {input}
Thought: {agent_scratchpad}
"""
)

# 创建 Agent
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)

# 创建 Executor
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # 打印中间步骤,方便调试
    handle_parsing_errors=True,  # 处理解析错误
    max_iterations=5,  # 防止无限循环
)

使用ollama本地模型时,需要安装额外的包langchain-ollama。

pip install langchain-ollama==0.3.9

主要修改llm的初始化:

# 初始化LLM

llm = OllamaLLM(model="qwen3:8b", base_url="http://localhost:11434") # temperature=0使输出更确定

4.4 运行查询

添加主程序入口,测试Agent:

if __name__ == "__main__":
    query = "现在几点了?"
    response = agent_executor.invoke({"input": query})
    print("最终回答:", response["output"])

4.5 完整代码

将上述片段整合成完整的脚本:

import os
from dotenv import load_dotenv
from langchain.agents import AgentExecutor, create_react_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate

load_dotenv()

@tool
def get_current_time() -> str:
    """获取当前日期和时间。当用户询问时间或日期时使用此工具。"""
    from datetime import datetime
    now = datetime.now()
    return now.strftime("%Y年%m月%d日 %H:%M:%S")

def main():
    llm = ChatOpenAI(model="deepseek-chat",
                 openai_api_key = os.getenv("DEEPSEEK_API_KEY"),
                 openai_api_base = os.getenv("DEEPSEEK_API_BASE_URL"),
                 temperature=0) # temperature=0使输出更确定
    tools = [get_current_time]

    prompt = PromptTemplate.from_template(
        """你是一个智能助手,可以调用工具来获取信息。
尽可能以有帮助的方式回答问题。

你有这些工具可用:
{tools}

工具名称:{tool_names}

使用以下格式:
Question: 用户输入的问题
Thought: 你下一步应该做什么
Action: 要使用的工具名称,必须是 [{tool_names}] 之一
Action Input: 工具的输入参数
Observation: 工具返回的结果
... (这个 Thought/Action/Action Input/Observation 可以重复多次)
Thought: 我现在知道最终答案
Final Answer: 对用户的最终回答

开始!

Question: {input}
Thought: {agent_scratchpad}
"""
    )

    agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
    agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        handle_parsing_errors=True,
        max_iterations=5,
    )

    query = "现在几点了?"
    result = agent_executor.invoke({"input": query})
    print("\n最终回答:", result["output"])

if __name__ == "__main__":
    main()

4.6 运行与预期输出

执行脚本(确保已设置OPENAI_API_KEY):

python time_agent.py

你会看到类似如下的日志(verbose=True会显示中间步骤):

> Entering new AgentExecutor chain...
Thought: 用户想知道当前时间,我需要调用时间工具来获取。
Action: get_current_time
Action Input: {}
Observation: 2025年03月04日 15:30:45
Thought: 我已经获得了当前时间,可以直接回答用户。
Final Answer: 现在是2025年03月04日 15:30:45。

> Finished chain.
最终回答: 现在是2025年03月04日 15:30:45。

成功运行了第一个LangChain Agent。

5. 小结

  • 理解了LangChain的四个核心概念:Chain、Tool、Agent、Executor。
  • 掌握了ReAct模式的工作原理。
  • 学会了如何自定义Tool。
  • 成功编写并运行了一个能查询时间的Agent。

这个简单的“Hello World” Agent为你后续构建更复杂的应用打下了基础。接下来可以尝试添加更多工具(如天气查询、计算器等),或探索不同类型的Agent(如conversational-react-description)来支持多轮对话。