node接入chatgpt 的最强对手claude-爱代码爱编程
由于个人的chatGPT免费版本即将到期, Claude 很火,在网上被说成是 ChatGPT 的最强对手,是 ChatGPT 的替代品。本文我将介绍下 Claude 是什么,以及如何免费使用 Claude.
什么是Claude
看一下它是如何自我介绍的
Slack
Slack 是一款流行的团队协作和通讯软件。
Claude 官方给我定义了很方便的 Slack 应用 Claude-in-Slack,我们直接把它添加到 Slack 中即可使用。添加的操作也很简单:
先注册 Slack
到 Claude 官网,点击 Add to Slack
在跳转的页面点击 添加到 Slack
在 Slack 中选择 Claude 应用,进入聊天窗口开始使用
接下来使用Slack的api来实现调用Claude, 虽然Claude也有接口申请, 但是一直没有通过, 不知道什么原因, 但是既然在Slack中可以使用Claude, 那就取巧, 通过Slack的api来实现调用Claude吧
Slack的api地址:
Slack 为我们提供了各个语言的 SDK,我们以 node-slack-sdk 来作为演示示例。
获取Slack Token
为了使用 Slack 的 SDK,我们需要新建一个 Slack APP。
选择Form scratch, 这里说一下, 我这里带翻译是使用了谷歌插件, 最近这个插件挺火的, 也很好用, 可以安利给大家, 插件的名字是沉浸式翻译, 大家直接去谷歌商店搜索就能找到了.
输入 app 名称并选择自己的 workspace。
创建后点击我们的 APP,然后点击左侧栏的 OAuth & Permissions。下拉找到 Scopes,为我们的 User Token 添加 Scope。
添加需要通过接口访问的权限
只有授权了才能够通过sdk调用这些接口哦
这些接口分别代表着不同的权限
然后我们把 APP 安装到我们的 workspace 中:
这个按钮在auth页面的上面
安装后,这里会生成我们需要的 User OAuth Token,我们复制这个 Token,后面需要用。
如果更改了权限, 就要重新下载到工作区才会出现效果哈
获取Claude Bot ID
进入Slack应用页面, 找到对应的Claude应用, 右击选择查看应用详情
复制Claude的成员ID和频道ID
Slack Claude Server
在服务端开始来获取应用里面的数据
我们需要安装的依赖有:
https://github.com/slackapi/node-slack-sdk
$ npm install @slack/web-api @slack/socket-mode
# Or, if you prefer yarn
$ yarn add @slack/web-api @slack/socket-mode
通过readme,我们可以看到如何像应用中发送一条数据
const { WebClient } = require('@slack/web-api');
// An access token (from your Slack app or custom integration - xoxp, xoxb)
const token = process.env.SLACK_TOKEN;
const web = new WebClient(token);
// This argument can be a channel ID, a DM ID, a MPDM ID, or a group ID
const conversationId = 'C1232456';
(async () => {
// See: https://api.slack.com/methods/chat.postMessage
const res = await web.chat.postMessage({ channel: conversationId, text: 'Hello there' });
// `res` contains information about the posted message
console.log('Message sent: ', res.ts);
})();
但是我们需要把这样的数据,做成一个接口的形式, 然后方便客户端调用, 我这里选择了express来创造路由接口, 很方便和快速.
拿到客户端输入的数据之后, 我们需要将Claude返回的数据返回给客户端, readme里面就没有提供例子了.
其实方法就是读取Claude这个应用窗口的历史数据, 拿到最新的这条回复, 通过conversations.history接口来拿取数据.
CALUDE_BOT_ID: 是频道ID
CALUDE_CHANNEL_ID: 是成员ID
CALUDE_TOKEN: 是上面拿到的User OAuth Token
import { WebClient } from '@slack/web-api'
import { Router } from "express";
const routes = Router();
// An access token (from your Slack app or custom integration - xoxp, xoxb)
const token = process.env.CALUDE_TOKEN;
const web = new WebClient(token);
// This argument can be a channel ID, a DM ID, a MPDM ID, or a group ID
const conversationId = process.env.CALUDE_CHANNEL_ID;
routes.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
})
function sleep(ms: number) {
return new Promise(resolve=>setTimeout(resolve, ms))
}
routes.post('/chat-process', async (req, res) => {
res.setHeader('Content-type', 'application/octet-stream')
try {
const { prompt } = req.body
const ans = await web.chat.postMessage({ channel: conversationId || '', text: prompt});
let firstChunk = true
for(let i = 0; i < 500; i++) {
try {
// Call the conversations.history method using WebClient
const result = await web.conversations.history({
channel: process.env.CALUDE_BOT_ID || '',
oldest: ans.ts,
limit: 2
});
const conversationHistory = result.messages;
if (!conversationHistory) {
sleep(1000)
continue
}
else if(conversationHistory != undefined && Array.isArray(conversationHistory) && conversationHistory[0]?.text?.indexOf('_Typing…_') != -1) {
res.write(firstChunk ? `${conversationHistory[0].text}` : `⭐${conversationHistory[0].text}`)
firstChunk = false
sleep(1000)
continue
}
res.write(`⭐${conversationHistory[0].text}`)
res.end()
break;
}
catch (error) {
console.error(error);
}
}
}
catch (error) {
res.json({
err: JSON.stringify(error)
})
}
finally {
res.end()
}
})
export default routes;
将接口的content-Type设置成application/octet-stream, 是为了不用等到Claude完全解答完再返回给客户端, 可以先返回一些内容, 然后慢慢加载, 这样会增强用户体验感, 同时也能防止接口响应时间太长, 用户一直未得到答案
这里设置一个循环500次, 其实就是为了保证500次之后, 能够返回完整的数据, 当然如果提前结束了,就会break退出循环.
Claude Client
我这里是一个vue+vite项目, 核心请求接口代码如下:
async function onConversation() {
let message = prompt.value
if (loading.value)
return
if (!message || message.trim() === '')
return
controller = new AbortController()
scrollToBottom()
addMessage('', false)
loading.value = true
prompt.value = ''
try {
const fetchChatAPIOnce = async () => {
await fetchCaludeAPIProcess({
prompt: message,
onDownloadProgress: ({ event }) => {
const xhr = event.target
const { responseText } = xhr
// Always process the final line
const lastIndex = responseText.lastIndexOf('⭐', responseText.length - 2)
let chunk = responseText
if (lastIndex !== -1) {
chunk = responseText.substring(lastIndex)
}
list.value[list.value.length - 1].message = chunk
scrollToBottom()
},
})
}
await fetchChatAPIOnce()
}
catch (error: any) {
const errorMessage = error?.message ?? 'common.wrong'
if (error.message === 'canceled') {
scrollToBottomIfAtBottom()
return
}
list.value[list.value.length - 1].message = errorMessage
console.error(errorMessage)
scrollToBottomIfAtBottom()
}
finally {
loading.value = false
}
}
每次都是以⭐开头, 所以我这边就是去取最后一个⭐开头的数据, 然后渲染到页面就可以了
最终的效果如下:
最后提供我的线上链接供大家体验一下:
Claude
好了, 今天的分享到这里就结束了, 感谢观看!