Декоратор @tool в LangChain — Определение инструментов

Инструмент — это мост для взаимодействия агента с внешним миром.

Используя@toolдекоратор, можно быстро преобразовать любую функцию Python в инструмент, вызываемый агентом.


@tool Базовый синтаксис

@tool — это декоратор, предоставляемый LangChain. Его использование чрезвычайно просто: добавьте декоратор @tool к функции, и функция становится инструментом.

Пример

from langchain.tools import tool

# Простейший инструмент: обычная функция + декоратор @tool
@tool
def hello_tool(name: str) -> str:
    """Поприветствуй указанного человека.

    Аргументы:
name: имя человека, которого нужно поприветствовать
    """

    return f"Привет, {name}! Добро пожаловать в EXAMPLE Tutorial."

# Инструмент — это тоже обычная функция Python, и её можно вызывать напрямую
result = hello_tool.invoke({"name": "Xiao Ming"})
print(result)

# Инструмент содержит автоматически сгенерированную описательную информацию
print(f"\nИмя инструмента: {hello_tool.name}")
print(f"Описание инструмента: {hello_tool.description}")

Вывод:

你好,小明!欢迎来到Example.

Tool name: hello_tool
Tool description: 向指定的人打招呼。

    Args:
        name: 要打招呼的人的名字

Docstring функции автоматически становится описанием инструмента. Агент полагается на это описание, чтобы определить, «что умеет этот инструмент» и «когда его следует вызывать». Чем яснее docstring, тем точнее Агент использует инструмент. В описании поясните значения параметров, поведение функции и сценарии использования.


Использование различных типов параметров

@tool поддерживает несколько типов параметров, включая int, float, bool и значения enum:

Пример

from langchain.tools import tool
from typing import Literal


@tool
def search_courses(
    keyword: str,
    level: Literal["Beginner", "Intermediate", "Advanced"],
    max_results: int = 5,
    free_only: bool = True,
) -> str:
    """Поиск курсов на EXAMPLE Tutorial.

    Аргументы:
keyword: поисковый запрос
level: уровень сложности курса
max_results: максимальное количество возвращаемых результатов
free_only: показывать ли только бесплатные курсы
    """

    # Симулировать результаты поиска
    courses = {
        ("python", "Начальный"): "Учебник по основам Python3 (бесплатно)",
        ("python", "Средний"): "Практика анализа данных на Python (бесплатно)",
        ("python", "Продвинутый"): "Продвинутое машинное обучение на Python (только для участников)",
        ("html", "Начальный"): "Учебник по основам HTML (бесплатно)",
    }
    key = (keyword.lower(), level)
    course = courses.get(key, f"Не найдено курсов по запросу {keyword} уровня {level}")
    if free_only and "только для участников" in course:
        return f"Результат поиска: {course} — для этого курса требуется членство, и он был отфильтрован для вас"
    return f"Результат поиска: {course}"


# Тестовые вызовы с разными параметрами
print(search_course.invoke({
    "keyword": "python",
    "level": "Новичок",
}))
# Вывод: Результат поиска: Учебник по основам Python3 (бесплатно)

print(search_course.invoke({
    "keyword": "python",
    "level": "Продвинутый",
    "free_only": False,
}))
# Вывод: Результат поиска: Продвинутое машинное обучение на Python (только для участников)

Регистрация инструментов в Agent

Передайте определённый инструмент в параметр tools функции create_agent(), и Agent сможет его использовать:

Пример

from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


@tool
def search_courses(keyword: str) -> str:
    """Поиск курсов на EXAMPLE Tutorial. Передайте ключевое слово и получите список связанных курсов.

    Аргументы:
keyword: ключевое слово для поиска, например python, html, java
    """

    courses = {
        "python": "Учебник по основам Python3, Анализ данных на Python, Введение в веб-краулеры на Python",
        "html": "Учебник по основам HTML, Новые возможности HTML5, Практика форм HTML",
        "java": "Учебник по основам Java, Объектно-ориентированное программирование на Java, Java Spring Framework",
    }
    return courses.get(keyword.lower(), f"Не найдено курсов, связанных с {keyword}")


@tool
def get_course_detail(course_name: str) -> str:
    """Получить подробную информацию об указанном курсе, включая количество глав и продолжительность обучения.

    Аргументы:
course_name: название курса, например, "Python3 Basics Tutorial"
    """

    details = {
        "python3 basics tutorial": "Всего 30 глав, ориентировочное время обучения 20 часов, подходит для абсолютных новичков",
        "html basics tutorial": "Всего 25 глав, ориентировочное время обучения 15 часов, подходит для абсолютных новичков",
    }
    return details.get(
        course_name.lower(),
        f"Подробности о {course_name}: подходит для начинающих, богатое содержание, с практическими примерами"
    )


# Создание агента
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[search_courses, get_course_detail],
    system_prompt="Вы консультант по обучению в EXAMPLE Tutorial, помогаете пользователям находить подходящие курсы.",
)


# Запуск агента
def ask(question: str):
    result = agent.invoke({"messages": [HumanMessage(content=question)]})
    print(f"Пользователь: {question}")
    print(f"Консультант: {result['messages'][-1].content}")
    print("-" * 60)


ask("Я хочу изучать Python. Какие курсы вы рекомендуете?")
ask("Сколько времени нужно, чтобы пройти Python3 Basics Tutorial?")

Вывод:

User: 我想学 Python,有什么课程推荐?
Advisor: 在Example 中有以下 Python 相关课程:
Python3 基础教程、Python 数据分析、Python 爬虫入门。
这些课程很适合 Python 初学者和进阶学习者,您可以根据兴趣选择!
------------------------------------------------------------
User: Python3 基础教程学完需要多久?
Advisor: Python3 基础教程共 30 章,预计学习时长 20 小时,非常适合零基础入门学习。
------------------------------------------------------------

Совместная работа нескольких инструментов

Агент может зарегистрировать несколько инструментов, и модель автоматически решит, когда какой инструмент использовать:

Пример

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage


@tool
def get_stock_price(symbol: str) -> str:
    """Запросить текущую цену акции.

    Аргументы:
symbol: символ акции, например, AAPL, GOOGL, TSLA
    """

    # Имитация данных об акциях
    prices = {"AAPL": 185.50, "GOOGL": 142.30, "TSLA": 245.80}
    price = prices.get(symbol.upper())
    if price is None:
        return f"Символ акции {symbol} не найден"
    return f"{symbol.upper()} текущая цена: ${price}"


@tool
def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:
    """Конвертация валют.

    Аргументы:
amount: сумма денег
from_currency: код исходной валюты, например, USD, CNY
to_currency: код целевой валюты, например, CNY, USD
    """

    # Имитация обменных курсов
    rates = {
        ("USD", "CNY"): 7.25,
        ("CNY", "USD"): 0.138,
    }
    rate = rates.get((from_currency.upper(), to_currency.upper()))
    if rate is None:
        return f"Конвертация из {from_currency} в {to_currency} не поддерживается"
    result = round(amount * rate, 2)
    return f"{amount} {from_currency} = {result} {to_currency}"


model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
    model=model,
    tools=[get_stock_price, convert_currency],
    system_prompt="Вы финансовый помощник, который помогает пользователям проверять цены акций и конвертировать валюты.",
)

# Агент автоматически решит, какой инструмент вызвать
result = agent.invoke({
    "messages": [HumanMessage(content="Какова цена акций Apple? Сколько это в RMB?")]
})

for msg in result["messages"]:
    if msg.type == "tool":
        print(f"[Вызов инструмента {msg.name}] {msg.content}")
    elif msg.type == "ai" and msg.content:
        print(f"\nИтоговый ответ: {msg.content}")

Вывод:

[调用工具 get_stock_price] AAPL 当前价格:$185.5
[调用工具 convert_currency] 185.5 USD = 1344.88 CNY

Final answer: 苹果(AAPL)当前股价为 $185.50,按当前汇率换算约为 1344.88 人民币。

Необязательные параметры и значения по умолчанию для инструментов

Установка значений по умолчанию для параметров инструмента делает инструменты более гибкими в использовании:

Пример

from langchain.tools import tool


@tool
def recommend_tutorial(
    language: str,
    level: str = "Beginner",
    count: int = 3,
) -> str:
    """Рекомендовать курсы из EXAMPLE Tutorial на основе языка программирования и сложности.

    Аргументы:
language: язык программирования, например, Python, Java, HTML
level: уровень сложности, варианты "Beginner", "Intermediate", "Advanced". По умолчанию "Beginner"
count: количество курсов для рекомендации, по умолчанию 3
    """

    tutorials = {
        "python": ["Основы Python3", "Объектно-ориентированный Python", "Веб-краулер на Python", "Анализ данных на Python"],
        "java": ["Основы Java", "Объектно-ориентированное программирование на Java", "Коллекции Java", "Многопоточность в Java"],
    }
    all_tutorials = tutorials.get(language.lower(), [f"Учебник по основам {language}"])

    selected = all_tutorials[:count]
    return f"Рекомендуемые курсы {language} {level}: {'、'.join(selected)}"


# При вызове нужно передавать только обязательные параметры
print(recommend_tutorial.invoke({"language": "Python"}))
# Вывод: Рекомендуемые курсы Python для начинающих: Основы Python3, Объектно-ориентированное программирование на Python, Веб-краулер на Python

# Вы также можете переопределить параметры по умолчанию
print(recommend_tutorial.invoke({
    "language": "Java",
    "level": "Средний",
    "count": 2,
}))
# Вывод: Рекомендуемые курсы Java среднего уровня: Java Basics, Java Object-Oriented

Значения по умолчанию позволяют Агенту не указывать все параметры каждый раз при вызове инструментов. Но обратите внимание: если у параметра нет значения по умолчанию и Агент не передаёт его, вызов завершится ошибкой. Не задавайте значения по умолчанию для ключевых параметров.


args_schema инструмента — пользовательская проверка параметров

Для сложных требований к валидации параметров можно использовать модель Pydantic в качестве args_schema:

Пример

from pydantic import BaseModel, Field
from langchain.tools import tool


# Определение модели параметров (обеспечивает более тонкий контроль параметров)
class CourseSearchInput(BaseModel):
    """Параметры поиска курса"""
    keyword: str = Field(
        description="Ключевое слово для поиска, поддерживает нечёткое сопоставление",
        min_length=1,            # Минимум 1 символ
        max_length=50,           # Не более 50 символов
    )
    category: str = Field(
        default="all",
        description="Категория курса: all (all), frontend (frontend), backend (backend), data (наука о данных)",
        pattern=r"^(all|frontend|backend|data)$",  # Ограничить допустимые значения
    )
    page: int = Field(
        default=1,
        description="Номер страницы, начиная с 1",
        ge=1,                    # Больше или равно 1
        le=100,                  # Меньше или равно 100
    )


@tool(args_schema=CourseSearchInput)
def search_course(keyword: str, category: str = "all", page: int = 1) -> str:
    """Поиск курсов на учебном сайте EXAMPLE"""
    return f"Поиск '{keyword}' (категория: {category}, страница {page}): всего найдено 15 результатов"


# Корректный вызов
print(search_course.invoke({"keyword": "Python", "category": "backend", "page": 1}))

# Некорректный вызов (category не входит в список допустимых значений)
try:
    search_course.invoke({"keyword": "Python", "category": "invalid"})
except Exception as e:
    print(f"Ошибка проверки параметров: {e}")

Результаты выполнения:

搜索 'Python' (分类: backend, 第 1 页):共找到 15 条结果
参数校验失败: ...

Сравнение методов определения инструментов

МетодОбъём кодаПрименимые сценарииПример
декоратор @toolМинимальныйОт простых до умеренно сложных инструментовБольшинство сценариев
@tool + args_schemaСредняяИнструменты, требующие детальной валидации параметровОбёртывание API, операции с базами данных
Класс Pydantic как инструментПодробнееИнструменты со сложной бизнес-логикойИнструменты, содержащие внутреннее состояние
Формат словаряНаименее предпочтительно (но не рекомендуется)Описание удалённых/встроенных инструментовИнструменты MCP, серверные инструменты
Другие расширения