Langchain ile Kod Oluşturma
Langchain ve OpenAI ChatGPT kullanarak kod oluşturma işlemleri.
LLM’ler tarafından oluşturulan kodun kalitesi nasıl doğru bir şekilde değerlendirilebilir?
Bu tür ajanlar komplekstir, stokastiktir ve sıklıkla birim testlerinin çalıştırılması gibi özel değerlendirme çözümleri gerektirir. Aşağıda, bunu kendiniz çalıştırmanız için gereken araçlara yönelik kod ve bağlantılar da dahil olmak üzere, bununla başa çıkmak için kendi sistemimin bir özeti bulunmaktadır.[1]
Peki binlerce çıktının toplu olarak değerlendirilmesi amacıyla LangSmith platformu olarak nasıl kullanabildiğimize bakalım.
LangSmith
Langchain ve LangSmith’i projeniz büyükse, onlarca chain ve istem kullanıyorsa tercih etmeniz önerilir.
LangSmith, LangChain ile oluşturulan uygulamalara yönelik bir veri platformudur. Python kodunda tanımladığınız LLM zincirlerini ve değerlendiricilerini toplu olarak çalıştıracak bir istemciyle birlikte gelir. LangSmith hala kapalı betadır. [1]
LangSmith, olaylar zincirindeki her adımın model girdilerine ve çıktılarına ilişkin detay sağlar. Bu, ekiplerin yeni zincirler ve istem şablonlarıyla denemeler yapmasını ve beklenmedik sonuçların, hataların veya gecikme sorunlarının kaynağını tespit etmesini kolaylaştırır. Hangi üretimlerin sorunlara neden olduğunu tespit etmek de dahil gecikme ve token kullanımını da açıklayabiliriz. [2]
Bir zinciri/istemi değiştirirsem, çıktılarım nasıl değişir sorusu geliştiricilerin uğraştığı ana sorulardan biridir. Bu soruyu yanıtlamanın en etkili yolu, önemsediğiniz örneklerden oluşan bir veri kümesi oluşturmak ve ardından değiştirilen istemleri/zincirleri bu veri kümesi üzerinde çalıştırmaktır. LangSmith öncelikle bu veri kümelerinin takibini veya manuel olarak oluşturduğunuz veri kümelerini yükleyerek oluşturmayı kolaylaştırır. Daha sonra bu veri kümeleri üzerinde kolayca zincirleri ve istemleri çalıştırabiliriz.[2]
LangSmith, açık kaynak değerlendirme modülleri ile sorunsuz bir şekilde çalışır. Bu modüllerin iki ana değerlendirme türü vardır: Sezgisel yöntemler ve LLM’ler. Sezgisel değerlendirme yazılımcıların cevaba bakıp başarıya dair bir çıkarıma ulaşmasıdır, LLM ile ölçümde ise cevap kalitesini ölçme amacıyla çalışan LLM’ler farklı alanlarda başarıyı ölçer. [2]
Uzun vadede LLM destekli değerlendirme konusunda son derece iyimseriz. Bu yaklaşımı eleştirenler bunun kavramsal olarak dengesiz ve pratik olarak maliyetli (zaman ve para) olduğunu söyleyecektir. Ancak üst düzey laboratuvarlardan bunun uygulanabilir bir strateji olduğuna dair çok ikna edici kanıtların çıktığını görüyoruz (https://arxiv.org/abs/2212.08073) . Ve bu modellerde (hem özel hem de açık kaynak) toplu olarak iyileştirmeler yaptıkça ve kullanım daha yaygın hale geldikçe, maliyetlerin önemli ölçüde azalmasını bekliyoruz.[2]
Hata ayıklama, test etme ve değerlendirme, prototip aşamasından üretim aşamasına geçmenize yardımcı olsa da iş, teslimatla bitmez. Geliştiricilerin performansı aktif olarak izlemesi ve ideal olarak bu performansı geri bildirime göre optimize etmesi gerekir. Geliştiricilerin, uygulamalarının sistem düzeyindeki performansını (gecikme ve maliyet gibi) takip etmek, model/zincir performansını takip etmek (geri bildirimleri çalıştırmalarla ilişkilendirerek), hata ayıklama sorunlarını (yanlış giden belirli bir çalıştırmaya dalmak) takip etmek için sürekli olarak LangSmith’e güvenirler.[2]
LangSmith Uygulama Örneği
LangSmith ile veri kümesi oluşturmak oldukça basittir. Aşağıdaki kodda ihtiyacımız olan alanları bulut platformundaki hesabımıza bağlı bir veri setine kopyalıyoruz [1]:
import langsmith
from human_eval.data import read_problems
# LangSmith'e bağlanın ve bir veri kümesi oluşturun.
client = langsmith.Client()
dataset = client.create_dataset(dataset_name, description=description)
# Veri kümesindeki her öğeyi LangSmith veri kümesine yükleyin.
for key, value in read_problems().items():
client.create_example(
inputs={ "prompt": value["prompt"], "task_id": key },
outputs={ "canonical_solution": value["canonical_solution"] },
dataset_id=dataset.id
)
Değerlendiricinin Tanımlanması
Kullandığımız HumanEval isimli veri kümesi problem setindeki her öğe, oluşturulan kodun doğru olup olmadığını kontrol etmek için bir dizi test senaryosu içerir. OpenAI’nin HumanEval değerlendiricisinde, bu “assert()“ ifadeleri ile oluşturulan kodun sonuna eklenir ve ardından kod çalıştırılarak başarı kontrolü yapılır. Bu özel değerlendirme işlevini LangSmith ile kullanmak için RunEvaluator’ı alt sınıflara ayırıyoruz [1]:
from langsmith.evaluation import RunEvaluator, EvaluationResult
from langsmith.schemas import Run, Example
from human_eval.execution import check_correctness
class HumanEvalEvaluator(RunEvaluator):
def evaluate_run(self, run: Run, example: Optional[Example] = None) -> EvaluationResult:
problem = problems[run.inputs["task_id"]]
solution = run.outputs["output"]
result = check_correctness(problem, solution, timeout=5)
return EvaluationResult(
key = "Correctness",
score = bool(result["passed"])
)
Toplu Değerlendirme Çalıştırma
Yukarıdaki basit örneğe benzer şekilde, veri kümesini çalıştıracak bir kod oluşturucu tanımlayacağız. HumanEvalChain, CompleteCodeChain’e benzemektedir ancak HumanEval veri kümesi tarafından tanımlanan girdi alanlarına uyarlanmıştır. [1]
from langchain.chat_models import ChatOpenAI
from codechain.generation import HumanEvalChain
# Factory for the generation chain
def chain_factory():
return HumanEvalChain.from_llm(
ChatOpenAI(model_name=model_name, temperature=temperature)
)
Artık bir veri kümesi oluşturulduğuna ve oluşturucu ile değerlendirici tanımlandığına göre geriye kalan tek şey toplu değerlendirmeyi çalıştırmaktır. LangChain’in arun_on_dataset() işlevi, bir veri kümesinde paralel olarak bir zincir çalıştırarak her örnek tamamlandığında değerlendirme sonuçlarını gerçek zamanlı olarak ekler. [1]
from langchain.smith import arun_on_dataset, RunEvalConfig
# Evaluator configuration
evaluation = RunEvalConfig(
custom_evaluators=[HumanEvalEvaluator()],
input_key="task_id"
)
# Run all generations and evaluations
chain_results = await arun_on_dataset(
client=client,
dataset_name=dataset_name,
num_repetitions=repetitions_per_problem,
concurrency_level=5,
llm_or_chain_factory=chain_factory,
evaluation=evaluation,
tags=["HumanEval"],
verbose=True
)
Bunu problem başına on kez çalıştırmak 1.640 değerlendirme çıktısı üretecektir. Denemek için: [1]
Azure OpenAI Kullanarak Kod Oluşturmak
Temel GPT-3 ailesinde, standart metin modeli (text-davinci-003 gibi) iyi bir temel kod anlayışına sahiptir. Daha yeni nesil modeller için hem doğal dil hem de kod işlemleri çok daha iyi performans (gpt-35-turbo ve gpt-4 gibi) gösterirler. [3]
Aşağıda yalnızca AzureOpenAI kullanarak bir kod üretme kod örneği bulunmaktadır[3]:
import openai
# Set OpenAI configuration settings
openai.api_type = "azure"
openai.api_base = azure_oai_endpoint
openai.api_version = "2023-05-15"
openai.api_key = azure_oai_key
# Build the messages array
messages =[
{"role": "system", "content": system_message},
{"role": "user", "content": user_message},
]
# Call the Azure OpenAI model
response = openai.ChatCompletion.create(
engine=model,
messages=messages,
temperature=0.7,
max_tokens=1000
)
Langchain Kod Oluşturma Örneği
Aşağıda LangChain-Coder projesinden aldığım bir kod parçası var [4]:
# Importing the libraries
from io import StringIO
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain, SequentialChain, SimpleSequentialChain
from langchain.memory import ConversationBufferMemory
import langchain.agents as lc_agents
from langchain.llms import OpenAI
from langchain.llms import OpenAI as LangChainOpenAI
import openai
from dotenv import load_dotenv
# Load the environment variables
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
global generated_code
LANGUAGE_CODES = {
'C': 'c',
'C++': 'cpp',
'Java': 'java',
'Ruby': 'ruby',
'Scala': 'scala',
'C#': 'csharp',
'Objective C': 'objc',
'Swift': 'swift',
'JavaScript': 'nodejs',
'Kotlin': 'kotlin',
'Python': 'python3',
'GO Lang': 'go',
}
code_prompt = input("Enter a prompt to generate the code")
code_language = list(LANGUAGE_CODES.keys())[2] # java
code_file = input("Enter file name:")
# Prompt Templates
code_template = PromptTemplate(
input_variables=['code_topic'],
template='Write me code in ' +
f'{code_language} language' + ' for {code_topic}'
)
code_fix_template = PromptTemplate(
input_variables=['code_topic'],
template='Fix any error in the following code in ' +
f'{code_language} language' + ' for {code_topic}'
)
# Memory for the conversation
memory = ConversationBufferMemory(
input_key='code_topic', memory_key='chat_history')
# LLM Chains definition
# Create an OpenAI LLM model
open_ai_llm = OpenAI(temperature=0.7, max_tokens=1000)
# Create a chain that generates the code
code_chain = LLMChain(llm=open_ai_llm, prompt=code_template,
output_key='code', memory=memory, verbose=True)
# Create a chain that fixes the code
code_fix_chain = LLMChain(llm=open_ai_llm, prompt=code_fix_template,
output_key='code_fix', memory=memory, verbose=True)
# Create a sequential chain that combines the two chains above
sequential_chain = SequentialChain(chains=[code_chain, code_fix_chain], input_variables=[
'code_topic'], output_variables=['code', 'code_fix'])
Kod Oluşturmak ve Yürütmek için Kişiselleştirilmiş Langchain Aracı Oluşturma Örneği
Code-it isminde Langchain Python REPL benzerini yapmayı deneyeceğiz. Ancak kütüphanemiz REPL’ın aksine[5]:
- Oluşturulan kaynak kodunun bir dosyaya kaydedilmesine izin verecek.[5]
- Temsilcinin pip paketlerini yüklemesine izin verilecek.[5]
- Tam çıktı/geri izleme ile kaynak kodunun izin verilen yürütülmesi olacak.[5]
- İzin verilen linterlerin uygulanması ve buna göre farklı kod nesillerinin örneklenmesi gerçekleşecek.[5]
- Kod oluşturma işlemini tamamen ajana devretme seçeneği bulunacak, böylece ana ajanın kodlamada mutlaka iyi olması gerekmeyecek (ancak kodda kullanılan modelin kodlamada yine de iyi olması gerekiyor).[5]
Planlayıcının İstem Örneği
You're an AI master at planning and breaking down a coding task into smaller, tractable chunks.
You will be given a task, please helps us thinking it through, step-by-step.
First, let's see an example of what we expect:
Task: Fetch the contents of the endpoint 'https://example.com/api' and write to a file
Steps:
1. I should import the requests library
2. I should use requests library to fetch the results from the endpoint 'https://example.com/api' and save to a variable called response
3. I should access the variable response and parse the contents by decoding the JSON contents
4. I should open a local file in write mode and use the json library to dump the results.
Girdi
Your job is to generate cat jokes and save in a file called 'cat_jokes.txt'. Be creative!
Çıktı
[
'1. Import the random library.',
'2. Define a function to generate a random cat joke.',
'3. Define a function to write the cat joke to a file.',
'4. Call the write function after generating the joke.'
]
Bu, LLM’nin işlevleri gerçekten tanımlamasına ve bağımlılıkları tanımlamasına yardımcı olmaktadır.[5]
Bağımlılık takip edici
Artık bir planımız olduğuna göre, onu bir sonraki LLM’e besleyebilir, böylece plandan gerekli bağımlılıkları çıkarabiliriz.[5]
İstem:
You're an AI master at understanding code.
You will be given a task plan, please helps us find the necessary python packages to install.
First, let's see an example of what we expect:
Plan:
1. import the requests library
2. use the requests library to retrieve the contents form
3. parse results into a dictionary
4. write dictionary tos a file
Requirements:
requests
END OF PLANNING FLOW
Plan rastgele bir kitaplık çağırıyor; bağımlılık çıkarıcı bunun kurulacak bir Python paketi olduğunu düşünecek. Bu nedenle, yanlış çıkarılan gereksinimleri ortadan kaldırmak için bazı kontrollere ihtiyacımız var. Aşağıdaki kod çıktının formatını düzeltmeye çalışır, paketin var olup olmadığını, pypi’yi ve kara listede olup olmadığını (rastgele veya json modülleri gibi) kontrol eder.[5]
dep = self.dependency_tracker.execute_task(plan="\n".join(plan))
for d in dep:
d = d.replace("-", "").strip()
if " " in d:
d = d.split(" ")[0]
if self.config.check_package_is_in_pypi:
url = f'https://pypi.org/project/{d}'
res = requests.get(url)
if res.status_code != 200:
pass
if len(d) < 2 or d in DEPENDENCY_BLACKLIST:
continue
dependencies.append(d)
Virtualenv Yöneticisi
Gereksinimleri ayrıştırdıktan sonra, virtualenv yöneticimizi kullanarak kurulum yapabilir ve aynı zamanda kod düzenleyicimiz/yürütücümüz için sanal bir ortam oluşturabiliriz[5]:
for dependency in dependencies:
self.code_editor.add_dependency(dependency)
self.code_editor.create_env()
process = self.code_editor.install_dependencies()
if process.returncode != 0:
logger.error("Dependency install failed for: %s", "\n".join(dependencies))
attempt += 1
Virtualenv Manager’ın kendisi, virtualenv paketinin gücünden yararlanan çok basit bir sınıftır[5]:
import logging
import string
import random
import os
import subprocess
from virtualenv import cli_run
logger = logging.getLogger(__name__)
RANDOM_NAME_LENGTH = 16
class VirtualenvManager:
def __init__(self, name: str = "", base_path="/tmp") -> None:
if not name:
name = ""
for _ in range(RANDOM_NAME_LENGTH):
population = string.ascii_letters + string.digits
char = random.sample(population, k=1)
name += char[0]
self.name = name
self.path = os.path.join(base_path, name)
self.python_interpreter = os.path.join(self.path, "bin/python3")
self.dependencies = []
def add_dependency(self, dependency):
logger.info("Adding dependency '%s' ", dependency)
self.dependencies.append(dependency)
def create_env(self):
logger.info("Creating virtualenv at path '%s' ", self.path)
cli_run([self.path], setup_logging=False)
def install_dependencies(self):
logger.info("Installing dependencies")
process = subprocess.run(
[self.python_interpreter, "-m", "pip", "install"] + self.dependencies,
capture_output=True,
)
return process
Kod oluşturma
Artık kod oluşturmak için kodlayıcı istemini çalıştırmaya hazırız. Bu istemin kalitesinin çok düşük olduğunu unutmayın! Yine de bağlam içinde oldukça iyi performans gösteriyor gibi görünüyor.[5]
You're an expert python programmer AI Agent. You solve problems by using Python code,
and you're capable of providing code snippets, debugging and much more, whenever it's asked of you. You are usually given
an existing source code that's poorly written and contains many duplications. You should make it better by refactoring and removing errors.
You should fulfill your role in the example below:
Objective: Write a code to print 'hello, world'
Plan: 1. Call print function with the parameter 'hello, world'
Source Code:
import os
import os
import os
print('hello, world')
Thought: The code contains duplication and an unused import. Here's an improved version.
New Code:
print('hello, world')
Objective:
Notice that you once you finish the subtask, you should add the word 'Objective:' in a new line,
like in the example above.
You should ALWAYS output the full code.
Orijinal görevi Planlayıcı tarafından oluşturulan “Objective” ve “Plan” olarak enjekte ettiğimize dikkat edin. Bu, kodlayıcının farklı paketler gerektiren çok farklı bir şey denememesini sağlamamıza yardımcı olur.[5]
Çıktıyla, bunu kod düzenleyici aracımızla yerel bir dosyaya kaydedebilir ve model bazen kodu python etiketlerinin içine sardığından işaretleme etiketlerini ortadan kaldırmaya çalışabiliriz.[5]
Lintleme ve örnekleme
Artık bir kaynak kodumuz olduğuna göre, kodumuzun ne kadar kötü olduğunu görmek için linter’ı kullanabiliriz. İşte ilgili pasaj[5]:
from pylint import epylint as lint
(...)
# Run pylint
(pylint_stdout, _) = lint.py_run(self.code_editor.filename, return_std=True)
pylint_stdout = pylint_stdout.getvalue()
pylint_lines = pylint_stdout.splitlines()
# Extract pylint score
linting_score_str = None
for line in pylint_lines:
if PYLINT_SCORE_SUBSTRING in line:
split_1 = line.split(PYLINT_SCORE_SUBSTRING)[1]
linting_score_str = split_1.split("/")[0]
# If we fail to extract core, we probably have a syntax error, so we set score to -1
if not linting_score_str:
logger.warn(f"Failed to parse pylint score from stdout: {pylint_stdout}")
score = -1 # Code probably does not compile
# Else finish converting the score to float
else:
score = float(linting_score_str)
Çıktı
2023-05-18 14:34:35,523 [INFO] Coding sample: 0 (temperature: 0.0)
2023-05-18 14:34:40,259 [INFO]
import matplotlib.pyplot as plt
import random
def generate_data():
return [random.randint(1, 10) for i in range(10)]
def plot_chart(data):
plt.plot(data)
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title('Example Chart')
plt.show()
data = generate_data()
plot_chart(data)
2023-05-18 14:34:40,259 [INFO] Applying linter...
2023-05-18 14:34:40,558 [INFO] Sample score: 0.83
2023-05-18 14:34:44,189 [INFO] <class 'list'>
Varsayılan olarak üç kod örneğini örnekliyoruz. Bunu her seferinde temperature değerini artırarak ve yeni bir plan oluşturarak yapıyoruz. İşleri basit tutmak için bağımlılıkları yeniden yüklemeye çalışmadığımıza dikkat edin, ancak bu hatalara neden olabilir (bu durumda örnek -1 ile filtrelenir).[5]
İşte oluşturulmakta olan ikinci planımız/örneğimiz[5]:
2023-05-18 14:34:55,841 [INFO] Parsed plan: ['1. Import the necessary libraries: matplotlib and random.', '2. Define the data points as a list of tuples. Each tuple contains x and y values.', '3. Define the chart type as a string.', '4. Create a figure and a subplot.', '5. Use the scatter function to plot the data points.', '6. Use the xlabel and ylabel functions to label the axes.', '7. Use the title function to title the chart.', '8. Show the chart using the show function.', '9. Clean up the figure and the subplot.']
2023-05-18 14:34:55,842 [INFO] Coding sample: 2 (temperature: 0.2)
2023-05-18 14:35:01,783 [INFO]
import matplotlib.pyplot as plt
import random
def generate_data():
return [random.randint(1, 10) for i in range(10)]
def plot_chart(data):
plt.plot(data)
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title('Example Chart')
plt.show()
plt.text(50, 90, 'Random Data', fontsize=16)
data = generate_data()
plot_chart(data)
2023-05-18 14:35:01,783 [INFO] Applying linter...
2023-05-18 14:35:02,096 [INFO] Sample score: 1.54
2023-05-18 14:35:02,096 [INFO] Score of highest sample: 1.54
Yani kod neredeyse aynı, ancak bir nedenden dolayı pylint bunun daha yüksek bir puan olduğuna karar verdi. Üçüncü örnek sadece aynı puana sahip bir tekrardır.[5]
Kodun Yürütülmesi
Tamam, artık örnekler elimizde olduğuna göre sıralayıp en yüksek puana sahip olanı alıyoruz.[5]
Daha sonra onu tekrar dosyaya kaydedip çalıştırıyoruz![5]
coding_samples.sort(key=lambda x: x["score"], reverse=True)
highest_score = coding_samples[0]
logger.info("Score of highest sample: %s", highest_score["score"])
self.code_editor.overwrite_code(highest_score["code"])
if not self.config.execute_code:
return self.code_editor.display_code()
result = self.code_editor.run_code()
Bu örnekte uygulama başarılı oldu (bu her zaman böyle değildir)![5]
CompletedProcess(args=['/tmp/2Gh5EfN6MNxahZcI/bin/python3', 'persistent_source.py'], returncode=0, stdout=b'', stderr=b'/home/paolo/code-it/persistent_source.py:10: UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.\n plt.show()\n') b'/home/paolo/code-it/persistent_source.py:10: UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.\n plt.show()\n'
2023-05-18 14:35:02,439 [INFO] Finished generating code!
2023-05-18 14:35:02,439 [INFO] Source code is functional!
Sonuç
Bu kütüphane taslağı, karmaşık görevler için Yüksek Lisans’lardan etkili bir şekilde yararlanmanın ilk göründüğü kadar kolay olmadığını gösteriyor. Bu nedenle rehberlik gibi kütüphaneler geliştirilmiştir:
Sanırım bu kod da benzer bir yöne gitti ancak Microsoft kütüphanesi kadar iyi yapılandırılmış ve esnek değildi.[5]
Bir sonraki iyi adım, guidance’ı code-it’e entegre etmek veya onu kullanarak yeni, tamamen yeni bir yaklaşım denemek olacaktır.[5]
Yerel kodu da yerleştirmelerle indeksle bu paket kod oluşturmaya yardımcı olmak için lokal snippet’lerle yeniden kullanabilirdi.[5]
Umduğum kadar işe yaramayan şeylerden biri de linter puanına göre farklı örneklerin örneklenmesiydi, dolayısıyla belki de bunun daha iyi yolları vardır[5]:
- Örnek çeşitliliğinin artırılması.
- Daha etkili bir kod puanlayıcı kullanma.
- Örnekleme yerine linter’ı kodda uygulamanın bir yolunu bulmak.
Kaynaklar
[1] James Murdza, (28 July 2023), Evaluating Code Generation Agents Langchain and CodeChain:
[2] LangChain, (18 July 2023), Announcing LangSmith:
[https://blog.langchain.dev/announcing-langsmith/]
[3] Microsoft, (August 2023), Generate Code with Azure OpenAI Service:
[https://learn.microsoft.com/en-us/training/modules/generate-code-azure-openai/1-introduction]
[4] Haseeb Heaven, (26 May 2023), LangChain-Coder:
[https://github.com/haseeb-heaven/LangChain-Coder]
[5] Paolo Rechia, (18 May 2023), Building a Custom Langchain Tool For Generating and Execting Code: