LCEL পরিচিতি

LCEL (LangChain Expression Language) হল একটি শক্তিশালী ওয়ার্কফ্লো সংরচনা টুল, যা আপনাকে বেসিক কোম্পোনেন্ট থেকে জটিল কাজ চেইন তৈরি করতে দেয় এবং স্ট্রিমিং প্রসেসিং, সমলিং প্রসেসিং, এবং লগিং সহ আউট-অফ-দি-বক্স ফিচার সমর্থন করে।

মৌলিক উদাহরণ: প্রম্পট + মডেল + আউটপুট পার্সার

এই উদাহরণে, আমরা দেখাবো কীভাবে LCEL (LangChain Expression Language) ব্যবহার করে তিনটি কম্পোনেন্ট - প্রম্পট টেমপ্লেট, মডেল, এবং আউটপুট পার্সার - একত্রিত করে "জোক" বলার কাজের জন্য একটি সম্পূর্ণ ওয়ার্কফ্লো তৈরি করতে পারি। কোডটি দেখায় কীভাবে চেইন তৈরি করতে হয়, পাইপ সিম্বল | ব্যবহার করে বিভিন্ন কম্পোনেন্ট যুক্ত করতে হয়, এবং প্রতিটি কম্পোনেন্টের ভূমিকা এবং আউটপুট ফলাফল প্রস্তুত করা হয়।

প্রথমে, আসুন দেখি কীভাবে প্রম্পট টেমপ্লেট এবং মডেল যুক্ত করে একটি বিশেষ বিষয়ের উপর জোক তৈরি করতে হয়:

Dependencies ইনস্টল করুন

%pip install --upgrade --quiet langchain-core langchain-community langchain-openai
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

prompt = ChatPromptTemplate.from_template("{topic} সম্পর্কে আমাকে একটি জোক বলুন")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()

chain = prompt | model | output_parser

chain.invoke({"topic": "আইসক্রিম"})

আউটপুট

"কেনো পার্টিতে আইসক্রিমকে পৌনা করা হয়না? কারণ এটি গরম হলে গলিয়ে পড়ে!"

এই কোডে, আমরা LCEL ব্যবহার করে বিভিন্ন কম্পোনেন্টগুলি চেইনে যোগ করেছি:

chain = prompt | model | output_parser

এখানে | সিম্বলটি উদাহরণটি একইভাবে Unix পাইপ অপারেটরের মতো, যা বিভিন্ন কম্পোনেন্টগুলি যোগ করে এবং একটি কম্পোনেন্টের আউটপুটকে পরবর্তী কম্পোনেন্টের ইনপুট হিসাবে পাঠায়।

এই চেইনে, ব্যবহারকারী ইনপুটটি প্রথমে প্রম্পট টেমপ্লেটে পাঠায়, তারপরে প্রম্পট টেমপ্লেটের আউটপুটটি মডেলে পাঠায়, এবং চেষ্টা করে আমাদের মডেল এর আউটপুটটি আউটপুট পার্সারে পাঠায়। অনুগ্রহ করে পৃথকভাবে কীভাবে প্রতিটি কম্পোনেন্টগুলির সাথে যোগাযোগ করা হয়েছে এটা বেশি স্পষ্ট করার জন্য।

1. প্রম্পট

prompt হল BasePromptTemplate, যা একটি টেমপ্লেট ভ্যারিয়েবল ডিকশনারি গ্রহণ করে এবং PromptValue তৈরি করে। PromptValue হল একটি প্রম্পট ধারণ করা Wrapped object , যা LLM (স্ট্রিং ফর্মে ইনপুট হিসাবে) বা ChatModel (মেসেজ সিকোয়েন্স হিসাবে ইনপুট হিসাবে) এর সাথে ব্যবহার করা যেতে পারে। এটি যেকোনো ধরনের ভাষার মডেল এর সাথে ব্যবহার করা যেতে পারে, কারণ এটি BaseMessage তৈরি এবং স্ট্রিং তৈরির লজিক উল্লেখ করে।

prompt_value = prompt.invoke({"topic": "আইসক্রিম"})
prompt_value

আউটপুট

ChatPromptValue(messages=[HumanMessage(content='আমাকে একটি জোক বলুন আইসক্রিমের বিষয়ে')])

নীচে, আমরা প্রম্পট ফরম্যাট করা ফলাফলটি চ্যাট মডেলগুলি দ্বারা ব্যবহার করা ফরম্যাটে রূপান্তর করেছি:

prompt_value.to_messages()

আউটপুট

[HumanMessage(content='আমাকে একটি জোক বলুন আইসক্রিমের বিষয়ে')]

এটি সরাসরি স্ট্রিং এবং রূপান্তর করা

prompt_value.to_string()

আউটপুট

'মানুষ: আমাকে একটি জোক বলুন আইসক্রিমের বিষয়ে।'

2. মডেল

পরবর্তী, PromptValue টি মডেল করুন। এই উদাহরণে, আমাদের model একটি ChatModel, যার মানে হল এটি একটি BaseMessage আউটপুট করবে।

আমরা প্রথমে model কে সরাসরি কল করে দেখতে পারি:

message = model.invoke(prompt_value)
message

ফেরত আসে:

AIMessage(content="কেনো পার্টিতে আইসক্রিমকে পৌনা করা হয়না? কারণ এটি গরম হলে গলিয়ে পড়ে!")

আমাদের model যদি LLM ধরনে নির্ধারিত করা হয়, তাহলে এটি একটি স্ট্রিং আউটপুট করবে।

from langchain_openai.llms import OpenAI

llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)

মডেল ফেরত দেয়:

'\n\nবট: কেনো আইসক্রিম ট্রাক বিগড়ে গেছে? কারণ এটা শীতল রাখার জন্য মেল্ট হয়ে গেছে!'

3. আউটপুট পার্সার

আমাদের মডেল থেকে সরাসরি আউটপুট টি আউটপুট পার্সারে পাঠানো হয়, যা একটি BaseOutputParser, অর্থাৎ এটি স্ট্রিং বা BaseMessage কে ইনপুট হিসাবে গ্রহণ করে। StrOutputParser নির্দিষ্টভাবে যে কোন ইনপুটকে একটি সাধারণ স্ট্রিংতে রূপান্তর করে।

output_parser.invoke(message)
কেনো আইসক্রিম ট্রাক বিগড়ে গেছে? কারণ এটা শীতল রাখার জন্য মেল্ট হয়ে গেছে!

৪. সম্পূর্ণ প্রক্রিয়া

বিচারণার প্রক্রিয়া নিম্নলিখিত:

  1. chain.invoke({"topic": "ice cream"}) কল করুন, যা আমরা সংজ্ঞায়িত ওয়ার্কফ্লো আরম্ভ করে এবং "আইসক্রিম" সম্পর্কিত জোকের জন্য প্যারামিটার {"topic": "ice cream"} পাস করে।
  2. প্রথম চেইনের কল প্যারামিটার {"topic": "ice cream"} পাস করা, prompt, যা প্রম্পট টেম্পলেট বিন্যাস করে "আইসক্রিম" সম্পর্কিত প্রম্পট Tell me a little joke about ice cream পেতে।
  3. প্রম্পট Tell me a little joke about ice cream কে model (gpt4 মডেল) এ পাস করুন।
  4. model দ্বারা প্রাপ্ত ফলাফলকে output_parser আউটপুট পার্সারে পাস করুন, যা মডেলের ফলাফল বিন্যাস করে এবং চেষ্টা প্রশ্নটি পাস করে।

যদি আপনি কোনও কোম্পোনেন্টের আউটপুট-এ আগ্রহী হন, তবে আপনি সাধারণভাবে চেইনের ছোট সাইজের একটি টেস্ট করতে পারেন, উদাহরণস্বরূপ prompt অথবা prompt | model, যাতে আপনি মধ্যমিক ফলাফলগুলি দেখতে পান:

input = {"topic": "ice cream"}

prompt.invoke(input)

(prompt | model).invoke(input)

RAG খোঁজার উদাহরণ

পরবর্তী, আসুন একটি বেশি জটিল LCEL উদাহরণ স্পষ্ট করি। আমরা জিজ্ঞাসা উত্তর দেওয়ার সময় কিছু প্রাথমিক তথ্য যোগ করার জন্য মজবুত পুনর্নিয়েন চালু করতে নির্দেশিত রিট্রিভাল একজনের উদাহরণ দিব।

from langchain_community.vectorstores import DocArrayInMemorySearch
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_openai.chat_models import ChatOpenAI
from langchain_openai.embeddings import OpenAIEmbeddings

vectorstore = DocArrayInMemorySearch.from_texts(
    ["harrison worked at kensho", "bears like to eat honey"],
    embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()

template = """Answer the question based only on the following context:
{context}

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()
output_parser = StrOutputParser()

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

chain.invoke("where did harrison work?")

এই মামলায়, বিচারণার সংযোজিত চেইন হল:

chain = setup_and_retrieval | prompt | model | output_parser

সাধারণভাবে, উপরের প্রম্পট টেমপ্লেটটি context এবং question মান গ্রহণ করার জন্য। প্রম্পট টেম্পলেটটি গড়ে নেয় এবং প্রম্পট তৈরি করতে সংশ্লিষ্ট নথির ডকুমেন্টগুলি পাওয়ার জন্য আমরা আগের দিকে সম্পর্কিত ডকুমেন্ট প্রাপ্তের জন্য DocArrayInMemorySearch ব্যবহার করতে চাই। এটা একটি চেইনযোগ্য রানাবল কম্পোনেন্ট, তবে আপনি এটি আলাদা করে চালিয়ে যেতে পারেন:

retriever.invoke("where did harrison work?")

তারপর, আমরা প্রম্পটের জন্য ইনপুট প্রস্তুত করার জন্য RunnableParallel ব্যবহার করি, রিট্রিভার ব্যবহার করে ডকুমেন্ট সন্ধান করে, এবং RunnablePassthrough ব্যবহার করে ব্যবহারকারীর প্রশ্ন পাস করে:

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)

সংক্ষেপণ করা যায়:

setup_and_retrieval = RunnableParallel(
    {"context": retriever, "question": RunnablePassthrough()}
)
chain = setup_and_retrieval | prompt | model | output_parser

প্রক্রিয়াটি এমনঃ

  1. প্রথমে, একটি RunnableParallel অবজেক্ট তৈরি করুন যা দুটি এন্ট্রি বিশিষ্ট করেছে। প্রথম এন্ট্রি মilূ কে 'retriever' দ্বারা নিয়োজিত ডকুমেন্টের ফল প্রস্তুত করে। দ্বিতীয় এন্ট্রি question ব্যবহারকারীর মূল প্রশ্ন ধরে। প্রশ্নটি পাঠাতে আমরা এই এন্ট্রিকে অনুমতি প্রদান করতে হবে RunnablePassthrough ব্যবহার করে।
  2. প্রায়: আমাদেরাত্র prompt কম্পোনেন্টের জন্য পূর্বের পদক্ষেপ থেকে ডিকশনারি প্রেরণ করুন। এটি ইউজার ইনপুট গ্রহণ (অর্থাৎ, question) সহ প্রাপ্ত ডকুমেন্টগুলি (অর্থাৎ, context) গ্রহণ করতে প্রথিত হয়, প্রম্পট গড়ে নেয় এবং prompt_value আউটপুট করে।
  3. model কম্পোনেন্ট গড়ে নেওয়া প্রম্পট নেওয়া এবং ওপেনএআই এর LLM মডেলে মূল্যায়নের জন্য মুহূর্তের প্রম্পট পাঠায়। মডেল দ্বারা পোষ্যমান আউটপুট মনাবে।
  4. শেষে, output_parser কম্পোনেন্ট একটি ChatMessage নেয়, এটি Python স্ট্রিং এ রূপান্তরে আন এবং তা থেকে invoke মেথড বাদ দেয়।