Как стать автором
Обновить

Комментарии 65

Очень интересно, а действительно в Google AI studio можно выгладывать простыни соурсов любой длины?

Подписался

У меня получалось до 100к токенов, народ пишет до 500к примерно удается работать. Но там проблема начинается, что он думает по 10 минут, а потом падает с ошибкой. Но до 500к достаточно надежно работает у многих.

Контекст до млн. Все работает. "Но есть нюанс" - чем больше вы работаете в рамках одного диалога, тем больше возникает путаница. Причем и как до 500к - если было множество маленьких взаимодействий и особенно, если меняли темы, так и после, даже если самих итераций было мало.

Что я заметил и что помогает мне:

  1. Четко следовать теме - 1 "проблема" (то есть даже например разработка 1 функции в одном файле) - 1 диалог.

  2. После того, как тема решена - просить составить описание проекта, файлов, их взаимосвязи, текущее состояние, глобальную цель и дальнейшие шаги.
    Это позволяет относительно "безболезненно" начинать новый диалог.

  3. Имейте ввиду, что модель может выдумывать версии библиотек, методов и т.д. - по возможности прикладывайте документацию, а еще лучше спрашивайте периодически "Какие файлы тебе нужны" - часто можно обойтись 1-2 файлами с гитхаба и модель здорово решает вопрос, на котором ходила кругами.

  4. Если "ходите кругами" - иногда новый диалог помогает.
    Еще заметил что запрос в ЧатГПТ 03 - его краткий ответ часто решает проблему замкнутого круга. Просто потом пересылаешь это обратно в ГуглСтудио и продлолжаешь. (Осознанно, а не просто копировать)

  5. Модель может больше верить комментариям, чем коду!

  6. По возможности разбить код на блоки и заменять и просить выводить только блоки (так быстрее и меньше путаницы)

  7. Очень хорошо помогает посте постановки задачи и приложения файлов попросить сеть сперва написать как и что она поняла и что будет делать. Помогает отсеять ошибки мышления уже в начале и "не утонуть".

  8. Ну и последнее - это логи - нужно больше логов хороших и разных.! Это в разы облегчает работу.

Вот такие мои наблюдения. П.с. я не проф. программист - скорее хобби и пет проекты, но может кому поможет.

Согласен, я даже более скажу. У меня строго один пост - новый диалог. Никогда не продолжаю. На больших контекстах это прямо критично.

После того, как тема решена - просить составить описание проекта, файлов, их взаимосвязи, текущее состояние, глобальную цель и дальнейшие шаги.
Это позволяет относительно "безболезненно" начинать новый диалог.

Я просто собираю в один промпт весь нужный код, описание проекта, комментарии должны быть хорошо прописаны, точные требования, архитектура проекта очень подробно простыми словами описана. Если так делать, то качество генерации практически стопроцентное даже на очень объёмных кусках кода. Промпт, правда, в сто килобайт выходит в среднем, но практически не делаю лишних запросов. Очень сильно качество генерации зависит от того, как LLM умеет разгребать контекст. Gemini вроде контекст большой, но он плохо понимает что ему кормят, приходится до мельчайших мелочей логику прописывать, Claude тут намного лучше. Но, в целом, можно даже от самых элементарных моделей добиться качественной генерации, вопрос желания и усилий. Сейчас уже можно свой проект прямо в LLM встроить, тогда контекст безлимитный, если LLM локальная.

А как встроить свой проект прямо в LLM?

Напишите плз, когда начнёте разворачивать по 20+ проектов для решения в k8 с нуля, если между ними будет неочевидная интеграция вообще замечательно, ну и поддержка кодовой базы в работающем проде. Очень интересен ваш прогресс!

Не в ближайшее время. Из того что заметил - администрирование инфры пока наименее затронуто AI, модели в этом довольно сильно тупят. Даже хороший докер композ написать - занятие не на парочку промптов. Но мб и до этого дойдет.

Буду ждать, плюсую статью!

Следующий этап должен быть без ручной работы - пишешь задание. на его основе LLM составляет список файлов которые изменение должно затронуть и составляет следующий промпт, который сейчас делается этим Вашим инструментом

Ну и переносить диффы тоже, кстати, не курсор должен делать, это легко делается кодом или даже баш скриптом

Все верно, первое что хочу сколхозить - сделать применялку диффов прямо из приложения. А вообще, тут просится собрать свой курсор на минималках, да.

Да, и проще конечно не писать IDE, а сделать плагин к VS Code или WebStrorm/PyCharm/IDEA

Я пользовался Cline. Он хорош если у тебя есть безлимитный ключик к апи, там сценарии не предусматривают копипасты в AI studio и использование этого бесплатно :)
Но в целом да - это оупенсорсный курсор, который нужно уметь готовить.

Мне кажется, что в каком-то смысле фишка моего приложения именно в том, что оно работает не как плагин к IDE. Т.к. есть куча народа, которым не подходят предлагаемые IDE, либо связки IDE/плагин для кодинга на рынке.

А почему не стандартный формат, который может применять git? Тот, что получается командой git format-patch?

Интересно, а можно без потери эффективности реструктурировать промпт так, чтобы более менее неизменяемые части шли вначале (гайдлайны, структура, листинг), а наиболее подверженная изменениям часть (собственного, задача) - в конце? Если не ошибаюсь, это на кэшировании положительно отразится.

Все так, для кэширования стабильные части промпта должны быть в начале. И вот тут фундаментальная проблема, что это плохо для качества выполнения - так как чем дальше в длину, тем сильнее теряется точность инструкций. А наболее важны они именно в изменяемой части самого задания. Какой-то трейдофф тут, думаю, можно поймать - нужны эксперименты.

Привет, это команда GitVerse! У тебя крутая статья, будем рады видеть тебя в сезоне open source. Для этого просто поставь тег "сезон open source" – и ты участвуешь :)

aider делает лучше. Он собирает структуру проекта tree-sitter'ом и скармливает ее как контекст в любой запрос. LLM видит все файлы, классы и методы в сжатом виде.

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

А это тоже было с Gemini как моделью? А то может эффект больше от смены модели с несильно большим контекстом на Gemini, чем от тонкостей промпта

По моим ощущениям сильно от модели не зависит, тримят все. В какой степени - оценить тяжело, т.к. они не показывают реального контекста

Если решите поразбираться, то, как предлагали в другой статье про Курсор, можете подсунуть собственный ключ от openAI/Google и т.д. и посмотреть реальный промт на другой стороне. На всякий случай, вдруг не в курсе..

Посмотреть можно, но эксперимент будет грязноват, т.к. чужие токены они могут и не экономить :)

Курсором не пользовался, но для каждого запроса в клод добавляю структуру проекта, сделанную через tree-sitter, это сэкономило много времени, а то их предположения о структуре кода, обычно, далеки от истины.
Скрипт простой, сам клод мне его и сгенерировал:

Скрытый текст
#!/usr/bin/env python3

"""
Project Hierarchy Tool

This script uses py-tree-sitter to parse Python, TypeScript, and JavaScript files
and generate a hierarchical representation of classes, functions, and methods.
"""

import os
import sys
import subprocess
import re
from pathlib import Path
from typing import Dict, List, Optional, Any, Tuple

# Global variables
LANGUAGES = {
    ".py": "python",
    ".js": "javascript", 
    ".ts": "typescript",
    ".tsx": "typescript"
}

# Language packages to install via pip
LANGUAGE_PACKAGES = {
    "python": "tree-sitter-python",
    "javascript": "tree-sitter-javascript",
    "typescript": "tree-sitter-typescript"
}

class HierarchyItem:
    """Class to represent an item in the hierarchy"""
    def __init__(self, name: str, item_type: str, line: int):
        self.name = name
        self.type = item_type
        self.line = line
        self.items = []
    
    def add_item(self, item: 'HierarchyItem'):
        self.items.append(item)

class FileHierarchy:
    """Class to represent a file's hierarchy"""
    def __init__(self, file_path: str):
        self.file_path = file_path
        self.file_name = os.path.basename(file_path)
        self.items = []
    
    def add_item(self, item: HierarchyItem):
        self.items.append(item)

def install_requirements():
    """Install required packages if they don't exist"""
    # First, check if tree-sitter is installed
    try:
        import tree_sitter
    except ImportError:
        print("Installing tree-sitter...")
        subprocess.run([sys.executable, "-m", "pip", "install", "tree-sitter"], check=True)
    
    # Now install language grammars
    for lang, package in LANGUAGE_PACKAGES.items():
        try:
            # Try to import the language module to check if it's installed
            __import__(f"tree_sitter_{lang}")
            print(f"{package} already installed")
        except ImportError:
            print(f"Installing {package}...")
            try:
                subprocess.run([sys.executable, "-m", "pip", "install", package], check=True)
            except subprocess.CalledProcessError:
                print(f"Failed to install {package}. You may need to install it manually:")
                print(f"pip install {package}")

def setup_parsers() -> Dict[str, Any]:
    """Set up tree-sitter parsers for each language"""
    from tree_sitter import Parser, Language
    
    parsers = {}
    
    # For each language, load it and create a parser
    for ext, lang_name in LANGUAGES.items():
        try:
            # Modern way to get language: import directly from the installed package
            lang_module = __import__(f"tree_sitter_{lang_name}")
            language = lang_module.language()
            
            parser = Parser()
            parser.set_language(language)
            parsers[lang_name] = parser
            
            # Special case for TypeScript: TSX is handled differently
            if lang_name == "typescript":
                try:
                    tsx_language = lang_module.language("tsx")
                    tsx_parser = Parser()
                    tsx_parser.set_language(tsx_language)
                    parsers["tsx"] = tsx_parser
                except Exception as e:
                    print(f"Warning: Could not load TSX parser: {e}")
            
        except Exception as e:
            print(f"Warning: Could not set up parser for {lang_name}: {e}")
    
    return parsers

def get_node_text(node, source_code: bytes) -> str:
    """Get the text of a node from the source code"""
    try:
        return source_code[node.start_byte:node.end_byte].decode('utf-8')
    except (AttributeError, IndexError, UnicodeDecodeError) as e:
        # Handle edge cases
        return "Unknown"

def find_identifier(node) -> Optional[Any]:
    """Find the identifier node in various node types"""
    # Check direct children first
    for child in node.children:
        if child.type in ('identifier', 'property_identifier'):
            return child
    
    # For Python
    if node.type == 'function_definition' or node.type == 'class_definition':
        for child in node.children:
            if child.type == 'identifier':
                return child
    
    # Try field-based access
    try:
        name_child = node.child_by_field_name("name")
        if name_child:
            return name_child
    except AttributeError:
        pass
    
    return None

def parse_file(file_path: str, parsers: Dict[str, Any]) -> Optional[FileHierarchy]:
    """Parse a file and extract its hierarchy"""
    ext = os.path.splitext(file_path)[1].lower()
    if ext not in LANGUAGES:
        return None
    
    lang_name = LANGUAGES[ext]
    if ext == ".tsx" and "tsx" in parsers:
        parser = parsers.get("tsx")
    else:
        parser = parsers.get(lang_name)
    
    if not parser:
        return None
    
    # Read file contents
    try:
        with open(file_path, 'rb') as f:
            source_code = f.read()
    except Exception as e:
        print(f"Error reading file {file_path}: {e}")
        return None
    
    try:
        tree = parser.parse(source_code)
        root_node = tree.root_node
    except Exception as e:
        print(f"Error parsing file {file_path}: {e}")
        return None
    
    file_hierarchy = FileHierarchy(file_path)
    
    def traverse(node, parent=None) -> None:
        if lang_name == "python":
            parse_python_node(node, parent, source_code)
        elif lang_name in ("javascript", "typescript", "tsx"):
            parse_js_ts_node(node, parent, source_code)
        
        # Recursively traverse children
        for child in node.children:
            if child.type not in ('class_body', 'block', 'statement_block'):
                traverse(child, parent)
    
    def parse_python_node(node, parent, source_code: bytes) -> None:
        if node.type == 'class_definition':
            # Find name
            name_node = find_identifier(node)
            if not name_node:
                return
            
            class_name = get_node_text(name_node, source_code)
            class_item = HierarchyItem(class_name, 'class', node.start_point[0] + 1)
            
            if parent:
                parent.add_item(class_item)
            else:
                file_hierarchy.add_item(class_item)
            
            # Process class body to find methods
            for child in node.children:
                if child.type == 'block':
                    for block_child in child.children:
                        traverse(block_child, class_item)
        
        elif node.type == 'function_definition':
            # Find name
            name_node = find_identifier(node)
            if not name_node:
                return
            
            func_name = get_node_text(name_node, source_code)
            func_item = HierarchyItem(func_name, 'function', node.start_point[0] + 1)
            
            if parent:
                parent.add_item(func_item)
            else:
                file_hierarchy.add_item(func_item)
    
    def parse_js_ts_node(node, parent, source_code: bytes) -> None:
        if node.type in ('class_declaration', 'class'):
            # Find the class name
            identifier = find_identifier(node)
            if not identifier:
                return
            
            class_name = get_node_text(identifier, source_code)
            class_item = HierarchyItem(class_name, 'class', node.start_point[0] + 1)
            
            if parent:
                parent.add_item(class_item)
            else:
                file_hierarchy.add_item(class_item)
            
            # Process class body
            for child in node.children:
                if child.type == 'class_body':
                    for body_child in child.children:
                        traverse(body_child, class_item)
        
        elif node.type in ('method_definition', 'method_declaration'):
            # Find the method name
            identifier = find_identifier(node)
            if not identifier:
                return
            
            method_name = get_node_text(identifier, source_code)
            method_item = HierarchyItem(method_name, 'method', node.start_point[0] + 1)
            
            if parent:
                parent.add_item(method_item)
            else:
                file_hierarchy.add_item(method_item)
        
        elif node.type in ('function_declaration', 'function'):
            # Find the function name
            identifier = find_identifier(node)
            if not identifier:
                return
            
            func_name = get_node_text(identifier, source_code)
            func_item = HierarchyItem(func_name, 'function', node.start_point[0] + 1)
            
            if parent:
                parent.add_item(func_item)
            else:
                file_hierarchy.add_item(func_item)
        
        elif node.type == 'lexical_declaration' or node.type == 'variable_declaration':
            # Check for arrow functions in variable declarations
            for child in node.children:
                if child.type == 'variable_declarator':
                    value_node = None
                    name_node = None
                    
                    for var_child in child.children:
                        if var_child.type == 'identifier':
                            name_node = var_child
                        elif var_child.type in ('arrow_function', 'function'):
                            value_node = var_child
                    
                    if name_node and value_node:
                        func_name = get_node_text(name_node, source_code)
                        func_item = HierarchyItem(func_name, 'function', node.start_point[0] + 1)
                        
                        if parent:
                            parent.add_item(func_item)
                        else:
                            file_hierarchy.add_item(func_item)
    
    # Start traversal from the root
    traverse(root_node)
    return file_hierarchy

def fallback_parse_file(file_path: str) -> Optional[FileHierarchy]:
    """Use regex to parse files when tree-sitter fails"""
    ext = os.path.splitext(file_path)[1].lower()
    if ext not in LANGUAGES:
        return None
    
    lang_name = LANGUAGES[ext]
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
    except Exception as e:
        print(f"Error reading file {file_path}: {e}")
        return None
    
    file_hierarchy = FileHierarchy(file_path)
    
    # Define regex patterns based on language
    if lang_name == 'python':
        # Match Python classes and functions
        class_pattern = r'^\s*class\s+(\w+)'
        function_pattern = r'^\s*def\s+(\w+)'
        
        lines = content.split('\n')
        current_class = None
        
        for i, line in enumerate(lines):
            # Check for class definition
            class_match = re.match(class_pattern, line)
            if class_match:
                class_name = class_match.group(1)
                class_item = HierarchyItem(class_name, 'class', i + 1)
                file_hierarchy.add_item(class_item)
                current_class = class_item
                continue
            
            # Check for function/method definition
            func_match = re.match(function_pattern, line)
            if func_match:
                func_name = func_match.group(1)
                func_item = HierarchyItem(func_name, 'function' if not current_class else 'method', i + 1)
                
                # Determine if this is a method in a class
                if current_class and re.match(r'^\s+def', line):  # Indented function = method
                    current_class.add_item(func_item)
                else:
                    file_hierarchy.add_item(func_item)
                    current_class = None  # Reset current class when we find a top-level function
    
    elif lang_name in ('javascript', 'typescript'):
        # Match JS/TS classes, methods and functions
        class_pattern = r'^\s*class\s+(\w+)'
        method_pattern = r'^\s+(\w+)\s*\('
        function_pattern = r'^\s*function\s+(\w+)'
        arrow_function_pattern = r'^\s*const\s+(\w+)\s*=\s*(\(.*\)|[^=]*)\s*=>'
        
        lines = content.split('\n')
        current_class = None
        in_class_body = False
        
        for i, line in enumerate(lines):
            # Check for class definition
            class_match = re.match(class_pattern, line)
            if class_match:
                class_name = class_match.group(1)
                class_item = HierarchyItem(class_name, 'class', i + 1)
                file_hierarchy.add_item(class_item)
                current_class = class_item
                in_class_body = True
                continue
            
            # Check for method if we're in a class
            if in_class_body and current_class:
                method_match = re.match(method_pattern, line)
                if method_match:
                    method_name = method_match.group(1)
                    if method_name not in ('constructor', 'if', 'for', 'while'):  # Skip keywords
                        method_item = HierarchyItem(method_name, 'method', i + 1)
                        current_class.add_item(method_item)
                    continue
            
            # Check for standalone function
            func_match = re.match(function_pattern, line)
            if func_match:
                func_name = func_match.group(1)
                func_item = HierarchyItem(func_name, 'function', i + 1)
                file_hierarchy.add_item(func_item)
                current_class = None
                in_class_body = False
                continue
            
            # Check for arrow function
            arrow_match = re.match(arrow_function_pattern, line)
            if arrow_match:
                func_name = arrow_match.group(1)
                func_item = HierarchyItem(func_name, 'function', i + 1)
                file_hierarchy.add_item(func_item)
                continue
            
            # Check if we're leaving a class body
            if in_class_body and re.match(r'^\s*}', line):
                in_class_body = False
                current_class = None
    
    return file_hierarchy

def find_files(directory: str, extensions: List[str]) -> List[str]:
    """Find files with specified extensions in the directory"""
    result = []
    
    # Directories to ignore
    ignore_dirs = {'.git', 'node_modules', '__pycache__', 'venv', '.venv', 'env'}
    
    for root, dirs, files in os.walk(directory):
        # Remove ignored directories from consideration
        dirs[:] = [d for d in dirs if d not in ignore_dirs]
        
        for file in files:
            if any(file.endswith(ext) for ext in extensions):
                result.append(os.path.join(root, file))
    
    return result

def print_hierarchy(hierarchy: FileHierarchy, indent: int = 0) -> None:
    """Print the hierarchy to the console"""
    print(f"{'  ' * indent}📄 {hierarchy.file_name}")
    
    for item in hierarchy.items:
        icon = '🔶' if item.type == 'class' else '🔹'
        print(f"{'  ' * (indent + 1)}{icon} {item.name} ({item.type}, line {item.line})")
        
        for subitem in item.items:
            subicon = '🔸' if subitem.type == 'class' else '▫️'
            print(f"{'  ' * (indent + 2)}{subicon} {subitem.name} ({subitem.type}, line {subitem.line})")

def main():
    """Main function"""
    # Get the project path
    project_path = sys.argv[1] if len(sys.argv) > 1 else '.'
    
    # Validate project path
    if not os.path.exists(project_path):
        print(f"Error: Path '{project_path}' does not exist.")
        sys.exit(1)
    
    # Flag to track if we're using tree-sitter or fallback mode
    using_tree_sitter = True
    
    # Install required packages
    try:
        install_requirements()
        
        # Setup parsers
        parsers = setup_parsers()
        if not parsers:
            print("Warning: No tree-sitter parsers could be set up.")
            using_tree_sitter = False
    except Exception as e:
        print(f"Warning: Could not set up tree-sitter: {e}")
        print("Falling back to regex-based parsing (less accurate but more compatible)")
        using_tree_sitter = False
    
    print(f"\n🔍 Analyzing project: {os.path.abspath(project_path)}")
    print("====================================\n")
    
    # Find files
    files = find_files(project_path, list(LANGUAGES.keys()))
    
    if not files:
        print("No Python, JavaScript, or TypeScript files found in the project.")
        sys.exit(0)
    
    print(f"Found {len(files)} file(s) to analyze.\n")
    print(f"Using {'tree-sitter' if using_tree_sitter else 'fallback regex-based'} parsing.\n")
    
    # Parse files
    for file_path in files:
        try:
            if using_tree_sitter:
                hierarchy = parse_file(file_path, parsers)
            else:
                hierarchy = fallback_parse_file(file_path)
                
            if hierarchy:
                print_hierarchy(hierarchy)
                print()  # Add a blank line between files
        except Exception as e:
            print(f"Error processing {file_path}: {e}")
            # Try fallback if tree-sitter fails for this file
            if using_tree_sitter:
                try:
                    print(f"Trying fallback parser for {file_path}...")
                    hierarchy = fallback_parse_file(file_path)
                    if hierarchy:
                        print_hierarchy(hierarchy)
                        print()
                except Exception as fallback_error:
                    print(f"Fallback parser also failed: {fallback_error}")
    
    print("✅ Analysis complete!")

if __name__ == "__main__":
    main()

Да, я как раз написал, что начинал именно с такого подхода

Из текста я понял что это была структура папок по типу tree утилиты из linux, а в моем случае передаются ещё классы, функции, типы.

А, понял, спасибо. Да, сигнатуры это интересно - можно сильно экономить объем

Примерно то же самое из вышеупомянутого @jamaze aider: https://github.com/Aider-AI/aider/blob/main/aider/repomap.py - в принципе код там самодостаточный, чтобы подцепить снаружи. Для разбора выхлопа tree-sitter и построения карт кода они используют кастомные .scm квери (вместо логики на питоне), а также умеют их кешировать.

Интересно, спасибо за ссылку.

Ничего себе простой скрипт, я его скроллить устал

Курсор же позволяет прикрепить к промпту ссылки на строки в файле или терминале, сам файл или целую папку. После этого область видимости AI-агента резко вырастает, хотя чистить баги и приговаривать "горшочек, не вари" всё равно приходится.

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

В этом и проблема. 10 файлов это очень много. Даже 5 файлов это очень много, если они не на 100-200 строк.

Вы перегружаете контекст и поэтому курсор плохо работает.

Моя примерная метрика 1000 строк кода + промпт. Больше уже намного хуже резульаты. Поэтому нужно делать адекватную архитектуру.

Ну и конечно если у вас в одном сообщении 1000 строк кода, то максимум что вы можете позволить в рамках одной чат-сессии - 3-5 сообщений.

Моя примерная метрика 1000 строк кода + промпт. Больше уже намного хуже результаты.

 Они хуже именно из-за того что курсор тримит контекст. В нативных тулах контексты в 100к залетают без сучка и задоринки.

Этот подход работает с любой LLM и делается даже без специальных программ - вручную. Этакая генерация "супер-промпта". Прекрасно работает и без IDE, даже в чате. Даже из очень старых и простых моделей так можно вытягивать, как из паука-шелкопряда, очень высококачественную генерацию. Я бы даже сказал, что не в десять раз вырастает качество генерации, при правильном подходе, а во все сто. Даже нет необходимости через IDE работать, потому что если промпт правильно составлен, то генерация практически идеальная, если контекстного окна LLM хватило. Конкретно по статье - видно в генерируемом промпте слишком общие выражения. Если более точно задавать команду LLM, не давая LLM вообще никакой возможности ошибиться, то можно огромную, 100%-работающую кодовую базу получить с первого раза, на любой задаче, если хватает базы знаний и контекстного окна. Можно вытягивать настолько качественный код с настольно базовых LLM, что для многих это покажется невероятным.

Я и один файл для изменения ИИшке отдаю скрепя сердце, а вы про огромный дифф. Достаточно забыть один нюанс указать в промпте, и все - идешь руками исправлять её прекрасные наработки во всех 10 файлах. Ну либо обновляешь промпт и надеешься, что в этот раз он сделает все так, как ты представил у себя в голове :)

Если промпт точно составлять, то надеется и представлять - не надо, если LLM с достаточной базой знаний и контекстное окно позволяет, то генерация 100% точная будет, правда у меня средний промпт под сто килобайт (кодовая база + комментарии + архитектура проекта + инструкции).

Отличная лицензия. Наверное. Только хрен я ту софтину поставлю — нет и не будет твиттера :-)

Это больше прикол, для русскоязычных можно не следовать =)

Наш слон! За такое можно и подписаться :)

Ага, в целом такая лицензия выходит за рамки понятия open source. Скорее source available.

Серьезно прогал мобильные приложения

Такое впечатление, что кроме веб и мобильной разработки другого программирования на Хабре не существует. Я, со своим пет-проектом( https://habr.com/ru/articles/848836/ ), на C++ / WTL, чувствую себя, здесь, как Робинзон Крузо на необитаемом острове…

Возможно, что есть, но меньше пишут и чаще на JS и тому подобном.

Я сам пишу десктоп приложение на .NET C#, хотя владею C++.

Я сам пишу десктоп приложение на .NET C#, хотя владею C++.

Интересно, не просто писать, а описывать собственные проекты на С++. А с этим, здесь, напряг…

Ещё многим NDA мешает.

Ещё многим NDA мешает.

Я имею в виду собственные пет-проекты, вроде моего. Свои рабочие программы я не публикую.

У меня тоже пет-проект на WTL, но начинал я его 18 лет назад. И уже тогда WTL была морально устаревшей. В наши дни начинать проект на WTL, имхо, странное решение. Есть гораздо более удобные инструменты. Я сам мучаюсь, поддерживая GUI на WTL. Жалею, что тогда не выбрал Qt.

У меня тоже пет-проект на WTL, но начинал я его 18 лет назад. И уже тогда WTL была морально устаревшей. В наши дни начинать проект на WTL, имхо, странное решение.

Кому-то «странное», а кому-то очень удобное. «На вкус и цвет товарищей нет.». Особенно для пет-проектов.

Кстати, 1С77 тоже все считают давно морально устаревшим. Но, это только если пользоваться типовыми конфигурациями. А если вы полностью разработали свою собственную конфигурацию с поддержкой DDE (для межпроцессного взаимодействия с Visual FoxPro, движок которого в 15 раз более производительный, чем встроенный, при этом 100% совместимый с dbf-файлами «семерки»), с использованием собственных внешних компонент, написанных на С++, для производственного предприятия (1000 сотрудников), со сложным, достаточно специфическим учетом заработной платы и учетом рабочего времени, то, как по мне, это идеальный вариант.

Я пробовал делать расчет зарплаты для наших отдельных сотрудников, со сложными данными, в 1С:ЗУП. Что я могу сказать? Нужный результат получил, но с очень большим трудом. А когда попытался перенести свой код в конфигурацию «восьмерки», то, понял, что это будет на порядок труднее, дороже (в смысле дополнительного оборудования) и медленнее. Вопрос. А нужна ли мне такая современная 1С8х?

Есть гораздо более удобные инструменты. Я сам мучаюсь, поддерживая GUI на WTL. Жалею, что тогда не выбрал Qt.

Так что вам мешает перенести свою программную логику из WTL на Qt или wxWidgets?

У меня было наоборот. Начинал я именно с Qt. Он достаточно мощный, но громоздкий. У него самый лучший грид из опенсорсных. Но, этот грид не поддерживает группы. А в 1С77-1С82 –поддерживают. Винапишный ListCtrl – поддерживает, но через одно пикантное место.

Потом выбрал wxWidgets. Он попроще, но тоже не слишком хорош для меня. Были еще MFC и его опенсорсный аналог Win32++. В чем-то хороши, но со своими заморочками. В итоге, остановился на WTL – «дешево и сердито» :) .

Так что вам мешает перенести свою программную логику из WTL на Qt или wxWidgets?

Как-то не хочется переписывать 58 диалоговых окон на Qt. Слишком трудозатратно.
Приходится работать с тем, что есть. Вот хотелось бы добавить в программу тёмную тему, но на WinAPI / WTL это сделать нетривиально.

У LLM происходит когнитивное истощение

Сегодня мы все немного психиатры для LLM

Ну и как вам эта новая профессия?

Для VSCode есть плагин File Content copier, выделяешь нужные файлы в дереве(с шифтом сразу все) и ПКМ - копирует структуру и содержимое файлов.

Пытался использовать «Task Master» для разбивки проекта на задачи, подзадачи, контроля за связями и зависимостями, приоритизацией и т.п. Что могу сказать — ерунда полная. Жрет токены и требует постоянного тыкания носом, чтобы делала то, для чего была создана. Если без нее в одном чате можно было долго решать проблему, то с ней, модель в 5 раз быстрее забывает проговоренное ранее.

Что касается отправки полного листинга, то никогда этого не делал потому, что в Cursor и в Windsurf есть возможность указать на конкретные файлы в проекте и он сам их прочитает.

Для остального есть правила глобальные и проектные. Нужно только подкрутить их под себя. Постоянно обновляемый набор здесь — Awesome CursorRules.

Для остального есть правила глобальные и проектные. Нужно только подкрутить их под себя.

Я читал статьи и исходники по правильной компоновке курсор рулов. И у меня они были написаны в огромном количестве. Пока я не заметил, что он просто их не применяет при тех условиях, когда они должны однозначно вызываться.
Проверка простая - напишите в правиле отвечать вам в сообщении определенным словом. И вы обнаружите, что при определенном объеме правил, курсор перестает это делать.

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

Когда в пример приводят Cline или Roo - там хотя бы оупенсорс, можно посмотреть как они формируют свои промпты. А курсор закрытая экосистема, и по моему мнению - крайне сильно зажимающая контекст, в т.ч. сказками что так и надо

Не самый практичный подход. Патч, конечно, это удобно, но куча токенов уходит на пробелы и переносы строки. Я для себя выработал подобное решение, но я использую JSON, в нем и контент, и путь к нему. Получается и экономно, и нейросети понимает. Сам инструмент читает конфиги из yaml, гибко настраивается по путям чтения, форматам файлов, можно собирать по определённым путям только path файла, без контента, возможны исключения (допустим, я хочу показать LLM мои SFC компоненты, но без стилей - настраиваю exclude на <style... /style>. В общем, вполне гибкий инчтрумент. Приходится копировать ручками, но есть задумка на то, чтобы нейросети писала ответ в том же JSON такого же формата. Если кому интересно - опубликую

Да, интересно

«отставшего от прогресса лет на 15» - аналогичная история) Один в один 🤝

можно добавить в сборку поддержку mac Intel?

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

Пользуюсь тремя нейросетями и в каждой из них есть одна проблема, подскажите может я что то не то делаю?
Начинаю с диалога "Привет ты программист ХХ-языка. Мне нужно разработать УУ приложение. С такимито требованиями". Дальше описываю что будем идти поэтапно.
ИИ соглащается и мы приступаем к диалогу как с наставником.

- Мне нужна точка входа
- Вот держи
- Поправь момент Х и сделай У
- Вот держи
- Теперь делаем функцию 1

и так далее. Примерно по одному новому файлу за одну сессию "вопрос-ответ". Контекст сохраняется, очень удобно. Но на каком то моменте, окгда приложение уже относительно большое ИИ сходит с ума, пишет длинный ответ из набора символов с иероглифами. Соершенно нечитаемо.

Как вариант, описанный автором, можно открыть новый диалог, скопировать уже готовый результат труда и продолжать. Но сам факт что чат вынужденно обрывается. не порядок.

Так и будет. Они не держат длинный контекст, а пока чат идет контекст все растет и растет.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации