by CIVAI (open source) Python 3.8+, Claude, OpenAI, Ollama, локальные модели
Python-агент, воспроизводящий возможности coding-ассистента Cursor — function calling, генерация кода, интеллектуальная помощь с Claude, OpenAI и локальными моделями Ollama.
Один из первых и самых известных проектов автономных AI-агентов: получает цель на естественном языке и самостоятельно …
Открытая платформа для ИИ-агентов разработки (ранее OpenDevin). Агент работает в изолированном Docker-окружении: пишет код, запускает терминальные …
Open-source реализация интерпретатора кода для LLM. Позволяет языковым моделям запускать код (Python, Shell, JavaScript) на вашей …
Автономный исследовательский агент: получает тему и самостоятельно проводит многошаговое веб-исследование, критически сопоставляет источники и выдаёт связный, …
pip install cursor-agent-tools git clone https://github.com/civai-technologies/cursor-agent.git cd cursor-agent pip install -e . # Или с зависимостями для разработки pip install -e ".[dev]" # Создайте файл .env из .env.example и укажите API-ключи (OPENAI_API_KEY, ANTHROPIC_API_KEY)
ИИ-агент на основе Python, воспроизводящий возможности ассистента для программирования Cursor: вызов функций, генерация кода и интеллектуальная помощь в написании кода с использованием Claude, OpenAI и локально размещённых моделей Ollama.
Данная реализация ИИ-агента предоставляет широкий набор возможностей:
Агент поддерживает обширный набор инструментов:
{"1-5": "new content", "10-12": "more content"})Все инструменты реализованы с реальной функциональностью и могут быть расширены пользовательскими инструментами при необходимости.
pip install cursor-agent-tools
git clone https://github.com/civai-technologies/cursor-agent.git
cd cursor-agent
pip install -e . # Install in development mode
# Or with development dependencies
pip install -e ".[dev]"
Создайте файл .env в корне вашего проекта (скопируйте из .env.example):
# Environment (local, development, production)
ENVIRONMENT=local
# OpenAI configuration
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_API_MODEL=gpt-4o
OPENAI_TEMPERATURE=0.0
# Anthropic configuration
ANTHROPIC_API_KEY=your_anthropic_api_key_here
ANTHROPIC_API_MODEL=claude-3-5-sonnet-latest
ANTHROPIC_TEMPERATURE=0.0
# Google Search API (for web_search tool)
GOOGLE_API_KEY=your_google_api_key_here
GOOGLE_SEARCH_ENGINE_ID=your_search_engine_id_here
# Ollama configuration (for local models)
OLLAMA_HOST=http://localhost:11434
import asyncio
from cursor_agent_tools import create_agent
async def main():
# Create a Claude agent instance
agent = create_agent(model='claude-3-5-sonnet-latest')
# Chat with the agent
response = await agent.chat("Create a Python function to calculate Fibonacci numbers")
print(response)
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from cursor_agent_tools import create_agent
# Use Claude
claude_agent = create_agent(model='claude-3-5-sonnet-latest')
response = await claude_agent.chat("What's a good way to implement a cache in Python?")
# Use OpenAI
openai_agent = create_agent(model='gpt-4o')
response = await openai_agent.chat("What's a good way to implement a cache in Python?")
# Use Ollama (local open-source model)
ollama_agent = create_agent(model='ollama-llama3')
response = await ollama_agent.chat("What's a good way to implement a cache in Python?")
import asyncio
from cursor_agent_tools import create_agent
async def main():
# Create an agent with a local Ollama model
# Models must be pulled via Ollama CLI first: ollama pull MODEL_NAME
agent = create_agent(
model='ollama-llama3', # prefix with "ollama-" followed by model name
host='http://localhost:11434', # optional, this is the default
temperature=0.3 # optional temperature setting
)
# Chat with the local model
response = await agent.chat("Write a Python script to download YouTube videos")
print(response)
# Handle multimodal capabilities if model supports it
# image_path = "/path/to/your/image.png"
# image_response = await agent.query_image(
# image_paths=[image_path],
# query="What does this code screenshot show?"
# )
# print(image_response)
if __name__ == "__main__":
asyncio.run(main())
Агент поддерживает любую модель, доступную в Ollama. Некоторые популярные варианты:
ollama-llama3 — модель Llama 3 от Metaollama-llama3.1 — модель Llama 3.1 от Metaollama-mistral — модель Mistral AIollama-gemma3 — модель Gemma от Googleollama-deepseek-r1 — модель логических рассуждений от DeepSeekollama-phi4 — модель Phi от Microsoftollama-qwen2.5 — новейшая модель QwenПолный список доступных моделей см. в библиотеке Ollama.
Обратите внимание, что поддержка вызова инструментов и мультимодальности зависит от возможностей конкретной модели.
import asyncio
from cursor_agent_tools import create_agent
async def main():
# Define a custom system prompt for a coding tutor
custom_system_prompt = """
You are an expert coding tutor specialized in helping beginners learn to code.
When asked coding questions:
1. First explain the concept in simple terms
2. Always provide commented example code
3. Suggest practice exercises
4. Anticipate common mistakes and warn against them
Be patient, encouraging, and avoid technical jargon unless you explain it.
Focus on building good habits and understanding core principles.
"""
# Create agent with custom system prompt
coding_tutor = create_agent(
model='claude-3-5-sonnet-latest',
system_prompt=custom_system_prompt
)
# Example interaction with the custom agent
student_question = "I'm confused about Python list comprehensions. Can you help me understand them?"
response = await coding_tutor.chat(student_question)
print(response)
# Example using image analysis capabilities
image_path = "/path/to/your/image.png"
image_response = await coding_tutor.query_image(
image_paths=[image_path],
query="What does this code screenshot show and what issues do you see?"
)
print(image_response)
# The response will follow the guidelines in the custom system prompt,
# explaining list comprehensions in a beginner-friendly way with examples,
# practice exercises, and common pitfalls to avoid
if __name__ == "__main__":
asyncio.run(main())
Этот пример создаёт специализированного агента-репетитора по программированию с настраиваемой личностью и правилами поведения. Аналогичным образом вы можете создавать собственных агентов для различных областей, составляя подходящие системные промпты:
from cursor_agent_tools import run_agent_interactive
import asyncio
async def main():
# Parameters:
# - model: The model to use (e.g., 'claude-3-5-sonnet-latest', 'gpt-4o')
# - initial_query: The task description
# - max_iterations: Maximum number of steps (default 10)
# - auto_continue: Whether to continue automatically without user input (default True)
await run_agent_interactive(
model='claude-3-5-sonnet-latest',
initial_query='Create a simple web scraper that extracts headlines from a news website',
max_iterations=15
# auto_continue=True is the default - agent continues automatically
# To disable automatic continuation, set auto_continue=False
)
if __name__ == "__main__":
asyncio.run(main())
Интерактивный режим спроектирован так, чтобы автоматически продолжать работу без участия пользователя, за исключением случаев, когда:
1. ИИ явно запрашивает дополнительную информацию у пользователя
2. Происходит ошибка, требующая решения пользователя
3. Используется флаг --auto, при котором после каждого шага запрашивается ввод пользователя
4. Достигнут максимальный лимит вызовов инструментов, и требуется подтверждение пользователя для продолжения
Когда запрашивается или предоставляется пользовательский ввод, система интеллектуально встраивает его в ход беседы следующим образом: 1. С помощью самой модели ИИ генерируется контекстно уместный промпт для продолжения 2. Ввод пользователя бесшовно интегрируется с существующей историей разговора 3. Сохраняется естественный ход процесса реализации
Интерактивный режим имеет встроенное определение ситуаций, когда агент явно запрашивает ввод от пользователя. Когда ответ агента содержит фразы вроде: - «Мне нужна дополнительная информация о...» - «Не могли бы вы предоставить больше подробностей о...» - «Пожалуйста, сообщите ваш выбор относительно...» - «Что бы вы хотели, чтобы я сделал с...»
Система автоматически приостановится и будет ждать ввода пользователя, даже в режиме автопродолжения. Это гарантирует, что когда агенту действительно нужно уточнение или решение от вас, разговор будет корректно приостановлен.
В целях безопасности и для предотвращения неконтролируемой автоматизации агент отслеживает общее количество вызовов инструментов при обработке одного ответа агента. Когда это число достигает определённого порога (по умолчанию: 5), агент запросит подтверждение пользователя перед внесением дальнейших изменений. Эта мера предосторожности: - Предотвращает непреднамеренные масштабные изменения вашей кодовой базы - Даёт вам наглядность сложных операций - Позволяет просмотреть прогресс перед продолжением - Предоставляет возможность перенаправить агента при необходимости
Важные детали работы отслеживания вызовов инструментов: - Счётчик учитывает все вызовы инструментов в рамках одной логической итерации (одного ответа агента) - Счётчик НЕ сбрасывается до тех пор, пока не будет предоставлен новый пользовательский ввод или не начат новый запрос - Счётчик сохраняется, даже когда агент продолжает ту же итерацию
Когда лимит достигнут, вам будет показан следующий запрос:
The agent has made 5 tool calls in this iteration.
Would you like to continue allowing the agent to make more changes?
Continue? (y/n):
Если вы одобрите, агент: - Продолжит делать больше вызовов инструментов в текущей итерации - Увеличит лимит на 5 для этой итерации - Снова запросит подтверждение при достижении нового лимита - Продолжит отслеживать общее количество вызовов инструментов (счётчик не сбрасывается)
Если вы отклоните, агент: - Прекратит делать дополнительные вызовы инструментов в этой итерации - Завершит текущую итерацию с уже внесёнными изменениями - Перейдёт к следующей итерации, когда будет готов
Этот адаптивный лимит гарантирует, что агент не внесёт слишком много изменений без вашего одобрения, но при этом позволяет эффективно выполнять сложные задачи.
from cursor_agent_tools import create_agent
agent = create_agent(model='claude-3-5-sonnet-latest')
user_info = {
"open_files": ["src/main.py", "src/utils.py"],
"cursor_position": {"file": "src/main.py", "line": 42},
"recent_files": ["src/config.py", "tests/test_main.py"],
"os": "darwin",
"workspace_path": "/Users/username/projects/myproject"
}
response = await agent.chat("Fix the bug in the main function", user_info=user_info)
from cursor_agent_tools import create_agent
agent = create_agent(model='claude-3-5-sonnet-latest')
def custom_tool(param1, param2):
# Tool implementation
return {"result": f"Processed {param1} and {param2}"}
# Register the tool
agent.register_tool(
name="custom_tool",
function=custom_tool,
description="Custom tool that does something useful",
parameters={
"properties": {
"param1": {"description": "First parameter", "type": "string"},
"param2": {"description": "Second parameter", "type": "string"}
},
"required": ["param1", "param2"]
}
)
Больше примеров можно найти в каталоге examples.
cursor-agent/
├── agent/ # Core agent implementation
│ ├── __init__.py # Package exports
│ ├── base.py # Base agent class
│ ├── claude_agent.py # Claude-specific implementation
│ ├── openai_agent.py # OpenAI-specific implementation
│ ├── factory.py # Agent factory function
│ ├── permissions.py # Permission system implementation
│ ├── interact.py # Interactive mode utilities
│ └── tools/ # Tool implementations
│ ├── __init__.py # Tool exports
│ ├── file_tools.py # File operations
│ ├── search_tools.py # Search functionalities
│ ├── system_tools.py # System commands
│ └── register_tools.py # Tool registration utilities
├── cursor_agent/ # Package directory for pip installation
│ ├── __init__.py # Package exports
│ └── agent/ # Re-exports of agent functionality
├── docs/ # Documentation
│ └── permissions_guide.md # Permission system documentation
├── examples/ # Example usage scripts
│ ├── basic_usage.py # Simple API usage example
│ ├── chat_conversation_example.py # Conversation example
│ ├── code_search_example.py # Code search demonstration
│ ├── file_manipulation_example.py # File tools example
│ ├── interactive_mode_example.py # Interactive session demo
│ ├── permission_example.py # Permission system demonstration
│ ├── simple_task_example.py # Basic task completion
│ ├── utils.py # Example utilities
│ └── demo_project/ # Demo project for examples
├── tests/ # Unit and integration tests
│ ├── test_permissions.py # Permission system tests
│ └── ... # Other test files
├── .env.example # Example environment variables
├── .gitignore # Git ignore patterns
├── CODE_OF_CONDUCT.md # Code of conduct for contributors
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # MIT License
├── README.md # This file
├── SECURITY.md # Security policy
├── constraints.md # Implementation constraints
├── pyproject.toml # Project configuration
├── requirements.txt # Project dependencies
├── run_tests.py # Test runner script
├── run_ci_checks.sh # CI check script
└── setup.py # Package installation
| Переменная | Описание | По умолчанию |
|---|---|---|
ANTHROPIC_API_KEY |
Ключ API Anthropic для Claude | Нет |
ANTHROPIC_API_MODEL |
Модель Claude для использования | claude-3-5-sonnet-latest |
ANTHROPIC_TEMPERATURE |
Настройка температуры Claude | 0.0 |
OPENAI_API_KEY |
Ключ API OpenAI | Нет |
OPENAI_API_MODEL |
Модель OpenAI для использования | gpt-4o |
OPENAI_TEMPERATURE |
Настройка температуры OpenAI | 0.0 |
ENVIRONMENT |
Режим окружения | local |
При создании агента вы можете настроить его поведение:
from cursor_agent_tools import create_agent
from cursor_agent_tools.permissions import PermissionOptions
# Create permission options
permissions = PermissionOptions(
yolo_mode=True,
command_allowlist=["ls", "echo", "git"],
command_denylist=["rm -rf", "sudo"],
delete_file_protection=True
)
agent = create_agent(
model='claude-3-5-sonnet-latest', # Specific model to use (determines the agent type)
temperature=0.2, # Creativity level
system_prompt=None, # Custom system prompt
tools=None, # Custom tools dictionary
permission_options=permissions # Permission configuration
)
CursorAgent включает надёжную систему разрешений для безопасной обработки системных операций:
from cursor_agent_tools import create_agent
from cursor_agent_tools.permissions import PermissionOptions
# Create an agent with default permissions (requires confirmation for all operations)
permissions = PermissionOptions(yolo_mode=False)
agent = create_agent(
model='claude-3-5-sonnet-latest',
permission_options=permissions
)
# Create an agent with YOLO mode (many operations auto-approved)
permissions = PermissionOptions(
yolo_mode=True,
command_allowlist=["ls", "echo", "git"],
delete_file_protection=True
)
agent = create_agent(
model='claude-3-5-sonnet-latest',
permission_options=permissions
)
Систему разрешений можно адаптировать к различным средам пользовательского интерфейса:
from cursor_agent_tools.permissions import PermissionOptions, PermissionRequest, PermissionStatus
# Create a custom permission handler for a GUI application
def gui_permission_handler(request: PermissionRequest) -> PermissionStatus:
# Implement GUI-based permission dialog
# ...
return PermissionStatus.GRANTED # or DENIED
# Create permission options with custom handler
permissions = PermissionOptions(
yolo_mode=False,
permission_callback=gui_permission_handler
)
agent = create_agent(
model='claude-3-5-sonnet-latest',
permission_options=permissions
)
Подробную документацию о системе разрешений смотрите в permissions_guide.md.
Мы рады любому вкладу! Подробности о том, как внести вклад в этот проект, смотрите в CONTRIBUTING.md.
pip install -e ".[dev]".envМы используем flake8 для линтинга. Чтобы обеспечить единообразный стиль, убедитесь, что ваш код:
flake8 cursor_agent_toolspython fix_whitespace_errors.py
Этот скрипт автоматически исправит пробелы в конце строк (W291) и пустые строки с пробельными символами (W293) в директории cursor_agent_tools.# Import the factory function
from cursor_agent_tools import create_agent
def create_agent(
model: str,
temperature: Optional[float] = None,
system_prompt: Optional[str] = None,
tools: Optional[Dict] = None,
) -> BaseAgent:
"""
Create an agent instance based on the specified model.
Args:
model: The model to use (e.g. 'claude-3-5-sonnet-latest', 'gpt-4o')
temperature: The temperature setting for generation
system_prompt: Custom system prompt to use
tools: Custom tools dictionary
Returns:
An instance of BaseAgent (either ClaudeAgent or OpenAIAgent)
"""
# Import necessary types
from typing import Dict, List, Callable, Optional
from cursor_agent_tools import BaseAgent
class BaseAgent:
async def chat(
self, user_message: str, user_info: Optional[Dict] = None
) -> str:
"""Send a message to the agent and get a response."""
def register_tool(
self, name: str, function: Callable, description: str, parameters: Dict
) -> None:
"""Register a custom tool with the agent."""
async def _prepare_tools(self) -> Dict:
"""Prepare tools for the model's API format."""
async def _execute_tool_calls(self, tool_calls: List[Dict]) -> List[Dict]:
"""Execute tool calls and return results."""
Полную документацию по API смотрите в docstring'ах исходного кода.
Инструменты — это функции Python, регистрируемые у агента. Библиотека cursor-agent поддерживает различные типы инструментов для расширения возможностей вашего агента:
from cursor_agent_tools import create_agent
agent = create_agent(model='claude-3-5-sonnet-latest')
def database_query(query: str, connection_string: str):
"""Execute a database query and return results."""
# Implementation...
return {"results": [...]}
agent.register_tool(
name="database_query",
function=database_query,
description="Execute a SQL query against a database",
parameters={
"properties": {
"query": {"description": "SQL query to execute", "type": "string"},
"connection_string": {"description": "Database connection string", "type": "string"}
},
"required": ["query", "connection_string"]
}
)
from cursor_agent_tools import create_agent
import requests
agent = create_agent(model='claude-3-5-sonnet-latest')
def fetch_weather(location: str, units: str = "metric"):
"""Fetch current weather data for a location."""
API_KEY = "your_api_key" # Better to use environment variables
url = f"https://api.weatherapi.com/v1/current.json?key={API_KEY}&q={location}&units={units}"
response = requests.get(url)
if response.status_code == 200:
return response.json()
else:
return {"error": f"API returned status code {response.status_code}"}
agent.register_tool(
name="fetch_weather",
function=fetch_weather,
description="Get current weather data for a specified location",
parameters={
"properties": {
"location": {"description": "City name or coordinates", "type": "string"},
"units": {"description": "Units system (metric or imperial)", "type": "string"}
},
"required": ["location"]
}
)
from cursor_agent_tools import create_agent
import pandas as pd
import json
agent = create_agent(model='claude-3-5-sonnet-latest')
def analyze_csv(file_path: str, operations: list):
"""Perform analytical operations on a CSV file."""
try:
# Load the data
df = pd.read_csv(file_path)
results = {}
for operation in operations:
if operation == "summary":
results["summary"] = json.loads(df.describe().to_json())
elif operation == "columns":
results["columns"] = df.columns.tolist()
elif operation == "missing":
results["missing"] = json.loads(df.isnull().sum().to_json())
return results
except Exception as e:
return {"error": str(e)}
agent.register_tool(
name="analyze_csv",
function=analyze_csv,
description="Analyze a CSV file with various statistical operations",
parameters={
"properties": {
"file_path": {"description": "Path to the CSV file", "type": "string"},
"operations": {
"description": "List of operations to perform",
"type": "array",
"items": {"type": "string"}
}
},
"required": ["file_path", "operations"]
}
)
Агент поддерживает точное редактирование файлов с использованием номеров строк:
import json
from cursor_agent_tools import create_agent
async def main():
agent = create_agent(model='claude-3-5-sonnet-latest')
# Define line-based edits as a dictionary with line ranges as keys
line_edits = {
"5-8": "def calculate_total(items):\n \"\"\"Calculate the total price of all items.\"\"\"\n return sum(item.price for item in items)\n",
"12-12": " # Log the transaction\n logging.info(f\"Processed order: {order_id}\")\n"
}
# Convert to JSON string for the edit_file function
code_edit_json = json.dumps(line_edits)
# Apply edits to specific line ranges
await agent.edit_file(
target_file="/path/to/your/file.py",
instructions="Update calculate_total function and add logging",
code_edit=code_edit_json
)
if __name__ == "__main__":
asyncio.run(main())
Этот подход имеет ряд преимуществ: - Точное указание конкретных диапазонов строк для редактирования - Внесение нескольких правок за одну операцию - Понятный и структурированный формат для программного редактирования - Более простая автоматизация и скриптование изменений файлов
Больше примеров можно найти в line_based_edit_example.py.
from cursor_agent_tools import create_agent
agent = create_agent(model='claude-3-5-sonnet-latest')
user_info = {
"open_files": ["src/main.py", "src/utils.py"],
"cursor_position": {"file": "src/main.py", "line": 42},
"recent_files": ["src/config.py", "tests/test_main.py"],
"os": "darwin",
"workspace_path": "/Users/username/projects/myproject"
}
response = await agent.chat("Fix the bug in the main function", user_info=user_info)
Подробный список ограничений и обходных путей смотрите в файле constraints.md.
Этот проект лицензирован под лицензией MIT — подробности смотрите в файле LICENSE.
Femi Amoo (Nifemi Alpine)
Основатель CIVAI TECHNOLOGIES