Coze扣子 API 聊天智能机器人封装

这篇文章将介绍一个用于与Coze平台API交互的JavaScript类封装,它简化了与聊天机器人的交互过程。

演示网站:gofly.v1kf.com

什么是Coze API?

Coze是一个提供智能对话服务的平台,开发者可以通过API与平台上的聊天机器人进行交互。这个封装类就是为了简化API调用过程而设计的。

核心功能

这个Coze类主要实现了以下功能:

  1. ​单例模式​​:确保整个应用中只有一个Coze实例
  2. ​会话管理​​:可以创建和管理与机器人的对话会话
  3. ​两种交互模式​​:
    • 流式聊天(实时接收消息片段)
    • 轮询模式(等待完整响应)

代码结构解析

1. 初始化

const coze = new Coze(BOT_ID, API_KEY);

初始化时需要提供机器人的ID和API密钥。由于采用单例模式,多次初始化会返回同一个实例。

2. 创建会话

const conversationId = await coze.CreateConversation("用户ID");

为特定用户创建一个新的对话会话,并返回会话ID。

3. 发送消息

有两种方式发送消息:

​流式模式(默认)​​:

const response = await coze.ChatCozeV3(conversationId, "用户ID", "你好");

​轮询模式​​:

const response = await coze._pollingChat(conversationId, "用户ID", "你好");

4. 辅助方法

类中还包含了一些内部使用的辅助方法:

  • _waitForCompletion: 等待机器人完成响应
  • _getFinalResponse: 获取最终回复内容

使用示例

简单使用

// 初始化
const coze = new Coze("你的机器人ID", "你的API密钥");

// 发送消息
const response = await coze.ChatCozeV3("", "test_user", "你好");
console.log(response);

兼容旧代码

还提供了与原代码兼容的函数:

chatCozeAPI("机器人ID", "API密钥", "你好");
chatCozeAPIPolling("机器人ID", "API密钥", "你好");

设计考虑

  1. ​灵活性​​:支持流式和轮询两种模式,适应不同场景需求
  2. ​错误处理​​:对API错误进行了捕获和处理
  3. ​会话管理​​:自动维护会话状态,简化开发者工作
  4. ​兼容性​​:保持与旧代码的兼容

适用场景

这个封装类适用于:

  • 需要快速集成Coze聊天机器人的Web应用
  • 需要同时支持实时流式响应和完整响应的场景
  • 需要管理多个用户对话的应用
class Coze {
    static instance = null;
    static API_URL = "https://api.coze.cn/v3/chat";

    constructor(BOT_ID, API_KEY) {
        if (Coze.instance) {
            return Coze.instance;
        }
        this.BOT_ID = BOT_ID;
        this.API_KEY = API_KEY;
        this.conversation = {};
        Coze.instance = this;
    }

    async CreateConversation(user) {
        if (this.conversation[user]) {
            return this.conversation[user];
        }

        try {
            const response = await fetch("https://api.coze.cn/v1/conversation/create", {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${this.API_KEY}`
                },
                body: JSON.stringify({
                    bot_id: this.BOT_ID,
                    user_id: user,
                    stream: false,
                    auto_save_history: true,
                    additional_messages: []
                })
            });

            const data = await response.json();
            if (data.code !== 0) {
                throw new Error(data.msg || 'Failed to create conversation');
            }

            this.conversation[user] = data.data.id;
            return data.data.id;
        } catch (error) {
            console.error("创建会话失败:", error);
            return "";
        }
    }

    async ChatCozeV3(conversation_id, user, query, messages = []) {
        // 兼容原逻辑:如果没有传入conversation_id,则创建新的
        conversation_id = conversation_id || await this.CreateConversation(user);
        
        // 两种模式:流式(原逻辑)和非流式(Python逻辑)
        const useStream = false; // 默认使用流式,保持原逻辑
        
        if (useStream) {
            return this._streamChat(conversation_id, user, query, messages);
        } else {
            return this._pollingChat(conversation_id, user, query, messages);
        }
    }

    async _streamChat(conversation_id, user, query, messages) {
        const url = `${Coze.API_URL}?conversation_id=${conversation_id}`;
        const message = { role: "user", content: query, content_type: "text" };
        messages.push(message);

        const params = {
            bot_id: this.BOT_ID,
            user_id: user,
            query: query,
            additional_messages: messages,
            stream: true,
            auto_save_history: true
        };

        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${this.API_KEY}`
                },
                body: JSON.stringify(params)
            });

            const reader = response.body.getReader();
            let result = "";
            
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;

                const decoder = new TextDecoder();
                const chunk = decoder.decode(value, { stream: true });
                const lines = chunk.split("\n");
                
                for (let i = 0; i < lines.length; i++) {
                    let line = lines[i].trim();
                    if (line === "") continue;
                    if (line.includes("[DONE]")) break;
                    
                    if (line.startsWith("event:conversation.message.delta")) {
                        const dataLineIndex = lines.slice(i + 1).findIndex(l => l.startsWith("data:"));
                        if (dataLineIndex !== -1) {
                            const dataLine = lines[i + 1 + dataLineIndex];
                            const resStr = dataLine.trim().replace("data:", "");
                            const respJson = JSON.parse(resStr);
                            result += respJson.content;
                            i += dataLineIndex;
                        }
                    }
                }
            }
            return result;
        } catch (error) {
            console.error("流式请求失败:", error);
            return "";
        }
    }

    async _pollingChat(conversation_id, user, query) {
        try {
            // 1. 发送消息
            const response = await fetch(Coze.API_URL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${this.API_KEY}`
                },
                body: JSON.stringify({
                    bot_id: this.BOT_ID,
                    user_id: user,
                    additional_messages: [{
                        "role": "user",
                        "content": query,
                        "content_type": "text"
                    }],
                    stream: false,
                    auto_save_history: true,
                    conversation_id: conversation_id
                })
            });

            const data = await response.json();
            if (data.code !== 0) {
                throw new Error(data.msg || 'API请求失败');
            }

            const chatId = data.data.id;
            conversation_id = data.data.conversation_id;

            // 2. 等待处理完成
            await this._waitForCompletion(chatId, conversation_id);

            // 3. 获取最终回复
            return await this._getFinalResponse(chatId, conversation_id);
        } catch (error) {
            // 3. 获取最终回复
            return await this._getFinalResponse(chatId, conversation_id);
            return "";
        }
    }

    async _waitForCompletion(chatId, conversationId, maxRetries = 30, interval = 1000) {
        const url = `${Coze.API_URL}/retrieve`;
        
        for (let i = 0; i < maxRetries; i++) {
            try {
                const response = await fetch(`${url}?chat_id=${chatId}&conversation_id=${conversationId}`, {
                    headers: {
                        'Authorization': `Bearer ${this.API_KEY}`
                    }
                });
                
                const data = await response.json();
                if (data.code !== 0) {
                    throw new Error(data.msg || 'API请求失败');
                }
                
                if (data.data.status === "completed") {
                    return true;
                }
                
                await new Promise(resolve => setTimeout(resolve, interval));
            } catch (error) {
                throw error;
            }
        }
        
        throw new Error("等待响应超时");
    }

    async _getFinalResponse(chatId, conversationId) {
        const url = `${Coze.API_URL}/message/list`;
        
        try {
            const response = await fetch(`${url}?chat_id=${chatId}&conversation_id=${conversationId}`, {
                headers: {
                    'Authorization': `Bearer ${this.API_KEY}`
                }
            });
            
            const data = await response.json();
            if (data.code !== 0) {
                throw new Error(data.msg || 'API请求失败');
            }
            
            // 提取助手的回复
            const assistantReplies = data.data.filter(msg => 
                msg.role === "assistant" && msg.type === "answer"
            );
            
            return assistantReplies.length > 0 ? assistantReplies[assistantReplies.length - 1].content : "";
        } catch (error) {
            throw error;
        }
    }
}

// 完全兼容原使用方式
async function chatCozeAPI(botId, apiKey, query) {
    const coze = new Coze(botId, apiKey);
    const user = "test_user";
    const messages = [];
    const response = await coze.ChatCozeV3("", user, query, messages);
    console.log("Chat Response:", response);
    return response;
}

// 也可以使用新的轮询模式
async function chatCozeAPIPolling(botId, apiKey, query) {
    const coze = new Coze(botId, apiKey);
    const user = "test_user";
    const messages = [];
    
    // 临时关闭流式模式
    const originalChat = coze.ChatCozeV3.bind(coze);
    coze.ChatCozeV3 = async function(...args) {
        return this._pollingChat(...args);
    };
    
    try {
        const response = await coze.ChatCozeV3("", user, query, messages);
        console.log("Chat Response (Polling):", response);
        return response;
    } finally {
        // 恢复原方法
        coze.ChatCozeV3 = originalChat;
    }
}

 

posted @ 2025-06-09 15:25  唯一客服系统开发笔记  阅读(528)  评论(0)    收藏  举报