Інтеграція Copilot-подібного ассистента для IDE
AI-ассистент для IDE — це не просто автодоповнення на стероїдах. Це система, яка тримає в контексті весь проект: відкриті файли, історію змін, схему БД, тести. Правильно побудований ассистент розуміє, що ви пишете функцію реєстрації користувача в Django-проекті з PostgreSQL, і пропонує код, сумісний з вашими моделями та конвенціями — а не абстрактний приклад зі Stack Overflow.
Архітектура IDE-ассистента
Повнофункціональний Copilot-подібний ассистент складається з кількох шарів:
Context Collector — збирає релевантний контекст: поточний файл, імпорти, пов'язані файли, позицію курсору, виділений код, clipboard.
LSP Bridge — взаємодіє з Language Server Protocol для отримання AST, типів, визначень. Tree-sitter розбирає код в AST без запуску компілятора.
Retrieval Engine — семантичний пошук по кодовій базі. Embeddings для коду (CodeBERT, text-embedding-3-small) + векторне сховище.
LLM Gateway — маршрутизація запитів: швидка модель для inline completion, потужна для chat/refactoring.
Response Renderer — форматування виводу: diff для рефакторингу, ghost text для completion, markdown для чату.
Continue.dev — open-source основа
Continue.dev — найбільш зріла open-source альтернатива GitHub Copilot. Підтримує VS Code та JetBrains, конфігурується через ~/.continue/config.json.
{
"models": [
{
"title": "Claude 3.5 Sonnet",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"apiKey": "$ANTHROPIC_API_KEY"
},
{
"title": "Ollama Qwen2.5-Coder",
"provider": "ollama",
"model": "qwen2.5-coder:7b",
"apiBase": "http://localhost:11434"
}
],
"tabAutocompleteModel": {
"title": "Autocomplete",
"provider": "ollama",
"model": "qwen2.5-coder:1.5b"
},
"contextProviders": [
{"name": "code", "params": {}},
{"name": "docs", "params": {}},
{"name": "diff", "params": {}},
{"name": "terminal", "params": {}},
{"name": "problems", "params": {}},
{"name": "folder", "params": {}},
{"name": "codebase", "params": {}}
],
"slashCommands": [
{"name": "edit", "description": "Редагувати виділений код"},
{"name": "comment", "description": "Написати коментарі для коду"},
{"name": "tests", "description": "Написати юніт-тести"},
{"name": "share", "description": "Експортувати сесію чату"}
]
}
Ключова особливість: tabAutocompleteModel використовує швидку локальну модель (1.5B параметрів), а чат — потужну хмарну. Latency inline completion: 80–150 мс на Qwen2.5-Coder 1.5B через Ollama.
Кастомний контекст-провайдер
Continue.dev дозволяє писати кастомні context providers для специфічних джерел даних:
// ~/.continue/config.ts
import { ContinueConfig, IContextProvider, ContextProviderDescription } from "@continuedev/core";
class DatabaseSchemaProvider implements IContextProvider {
get description(): ContextProviderDescription {
return {
title: "db",
displayTitle: "Database Schema",
description: "Current database schema",
type: "normal",
};
}
async getContextItems(query: string, extras: any) {
const schema = await fetchDatabaseSchema(); // ваш API
return [{
name: "Database Schema",
description: "Current DB schema",
content: schema,
}];
}
}
class JiraContextProvider implements IContextProvider {
get description(): ContextProviderDescription {
return {
title: "jira",
displayTitle: "Jira Ticket",
description: "Fetch Jira ticket by ID",
type: "query",
};
}
async getContextItems(query: string, extras: any) {
// query = "PROJ-123"
const ticket = await fetchJiraTicket(query);
return [{
name: `Jira ${query}`,
description: ticket.summary,
content: `**${ticket.summary}**\n\n${ticket.description}\n\nAcceptance Criteria:\n${ticket.acceptance_criteria}`,
}];
}
}
export function modifyConfig(config: ContinueConfig): ContinueConfig {
config.contextProviders = [
...(config.contextProviders || []),
new DatabaseSchemaProvider(),
new JiraContextProvider(),
];
return config;
}
Inline Completion через Language Server Protocol
Для вбудовування в будь-який LSP-сумісний редактор (Neovim, Emacs, Helix):
from pygls.server import LanguageServer
from lsprotocol.types import (
TEXT_DOCUMENT_COMPLETION,
CompletionParams,
CompletionList,
CompletionItem,
CompletionItemKind,
)
from anthropic import Anthropic
import asyncio
server = LanguageServer("ai-completion-server", "v0.1")
anthropic_client = Anthropic()
@server.feature(TEXT_DOCUMENT_COMPLETION)
async def completions(params: CompletionParams):
document = server.workspace.get_document(params.text_document.uri)
# Отримуємо контекст: 50 рядків перед курсором
lines = document.lines
cursor_line = params.position.line
prefix = "\n".join(lines[max(0, cursor_line - 50):cursor_line + 1])
suffix = "\n".join(lines[cursor_line + 1:cursor_line + 10])
# Fill-in-the-middle prompt
prompt = f"<fim_prefix>{prefix}<fim_suffix>{suffix}<fim_middle>"
# Використовуємо швидку модель для completion
response = anthropic_client.messages.create(
model="claude-haiku-4-5",
max_tokens=150,
messages=[{
"role": "user",
"content": f"Complete this code (return only the completion, no explanation):\n{prompt}"
}]
)
completion_text = response.content[0].text.strip()
return CompletionList(
is_incomplete=False,
items=[CompletionItem(
label=completion_text[:50] + "..." if len(completion_text) > 50 else completion_text,
kind=CompletionItemKind.Snippet,
insert_text=completion_text,
detail="AI Suggestion",
)]
)
if __name__ == "__main__":
server.start_io()
Індексування кодової бази з Tree-sitter
Семантичний пошук по кодовій базі — основа контекстно-залежних підказок:
from tree_sitter import Language, Parser
from tree_sitter_languages import get_language, get_parser
from openai import OpenAI
import chromadb
from pathlib import Path
client = OpenAI()
chroma_client = chromadb.PersistentClient(path="./.codebase_index")
collection = chroma_client.get_or_create_collection("code_chunks")
def extract_functions(file_path: str, language: str) -> list[dict]:
"""Витягує функції/методи через Tree-sitter AST"""
parser = get_parser(language)
with open(file_path) as f:
source = f.read()
tree = parser.parse(source.encode())
# Tree-sitter query для Python функцій
lang = get_language(language)
query = lang.query("""
(function_definition
name: (identifier) @func_name
body: (block) @func_body) @func_def
(class_definition
name: (identifier) @class_name
body: (block) @class_body) @class_def
""")
captures = query.captures(tree.root_node)
functions = []
for node, capture_name in captures:
if capture_name == "func_def":
func_text = source[node.start_byte:node.end_byte]
functions.append({
"file": file_path,
"type": "function",
"code": func_text,
"start_line": node.start_point[0],
})
return functions
def index_codebase(project_root: str):
"""Індексує всю кодову базу"""
project = Path(project_root)
all_chunks = []
for py_file in project.rglob("*.py"):
if "migrations" in str(py_file) or "__pycache__" in str(py_file):
continue
chunks = extract_functions(str(py_file), "python")
all_chunks.extend(chunks)
# Batch embedding
batch_size = 100
for i in range(0, len(all_chunks), batch_size):
batch = all_chunks[i:i + batch_size]
response = client.embeddings.create(
model="text-embedding-3-small",
input=[chunk["code"][:2000] for chunk in batch]
)
collection.add(
ids=[f"{c['file']}:{c['start_line']}" for c in batch],
embeddings=[e.embedding for e in response.data],
documents=[c["code"] for c in batch],
metadatas=[{"file": c["file"], "line": c["start_line"]} for c in batch],
)
print(f"Indexed {len(all_chunks)} code chunks")
def find_relevant_code(query: str, k: int = 5) -> list[str]:
"""Знаходить подібний код для контексту"""
response = client.embeddings.create(
model="text-embedding-3-small",
input=query
)
results = collection.query(
query_embeddings=[response.data[0].embedding],
n_results=k,
)
return results["documents"][0]
Chat-режим з проектним контекстом
class IDEChatAssistant:
"""Повнофункціональний chat-ассистент з проектним контекстом"""
def __init__(self, project_root: str):
self.project_root = project_root
self.client = Anthropic()
index_codebase(project_root)
async def chat(
self,
user_message: str,
current_file: str,
selected_code: str = None,
conversation_history: list = None,
) -> str:
# Збираємо контекст
context_parts = []
# Поточний файл
if current_file:
with open(current_file) as f:
file_content = f.read()
context_parts.append(f"## Current file: {current_file}\n```python\n{file_content[:3000]}\n```")
# Виділений код
if selected_code:
context_parts.append(f"## Selected code\n```python\n{selected_code}\n```")
# Подібний код з кодової бази
relevant = find_relevant_code(user_message, k=3)
if relevant:
context_parts.append("## Similar code from project\n" + "\n\n".join(relevant))
system_prompt = f"""You are an experienced engineer working on the project.
{chr(10).join(context_parts)}
Rules:
- Write code in the style of the existing codebase
- Use the same dependencies already in the project
- Explain architectural decisions briefly
- If modifying existing code — show diff"""
messages = conversation_history or []
messages.append({"role": "user", "content": user_message})
response = self.client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
system=system_prompt,
messages=messages,
)
return response.content[0].text
Практичний кейс: впровадження в команді з 12 розробників
Стартовий стан: команда використовувала GitHub Copilot ($19/мес на розробника), скаржилась на нерелевантні підказки — Copilot не знав внутрішніх паттернів Django-проекту з 800+ моделями.
Рішення: Continue.dev + локальний Ollama для autocomplete + Claude через API для chat/refactoring + кастомний context provider з індексом кодової бази.
Інфраструктура: сервер з RTX 4090 (Qwen2.5-Coder 7B для autocomplete), API Claude для складних запитів.
Результати через 2 місяці:
- Прийняття inline suggestions: 23% (Copilot) → 41% (кастомний)
- Середній час написання типового CRUD endpoint: 52 хв → 31 хв
- Задачі типу "написати тест для цієї функції": 100% ручні → 70% автоматичні
- Вартість: $228/мес (Copilot) → ~$85/мес (Ollama server amortized + Claude API)
Ключовий фактор покращення acceptance rate: context provider з індексом кодової бази давав моделі реальні приклади з проекту, а не абстрактний код.
Локальні моделі для completion
Для команд з вимогами до конфіденціальності коду — повністю локальний стек:
| Модель | Розмір | Latency (RTX 3080) | Якість |
|---|---|---|---|
| Qwen2.5-Coder 1.5B | 1.5B | 50–80 мс | Базова |
| Qwen2.5-Coder 7B | 7B | 150–250 мс | Хороша |
| DeepSeek-Coder 6.7B | 6.7B | 140–230 мс | Хороша |
| CodeLlama 13B | 13B | 350–500 мс | Висока |
Для inline completion критична latency < 200 мс — користувач помічає затримку. Тому для FIM (fill-in-the-middle) використовують моделі до 7B.
Терміни
- Continue.dev + конфігурація моделей + базові context providers: 2–3 дні
- Кастомні context providers (БД, Jira, документація): 1 тиждень
- Індексування кодової бази + семантичний пошук: 1–2 тижні
- LSP-сервер для нестандартного редактора: 2–3 тижні
- Всього з онбордингом команди: 3–5 тижнів







