Powrót do listy artykułów

Lokalne llm w automatycznym generowaniu kodu

Czy LLMy potrafią skutecznie generować kod? Obecnie bez wątpienia. Jednak pytanie czy jesteśmy skazani na używanie publicznych API, które są płatne? czy możemy również generować kod własnym sprzętem?

Przeprowadzony eksperyment pokazuje próby rozwiązania prostego problemu pakowania z kilkoma dodakowymi przeszkodami. Do testów używałem rozszerzenia Cline jako że daje możliwość modelom uruchamianie aplikacji, interpretację wyników i poprawę własnego kodu.

Zadanie wyglądało tak:

#main.py
from random import randint
import solution

def generate_items():
    """
    Generate a list of random items with lengths between 4 to 20.
    """
    length = randint(4, 20)
    return [randint(4, 20) for _ in range(length)]

def calculate_cost(package_list):
    """
    Calculate the total cost of packaging based on the packages.
    """
    cost = 0
    for package in package_list:
        size = 0
        for layer in package:
            if size < sum(layer):
                size = sum(layer)
        cost += size
    return cost

def check_rolues(package_list, items):
    """
    Check various roles and constraints of the packaging.
    """  
    max_weight_package = 40
    total_weight = 0

    for package in package_list:
        single_element_layer = 0
        weight = 0

        #find the largest item in each package
        for layer in package:
            if len(layer) == 1 and layer[0] > single_element_layer:
                single_element_layer = layer[0]


        #the largest item does not define the size of the package
        for layer in package:
            if sum(layer) > single_element_layer:
                print("The biggest item is not define the size of the package")
                return False

        #check weight of each package to be less than 40
        for layer in package:
            weight += sum(layer)
        total_weight += weight
        if weight > max_weight_package:
            print("Weight is more than 40")
            return False

    #check total weight to be equal to sum of all item
    if total_weight != sum(items):
        print("The items have been changed")
        return False
    return True

if __name__ == "__main__":
    # Generate a list of random items
    items = generate_items()
    print("Generated items:", items)

    # Pack the items into packages using LLM
    package_list = solution.packing(items)

    # Calculate the cost of packaging if checks pass

    print("Cost of Packaging:")
    cost = None

    if check_rolues(package_list, items):
        for package in package_list:
            print(package)
        cost = calculate_cost(package_list)
        print("Packing Cost:", cost)

Modele które udało się przetestować to deepcoder:14b, deepseek-r1:14b, gemma3:12b, qwen3:14b. Użytkowanie tych modeli nie jest plug and play. Potrafiły się one zapętlać, bądź zmieniać sam plik main.py Generowana logika potrafiła zmieniać się np jeśli nazwę zmiennej zawierającą wygenerowane dane zmieniłem z numers na items, lub gdy we wcześniejszych wersjach rozpisałem check_rolues dla kilku modeli.

Ten ostatni przypadek jest szczególnie ciekawy gdyż wydawało się że modele zaczynają rozumieć że dochodzi do rywalizacji, a generowane rozwiązania były lepsze.

Koniec końców wszystkie funkcje nie są wyszukane, nie ma zaawansowanych heurystyk czy generowania całej przestrzeni rozwiązań. Wszystkie okazały się dość leniwe, co więcej tylko gemma wykorzystała inny algorytm który w dodatku jest gorszy od pozostałych. Wszsytkie modele obrały strategię by nie pchać kilku elementów do warstwy w paczce a optymalizować rozmiary każdej z nich, sortując te elementy. Należy także brać pod uwagę że może być to efekt niezamierzony lub intuicja(?). Poniżej przedstawiam przykładowe wyniki

Generated items: [15, 15, 18, 11, 14, 7, 16, 9, 12, 8, 9, 18, 20, 6, 8, 18, 4, 9]
Cost of Packaging:
[[20], [18]]
[[18], [18], [4]]
[[16], [15], [9]]
[[15], [14], [11]]
[[12], [9], [9], [8]]
[[8], [7], [6]]
Deepcoder Cost: 89
[[20], [18]]
[[18], [18], [4]]
[[16], [15], [9]]
[[15], [14], [11]]
[[12], [9], [9], [8]]
[[8], [7], [6]]
Deepseek Cost: 89
[[15], [15]]
[[18], [11]]
[[14], [7], [16]]
[[9], [12], [8], [9]]
[[18], [20]]
[[6], [8], [18], [4]]
[[9]]
Gemma Cost: 108
[[20], [18]]
[[18], [18], [4]]
[[16], [15], [9]]
[[15], [14], [11]]
[[12], [9], [9], [8]]
[[8], [7], [6]]
Qwen3 Cost: 89

Dodatkowo zamieszczam najlepsze rozwiązania każdego z modelu:

Deepcoder:14b

def packing(numbers):
    """
    Pack numbers into packages following the constraints:
    - Each package's total weight must not exceed 40
    - The largest element in each package must equal the sum of its layers
    - Minimize the number of packages and their total cost

    Returns a list of packages, where each package is a list of layers.
    """
    # Sort numbers in descending order to prioritize larger elements first
    sorted_numbers = sorted(numbers, reverse=True)

    # List to hold the packages
    packages = []

    for number in sorted_numbers:
        placed = False

        # Try to place the number into an existing package
        for i in range(len(packages)):
            current_package = packages[i]

            # Calculate the sum of all elements in the package
            total_sum = sum(sum(layer) for layer in current_package)

            # Check if adding this number would exceed the weight limit
            if (total_sum + number) > 40:
                continue

            # Create a new layer with just this number (single element)
            new_layer = [number]

            # Add the layer to the package
            current_package.append(new_layer)
            placed = True
            break

        if not placed:
            # Start a new package with this number as the first layer
            new_package = [[number]]
            packages.append(new_package)

    return packages

Deepseek-r1:14b

def packing(items):
    """
    Pack items into separate packages while optimizing for minimal cost.
    Returns a list of packages, where each package contains layers of items
    that meet the constraints from check_rolues().
    """
    def calculate_layer_sum(layer):
        return sum(layer)

    def can_add_to_package(package, item):
        # Check if adding this item as a new layer would violate constraints
        current_max = max(sum(l) for l in package)
        proposed_layer = [item]

        # Check size constraint (max layer sum must be at least the largest single element)
        if item > current_max:
            return False

        # Check weight constraint (sum of all layers in package <= 40)
        total_weight = sum(calculate_layer_sum(l) for l in package) + item
        if total_weight > 40:
            return False

        return True

    packages = []

    # Start with the largest items first to optimize packing
    sorted_items = sorted(items, reverse=True)

    for item in sorted_items:
        placed = False

        # Try placing in an existing package
        for package in packages:
            if can_add_to_package(package, item):
                package.append([item])
                placed = True
                break

        if not placed:
            # Start new package
            packages.append([[item]])

    return packages

Gemma3:12b

from typing import List

def packing(numbers: List[int]) -> List[List[List[int]]]:
    """
    Packs the given numbers into packages, minimizing cost.
    """
    packages = []
    current_package = []
    current_weight = 0

    for number in numbers:
        if current_weight + number <= 40:
            current_package.append([number])
            current_weight += number
        else:
            packages.append(current_package)
            current_package = [[number]]
            current_weight = number

    if current_package:
        packages.append(current_package)

    return packages

Qwen3:14b

def packing(items):
    items_sorted = sorted(items, reverse=True)
    packages = []
    for item in items_sorted:
        placed = False
        for package in packages:
            # Check if adding the item is possible
            if sum(package['items']) + item <= 40 and item <= package['max_item']:
                package['items'].append(item)
                placed = True
                break
        if not placed:
            new_package = {
                'items': [item],
                'max_item': item
            }
            packages.append(new_package)

    # Function to arrange items into layers
    def arrange_layers(items, max_item):
        layers = []
        remaining = items.copy()
        while remaining:
            layer = []
            current_sum = 0
            i = 0
            while i < len(remaining):
                if current_sum + remaining[i] <= max_item:
                    layer.append(remaining[i])
                    current_sum += remaining[i]
                    i += 1
                else:
                    break
            layers.append(layer)
            remaining = remaining[i:]
        return layers

    # Arrange items into layers for each package
    for package in packages:
        package['layers'] = arrange_layers(package['items'], package['max_item'])

    # Return the list of packages as a list of layers
    result = []
    for package in packages:
        result.append(package['layers'])
    return result