Alat dan Plugin
Dokumen ini menunjukkan cara membuat plugin kustom untuk LibreChat dengan memperluas kelas `Tool` LangChain. Anda akan mempelajari cara menggunakan berbagai API dan fungsi dengan plugin Anda, serta cara mengintegrasikannya dengan kerangka kerja LangChain.
Halaman ini sudah tidak digunakan lagi. Silakan merujuk ke Panduan Agen untuk informasi terkini mengenai penggunaan alat.
Sangat disarankan untuk menggunakan Model Context Protocol atau OpenAPI Actions untuk mengintegrasikan alat kustom.
Membuat Tools/Plugins Anda sendiri
Peringatan
Silakan merujuk ke alat terbaru yang digunakan dengan asisten di api/app/clients/tools/structured/ karena plugin akan segera tidak digunakan lagi dan digantikan oleh tools dalam waktu dekat.
Membuat plugin kustom untuk proyek ini melibatkan perluasan kelas Tool dari modul langchain/tools.
Catatan: Saya akan menggunakan kata plugin secara bergantian dengan tool, karena istilah terakhir bersifat spesifik untuk LangChain, dan kami terutama menyesuaikan dengan pustaka tersebut.
Anda pada dasarnya membuat DynamicTools dalam istilah LangChain. Lihat dokumentasi LangChainJS untuk info lebih lanjut.
Panduan ini akan memandu Anda melalui proses pembuatan plugin kustom Anda sendiri, dengan menggunakan alat StableDiffusionAPI dan WolframAlphaAPI sebagai contoh.
Saat menggunakan Functions Agent (mode default untuk plugin), alat (tools) dikonversi menjadi OpenAI functions; dalam hal apa pun, plugin/alat dipanggil secara kondisional berdasarkan LLM yang menghasilkan format spesifik yang kami urai (parse).
Implementasi plugin yang paling umum adalah melakukan panggilan API berdasarkan input bahasa alami dari AI, namun hampir tidak ada batasan dalam kasus penggunaan secara terprogram.
Poin-Poin Penting
Berikut adalah poin-poin penting untuk membuat plugin Anda sendiri:
1. Impor Modul yang Diperlukan: Impor modul yang diperlukan untuk plugin Anda, termasuk kelas Tool dari langchain/tools dan modul lain apa pun yang mungkin dibutuhkan oleh plugin Anda.
2. Definisikan Kelas Plugin Anda: Definisikan kelas untuk plugin Anda yang memperluas kelas Tool. Atur properti name dan description di dalam konstruktor. Jika plugin Anda memerlukan kredensial atau variabel lain, atur variabel tersebut dari parameter fields atau dari metode yang mengambilnya dari lingkungan proses Anda. Catatan: jika plugin Anda memerlukan instruksi yang panjang dan mendetail, Anda dapat menambahkan properti description_for_model dan membuat description menjadi lebih umum.
3. Definisikan Metode Pembantu: Definisikan metode pembantu di dalam kelas Anda untuk menangani tugas-tugas tertentu jika diperlukan.
4. Implementasikan Metode _call: Implementasikan metode _call di mana fungsionalitas utama plugin Anda didefinisikan. Metode ini dipanggil ketika model bahasa memutuskan untuk menggunakan plugin Anda. Metode ini harus menerima parameter input dan mengembalikan hasil. Jika terjadi kesalahan, fungsi harus mengembalikan string yang merepresentasikan kesalahan, alih-alih melempar error. Jika plugin Anda memerlukan beberapa input dari LLM, baca bagian StructuredTools.
5. Ekspor Plugin Anda dan Impor ke handleTools.js: Ekspor plugin Anda dan impor ke dalam handleTools.js. Tambahkan plugin Anda ke objek toolConstructors di dalam fungsi loadTools. Jika plugin Anda memerlukan inisialisasi yang lebih lanjut, tambahkan ke objek customConstructors.
6. Ekspor Plugin Anda ke index.js: Ekspor plugin Anda ke index.js di bawah tools. Tambahkan plugin Anda ke module.exports dari index.js, sehingga Anda juga perlu mendeklarasikannya sebagai const di dalam file ini.
7. Tambahkan Plugin Anda ke manifest.json: Tambahkan plugin Anda ke manifest.json. Ikuti format ketat untuk setiap bidang dari objek "plugin". Jika plugin Anda memerlukan autentikasi, tambahkan detail tersebut di bawah authConfig sebagai array. pluginKey harus sesuai dengan name kelas dari kelas Tool yang Anda buat, dan properti authField harus sesuai dengan nama variabel process.env.
Ingat, kunci untuk membuat plugin kustom adalah dengan memperluas kelas Tool dan mengimplementasikan metode _call. Metode _call adalah tempat Anda menentukan apa yang dilakukan plugin Anda. Anda juga dapat menentukan metode dan properti pembantu di dalam kelas Anda untuk mendukung fungsionalitas plugin Anda.
Catatan: Anda dapat menemukan semua file yang disebutkan dalam panduan ini di folder .\api\app\langchain\tools.
StructuredTools
Plugin Multi-Input
Jika Anda ingin membuat plugin yang memanfaatkan banyak input dari LLM, alih-alih satu string input seperti yang akan kita tinjau, Anda perlu membuat StructuredTool LangChain sebagai gantinya. Panduan mendetail untuk hal ini sedang dalam proses, namun untuk saat ini, Anda dapat melihat bagaimana saya membuat StructuredTools di direktori ini: api\app\clients\tools\structured\. Panduan ini merupakan dasar untuk memahami StructuredTools, dan disarankan agar Anda terus membaca untuk memahami alat LangChain terlebih dahulu. Blog yang ditautkan di atas juga bermanfaat setelah Anda membaca panduan ini.
Langkah 1: Impor Modul yang Diperlukan
Mulailah dengan mengimpor modul yang diperlukan. Ini akan mencakup kelas Tool dari langchain/tools dan modul lain apa pun yang mungkin dibutuhkan oleh alat Anda. Sebagai contoh:
const { Tool } = require('langchain/tools')
// ... whatever else you needLangkah 2: Definisikan Kelas Tool Anda
Selanjutnya, definisikan kelas untuk plugin Anda yang memperluas kelas Tool. Kelas tersebut harus memiliki konstruktor yang memanggil metode super() dan menetapkan properti name dan description. Properti-properti ini akan digunakan oleh model bahasa untuk menentukan kapan harus memanggil alat Anda dan dengan parameter apa.
Penting: Anda harus mengatur kredensial/variabel yang diperlukan dari parameter fields, atau sebagai alternatif dari metode yang mengambilnya dari lingkungan proses Anda
class StableDiffusionAPI extends Tool {
constructor(fields) {
super();
this.name = 'stable-diffusion';
this.url = fields.SD_WEBUI_URL || this.getServerURL(); // <--- important!
this.description = `You can generate images with 'stable-diffusion'. This tool is exclusively for visual content...`;
}
...
}Opsional: Mulai v0.5.8, saat menggunakan Functions, Anda dapat menambahkan instruksi yang lebih panjang dan mendetail dengan properti description_for_model. Saat melakukannya, disarankan agar Anda membuat properti description lebih umum untuk mengoptimalkan token. Setiap baris dalam properti ini diawali dengan // untuk mencerminkan bagaimana prompt dibuat untuk ChatGPT (chat.openai.com). Format ini lebih selaras dengan rekayasa prompt plugin resmi ChatGPT.
// ...
this.description_for_model = `// Generate images and visuals using text with 'stable-diffusion'.
// Guidelines:
// - ALWAYS use {{"prompt": "7+ detailed keywords", "negative_prompt": "7+ detailed keywords"}} structure for queries.
// - Visually describe the moods, details, structures, styles, and/or proportions of the image. Remember, the focus is on visual attributes.
// - Craft your input by "showing" and not "telling" the imagery. Think in terms of what you'd want to see in a photograph or a painting.
// - Here's an example for generating a realistic portrait photo of a man:
// "prompt":"photo of a man in black clothes, half body, high detailed skin, coastline, overcast weather, wind, waves, 8k uhd, dslr, soft lighting, high quality, film grain, Fujifilm XT3"
// "negative_prompt":"semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime, out of frame, low quality, ugly, mutation, deformed"
// - Generate images only once per human query unless explicitly requested by the user`
this.description =
"You can generate images using text with 'stable-diffusion'. This tool is exclusively for visual content."
// ...Di dalam konstruktor, perhatikan bahwa kita mengambil variabel sensitif baik dari objek fields atau dari metode getServerURL yang kita definisikan untuk mengakses variabel lingkungan.
this.url = fields.SD_WEBUI_URL || this.getServerURL()Setiap kredensial yang diperlukan diteruskan melalui fields saat pengguna menyediakannya dari frontend; jika tidak, admin dapat "mengotorisasi" plugin untuk semua pengguna melalui variabel lingkungan. Semua kredensial yang diteruskan dari frontend dienkripsi.
// It's recommended you follow this convention when accessing environment variables.
getServerURL() {
const url = process.env.SD_WEBUI_URL || '';
if (!url) {
throw new Error('Missing SD_WEBUI_URL environment variable.');
}
return url;
}Langkah 3: Definisikan Metode Pembantu
Anda dapat mendefinisikan metode pembantu (helper methods) di dalam kelas Anda untuk menangani tugas-tugas tertentu jika diperlukan. Sebagai contoh, kelas StableDiffusionAPI menyertakan metode seperti replaceNewLinesWithSpaces, getMarkdownImageUrl, dan getServerURL untuk menangani berbagai tugas.
class StableDiffusionAPI extends Tool {
...
replaceNewLinesWithSpaces(inputString) {
return inputString.replace(/\r\n|\r|\n/g, ' ');
}
...
}Langkah 4: Implementasikan Metode _call
Metode _call adalah tempat di mana fungsionalitas utama plugin Anda diimplementasikan. Metode ini dipanggil ketika model bahasa memutuskan untuk menggunakan plugin Anda. Metode ini harus menerima parameter input dan mengembalikan sebuah hasil.
Dalam Tool dasar, LLM akan menghasilkan satu nilai string sebagai input. Jika plugin Anda memerlukan beberapa input dari LLM, baca bagian StructuredTools.
class StableDiffusionAPI extends Tool {
...
async _call(input) {
// Your tool's functionality goes here
...
return this.result;
}
}Penting: Fungsi _call adalah fungsi yang sebenarnya akan dipanggil oleh agen. Ketika terjadi kesalahan, fungsi tersebut sebaiknya, jika memungkinkan, mengembalikan string yang merepresentasikan kesalahan, alih-alih melempar (throwing) error. Hal ini memungkinkan kesalahan tersebut diteruskan ke LLM dan LLM dapat memutuskan cara menanganinya. Jika error dilempar, maka eksekusi agen akan berhenti.
Langkah 5: Ekspor Plugin Anda dan impor ke dalam handleTools.js
Proses ini akan sedikit diotomatisasi di masa mendatang, selama Anda memiliki plugin/tool Anda di api\app\langchain\tools
// Export
module.exports = StableDiffusionAPI/* api\app\langchain\tools\handleTools.js */
const StableDiffusionAPI = require('./StableDiffusion');
...Di handleTools.js, temukan awal fungsi loadTools dan tambahkan plugin/tool Anda ke objek toolConstructors.
const loadTools = async ({ user, model, tools = [], options = {} }) => {
const toolConstructors = {
calculator: Calculator,
google: GoogleSearchAPI,
wolfram: WolframAlphaAPI,
'dall-e': OpenAICreateImage,
'stable-diffusion': StableDiffusionAPI // <----- Newly Added. Note: the key is the 'name' provided in the class.
// We will now refer to this name as the `pluginKey`
};Jika kelas Tool Anda memerlukan inisialisasi yang lebih lanjut, Anda harus menambahkannya ke objek customConstructors.
Inisialisasi default dapat dilihat pada fungsi loadToolWithAuth, dan sebagian besar plugin kustom harus diinisialisasi dengan cara ini.
Berikut adalah beberapa customConstructors, yang memiliki inisialisasi yang berbeda-beda
const customConstructors = {
browser: async () => {
let openAIApiKey = process.env.OPENAI_API_KEY
if (!openAIApiKey) {
openAIApiKey = await getUserPluginAuthValue(user, 'OPENAI_API_KEY')
}
return new WebBrowser({ model, embeddings: new OpenAIEmbeddings({ openAIApiKey }) })
},
// ...
plugins: async () => {
return [
new HttpRequestTool(),
await AIPluginTool.fromPluginUrl(
'https://www.klarna.com/.well-known/ai-plugin.json',
new ChatOpenAI({ openAIApiKey: options.openAIApiKey, temperature: 0 }),
),
]
},
}Langkah 6: Ekspor Plugin Anda ke index.js
Temukan index.js di bawah api/app/clients/tools. Anda perlu memasukkan plugin Anda ke dalam module.exports, agar dapat dikompilasi, Anda juga perlu mendeklarasikan plugin Anda sebagai consts:
const StructuredSD = require('./structured/StableDiffusion');
const StableDiffusionAPI = require('./StableDiffusion');
...
module.exports = {
...
StableDiffusionAPI,
StructuredSD,
...
}Langkah 7: Tambahkan Plugin Anda ke manifest.json
Proses ini akan sedikit diotomatisasi di masa mendatang bersama dengan langkah 5, selama Anda memiliki plugin/tool Anda di api\app\langchain\tools, dan plugin Anda dapat diinisialisasi dengan metode default
{
"name": "Calculator",
"pluginKey": "calculator",
"description": "Perform simple and complex mathematical calculations.",
"icon": "https://i.imgur.com/RHsSG5h.png",
"isAuthRequired": "false",
"authConfig": []
},
{
"name": "Stable Diffusion",
"pluginKey": "stable-diffusion",
"description": "Generate photo-realistic images given any text input.",
"icon": "https://i.imgur.com/Yr466dp.png",
"authConfig": [
{
"authField": "SD_WEBUI_URL",
"label": "Your Stable Diffusion WebUI API URL",
"description": "You need to provide the URL of your Stable Diffusion WebUI API. For instructions on how to obtain this, see <a href='url'>Our Docs</a>."
}
]
},Setiap kolom pada objek "plugin" adalah penting. Ikuti format ini dengan ketat. Jika plugin Anda memerlukan autentikasi, Anda akan menambahkan detail tersebut di bawah authConfig sebagai array karena mungkin terdapat beberapa variabel autentikasi. Lihat plugin Calculator untuk contoh yang tidak memerlukan autentikasi, di mana authConfig berupa array kosong (array selalu diperlukan).
Catatan: seperti yang disebutkan sebelumnya, pluginKey harus sama dengan name kelas dari kelas Tool yang Anda buat.
Catatan: properti authField harus sama dengan nama variabel process.env.
Catatan: entri authConfig dapat menyertakan sensitive. Hilangkan atau atur ke true untuk kunci API dan rahasia. Atur sensitive: false untuk nilai pengaturan yang bukan rahasia seperti URL, nama pengguna, nama deployment, atau ID proyek agar UI merender kolom teks biasa alih-alih input rahasia.
Berikut adalah contoh plugin dengan lebih dari satu variabel kredensial
[
{
"name": "Google",
"pluginKey": "google",
"description": "Use Google Search to find information about the weather, news, sports, and more.",
"icon": "https://i.imgur.com/SMmVkNB.png",
"authConfig": [
{
"authField": "GOOGLE_CSE_ID",
"label": "Google CSE ID",
"description": "This is your Google Custom Search Engine ID. For instructions on how to obtain this, see <a href='https://github.com/danny-avila/LibreChat/blob/main/docs/features/plugins/google_search.md'>Our Docs</a>.",
"sensitive": false
},
{
"authField": "GOOGLE_SEARCH_API_KEY",
"label": "Google API Key",
"description": "This is your Google Custom Search API Key. For instructions on how to obtain this, see <a href='https://github.com/danny-avila/LibreChat/blob/main/docs/features/plugins/google_search.md'>Our Docs</a>.",
"sensitive": true
}
]
},Contoh: Tool WolframAlphaAPI
Berikut adalah contoh lain dari alat kustom, yaitu alat WolframAlphaAPI. Alat ini menggunakan modul axios untuk membuat permintaan HTTP ke Wolfram Alpha API.
const axios = require('axios')
const { Tool } = require('langchain/tools')
class WolframAlphaAPI extends Tool {
constructor(fields) {
super()
this.name = 'wolfram'
this.apiKey = fields.WOLFRAM_APP_ID || this.getAppId()
this.description = `Access computation, math, curated knowledge & real-time data through wolframAlpha...`
}
async fetchRawText(url) {
try {
const response = await axios.get(url, { responseType: 'text' })
return response.data
} catch (error) {
console.error(`Error fetching raw text: ${error}`)
throw error
}
}
getAppId() {
const appId = process.env.WOLFRAM_APP_ID || ''
if (!appId) {
throw new Error('Missing WOLFRAM_APP_ID environment variable.')
}
return appId
}
createWolframAlphaURL(query) {
const formattedQuery = query.replaceAll(/`/g, '').replaceAll(/\n/g, ' ')
const baseURL = 'https://www.wolframalpha.com/api/v1/llm-api'
const encodedQuery = encodeURIComponent(formattedQuery)
const appId = this.apiKey || this.getAppId()
const url = `${baseURL}?input=${encodedQuery}&appid=${appId}`
return url
}
async _call(input) {
try {
const url = this.createWolframAlphaURL(input)
const response = await this.fetchRawText(url)
return response
} catch (error) {
if (error.response && error.response.data) {
console.log('Error data:', error.response.data)
return error.response.data
} else {
console.log(`Error querying Wolfram Alpha`, error.message)
return 'There was an error querying Wolfram Alpha.'
}
}
}
}
module.exports = WolframAlphaAPIDalam contoh ini, kelas WolframAlphaAPI memiliki metode pembantu seperti fetchRawText, getAppId, dan createWolframAlphaURL untuk menangani tugas-tugas tertentu. Metode _call melakukan permintaan HTTP ke API Wolfram Alpha dan mengembalikan responsnya.
Bagaimana panduan ini?