v0.0.2
Openai-Whisper-talk هو تطبيق محادثة صوتية مدعوم من تقنيات Openai مثل Whisper ، ونظام التعرف على الكلام التلقائي (ASR) ، وإكمال الدردشة ، والواجهة التي تحاكي المحادثة مع نموذج يلعب دور المساعد ، والتضمينات ، وتحويل النص إلى بيانات المتجهة التي يمكن استخدامها في المهام مثل البحث الدلالي ، وأحدث نصوص نصية ، وتكثفها. تم تصميم التطبيق باستخدام Nuxt ، وهو إطار JavaScript يعتمد على Vue.js.
يحتوي التطبيق على ميزتين جديدتين: إدارة الجدول الزمني والذاكرة طويلة الأجل. مع إدارة الجدول الزمني ، يمكنك قيادة chatbot لإضافة الأحداث المجدولة وتعديلها وحذفها واستردادها. تتيح لك ميزة الذاكرة طويلة المدى تخزين قصاصات المعلومات التي سيتذكرها chatbot للرجوع إليها في المستقبل. يمكنك دمج كلتا الوظيفتين بسلاسة في محادثاتك ببساطة عن طريق التفاعل مع chatbot.
تحديث: تم تحديث وحدة Openai إلى أحدث إصدار واستبدل نموذج التضمين من
text-embedding-ada-002إلى طراز V3 الجديد-text-embedding-3-small.
Openai-Whisper-Talk は、 Whisper (自動音声認識 ((as) システム) 、 、 、 、 、 (アシスタントの役割を果たすモデルとの会話をシミュレートするインターフェース) 、 、 ((セマンティック検索などのタスクで使用できるベクターデータにテキストを変換する) 、そして最新の 、そして最新の 、そして最新の 、そして最新の (テキストをリアルな話し言葉のオーディオに変える) など、 など、 など、 など、 など、 の技術を駆使したサンプル音声会話アプリケーションです。このアプリケーションは、 の技術を駆使したサンプル音声会話アプリケーションです。このアプリケーションは、 の技術を駆使したサンプル音声会話アプリケーションです。このアプリケーションは、 の技術を駆使したサンプル音声会話アプリケーションです。このアプリケーションは、 、 に基づいた に基づいた セマンティック検索などのタスクで使用できるベクターデータにテキストを変換する) 、そして最新の
このアプリケーションには、「スケジュール管理」と「永続メモリ」の 2 つの新機能があります。スケジュール管理を使用すると、チャットボットにスケジュールイベントの追加、変更、削除、取得を指示できます。永続メモリ機能を使用すると、将来の参照のためにチャットボットが覚えておく情報のスニペットを保存できます。これらの機能をチャットボットとの対話を通じてシームレスに統合することができます。将来的に、いくつかの機能強化、たとえばメールやメッセージング機能を追加することで、完全な個人アシスタントになるかもしれません。

من الصفحة الرئيسية ، يمكنك اختيار chatbot للتفاعل معها. كل chatbot لديه شخصية متميزة ، ويتحدث لغة مختلفة ، ويمتلك صوتًا فريدًا. يمكنك تغيير اسم وشخصية أي chatbot من خلال النقر فوق زر التحرير المجاور لاسمه. حاليًا ، لا تدعم واجهة المستخدم مباشرة إضافة chatbots الجديدة ؛ ومع ذلك ، يمكنك إضافة chatbot يدويًا وتخصيص إعدادات الصوت واللغة لكل منها عن طريق تعديل ملف Assets/Contacts.json.

بالإضافة إلى ذلك ، يمكنك تخصيص ملفك الشخصي من خلال النقر على رمز الصورة الرمزية في الزاوية اليمنى العليا. يتيح لك ذلك إدخال اسمك ومشاركة التفاصيل عن نفسك ، مما يتيح لـ chatbot التفاعل معك بطريقة أكثر تخصيصًا.


يتم تسجيل بيانات الصوت تلقائيًا إذا تم اكتشاف الصوت. يتوفر إعداد العتبة لمنع ضوضاء الخلفية من تشغيل التقاط الصوت. بشكل افتراضي ، يتم تعيين هذا على -45dB (مع -10dB يمثل أعلى صوت أعلى). يمكنك ضبط هذه العتبة عن طريق تعديل متغير MIN_DECIBELS 1 وفقًا لاحتياجاتك.
عند تمكين التسجيل ولا يتم اكتشاف أي صوت لمدة 3 ثوان ، يتم تحميل بيانات الصوت وإرسالها إلى الواجهة الخلفية للنسخ. تجدر الإشارة إلى أنه في المحادثات النموذجية ، يكون متوسط الفجوة بين كل منعطف حوالي 2 ثانية. وبالمثل ، فإن التوقف بين الجمل عند التحدث هو نفسه تقريبًا. لذلك ، لقد اخترت مدة طويلة بما يكفي لانتظار الرد. يمكنك ضبط هذه المدة عن طريق تحرير متغير MAX_COUNT 1 .
يمكن للنظام تسجيل بيانات الصوت بشكل مستمر حتى يتم استلام الرد. بمجرد استلام الرد الصوتي من chatbot وتشغيله ، يتم تعطيل التسجيل الصوتي لمنع التسجيل غير المقصود لاستجابة chatbot الخاصة.
يتم توفير إدخال نص أيضًا إذا كنت ترغب في كتابة رسائلك.
يتم تحميل جميع بيانات الصوت المسجلة على دليل public/upload بتنسيق ملف الويب. قبل إرسال ملف الصوت إلى واجهة برمجة تطبيقات Whisper ، من الضروري إزالة جميع الأجزاء الصامتة. تساعد هذه الخطوة على منع القضية المعروفة من الهلوسة الناتجة عن الهمس. لهذا السبب نفسه ، يوصى بتعيين قيمة MIN_DECIBELS بأكبر قدر ممكن ، مما يضمن تسجيل الكلام فقط.
لإزالة الأجزاء الصامتة من الصوت ، سنستخدم ffmpeg . تأكد من تثبيته.
ffmpeg -i sourceFile -af silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-50dB outputFileفي هذا الأمر:
-1 sourceFile يحدد ملف الإدخال.-af silenceremove يطبق مرشح silencerremove .stop_periods=-1 يزيل جميع فترات الصمت.stop_duration=1 يحدد أي فترة من الصمت أطول من ثانية واحدة كسيم.stop_threshold=-50dB يحدد أي مستوى ضوضاء أقل من -50DB كسيم.outputFile ملف الإخراج. لاستدعاء هذا الأمر Shell في مسار API الخاص بنا ، سنستخدم exec من وحدة child-process .
بعد إزالة الأجزاء الصامتة ، يجب فحص حجم الملف. عادة ما يكون حجم الملف النهائي أصغر بكثير من الأصل. خلال هذا الشيك ، يتم تجاهل الملفات التي تزيد عن 16 كيلو بايت ، على افتراض أن ملفًا بهذا الحجم ، بعمق 16 بت ، يعادل حوالي ثانية واحدة من الصوت. من المحتمل أن يكون أي شيء أقصر غير مسموع.
// simple file size formula
const audio_file_size = duration_in_seconds * bitrateتم تصميم العملية بأكملها ، من إزالة الأجزاء الصامتة إلى فحص حجم الملف ، لضمان إرسال بيانات الصوت القابلة للحياة فقط إلى واجهة برمجة تطبيقات Whisper. هدفنا هو تجنب الهلوسة والنقل غير الضروري للبيانات.
بعد تأكيد جدوى بياناتنا الصوتية ، ننتقل بعد ذلك للاتصال بأجهزة واجهة برمجة تطبيقات Whisper.
const transcription = await openai . audio . transcriptions . create ( {
file : fs . createReadStream ( filename ) ,
language : lang ,
response_format : 'text' ,
temperature : 0 ,
} ) حيث lang هو رمز ISO 639-1 للغة المحددة لـ chatbot لدينا. يرجى التحقق من قائمة اللغة المدعومة حاليًا.
نحن نختار تنسيق text بسيط نظرًا لأن الطوابع الزمنية غير مطلوبة ، وقمنا بتعيين معلمة temperature على الصفر لتحقيق الإخراج الحتمي.
بعد تلقي النص من Whisper ، ننتقل إلى إرساله إلى API لإكمال الدردشة مع استدعاء الوظيفة. نستخدم أحدث وحدة Openai Node.js (الإصدار 4) التي تم إصدارها بالأمس (2023/11/07) ، والتي تتضمن تنسيقًا محدثًا للوظائف. يتيح هذا التكرار الأحدث الاحتجاج على وظائف متعددة في طلب واحد.
let messages = [ { role : 'system' , content : system_prompt } ]
let all_history = await mongoDb . getMessages ( )
if ( all_history . length > 0 ) {
const history_context = trim_array ( all_history , 20 )
messages = messages . concat ( history_context )
}
messages . push ( { role : 'user' , content : user_query } )
let response = await openai . chat . completions . create ( {
temperature : 0.3 ,
messages ,
tools : [
{ type : 'function' , function : add_calendar_entry } ,
{ type : 'function' , function : get_calendar_entry } ,
{ type : 'function' , function : edit_calendar_entry } ,
{ type : 'function' , function : delete_calendar_entry } ,
{ type : 'function' , function : save_new_memory } ,
{ type : 'function' , function : get_info_from_memory }
]
} )دعنا نتشريح المكونات المختلفة لهذه الدعوة في الأقسام اللاحقة.
يلعب موجه النظام دورًا حاسمًا في إعطاء الحياة إلى chatbot لدينا. إنه هنا حيث نقوم بإنشاء اسمها وشخصيته ، بناءً على chatbot الذي اختاره المستخدم. نحن نقدمها بتعليمات محددة حول كيفية الاستجابة ، إلى جانب قائمة بالوظائف التي يمكن تنفيذها. نعلم ذلك أيضًا عن هوية المستخدم وبعض التفاصيل الشخصية. أخيرًا ، قمنا بتعيين التاريخ والوقت الحاليين ، وهو أمر ضروري لتفعيل وظائف التقويم.
const today = new Date ( )
let system_prompt = `In this session, we will simulate a voice conversation between two friends.nn` +
`# Personan` +
`You will act as ${ selPerson . name } .n` +
` ${ selPerson . prompt } nn` +
`Please ensure that your responses are consistent with this persona.nn` +
`# Instructionsn` +
`The user is talking to you over voice on their phone, and your response will be read out loud with realistic text-to-speech (TTS) technology.n` +
`Use natural, conversational language that are clear and easy to follow (short sentences, simple words).n` +
`Keep the conversation flowing.n` +
`Sometimes the user might just want to chat. Ask them relevant follow-up questions.nn` +
`# Functionsn` +
`You have the following functions that you can call depending on the situation.n` +
`add_calendar_entry, to add a new event.n` +
`get_calendar_entry, to get the event at a particular date.n` +
`edit_calendar_entry, to edit or update existing event.n` +
`delete_calendar_entry, to delete an existing event.n` +
`save_new_memory, to save new information to memory.n` +
`get_info_from_memory, to retrieve information from memory.nn` +
`When you present the result from the function, only mention the relevant details for the user query.n` +
`Omit information that is redundant and not relevant to the query.n` +
`Always be concise and brief in your replies.nn` +
`# Usern` +
`As for me, in this simulation I am known as ${ user_info . name } .n` +
` ${ user_info . prompt } nn` +
`# Todayn` +
`Today is ${ today } .n` سيتم تخزين جميع الرسائل في MongoDB .
لإدارة الرموز وتجنب تجاوز الحد الأقصى للنموذج ، سنرسل فقط آخر 20 تفاعلًا. تم تصميم وظيفة trim_array لتقليص سجل الرسالة إذا كانت تتجاوز 20 منعطفًا. يمكن تعديل هذه العتبة لتلبية متطلباتك المحددة.
// allow less than or equal to 15 turns
const history_context = trim_array ( all_history , 15 ) من الشاشة الرئيسية ، لديك خيار محو التاريخ السابق لكل chatbot.

يرجى ملاحظة أننا قد فصلنا معالجة استدعاء الوظائف (function_call.js) من مكالمة API الرئيسية للدردشة (tescribe.php). يتم إجراء هذا التمييز لمعالجة الحالات التي يكون فيها محتوى النص موجودًا أثناء استدعاء استدعاء الوظيفة ، وهو عادة ما يكون فارغًا. يمكّن هذا الفصل التطبيق من عرض النص أثناء معالجة استدعاء الوظيفة في وقت واحد. لقد أرفقت العملية أيضًا في حلقة في حالة قيام مكالمة API الثانية بتأسيس مكالمة وظيفة أخرى. من المحتمل أن يتم التعامل مع هذا بشكل أكثر أناقة من خلال تنفيذ البث ، لكنني لم أتعلم بعد كيفية استخدام البث مع nuxt.
يتم تصنيف وظائفنا تحت ميزتين جديدتين: إدارة الجدول الزمني والذاكرة طويلة الأجل .
دعنا ندرس أولاً كيف ندير استدعاء الوظيفة بالتنسيق الجديد. يجب علينا استخدام معلمة Tools الجديدة بدلاً من معلمة الوظائف التي تم إرسالها الآن لتمكين استدعاء مكالمات الوظائف المتعددة.
let response = await openai . chat . completions . create ( {
temperature : 0.3 ,
messages ,
tools : [
{ type : 'function' , function : add_calendar_entry } ,
{ type : 'function' , function : get_calendar_entry } ,
{ type : 'function' , function : edit_calendar_entry } ,
{ type : 'function' , function : delete_calendar_entry } ,
{ type : 'function' , function : save_new_memory } ,
{ type : 'function' , function : get_info_from_memory }
]
} ) تحقق من مخطط JSON لكل وظائف من lib/ DIRECTORY. هنا هو لـ add_calendar_entry :
{
"name" : "add_calendar_entry" ,
"description" : "Adds a new entry to the calendar" ,
"parameters" : {
"type" : "object" ,
"properties" : {
"event" : {
"type" : "string" ,
"description" : "The name or title of the event"
} ,
"date" : {
"type" : "string" ,
"description" : "The date of the event in 'YYYY-MM-DD' format"
} ,
"time" : {
"type" : "string" ,
"description" : "The time of the event in 'HH:MM' format"
} ,
"additional_detail" : {
"type" : "string" ,
"description" : "Any additional details or notes related to the event"
}
} ,
"required" : [ "event" , "date" , "time" , "additional_detail" ]
}
}لتوضيح كيفية عمل كل هذه هذه ، دعنا ننتقل إلى القسم التالي.
لإدارة الجدول الزمني ، لدينا الوظائف التالية:
سيتم تخزين جميع إدخالات التقويم في MongoDB. يرجى ملاحظة أن جميع الإدخالات ستكون متاحة لجميع chatbots.
دعنا نلقي نظرة على محادثة دردشة عينة لإظهار كيفية تفاعل هذه العناصر:
المستخدم: صباح الخير ، جيفيس. ما هو جدول زمني لهذا اليوم؟
استدعاء الوظيفة (استدعاء get_calendar_entry):
{ role : 'assistant' ,
content : null ,
tool_calls : [ {
id : 'call_4cCtmNlgYN5o4foVOQM9MIdA' ,
type : 'function' ,
function : { name : 'get_calendar_entry' , arguments : '{"date":"2023-11-10"}' }
} ]
}استجابة الوظيفة:
[
{
tool_call_id : 'call_4cCtmNlgYN5o4foVOQM9MIdA' ,
role : 'tool' ,
name : 'get_calendar_entry' ,
content : '{n' +
' "message": "Found 3 entries",n' +
' "items": [n' +
' {n' +
' "_id": "654b2805e51fcd815dea8e2d",n' +
' "event": "Project presentation",n' +
' "date": "2023-11-10",n' +
' "time": "10:00",n' +
' "additional_detail": "Important project presentation",n' +
' "__v": 0n' +
' },n' +
' {n' +
' "_id": "654d7b53ff0bf31de6c2af29",n' +
' "event": "Meet Tony",n' +
' "date": "2023-11-10",n' +
' "time": "19:00",n' +
' "additional_detail": "Electric Sheep Bar",n' +
' "__v": 0n' +
' },n' +
' {n' +
' "_id": "654d7d868751ea8704fc9f11",n' +
' "event": "Dinner with Anna",n' +
' "date": "2023-11-10",n' +
' "time": "19:00",n' +
' "additional_detail": "Electric Sheep Bar",n' +
' "__v": 0n' +
' }n' +
' ]n' +
'}'
}
]ملخص الدردشة:
{ role : 'assistant' ,
content : 'Good morning, Jerry. Today, you have a project presentation at 10:00 AM and a dinner meeting with Anna at 7:00 PM at the Electric Sheep Bar. You also have a meeting with Tony at 7:00 PM at the same location.'
}المستخدم: يبدو من الضروري أن يكون لديك إدخال منفصل لنفس الحدث. هل يمكنك تحرير الحدث مع آنا لتشمل لقاء توني وحذف الحدث الآخر؟
استدعاء الوظيفة (تم استدعاء EDIT_CALENDAR_ENTRY و DELETE_CALENDAR_ENTRY):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_ZVAwO5YsXMuWr8876tyCIPIX' ,
type : 'function' ,
function : {
name : 'edit_calendar_entry' ,
arguments : '{"event": "Dinner Meeting with Anna", "date": "2023-11-10", "time": "19:00", "additional_detail": "Meeting with Tony included."}'
}
} ,
{
id : 'call_eMGuI8LxLbXFsLp8FduCrxgp' ,
type : 'function' ,
function : {
name : 'delete_calendar_entry' ,
arguments : '{"delete_type": "event_name", "event": "Meeting with Tony"}'
}
}
]
}استجابة الوظيفة:
[ {
tool_call_id : 'call_ZVAwO5YsXMuWr8876tyCIPIX' ,
role : 'tool' ,
name : 'edit_calendar_entry' ,
content : '{n "message": "Entry edited",n "name": "Dinner Meeting with Anna"n}'
} ,
{
tool_call_id : 'call_eMGuI8LxLbXFsLp8FduCrxgp' ,
role : 'tool' ,
name : 'delete_calendar_entry' ,
content : '{n "name": "Meeting with Tony",n "message": "Entry deleted"n}'
}
]ملخص الدردشة:
{ role : 'assistant' ,
content : 'The event "Dinner Meeting with Anna" has been updated to include the meeting with Tony, and the separate event "Meeting with Tony" has been deleted.'
}المستخدم: حسنًا ، فقط للتأكد ، هل يمكن أن تخبرني جدولي مرة أخرى لهذا اليوم؟
استدعاء الوظيفة (استدعاء get_calendar_entry):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_liqFn24Ds4qp0cy2V5NBg62v' ,
type : 'function' ,
function : { name : 'get_calendar_entry' , arguments : '{"date":"2023-11-10"}' }
}
]
}استجابة الوظيفة:
[ {
tool_call_id : 'call_liqFn24Ds4qp0cy2V5NBg62v' ,
role : 'tool' ,
name : 'get_calendar_entry' ,
content : '{n' +
' "message": "Found 2 entries",n' +
' "items": [n' +
' {n' +
' "_id": "654b2805e51fcd815dea8e2d",n' +
' "event": "Project presentation",n' +
' "date": "2023-11-10",n' +
' "time": "10:00",n' +
' "additional_detail": "Important project presentation",n' +
' "__v": 0n' +
' },n' +
' {n' +
' "_id": "654d7d868751ea8704fc9f11",n' +
' "event": "Dinner with Anna",n' +
' "date": "2023-11-10",n' +
' "time": "19:00",n' +
' "additional_detail": "Meeting with Tony included.",n' +
' "__v": 0n' +
' }n' +
' ]n' +
'}'
}
]ملخص الدردشة:
{ role : 'assistant' ,
content : 'Your schedule for today is as follows:n' +
'- Project presentation at 10:00 AMn' +
'- Dinner with Anna at 7:00 PM, which includes a meeting with Tony.'
}المستخدم: آه لقد نسيت إضافة الموقع في العشاء. آسف ولكن هل يمكنك تحديثه مرة أخرى؟
استدعاء الوظيفة (تم استدعاء edit_calendar_entry):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_eRTFMcz2l8qHO7nhp3msI3v8' ,
type : 'function' ,
function : {
name : 'edit_calendar_entry' ,
arguments : '{"event":"Dinner with Anna","date":"2023-11-10","time":"19:00","additional_detail":"Meeting with Tony at the Electric Sheep Bar."}'
}
}
]
}استجابة الوظيفة:
{
tool_call_id : 'call_eRTFMcz2l8qHO7nhp3msI3v8' ,
role : 'tool' ,
name : 'edit_calendar_entry' ,
content : '{n "message": "Entry edited",n "name": "Dinner with Anna"n}'
}ملخص الدردشة:
{ role : 'assistant' ,
content : 'The event "Dinner with Anna" has been updated to include the meeting with Tony at the Electric Sheep Bar.'
}المستخدم: حسنًا ، أريد أن أؤكد ذلك مرة أخرى ، هل يمكن أن تخبرني جدول زمني لهذا اليوم؟
استدعاء الوظيفة (تم استدعاء get_calendar_entry):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_HYlAj4BTeWcu2CxSp4zBYPuC' ,
type : 'function' ,
function : { name : 'get_calendar_entry' , arguments : '{"date":"2023-11-10"}' }
}
]
}استجابة الوظيفة:
[ {
tool_call_id : 'call_HYlAj4BTeWcu2CxSp4zBYPuC' ,
role : 'tool' ,
name : 'get_calendar_entry' ,
content : '{n' +
' "message": "Found 2 entries",n' +
' "items": [n' +
' {n' +
' "_id": "654b2805e51fcd815dea8e2d",n' +
' "event": "Project presentation",n' +
' "date": "2023-11-10",n' +
' "time": "10:00",n' +
' "additional_detail": "Important project presentation",n' +
' "__v": 0n' +
' },n' +
' {n' +
' "_id": "654d7d868751ea8704fc9f11",n' +
' "event": "Dinner with Anna",n' +
' "date": "2023-11-10",n' +
' "time": "19:00",n' +
' "additional_detail": "Meeting with Tony at the Electric Sheep Bar.",n' +
' "__v": 0n' +
' }n' +
' ]n' +
'}'
}
]ملخص الدردشة:
{ role : 'assistant' ,
content : 'Your schedule for today is as follows:n' +
'- Project presentation at 10:00 AMn' +
'- Dinner with Anna at 7:00 PM, which includes a meeting with Tony at the Electric Sheep Bar.'
}للذاكرة طويلة الأجل ، لدينا الوظائف التالية
ركزت جميع الوظائف السابقة في المقام الأول على استرجاع البيانات النموذجي وتحديد المهام. ولكن لاسترجاع الذاكرة على المدى الطويل ، لا يمكننا ببساطة إجراء البحث عن الكلمات الرئيسية. نحن بحاجة إلى النظر في سياق الاستعلام. نحن بحاجة إلى البحث الدلالي. وفي هذا الصدد ، سوف نستخدم API التضمين . دعنا ننتقل إلى القسم التالي لمناقشة هذا الأمر أكثر.
استكمال : لقد استبدلت
text-embedding-ada-002معtext-embedding-3-small. بناءً على اختباراتي ، فإن هذا الأخير يعمل بشكل جيد بما فيه الكفاية. إن نتائج تشابه جيب التمام بين الإجابات ذات الصلة الوثيقة والإجابات غير ذات الصلة متميزة بشكل ملحوظ. علاوة على ذلك ، فإن الأداء التكلفة للنموذج الصغير V3 ، بسعر 0.00002/1K رموز ، هو عدم التفكير؟ ومع ذلك ، تحتاج إلى تحويل بيانات المتجه الخاصة بك من ADA إلى V3 صغير لأنها غير متوافقة. لاحظ أننا خفضنا أيضًا العتبة من 0.72 إلى 0.3.
بعبارة ببساطة ، يقيس التضمينات علاقة السلاسل النصية. إذا استدعينا واجهة برمجة التطبيقات ، فسوف يمنحنا بيانات المتجهات للأرقام العائمة المرتبطة بنص الإدخال.
const embedding = await openai . embeddings . create ( {
model : "text-embedding-3-small" , //"text-embedding-ada-002",
input : "The quick brown fox jumped over the lazy dog" ,
encoding_format : "float" ,
} )لاستخدام هذا في تطبيقنا ، سوف ننفذ ما يسمونه جيلًا مسبقًا أو خرقة بسيطة.
في البداية ، عند استلام بيانات جديدة من وظيفة SAVE_NEW_MEMORY ، فإننا نسمي واجهة برمجة تطبيقات التضمين لإنشاء تمثيل المتجه الخاص بها. ثم يتم تخزين بيانات المتجه هذه في MongoDB للاستخدام في المستقبل.
بعد ذلك ، عندما يقدم المستخدم استعلامًا يتطلب استرجاع الذاكرة ، يتم تشغيل وظيفة get_info_from_memory. نسمي API التضمين لمعلمات البحث ومقارنتها مع بيانات المتجه المخزنة باستخدام تشابه جيب التمام بسيط. هذه المقارنة عادة ما تعطي عدة مباريات مع درجات متفاوتة. لقد حددنا عتبة لدينا على درجة بين 0.3 (0.72 لنموذج ADA) أو أعلى ، ونحن نحد النتائج بحد أقصى 10.
ثم يتم تمرير النتائج إلى API لإكمال الدردشة النهائية ، والتي تحدد الاستجابة الأنسب لاستعلام المستخدم. يتمتع الذكاء الاصطناعي بالقدرة على اختيار نتيجة أو أكثر كأساس لاستجابتها ، اعتمادًا على طبيعة الاستعلام. هذا هو المكان الذي تظهر فيه القوة الحقيقية لمنظمة العفو الدولية. بدلاً من مجرد إعادة تجديد جميع المعلومات التي يتلقاها ، يحلل الذكاء الاصطناعى البيانات ويقوم بصياغة استجابة مناسبة. إذا تم اعتبار النتيجة من القطع كافية ، فسوف تولد استجابة نصية إيجابية.
للمساعدة في تسهيل تخزين بيانات المتجهات وغيرها من عمليات المتجهات في MongoDB ، أستخدم وحدة Mongoose. إلى حد بعيد ، هذا هو الحل غير المؤلم للتعامل مع التخزين واسترجاع بيانات المتجهات.
أولاً ، نحدد مخططنا:
const memoryVectorsSchema = new mongoose . Schema ( {
chunks : [ {
embedding : [ Number ] ,
text : String
} ]
} ) ثم نرفق وظيفة getScore التي تنفذ تشابه جيب التمام بين نموذج المستند وسلسلة الاستعلام المدمجة:
memoryVectorsSchema . methods . getScore = function getScore ( query_embedding ) {
return this . chunks . map ( ( chunk ) => {
const dot_product = chunk . embedding . reduce ( ( sum , val , i ) => sum + val * query_embedding [ i ] , 0 )
const magnitude = ( vec ) => Math . sqrt ( vec . reduce ( ( sum , val ) => sum + val * val , 0 ) )
const cosine_similarity = dot_product / ( magnitude ( chunk . embedding ) * magnitude ( query_embedding ) )
return { text : chunk . text , score : cosine_similarity }
} )
}بعد ذلك ، نقوم بتجميع مخططنا في نموذج:
const MemoryVectors = mongoose . model ( 'MemoryVectors' , memoryVectorsSchema )لحفظ بيانات المتجه:
const newVector = new MemoryVectors ( {
chunks
} )
newVector . save ( )للبحث في بيانات المتجه المخزنة مع تضمينات استعلام المستخدم
const cosineSimThreshold = 0.3 // Note: for text-embedding-ada-002, 0.72 but for v3 embed models, use 0.3
const maxResults = 10
const maxFilesLength = 2000 * 3
const vectors = await MemoryVectors . find ( )
const vector_result = vectors . map ( ( v ) => {
return v . getScore ( search_query )
} ) . flat ( ) . sort ( ( a , b ) => b . score - a . score ) . filter ( ( chunk ) => chunk . score > cosineSimThreshold ) . slice ( 0 , maxResults )
const query_result = vector_result . length > 0 ? vector_result . map ( ( v ) => v . text ) . join ( "n" ) . slice ( 0 , maxFilesLength ) : ''هذا كل شيء. أنيق ، هاه؟
لتوضيح كيفية كل هذه الأعمال ، دعنا نتحقق من محادثة عينة.
المستخدم: مهلا ، أخبرني ، ما هو برجر الخاص بك؟ الجبن؟ لحم خنزير مقدد؟ فطر؟
الروبوت: أوه ، بالتأكيد! أنا كل شيء عن برجر لحم الخنزير المقدد ، هل تعلم؟ لحم الخنزير المقدد المقرمش يأخذ الأمر إلى مستوى جديد تمامًا. ماذا عنك؟
المستخدم: الفطر! مع الجبن! أوه ، أنا أحب ذلك.
الروبوت: تماما! الفطر والجبن هي مباراة مصنوعة في برجر السماء. هل حاولت صنع الفطر والجبن في المنزل؟ إنه يستحق كل هذا العناء!
المستخدم: ليس بعد. هل جربت الأفوكادو؟
الروبوت: أوه ، تماما! الأفوكادو على برغر مثل الكرز في الأعلى ، هل تعلم؟ ويضيف ذلك الخير الكريمي والزبداني الذي يأخذ كل شيء إلى المستوى التالي. هل جربته؟
المستخدم: نعم. هناك بالفعل قصة مضحكة تتعلق بها. لذلك كان لدي برجر الأفوكادو من قبل. ثم مرة واحدة ، ذهبت أنا وأصدقائي إلى رحلة التزلج. على طول الطريق توقفنا من متجر برغر. بينما كنت أتصفح القائمة ، رأيت الأفوكادو وأشارتها عندما طلبت. هل تعرف ماذا حدث بعد ذلك؟
الروبوت: بأي حال من الأحوال! ماذا حدث؟ هل أعطاك برغر مع ، مثل ، الأفوكادو الكامل عليه؟ سيكون ذلك فرحانًا!
المستخدم: لا. لكن قريب. أعطوني برغر مع شريحة من الأفوكادو. لا شيء آخر. اتضح أن الشخص الذي اخترته في القائمة مخصص لخيار نباتي!
الروبوت: يا إلهي ، هذا مضحك للغاية! لذلك انتهى بك الأمر مع برجر الأفوكادو العادي بدلاً من تلك التي كنت تتوقعها. هذا مثل مزيج مباشرة من فيلم كوميدي! هل انتهى بك الأمر إلى الاستمتاع برجر الأفوكادو ، رغم ذلك؟
المستخدم: أكلت ذلك على ما يرام ولكن لم أكن راضيا ، بالطبع. مهلا ، هل يمكنك حفظ هذه الذاكرة المضحكة ، سيكون من الجيد أن تتذكر أحيانًا
استدعاء الوظيفة (استدعاء Save_New_Memory):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_2TIYJOo9WJcprD7ozQsERoo4' ,
type : 'function' ,
function : {
name : 'save_new_memory' ,
arguments : '{"memory_title":"Avocado Burger Mix-Up","memory_detail":"Ordered an avocado burger at a ski trip burger shop, but got a plain avocado burger instead. Turns out, the one I picked on the menu was the vegan option! Hilarious mix-up.","memory_date":"2023-11-10","memory_tags":["funny","food","burger"]}'
}
}
]
}في هذه المرحلة ، نسمي واجهة برمجة تطبيقات التضمين والحصول على بيانات المتجه للوسائط بأكملها وحفظها مع النص إلى MongoDB.
استجابة الوظيفة:
[ {
tool_call_id : 'call_2TIYJOo9WJcprD7ozQsERoo4' ,
role : 'tool' ,
name : 'save_new_memory' ,
content : '{n "message": "New memory saved"n}'
}
]الروبوت: تم حفظ الذاكرة! الآن لديك قصة مختلطة مضحكة لتنظر إلى الوراء كلما احتجت إلى ضحكة جيدة. إنها مثل شريحة من الحياة الصغيرة ، هل تعلم؟
الآن ، لاسترداد المعلومات من الذاكرة
المستخدم: بالمناسبة ، هل تعرف مفصل البرجر الذي يغلق هذا الشهر؟ واحد في شيبويا؟ ما هو الاسم؟
استدعاء الوظيفة (استدعاء get_calendar_entry):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_3PNs56LMrmDViZUnrxdBScGz' ,
type : 'function' ,
function : { name : 'get_calendar_entry' , arguments : '{"date":"2023-11-30"}' }
}
]
}استجابة الوظيفة:
[ {
tool_call_id : 'call_3PNs56LMrmDViZUnrxdBScGz' ,
role : 'tool' ,
name : 'get_calendar_entry' ,
content : '{n "message": "No entries found"n}'
}
]الروبوت: لم أتمكن من العثور على أي معلومات حول إغلاق مفصل البرجر في شيبويا هذا الشهر. ربما تكون مجرد شائعة ، هل تعلم؟ ولكن إذا اكتشفت الاسم ، فأخبرني!
المستخدم: هل يمكنك التحقق من ذاكرتك ، وأعتقد أنني ربما أخبرتك بذلك من قبل
استدعاء الوظيفة (تم استدعاء get_info_from_memory):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_WVBvjR0MKgaaTzik7QBwk3Ei' ,
type : 'function' ,
function : {
name : 'get_info_from_memory' ,
arguments : '{"search":["burger","Shibuya","closing"]}'
}
}
]
} هنا نحصل على التضمين لمصطلحات البحث بدلاً من استعلام المستخدم الفعلي. ثم باستخدام بيانات المتجه الناتجة ، ندعو mongoDb.searchEntry للبحث في بيانات المتجه المخزنة في MongoDB.
استجابة الوظيفة:
[ {
tool_call_id : 'call_WVBvjR0MKgaaTzik7QBwk3Ei' ,
role : 'tool' ,
name : 'get_info_from_memory' ,
content : '{n' +
' "message": "Retrieved related info for burger,Shibuya,closing",n' +
` "result": "score: 0.8513867910905064\n title: Becker's ハンバーガーショップ閉店 detail: 千葉の人気なバーガーショップ「Becker's」が閉店するらしい。 tags: 閉店,ハンバーガー,千葉\nscore: 0.8506170963709474\n title: トリキバーガーの渋谷店閉店情報 detail: 渋谷のトリキバーガーの店舗が閉店することが決まりました。最後の日は今月の20日です。お店は渋谷の井の頭通りにあります。 date: 2023-11-10 tags: トリキバーガー,渋谷,閉店情報\nscore: 0.8307398914702265\n title: Becker's閉店情報 detail: 千葉の人気なバーガーショップBecker'sが閉店する。最後の日は今月の22日。 date: 2023-11-22 tags: 閉店情報,Becker's...n` +
'}'
}
]أنا أعرض فقط نتيجة جزئية للمحتوى ولكنه يحتوي على عدة مرات من قاعدة البيانات المرتبطة بمصطلحات البحث. نرسل جميع النتائج إلى API لإكمال الدردشة ودع الذكاء الاصطناعي يقرر ما يجب القيام به.
الروبوت: أوه ، صحيح! مفصل البرجر الذي يغلق في Shibuya هو Triki Burger. يتم إغلاق موقع Shibuya في 20 من هذا الشهر. من المحزن أن نراهم يذهبون ، هل تعلم؟
إذا لاحظت ، فإن النتيجة الجزئية المعروضة هي باللغة اليابانية! لذلك ، بالتأكيد ، هذا يدل على أننا لا نقوم فقط بالبحث عن الكلمات الرئيسية البسيطة هنا. يمكنك إضافة بيانات للغة المختلطة والاستعلام عن الذاكرة باستخدام لغة مختلفة وفرص هل ستظل تحصل على الإجابة. دعونا نحاول استخدام روبوتنا الروسي ، Vitaly . سأستخدم ترجمة Google لأنني لا أتحدث الروسية.
المستخدم: виталий ، жжеш вомннить нзоние оих юиыхых картبعض чипсов؟ (Vitaly ، هل يمكنك أن تتذكر اسم رقائق البطاطا المفضلة لدي؟)
استدعاء الوظيفة (تم استدعاء get_info_from_memory):
{ role : 'assistant' ,
content : null ,
tool_calls : [
{
id : 'call_8fXETNlDMuKEXsitUuqt5PlE' ,
type : 'function' ,
function : {
name : 'get_info_from_memory' ,
arguments : '{"search":["картофельные чипсы"]}'
}
}
]
}لذلك ، مرة أخرى ، نحصل على التضمين لمصطلح البحث ونستخدمه مقابل كل بيانات متجه مخزنة في MongoDB.
نتيجة الوظيفة:
[ {
tool_call_id : 'call_8fXETNlDMuKEXsitUuqt5PlE' ,
role : 'tool' ,
name : 'get_info_from_memory' ,
content : '{n' +
' "message": "Retrieved related info for картофельные чипсы",n' +
` "result": "score: 0.7718838658905126\n title: トムさんのお気に入りのパン detail: トムさんのお気に入りのパンはカレーパンとあんドーナツです。カレーパンはスパイシーなカレーとサクサクの生地が絶妙に合い、あんドーナツは甘さともちもち感が魅力です。\nscore: 0.7560670627592779\n title: Crisp Bacon Potato Chips detail: Brand: Dice tags: snack,potato chips,brand\nscore: 0.7354783965092787\n title: あまとうのあんみつ detail: 小樽のあまとうで食べるあんみつは絶品。...n` +
'}'
}
]يمكنك أن ترى أن نتيجة البيانات المعروضة مختلطة ، مع النصوص اليابانية والإنجليزية.
BOT: тви юиые картофелные чипы - رقائق البطاطس لحم الخنزير المقدد. (رقائق البطاطا المفضلة لديك هي رقائق البطاطس لحم الخنزير المقدد.)
حسنًا ، دعنا ننتقل إلى الخطوة الأخيرة.
عند تلقي استجابة النص من الخطوات السابقة ، سنقوم الآن باستدعاء واجهة برمجة تطبيقات النص إلى كلام لتمكين chatbot الخاص بنا من الصخب
try {
const mp3 = await openai . audio . speech . create ( {
model : 'tts-1' ,
voice : 'alloy' ,
input : 'The quick brown fox jumps over the lazy dog.' ,
} )
const buffer = Buffer . from ( await mp3 . arrayBuffer ( ) )
await fs . promises . writeFile ( filename , buffer )
} catch ( error ) {
throw error
} نظرًا لأن Doc لا يوفر وصفًا محددًا لكل صوت ، لذا طلبت من ChatGpt إجراء بعض التخمينات المتعلمة بناءً على أسمائهم:
سبيكة (و): متعددة الاستخدامات ويمكن أن تكون مناسبة للمحتوى التقني أو التعليمي ، مثل البرامج التعليمية أو المواد التعليمية أو أي محتوى يكون فيه الوضوح والدقة ضرورية.
Echo (M): قد يكون مناسبًا لرواية القصص أو الكتب الصوتية أو المحتوى الذي يتطلب نغمة أكثر دراماتيكية أو سردية.
Fable (F): يمكن أن تكون مثالية لقصص الأطفال أو المحتوى الخيالي أو أي سرد يتطلب نغمة أكثر مرحة أو خيالية.
Onyx (M): قد تكون مناسبة لقراءة الأدب الخطيرة ، أو تقديم محتوى الأخبار ، أو أي مادة تتطلب توصيلًا أكثر خطورة أو رسمية.
نوفا (و): مناسبة للإعلانات ، المحتوى التحفيزي ، أو أي مادة تتطلب توصيل إيجابي ومتحمس.
Shimmer (F): مناسبة لمحتوى نمط الحياة ، والبودكاست ، أو أي شيء يتطلب لهجة جذابة وودية وجذابة.
سيتم بعد ذلك حفظ بيانات الصوت التي تم إنشاؤها إلى دليل public/upload بتنسيق ملف MP3. سنرسل بعد ذلك الرابط إلى جانب العميل حيث سيتم تحميل htmlaudioElement الديناميكي وتشغيله.
const audioDomRef = new Audio ( )
audioDomRef . type = 'audio/mpeg'
audioDomRef . addEventListener ( 'loadedmetadata' , handleAudioLoad )
audioDomRef . addEventListener ( 'ended' , handleAudioEnded )
audioDomRef . addEventListener ( 'error' , handleAudioError )
audioDomRef . src = audio_url
function handleAudioLoad ( ) {
audioDomRef . value . play ( )
}يرجى ملاحظة أنه سيتم تعطيل تسجيل الصوت أثناء تشغيل الاستجابة الصوتية. هذا يضمن أن التطبيق لا يسجل استجابة الروبوت.
يتم استخدام FFMPEG لإزالة الأجزاء الصامتة في ملف الصوت.
لتثبيت أداة سطر الأوامر FFMPEG
# on Ubuntu or Debian
sudo apt update && sudo apt install ffmpeg
# on Arch Linux
sudo pacman -S ffmpeg
# on MacOS using Homebrew (https://brew.sh/)
brew install ffmpeg
# on Windows using Chocolatey (https://chocolatey.org/)
choco install ffmpeg
# on Windows using Scoop (https://scoop.sh/)
scoop install ffmpeg
سيتم استخدام MongoDB لتخزين إدخالات التقويم وبيانات المتجهات لوظيفة الذاكرة طويلة الأجل.
لتثبيت MongoDB Community Edition ، يرجى التحقق من هذه الصفحة. قد ترغب أيضًا في تثبيت MongoDB Shell لعرض قاعدة البيانات.
أولاً ، تأكد من تثبيت FFMPEG و MongoDB في نظامك.
لاستنساخ مستودع المشروع وتثبيت التبعيات
$ git clone https://github.com/supershaneski/openai-whisper-talk.git myproject
$ cd myproject
$ npm install copy .env.example file وقم بإعادة تسمية إلى .env ، ثم افتحه وقم بتحرير العناصر هناك بالقيم الفعلية. بالنسبة لعناصر MongoDB ، ربما لا تحتاج إلى تحريرها إلا إذا كان لديك إعداد مختلف.
NUXT_OPENAI_API_KEY=your-openai-api-key
NUXT_MONGODB_HOST_NAME=localhost
NUXT_MONGODB_PORT=27017
NUXT_MONGODB_DB_NAME=embeddingvectorsdb
ثم لتشغيل التطبيق
$ npm run dev افتح المتصفح الخاص بك إلى http://localhost:5000/ (رقم المنفذ يعتمد على التوفر) لتحميل صفحة التطبيق.
ملاحظة: لم أختبر هذا بعد مع آخر تحديث
قد ترغب في تشغيل هذا التطبيق باستخدام بروتوكول https . هذا مطلوب لتمكين التقاط الصوت باستخدام جهاز منفصل مثل الهاتف الذكي.
من أجل القيام بذلك ، قم بإعداد certificate المناسبة والملفات key وتحرير server.mjs في دليل الجذر.
ثم buid المشروع
$ npm run buildأخيرًا ، قم بتشغيل التطبيق
$ node server.mjs الآن ، افتح متصفحك على https://localhost:3000/ (رقم المنفذ يعتمد على التوفر) أو استخدم عنوان IP المحلي لتحميل الصفحة.
يمكنك العثور على هذه المتغيرات في صفحات/نقاش/[معرف]. ↩ ↩ 2
إذا كان حسب التاريخ ، يجب أن يكون العنصر واحد فقط تحت التاريخ وإلا سيتم إرجاع خطأ. ↩