新手小白如何使用Laf免费接入Claude,并快速拥有一个属于自己的AI助手

慈云数据 2024-03-13 技术支持 43 0

一、关于Claude

1.什么是Claude?

Claude是一款人工智能聊天机器人。它可以像朋友一样和你自然地互动聊天。和Claude聊天体验很像跟人聊天,你可以讨论任何话题,问各种各样的问题。Claude会尽量理解你说的每一句话,并给出合适的回复。相比之下,Chat GPT是一个开源的对话模型,主要用于生成对话的回复内容。和Chat GPT聊天感觉更像在和一个自动回复机器聊天,它给出的回复不太个性化,也不会真正理解语义。而Claude是一个完整的人工智能对话系统,专注于提供更富有个性的沟通交互体验。

2.Claude和Chat GPT的区别

  • 理解力:Claude具有较强的语言理解能力,能理解上下文和语句意思,提取关键信息。Chat GPT主要依靠统计学习,对语义理解较为薄弱。

  • 知识量:Claude有一个较为广泛的知识图谱,包括常识、词汇等,以帮助理解语言和回答问题。Chat GPT主要依靠预训练的语言模型,知识面相对有限。

  • 个性化:Claude的回复更加个性化,可以根据聊天内容和上下文作出恰当的回应和提问。Chat GPT的回复比较固定和非个性化,缺乏连贯性。

  • 交互体验:和Claude的对话更像人与人的自然交流,有问有答,可以交换多个轮次。而Chat GPT更类似自动问答,一问一答,交互体验稍显生硬。

    总之,尽管两者都是人工智能对话技术产品,但Claude在理解力、知识量、个性化和交互体验等方面都优于Chat GPT,可以提供更加近似人的沟通互动体验。但无论哪一种技术,人机交互还需要继续进步和提高。

    二、接入Claude前的准备

    1.注册Slack

    Slack是一个工作效率管理平台,让每个人都能够使用无代码自动化和 AI 功能,还可以无缝连接搜索和知识共享,并确保团队保持联系和参与。在世界各地,Slack 不仅受到公司的信任,同时也是人们偏好使用的平台。目前市面上使用Claude的方式都是通过Slack接入。

    注册地址:https://slack.com/get-started#/createnew

    注意:注册时尽量使用谷歌邮箱,这样后续操作的成功率高,不会因为各种各样的问题导致无法使用Claude。

    2.创建工作区

    注册成功之后我们首先需要创建一个工作区,工作区是一个独立的协作环境,每个工作区有自己的渠道(Channels)、成员、权限设置等,不同工作区之间彼此隔离,成员和资源不共享。

     然后填写“工作区名称”,点击“下一步”

     输入姓名,上传照片(选填),继续点击“下一步”

     接着,输入频道名称,继续点击“下一步”

     添加成功后便会出现如下界面:

     3.添加Claude应用到工作区(此步骤需要魔法)

    点击左侧菜单【浏览Slack】下的【应用】,如下图:

     在应用列表中搜索Claude,并点击“添加”

     点击“了解更多”,并授权添加Claude到Slack

    说明:出现以上界面说明当前ip被封锁了,需要使用魔法上网(代理地区建议选择US),并使用全局代理或切换无痕浏览等方法。

    切换正确的地区之后点击 “Add to Slask”将会出现以下界面,然后点击“允许”按钮

     4.开通高级功能

    回到工作区主界面,在左侧中会自动出现Claude应用,此时跟Claude聊天会发现它是不会回复任何消息的

     然后点击左侧菜单【更多】-【Slack Connect】

     接着点击“创建频道”,如下图:

     注意:若出现以下界面,没有“开始免费试用”的按钮,建议重新创建一个工作区(从第二步开始重新来一遍)

     正常界面如下图,点击“开始免费试用”按钮,这里不需要输入任何的信用卡等的信息

     创建一个频道,随便输入一个名称,然后点击“下一步”

     完成后会在左侧菜单中出现刚才新建的频道,如下图:

     接下来,我们为这个频道添加Claude应用

     然后,选择刚刚创建的频道

     完成之后,在左侧菜单中,选择我们刚刚创建的频道,@Claude 发送任意消息,首次@会触发一个申请,点一下“Agree”即可

     完成以上操作后,就相当于拥有了一个跟ChatGPT一样聪明的人工智能AI助手。同时也可以在Slack内通过@Claude的方式跟他进行对话,如下图:

     5.获取Token和授权

    进入Slack API官网,地址:https://api.slack.com/,然后在顶部右上角的“Your apps ”处点击“Create your first app”,如下图:

     然后进入界面后,点击“Create an App”,接着选择“From scratch”

     然后输入“App Name”,选择前面创建的工作空间,再点击“Create App”

     创建成功之后,在左侧的菜单中找到【OAuth & Permissions】,然后在页面中间往下滑找到【User Token Scopes】,点击"Add an OAuth Scopes"按钮,依次搜索添加以下权限:

    channels:history
    channels:read
    channels:write
    groups:history
    groups:read
    groups:write
    chat:write
    im:history
    im:write
    mpim:history
    mpim:write

     一共11个权限,添加完成之后。回到顶部【OAuth Tokens for Your Workspace】栏,点击【Install to Workspace】,然后确认授权即可

     至此,便拿到了App的Token,将其复制出来(后面有用)

     接着,回到工作区,点击左侧菜单中的【Claude】,获取Claude ID,如下图:

     三、在Laf中接入Claude

    登录Laf云开发平台 https://laf.dev/,在应用列表中选择一个应用后点击【开发】按钮,进入 Laf 应用开发 IDE

     1. 添加NPM依赖

    点击左下角【NPM依赖】处的“+”按钮,在弹框中搜索“claude-api-slack”,选中第一个后再点击“保存并重启”,等待3秒左右依赖会添加完成

     2. 添加函数

    点击左上角【函数列表】处的“+”按钮,在弹框中输入函数名(比如:claude-func),其他默认,完成后点击“确认”按钮,等待3秒左右函数会添加完成

     3.云函数完整代码

    云函数“claude-func”的完整代码如下:

    import cloud from '@lafjs/cloud'
    export default async function (ctx: FunctionContext) {
      // 接收一个question,和一个可选的上下文id:conversationId
      const { question, conversationId } = ctx.body;
      //参数校验
      if (!question) {
        return resultData(-1, '参数question不能为空!');
      }
      return await askCluadeAPi(question, conversationId);
    }
    async function askCluadeAPi(question, conversationId) {
      // slack应用的token
      const token = 'xxx-xxxxxx';
      // claude的ID
      const bot = 'xxx';
      // 注意,这里是频道名称,不是频道id,开头不用加#
      const chatId = 'channel_name';
      try {
        // 初始化claude
        const { Authenticator } = await import('claude-api-slack');
        // 通过缓存保存客户端,可以避免每次提问都是在新会话
        let claudeClient = cloud.shared.get('claudeClient');
        if (!claudeClient) {
          claudeClient = new Authenticator(token, bot);
          cloud.shared.set('claudeClient', claudeClient);
        }
        // 创建频道并返回房间ID:channel
        const channel = await claudeClient.newChannel(chatId);
        let result;
        if (conversationId) {
          result = await claudeClient.sendMessage({
            text: question,
            channel,
            conversationId,
            onMessage: (originalMessage) => {
              console.log("loading", originalMessage);
            }
          });
        } else {
          result = await claudeClient.sendMessage({
            text: question,
            channel,
            onMessage: (originalMessage) => {
              // console.log("loading", originalMessage)
              console.log("loading", originalMessage);
            }
          });
        }
        console.log("success", result);
        let data = {
          answer: result.text,
          conversationId: result.conversationId
        };
        return resultData(0, '成功!', data);
      }
      catch (e) {
        console.log('出现异常', e.message);
        return resultData(-1, '出现异常:' + e.message);
      }
    }
    //返回结果数据
    async function resultData(code = -1, msg = '', data = null) {
      return { code, msg, data }
    }

    点击发布后使用Apipost调用结果如下:

     四、前端整合

    前端技术栈:vue + element plus

    1.创建vue项目

    这里使用vue脚手架创建项目,运行以下命令来创建一个新项目:

    vue create hello-world

    2.安装element plus依赖并引入

    建议使用包管理器(如 NPM、Yarn 或 pnpm)安装 Element Plus

    # NPM
    $ npm install element-plus --save
    # Yarn
    $ yarn add element-plus
    # pnpm
    $ pnpm install element-plus

    官网:https://element-plus.gitee.io/zh-CN/guide/installation.html 

    安装完成后在main.js中引入组件

    import { createApp } from 'vue'
    import App from './App.vue'
    import router from './router'
    import store from './store'
    // 引入element-plus
    import ElementPlus from 'element-plus'
    import * as ElIcons from '@element-plus/icons-vue'
    import 'element-plus/dist/index.css'
    import locale from 'element-plus/lib/locale/lang/zh-cn'
    const app = createApp(App)
    for (const name in ElIcons) {
        app.component(name, ElIcons[name]);
    }
    app.use(ElementPlus, { locale })
    app.use(store)
    app.use(router)
    app.mount('#app')

    3.代码实现

    修改“vue.config.js”文件中的配置,其中“target”为Laf接口访问前缀

    module.exports = {
      transpileDependencies: true,
      lintOnSave: false,
      productionSourceMap: false,
      devServer: {
          proxy: {
              '/api': {
                  target: 'https://gghfpp.laf.dev',
                  changeOrigin: true,
                  ws: false,
                  pathRewrite: {
                      '^/api': ''
                  }
              }
          }
      }
    }

    接着,完成页面布局和逻辑业务实现,以“home.vue”为例,完整代码如下:

      
        
          
            

    欢迎体验Claude AI~

    {{ item.time }} 发送 import topBar from '@/components/topBar.vue' var _this; export default { name: 'home', components: { topBar }, data() { return { // 容器高度初始化为0 scrollerHeight: 0, // 查询参数信息 queryInfo: { conversationId: null, prompt: null, question: null }, // 信息集合 msgList: [], msgIndex: 0, isDisabled: false } }, created() { _this = this; _this.queryInfo.conversationId = Math.ceil(Math.random() * 1000000000000); }, mounted: function () { _this.calculateHeight(); // 当浏览器窗口大小发生变化时,重新计算容器高度 window.addEventListener('resize', () => { _this.calculateHeight(); }); }, methods: { // 计算容器高度 calculateHeight() { // 获取顶部元素 let elHeader = _this.$refs.elHeader; // 获取底部元素 let elFooter = _this.$refs.elFooter; if (elHeader && elFooter) { _this.scrollerHeight = document.documentElement.clientHeight - elHeader.$el.offsetHeight - elFooter.$el.offsetHeight - 1; } }, //发送消息 sendMessage() { const _prompt = _this.queryInfo.prompt; if (!_prompt) { _this.$message.toast({ message: '请输入消息!' }); return; } _this.queryInfo.question = _prompt; _this.queryInfo.prompt = ''; _this.insertMsg(2, _prompt); _this.isDisabled = true; //延迟0.5秒执行 setTimeout(function () { _this.insertMsg(1, '🤖机器人正在思考🤔中...'); }, 500); //开始计时 let seconds = 0; let timer = setInterval(function () { seconds++; _this.msgList[_this.msgList.length - 1].content = '🤖机器人正在思考🤔中...' + seconds + ' 秒'; }, 1000); _this.sendRequest(timer); }, //发起请求 sendRequest(timer) { _this.$requests .post("/api/claude-func", _this.queryInfo) .then((res) => { _this.isDisabled = false; //关闭计时 clearInterval(timer); console.log('请求结果', res); if (res != null) { // _this.$message.toast({ // message: res.msg, // type: "success", // }); if (res.data) { let answer = res.data.answer.replaceAll(',', ',').replaceAll(':', ':').replaceAll('!', '!').replaceAll(/\n\n/g, '
    ').replaceAll(/\n/g, '
    '); // _this.insertMsg(1, answer); _this.msgList[_this.msgList.length - 1].content = ''; //实现打字机效果 let index = 0; let _timer = setInterval(function () { if (answer.length 时自动换行 if ((index + 4) { let divScroll = _this.$refs.divScroll; let elScrollbar = _this.$refs.elScrollbar; if (divScroll.offsetHeight > elScrollbar.$el.offsetHeight) { elScrollbar.setScrollTop(divScroll.offsetHeight - elScrollbar.$el.offsetHeight); } }); } } } .page { .el-header { line-height: 20px; text-align: left; background-color: var(--el-color-primary-light-7); color: var(--el-text-color-primary); height: auto; padding: 8px 15px; p { font-size: 14px; color: #303133; } .tips { color: red; } } .el-main { padding: 12px 15px; .msg-list { .msg-item:last-child { margin-bottom: 0; } .msg-item { margin-bottom: 15px; text-align: left; .content { display: flex; } .img_1 { width: 36px; height: 36px; margin-right: 8px; } .img_2 { width: 36px; height: 36px; margin-left: 8px; } .el-card__body { padding: 8px 15px; font-size: 14px; color: #303133; min-width: 130px; } .time { margin-bottom: 5px; color: #909399; font-size: 12px; } .t1 { text-align: right; margin-right: 45px; } .t2 { margin-left: 45px; } } } } .el-footer { line-height: 60px; background-color: #ecf5ff; color: var(--el-text-color-primary); .el-input-group__append { background-color: #409EFF; color: #ffffff; box-shadow: none; } } }

    说明:post请求时,第一个url参数需要使用自己在Laf中创建的云函数的名称

    1)内容输出时实现打字机效果,这里采用了定时器的方式,每隔100毫秒将后端返回的内容拆分后逐个进行追加,核心代码如下:

    //实现打字机效果
    let index = 0;
    let _timer = setInterval(function () {
      if (answer.length 时自动换行
      if ((index + 4) { item.time }}
            
               {
        let divScroll = _this.$refs.divScroll;
        let elScrollbar = _this.$refs.elScrollbar;
        if (divScroll.offsetHeight > elScrollbar.$el.offsetHeight) {
          elScrollbar.setScrollTop(divScroll.offsetHeight - elScrollbar.$el.offsetHeight);
        }
      });
    }

    在src目录下创建utils文件夹,用于存放以上关联的js,具体代码如下:

    日期格式化(date.js)

    //时间处理
    const date = {
        /**
         * 
         * @param {时间} dateTime 
         * @param {类型} type 默认:年月日
         * type=1 年-月-日
         * type=2 年.月.日
         * type=3 年-月-日 时:分:秒
         * type=4 年.月.日 时:分:秒
         * @returns 
         */
        fmtDateTime(dateTime, type) {
            if (dateTime == '' || dateTime == null) {
                dateTime = new Date();
            } else {
                // dateTime = dateTime.substr(0, 4) + "/" + dateTime.substr(5, 2) + "/" + dateTime.substr(8, 2) + " " + dateTime.substr(11, 8);
                dateTime = new Date(dateTime);
            }
            var y = dateTime.getFullYear();
            var m = dateTime.getMonth() + 1;
            var d = dateTime.getDate();
            var h = dateTime.getHours() > 9 ? dateTime.getHours().toString() : '0' + dateTime.getHours();
            var mm = dateTime.getMinutes() > 9 ? dateTime.getMinutes().toString() : '0' + dateTime.getMinutes();
            var ss = dateTime.getSeconds() > 9 ? dateTime.getSeconds().toString() : '0' + dateTime.getSeconds();
            if (type === 1) {
                return y + '-' + (m  
    

    消息弹框提示(message.js)

    import { ElMessageBox, ElMessage } from "element-plus";
    const message = {
        //询问框
        confirm(obj) {
            var message = obj.message || '确定执行此操作吗?'
            var title = obj.title || '温馨提示'
            return ElMessageBox.confirm(message, title, {
                type: 'warning',//success info warning error
                showCancelButton: obj.showCancelButton || true,
                cancelButtonText: obj.cancelButtonText || '取消',
                confirmButtonText: obj.confirmButtonText || '确定',
            }).then(obj.success || function () {
            }).catch(obj.fail || function () {
            });
        },
        //信息提示
        toast(obj) {
            return ElMessage({
                message: obj.message || 'this is a message.',
                type: obj.type || 'warning',//success warning info error
                duration: obj.duration || 3000,
                showClose: obj.showClose || false
            });
        }
    }
    export default message

    http请求(requests.js)

    import axios from 'axios'
    import message from './message';
    // 创建 axios 实例
    const requests = axios.create({
        // baseURL: 'http://localhost:8088',
        withCredentials: true,
        headers: {
            // 'Content-Type': 'application/json',
            // 'content-type' : 'application/x-www-form-urlencoded',
            // 'auth': 'test'
        },
        timeout: 30 * 1000 // 请求超时时间 30秒
    })
    // 错误处理函数
    const err = (error) => {
        if (error.response) {
            const data = error.response.data;
            if (error.response.status === 403) {
                message.toast({
                    message: data.message || data.msg
                });
            }
            if (error.response.status === 401) {
                message.toast({
                    type: 'warning',
                    message: '你没有权限。'
                });
            }
        }
        return Promise.reject(error)
    }
    // requests interceptor(请求拦截器)
    requests.interceptors.request.use(config => {
        const token = localStorage.getItem('token')
        if (token) {
            config.headers['auth'] = token // 让每个请求携带自定义 token
        }
        return config
    }, err)
    // requests interceptor(接收拦截器)
    requests.interceptors.response.use((response) => {
        const res = response.data;
        if (res.code == 0) {
            return Promise.resolve(res).catch((e) => { });
        } else {
            message.toast({
                message: res.msg
            });
            return Promise.reject(res).catch((e) => { });
        }
    }, err)
    export default {
        requests
    }

    同时,需要修改“main.js”,进行引入,配置全局变量

    import message from './utils/message'
    import requests from './utils/requests'
    import date from './utils/date'
    //配置全局属性
    app.config.globalProperties.$requests = requests.requests
    app.config.globalProperties.$message = message
    app.config.globalProperties.$date = date

    “App.vue”页面中样式如下:

    
      
    
    
    body {
      padding: 0;
      margin: 0;
    }
    #app {
      font-family: Avenir, Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
    }
    p {
      margin: 0;
      padding: 0;
    }
    .el-message{
      width: max-content;
    }
    

    以为全部完成后,使用命令npm run serve运行项目,界面效果如下:

     在线体验地址:http://claude.jhcrs.cn/ 

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon