Разработка с Claude API

Запросы, потоковые ответы, tool use, структурированный вывод и стоимость.

92 уроков, первые 3 бесплатно. Полный доступ: 1490 руб.

Уроки курса

  1. Welcome to the course
  2. Overview of Claude models
  3. Accessing the API
  4. Getting an API key
  5. Making a request
  6. Multi-Turn conversations
  7. Chat exercise
  8. System prompts
  9. System prompts exercise
  10. Temperature
  11. Course satisfaction survey
  12. Response streaming
  13. Structured data
  14. Structured data exercise
  15. Quiz on accessing Claude with the API
  16. Prompt evaluation
  17. A typical eval workflow
  18. Generating test datasets
  19. Running the eval
  20. Model based grading
  21. Code based grading
  22. Exercise on prompt evals
  23. Quiz on prompt evaluation
  24. Prompt engineering
  25. Being clear and direct
  26. Being specific
  27. Structure with XML tags
  28. Providing examples
  29. Exercise on prompting
  30. Quiz on prompt engineering techniques
  31. Introducing tool use
  32. Project overview
  33. Tool functions
  34. Tool schemas
  35. Handling message blocks
  36. Sending tool results
  37. Multi-turn conversations with tools
  38. Implementing multiple turns
  39. Using multiple tools
  40. Fine grained tool calling
  41. The text edit tool
  42. The web search tool
  43. Quiz on tool use with Claude
  44. Introducing Retrieval Augmented Generation
  45. Text chunking strategies
  46. Text embeddings
  47. The full RAG flow
  48. Implementing the RAG flow
  49. BM25 lexical search
  50. A Multi-Index RAG pipeline
  51. Extended thinking
  52. Image support
  53. PDF support
  54. Citations
  55. Prompt caching
  56. Rules of prompt caching
  57. Prompt caching in action
  58. Code execution and the Files API
  59. Quiz on features of Claude
  60. Introducing MCP
  61. MCP clients
  62. Project setup
  63. Defining tools with MCP
  64. The server inspector
  65. Implementing a client
  66. Defining resources
  67. Accessing resources
  68. Defining prompts
  69. Prompts in the client
  70. MCP review
  71. Quiz on Model Context Protocol
  72. Anthropic apps
  73. Claude Code setup
  74. Claude Code in action
  75. Enhancements with MCP servers
  76. Agents and workflows
  77. Parallelization workflows
  78. Chaining workflows
  79. Routing workflows
  80. Agents and tools
  81. Environment inspection
  82. Workflows vs agents
  83. Quiz on Agents and Workflows
  84. Final Assessment
  85. Course Wrap Up
  86. What You'll Learn
  87. What You'll Learn
  88. What You'll Learn
  89. What You'll Learn
  90. What You'll Learn
  91. What You'll Learn
  92. What You'll Learn

Многошаговые диалоги с инструментами: Когда Claude нужен не один инструмент

Разработка приложений с использованием больших языковых моделей (LLM), таких как Claude, часто включает интеграцию с внешними инструментами. Эти инструменты позволяют Claude выполнять действия в реальном мире, получать актуальную информацию или проводить сложные вычисления, выходящие за рамки его собственных знаний. В простых сценариях Claude может вызвать один инструмент, получить результат и сразу же ответить пользователю.

Однако что происходит, когда для ответа на один вопрос пользователя Claude требуется выполнить несколько последовательных действий, каждое из которых требует вызова отдельного инструмента? Например, если пользователь спрашивает: "Какой день будет через 103 дня от сегодняшней даты?", Claude не может ответить на этот вопрос напрямую. Ему сначала нужно узнать текущую дату, а затем прибавить к ней 103 дня. Это подразумевает два отдельных вызова инструмента: один для получения текущей даты, другой для выполнения арифметической операции с датами.

Такие ситуации создают так называемый многошаговый диалог с инструментами (Multi-Turn Tool Pattern). В этом паттерне Claude делает несколько запросов к инструментам, прежде чем предоставить окончательный ответ. Ваше приложение должно быть спроектировано таким образом, чтобы автоматически обрабатывать эту последовательность взаимодействий, создавая впечатление бесшовного и мгновенного ответа для конечного пользователя.

Как Claude использует несколько инструментов: Сценарий взаимодействия

Чтобы лучше понять, как работает многошаговый диалог, давайте рассмотрим пример с датой:

  1. Пользователь спрашивает: "Какой день будет через 103 дня от сегодняшней даты?"
  2. Claude анализирует запрос: Он понимает, что для ответа ему нужна текущая дата.
  3. Claude отвечает с блоком tool_use: Он запрашивает вызов инструмента get_current_datetime.
  4. Ваш сервер получает запрос: Он вызывает функцию get_current_datetime (например, через API операционной системы или внешнего сервиса) и возвращает результат Claude (например, "2023-10-27").
  5. Claude обрабатывает результат: Теперь у него есть текущая дата. Он понимает, что следующим шагом является добавление 103 дней.
  6. Claude снова отвечает с блоком tool_use: Он запрашивает вызов инструмента add_duration_to_datetime, передавая ему текущую дату и "103 дня".
  7. Ваш сервер получает второй запрос: Он вызывает функцию add_duration_to_datetime с предоставленными параметрами и возвращает результат (например, "2024-02-07").
  8. Claude получает окончательный результат: Теперь у него достаточно информации, чтобы сформулировать окончательный ответ.
  9. Claude предоставляет окончательный ответ: "Через 103 дня от сегодняшней даты будет 7 февраля 2024 года."

Ключевой момент здесь заключается в том, что ваше приложение выступает в роли оркестратора, управляя потоком сообщений между пользователем, Claude и внешними инструментами. Claude не выполняет инструменты сам; он лишь указывает, какие инструменты ему нужны и с какими параметрами. Ваша задача — выполнить эти вызовы и передать результаты обратно Claude.

Реализация цикла обработки диалога

Для обработки многошаговых диалогов с инструментами вам потребуется реализовать цикл на стороне вашего приложения, который будет продолжаться до тех пор, пока Claude не перестанет запрашивать вызовы инструментов. Этот цикл гарантирует, что все необходимые инструменты будут вызваны, а их результаты будут переданы Claude для получения окончательного ответа.

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

def run_conversation(messages):
    while True:
        # 1. Отправляем текущую историю сообщений Claude и получаем ответ.
        response = chat(messages) 

        # 2. Добавляем ответ Claude в историю сообщений.
        add_assistant_message(messages, response) 

        # 3. Проверяем, запрашивает ли Claude вызов инструмента.
        if not response_contains_tool_use(response):
            # Если нет, значит, Claude готов дать окончательный ответ или уже дал его.
            break 

        # 4. Если Claude запросил инструменты, извлекаем их.
        tool_calls = extract_tool_calls(response) 

        # 5. Выполняем запрошенные инструменты на вашем сервере.
        tool_results = run_tools(tool_calls) 

        # 6. Добавляем результаты выполнения инструментов в историю сообщений
        #    как сообщения от пользователя (имитируя, что пользователь "предоставил" результаты).
        add_user_message(messages, tool_results) 

    # 7. После завершения цикла возвращаем полную историю сообщений.
    return messages

Этот цикл позволяет Claude и вашему приложению обмениваться сообщениями до тех пор, пока Claude не соберет всю необходимую информацию для формирования окончательного ответа. Важно, чтобы функция chat возвращала полный объект сообщения, а не только текстовое содержимое, чтобы вы могли анализировать блоки tool_use.

Обновление вспомогательных функций для работы с инструментами

Для эффективной работы с многошаговыми диалогами и инструментами необходимо обновить некоторые вспомогательные функции, которые управляют историей сообщений и взаимодействием с API Claude.

Гибкая обработка сообщений (add_user_message и add_assistant_message)

Ваши функции для добавления сообщений в историю (например, add_user_message) должны стать более гибкими. Ранее они могли просто принимать строку текста. Теперь им нужно уметь обрабатывать различные типы контента, включая:

Это позволяет вам передавать результаты выполнения инструментов в виде структурированных блоков, которые Claude может легко интерпретировать. Например, функция add_user_message может быть изменена для проверки типа входных данных:

from anthropic.types import Message, ToolUseBlock, ToolResultBlock

def add_user_message(messages, content):
    if isinstance(content, Message):
        # Если это полный объект Message, берем его content
        user_content = content.content
    elif isinstance(content, list):
        # Если это список блоков (например, ToolResultBlock), используем его напрямую
        user_content = content
    else:
        # В противном случае, это просто строка текста
        user_content = [{"type": "text", "text": str(content)}]

    user_message = {
        "role": "user",
        "content": user_content
    }
    messages.append(user_message)

Аналогичные изменения потребуются и для add_assistant_message, чтобы она могла корректно добавлять ответы Claude, содержащие блоки tool_use.

Модификация функции chat

Ваша основная функция для взаимодействия с Claude (например, chat) также требует изменений:

  1. Прием схем инструментов: Она должна принимать список схем инструментов (tools), которые вы хотите предоставить Claude. Это позволяет Claude знать, какие инструменты доступны и как их вызывать.
  2. Возврат полного объекта Message: Вместо того чтобы возвращать только текстовое содержимое ответа Claude, функция chat теперь должна возвращать полный объект Message, который содержит все блоки контента, включая tool_use.
def chat(messages, system=None, temperature=1.0, stop_sequences=[], tools=None):
    params = {
        "model": "claude-3-opus-20240229", # Пример модели
        "max_tokens": 1000,
        "messages": messages,
        "temperature": temperature,
        "stop_sequences": stop_sequences,
    }
    if tools:
        params["tools"] = tools # Передаем схемы инструментов
    if system:
        params["system"] = system

    # Вызываем API и возвращаем полный объект Message
    message = client.messages.create(**params) 
    return message

Извлечение текстового контента (text_from_message)

Поскольку функция chat теперь возвращает полные объекты Message, которые могут содержать не только текст, но и другие блоки (например, tool_use), вам понадобится вспомогательная функция для извлечения только текстового содержимого, когда это необходимо (например, для отображения ответа пользователю).

def text_from_message(message):
    # Ищем все блоки типа "text" и объединяем их содержимое
    return "\n".join(
        [block.text for block in message.content if block.type == "text"]
    )

Эта функция позволяет легко получить читаемый текст из сложного объекта Message, игнорируя блоки, не предназначенные для прямого отображения пользователю.

Ключевые преимущества и подготовка к сложным сценариям

Эти шаги по рефакторингу и обновлению кода подготавливают ваше приложение к надежной и гибкой работе с инструментами:

С этими основами вы готовы реализовать полноценный цикл обработки диалога, который автоматически обрабатывает множественные вызовы инструментов. Это создает бесшовный опыт, при котором Claude может использовать столько инструментов, сколько необходимо, чтобы ответить на вопросы пользователя, а ваше приложение эффективно управляет этим сложным взаимодействием за кулисами.