掘金 人工智能 04月01日 18:18
Terminal里的ChatGPT:用80行代码实现带记忆的智能对话流
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了如何利用JavaScript和阿里云通义千问模型,在本地命令行环境中构建一个功能完善的AI聊天机器人。文章从基础的API调用入手,逐步讲解了实现持续对话、记忆功能和流式输出的技巧,最终完成一个实用的小工具。通过代码示例和详细解释,读者可以轻松掌握构建此类应用的核心技术,体验AI技术带来的便利。

🔑 基础对话实现:文章首先展示了如何通过HTTP接口调用阿里云通义千问模型,实现命令行环境下的基础一问一答功能。这为后续功能的实现奠定了基础。

🔄 持续对话功能:为了提升用户体验,文章通过递归方法实现了持续对话功能,允许用户与AI进行多轮交互。同时,通过输入“exit”命令,用户可以随时结束对话。

🧠 记忆功能的实现:为了让AI能够记住之前的对话内容,文章介绍了如何将对话历史记录添加到API请求中。通过维护一个history数组,AI可以记住之前的对话,从而提供更连贯的回复。

💬 流式输出的实现:文章探讨了如何实现流式输出,即让AI的回复像在线服务一样逐字显示。通过设置API的“stream”参数,并处理返回的数据流,可以实现这一效果,提升用户交互体验。

阅读完本文,你可以轻松实现一个在本地可以运行的小工具: 一个可以随时在命令行唤醒的 AI 聊天机器人

    它足够简单命令行直接唤起支持记忆功能支持流式输出

前置知识要求:

    知道如何使用命令行本文基于 JavaScript 实现,如果您具备基本的 JavaScript 知识更好

先来看一下最终的实现效果:

1. 实现基础对话

本文基于阿里云提供的通义千问模型能力来实现,其他模型同理。

首先,你需要去官网申请一个 api-key,跟着链接文档操作即可,不麻烦,前期都是免费的。

接下来,我选中一个模型,示例一下如何调接口。

阿里云-模型广场

API 示例,我们看到示例里有 http 调用,比较直观。

我们来测试一把 ai 接口调用,这是一个最基础的版本:

const readline = require('readline');const axios = require('axios');const headers = {  'Content-Type': 'application/json',     'Authorization': 'Bearer $DASHSCOPE_API_KEY'};const rl = readline.createInterface({  input: process.stdin,  output: process.stdout});rl.question('You: ', async (input) => {  const messages = [    { "role": "system", "content": "You are a helpful assistant." },    { "role": "user", "content": input }  ];  const response = await axios.post('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {    "model": "qwen-turbo",    "messages": messages  }, { headers });  console.log('AI: ', response.data.choices[0].message.content);  rl.close();});

如图所示,我们第一个版本实现了基础版的一问一答。

这里我们知道了最基础的 ai 调用方式,就是调 http 接口。

2. 可持续对话

如果每次都一问一答就结束对话,有点麻烦,我们改造一下让他可持续对话:

const readline = require('readline');const axios = require('axios');const headers = {    'Content-Type': 'application/json',          'Authorization': 'Bearer $DASHSCOPE_API_KEY'};const rl = readline.createInterface({    input: process.stdin,    output: process.stdout});function startChat() {    rl.question('You: ', async (input) => {        if (input === 'exit') {            console.log('Goodbye!');            rl.close();            return;        }        const messages = [            { "role": "system", "content": "You are a helpful assistant." },            { "role": "user", "content": input }        ];            const response = await axios.post('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {            "model": "qwen-turbo",            "messages": messages        }, { headers });        console.log('\n\nAI: ', response.data.choices[0].message.content, '\n\n');        startChat();    });}startChat();

这里我们使用了递归,每次 ai 回答完,我们需要再次进入对话过程;

我们实现了持续对话的能力,并且可以通过 exit 来退出当前对话。

但是有一个明显的问题,ai 并不记得我们之前的对话。

3. 保持记忆

我想实现让 ai 保持记忆,该怎么做?

我们可以观察一下之前调用接口的入参:

const messages = [    { "role": "system", "content": "You are a helpful assistant." },    { "role": "user", "content": input }];

我们只需要把对话历史都添加进来,ai 就知道之前聊了些什么。

这里我们维护一个 history 数组即可。

const history = [];function startChat() {    rl.question('You: ', async (input) => {        if (input === 'exit') {            console.log('Goodbye!');            rl.close();            return;        }        const messages = [            { "role": "system", "content": "You are a helpful assistant." },            ...history,            { "role": "user", "content": input }        ];            const response = await axios.post('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {            "model": "qwen-turbo",            "messages": messages        }, { headers });        const aiResponse = response.data.choices[0].message.content;        console.log('\n\nAI: ', aiResponse, '\n\n');        history.push({ "role": "user", "content": input });        history.push({ "role": "assistant", "content": aiResponse });        startChat();    });}startChat();

对话测试结果如下:

4. 流式输出

如果你已经到了这一步,你会发现输出好像有点慢,这是因为每次要等待 ai 回答完才能一次性输出。

如何能想在线服务那样一个字一个字敲出答案(流式输出)?

这个优化点我大概清楚接口支持 "stream" 返回,但是具体怎么改造我也不知道,于是我问了 ai

惊喜的是,ai 一把帮我改造好了,测试运行效果符合预期。(截图为 Trae 编辑器)

最终代码如下:

const readline = require('readline');const axios = require('axios');const headers = {    'Content-Type': 'application/json',          'Authorization': 'Bearer $DASHSCOPE_API_KEY'};const rl = readline.createInterface({    input: process.stdin,    output: process.stdout});const history = [];function startChat() {    rl.question('You: ', async (input) => {        if (input === 'exit') {            console.log('Goodbye!');            rl.close();            return;        }        const messages = [            { "role": "system", "content": "You are a helpful assistant." },            ...history,            { "role": "user", "content": input }        ];                    try {            const response = await axios.post('https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', {                "model": "qwen-turbo",                "messages": messages,                "stream": true              }, {                 headers,                responseType: 'stream'             });                        let aiResponse = '';            process.stdout.write('\n\nAI: ');                                    response.data.on('data', (chunk) => {                const lines = chunk.toString().split('\n').filter(line => line.trim() !== '');                                for (const line of lines) {                    if (line.startsWith('data: ')) {                        const data = line.substring(6);                        if (data === '[DONE]') return;                                                try {                            const parsed = JSON.parse(data);                            const content = parsed.choices[0]?.delta?.content || '';                            if (content) {                                process.stdout.write(content);                                aiResponse += content;                            }                        } catch (e) {                            console.error('解析响应出错:', e);                        }                    }                }            });                        response.data.on('end', () => {                console.log('\n\n');                history.push({ "role": "user", "content": input });                history.push({ "role": "assistant", "content": aiResponse });                startChat();            });                        response.data.on('error', (err) => {                console.error('流处理错误:', err);                startChat();            });        } catch (error) {            console.error('请求错误:', error.message);            startChat();        }    });}startChat();

5. 总结

本文我们实现了一个运行在本地的命令行 AI chatbot

    如何调用 ai?其实就是通过调用 http 接口实现如何保持记忆?就是把历史数据都传给他,同时我们也知道了入参格式如何流式输出?接口支持 stream 返回,每次把返回答案片段拼接输出即可;

基于上述,我们已经实打实地做了一款 AI 实用工具,尽管不完美,但是 AI 离我们更近了一步不是么?

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AI聊天机器人 命令行工具 JavaScript 通义千问 流式输出
相关文章