当前位置: 首页 > news >正文

Deepseek-v3 / Dify api接入飞书机器人go程序

准备工作

  1. 开通了接收消息权限的飞书机器人,例如我希望用户跟飞书机器人私聊,就需要开通这个权限:读取用户发给机器人的单聊消息 im:message.p2p_msg:readonly
  2. 准备好飞书机器人的API key 和Secret
  3. deepseek-v3的api key+secret:https://platform.deepseek.com/api_keys 这里获取,一开始有10元的免费额度,趁能充多充点,经常不让充值。
  4. 自己部署一下dify,推荐使用docker-compose方式,这个有很多教程就不赘述了

飞书机器人通过长连接获取用户私聊发的消息

我们使用长连接的方式接收用户消息,需要在飞书开发者后台中配置一下应用,见
配置回调订阅方式
代码如下:

import(larkevent "github.com/larksuite/oapi-sdk-go/v3/event""github.com/larksuite/oapi-sdk-go/v3/event/dispatcher""github.com/larksuite/oapi-sdk-go/v3/service/auth/v3"larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1"larkws "github.com/larksuite/oapi-sdk-go/v3/ws"
)
var sent map[string]struct{} // 这里简单去个重 实际使用要自己再写去重部分
// 飞书消息过来Content字段值是{\"text\":\"早上好~\"}这样的,需要再解析一下
type Text struct {Text string `json:"text"`
}
// 处理接收到用户消息的事件
func callback() {sent = make(map[string]struct{})// 注册事件回调,OnP2MessageReceiveV1 为接收消息 v2.0;OnCustomizedEvent 内的 message 为接收消息 v1.0。NewEventDispatcher()里的两个参数都填空字符串eventHandler := dispatcher.NewEventDispatcher("", "").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error {// messageid简单去重if _, ok := sent[*event.Event.Message.MessageId]; ok {return nil} else {sent[*event.Event.Message.MessageId] = struct{}{}}fmt.Printf("[ OnP2MessageReceiveV1 access ], data: %s\n", larkcore.Prettify(event))fmt.Println(event.Event.Message.Content) // content中就是用户发过来的消息内容var text Textjson.Unmarshal([]byte(*event.Event.Message.Content), &text)fmt.Println(text.Text)// 这里可以把用户输入发给deepseek或者dify并接收其响应,具体实现后面讲//resp, e := deepseek.CallDeepSeekAPI(text.Text)//if e != nil {//	return e//}resp := dify.ChatMessages(text.Text)fmt.Println(resp)// 这里组织飞书机器人发送消息的content格式,跟接收到消息的一样,也是{\"text\":\"say something\"}var contentStruct struct {Text string `json:"text"`}contentStruct.Text = respcontent, _ := json.Marshal(contentStruct)// 这个messages是机器人发送消息函数,见下方messages(getTernantAccessToken(), *event.Event.Sender.SenderId.OpenId, *event.Event.Message.MessageType, string(content))return nil}).//im:message.p2p_msg:readonly 这个先不用管OnCustomizedEvent("", func(ctx context.Context, event *larkevent.EventReq) error {fmt.Printf("[ OnCustomizedEvent access ], type: message, data: %s\n", string(event.Body))return nil})// 创建Clientcli := larkws.NewClient(AppID, AppSecret,larkws.WithEventHandler(eventHandler),larkws.WithLogLevel(larkcore.LogLevelDebug),)// 启动客户端 保持一个长链接err := cli.Start(context.Background())if err != nil {panic(err)}
}//发送消息
func messages(token, receiveID, msgType, content string) {// 创建 Clientclient := lark.NewClient(AppID, AppSecret)// 创建请求对象receiveIDType := "open_id"if strings.HasPrefix(receiveID, "oc") { // 这里我简单区分了一下群聊和个人receiveIDType = "chat_id"}req := larkim.NewCreateMessageReqBuilder().ReceiveIdType(receiveIDType).Body(larkim.NewCreateMessageReqBodyBuilder().ReceiveId(receiveID).MsgType(msgType).Content(content).Build()).Build()// 发起请求resp, err := client.Im.Message.Create(context.Background(), req, larkcore.WithTenantAccessToken(token))// 处理错误if err != nil {fmt.Println(err)return}// 服务端错误处理if !resp.Success() {fmt.Println(resp.Code, resp.Msg, resp.RequestId())return}// 业务处理//fmt.Println(larkcore.Prettify(resp))fmt.Println(string(resp.RawBody))
}

tips: 获取的用户消息长这样:

{EventV2Base: {Schema: "2.0",Header: {EventID: "xx",EventType: "im.message.receive_v1",AppID: "xx",TenantKey: "xx",CreateTime: "1738892348642",Token: ""}},EventReq: {Body: <binary> len 672,RequestURI: ""},Event: {Sender: {SenderId: {UserId: "xx",OpenId: "xx",UnionId: "xx"},SenderType: "user",TenantKey: "xx"},Message: {MessageId: "xx",CreateTime: "1738892348363",UpdateTime: "1738892348363",ChatId: "xx",ChatType: "p2p",MessageType: "text",Content: "{\"text\":\"早上好~\"}"}}
}

接下来就是实现调用deepseek或dify的api的逻辑了

Deepseek-v3 API调用代码

package deepseekimport ("bytes""encoding/json""fmt""io""net/http""net/http/httputil"
)
var (// DeepSeek-R1 API 的配置DeepSeekAPIURL = "https://api.deepseek.com/chat/completions" // 直接用这个就行DeepSeekAPIKey = "你的key"
)
// DeepSeek-R1 API 请求数据结构
type DeepSeekRequest struct {Model    string        `json:"model"`Messages []RoleContent `json:"messages"`Stream   bool          `json:"stream"`
}
type RoleContent struct {Role    string `json:"role"`Content string `json:"content"`
}// DeepSeek-R1 API 响应数据结构
type DeepSeekResponse struct {Choices []struct {Message struct {Content string `json:"content"`} `json:"message"`} `json:"choices"`
}// 调用 DeepSeek-R1 API
func CallDeepSeekAPI(msg string) (string, error) {requestBody := DeepSeekRequest{Model: "deepseek-chat",Messages: []RoleContent{{Role: "system", Content: "You are a helpful assistant."}, // 这里可以自行修改{Role: "user", Content: msg}, // msg就是用户发的消息},Stream: false, // 这里先不用流式输出}requestBytes, err := json.Marshal(requestBody)if err != nil {return "", err}req, err := http.NewRequest("POST", DeepSeekAPIURL, bytes.NewBuffer(requestBytes))if err != nil {return "", err}req.Header.Set("Authorization", "Bearer "+DeepSeekAPIKey)req.Header.Set("Content-Type", "application/json")// 这里我dump了一下请求看发的是否正确 可以删掉dump, _ := httputil.DumpRequest(req, true)fmt.Println(string(dump))// 发请求client := &http.Client{}resp, err := client.Do(req)if err != nil {return "", err}defer resp.Body.Close()body, err := io.ReadAll(resp.Body)if err != nil {return "", err}// 解析响应var deepSeekResponse DeepSeekResponseif err := json.Unmarshal(body, &deepSeekResponse); err != nil {return "", err}// 拿content返回if len(deepSeekResponse.Choices) > 0 {return deepSeekResponse.Choices[0].Message.Content, nil}return "", fmt.Errorf("no response from DeepSeek API")
}

Dify api调用方法

如何在dify中接入大模型并制作一个问答机器人参考:https://docs.dify.ai/zh-hans/guides/application-orchestrate/conversation-application
点击【发布】之后,去【访问api】页面,右上角有一个在这里插入图片描述
点击这个API密钥保存下来

调用代码如下:

package difyimport ("bytes""encoding/json""fmt""io""log""net/http""net/http/httputil""strconv""strings"
)type ChatMessageRequest struct {Inputs         map[string]interface{} `json:"inputs"`Query          string                 `json:"query"`ResponseMode   string                 `json:"response_mode"`ConversationID string                 `json:"conversation_id,omitempty"`User           string                 `json:"user"`
}type ChatMessageResponse struct {ID             string `json:"id"`Answer         string `json:"answer"`ConversationID string `json:"conversation_id"`CreatedAt      int    `json:"created_at"`
}const (DifyBaseURL = "http://192.168.xx.xx:12345/v1" // 这里是你的dify服务地址DifyApiKey  = "app-xxxx" // dify提供的api密钥ChatMsgPath = "/chat-messages"
)func ChatMessages(msg string) string {requestData := ChatMessageRequest{Query:        msg,ResponseMode: "blocking", // 我们先选择阻塞模式,就是等回答全部生成后发回来,而不是sse那种模拟打字输出的形式(streaming)User:         "abc123",}// 将请求数据序列化为 JSONrequestBody, err := json.Marshal(requestData)if err != nil {fmt.Errorf("failed to marshal request data: %v", err)}// 创建 HTTP 请求req, err := http.NewRequest("POST", DifyBaseURL+ChatMsgPath, bytes.NewBuffer(requestBody))if err != nil {log.Fatalf("Failed to create request: %v", err)}// 设置请求头req.Header.Set("Authorization", "Bearer "+DifyApiKey)req.Header.Set("Content-Type", "application/json")// 发送请求client := &http.Client{}// 这里dump了一下看发送请求是否正确,可以删掉dump, _ := httputil.DumpRequest(req, true)fmt.Println(string(dump))resp, err := client.Do(req)if err != nil {log.Fatalf("Failed to send request: %v", err)}defer resp.Body.Close()// 读取响应body, err := io.ReadAll(resp.Body)if err != nil {log.Fatalf("Failed to read response body: %v", err)}// 输出响应fmt.Println("Response Status:", resp.Status)fmt.Println("Response Body:", string(body))var res ChatMessageResponseif err := json.Unmarshal(body, &res); err != nil {fmt.Errorf("Failed to unmarshal response body: %v", err)return ""}fmt.Println("Answer:", res.Answer)return res.Answer // 这个就是dify调大模型获得的返回内容
}

效果

如此这般就可以让飞书机器人接收消息->调用dify或者deepseek的api获得回答->把回答发给用户了
在这里插入图片描述

相关文章:

Deepseek-v3 / Dify api接入飞书机器人go程序

准备工作 开通了接收消息权限的飞书机器人&#xff0c;例如我希望用户跟飞书机器人私聊&#xff0c;就需要开通这个权限&#xff1a;读取用户发给机器人的单聊消息 im:message.p2p_msg:readonly准备好飞书机器人的API key 和Secretdeepseek-v3的api keysecret&#xff1a;http…...

2025.2.9 每日学习记录2:技术报告写了一半+一点点读后感

0.近期主任务线 1.完成小论文准备 目标是3月份完成实验点1的全部实验和论文。 2.准备教资笔试 打算留个十多天左右&#xff0c;一次性备考笔试的三个科目 1.实习申请技术准备&#xff1a;微调、Agent、RAG 1.今日完成任务 1.电子斗蛐蛐&#xff08;文本书写领域&am…...

qml ToolBar详解

1、概述 在 QML 中&#xff0c;ToolBar 是一种常用的 UI 组件&#xff0c;通常位于窗口的顶部或底部&#xff0c;用于提供一系列的操作按钮、菜单或其他交互元素。它可以帮助用户快速访问应用程序的常用功能&#xff0c;提高用户操作的便捷性。ToolBar 可以包含多个 ToolButto…...

机器学习在癌症分子亚型分类中的应用

学习笔记&#xff1a;机器学习在癌症分子亚型分类中的应用——Cancer Cell 研究解析 1. 文章基本信息 标题&#xff1a;Classification of non-TCGA cancer samples to TCGA molecular subtypes using machine learning发表期刊&#xff1a;Cancer Cell发表时间&#xff1a;20…...

Ansible自动化部署K8s集群一 Ansible的基础使用实战

一、Ansible介绍 1.安装ansible: yum install ansible -y 2.ansible的架构图&#xff1a; 3.ansible四部分&#xff1a; inventory:ansible管理的主机信息&#xff0c;包括ip地址、ssh端口、账号和密码等 modules:任务均由模块完成 plugins:增加ansible的核心功能 pla…...

ZooKeeper Watcher 机制详解:从注册到回调的全过程

引言 在分布式系统中&#xff0c;数据的实时性和一致性是至关重要的。ZooKeeper 通过其 Watcher 机制提供了一种高效的方式来监听数据变化或事件&#xff0c;从而使客户端能够在数据发生变化时立即收到通知。本文将深入探讨 ZooKeeper 的 Watcher 机制&#xff0c;具体包括客户…...

flutter_tools/gradle Unsupported class file major version 65 问题解决

1.问题定位 使用 命令 flutter doctor --verbose 可以查看当前项目中&#xff0c;使用的java的版本。 [✓] Android Studio (version 2024.2)• Android Studio at /Applications/Android Studio.app/Contents• Flutter plugin can be installed from:&#x1f528; https…...

C++设计模式 - 模板模式

一&#xff1a;概述 模板方法&#xff08;Template Method&#xff09;是一种行为型设计模式。它定义了一个算法的基本框架&#xff0c;并且可能是《设计模式&#xff1a;可复用面向对象软件的基础》一书中最常用的设计模式之一。 模板方法的核心思想很容易理解。我们需要定义一…...

mysql查缺补漏

好文推荐&#xff1a; 【数据库】快速理解脏读、不可重复读、幻读-CSDN博客 再探幻读&#xff01;什么是幻读?为什么会产生幻读&#xff0c;MySQL中是怎么解决幻读的&#xff1f;-CSDN博客 引擎 mysql默认引擎&#xff1a;innodb 1.支持行锁 2.支持事务 3.支持外键 索引…...

跨越边界,大模型如何助推科技与社会的完美结合?

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 概述 2024年&#xff0c;大模型技术已成为人工智能领域的焦点。这不仅仅是一项技术进步&#xff0c;更是一次可能深刻影响社会发展方方面面的变革。大模型的交叉能否推动技术与社会的真正融合&#xff1f;2025年…...

哪吒闹海!SCI算法+分解组合+四模型原创对比首发!SGMD-FATA-Transformer-LSTM多变量时序预测

哪吒闹海&#xff01;SCI算法分解组合四模型原创对比首发&#xff01;SGMD-FATA-Transformer-LSTM多变量时序预测 目录 哪吒闹海&#xff01;SCI算法分解组合四模型原创对比首发&#xff01;SGMD-FATA-Transformer-LSTM多变量时序预测效果一览基本介绍程序设计参考资料 效果一览…...

前端【技术方案】浏览器兼容问题(含解决方案、CSS Hacks、条件注释、特性检测、Polyfill 等)

浏览器兼容性测试工具 https://www.browserstack.com/ HTML 兼容处理 问题1 - 不支持 HTML5 新标签 旧版浏览器&#xff08;主要是 IE8 及以下&#xff09;不支持 HTML5 新标签&#xff08;如 <header>、<nav>、<article> 等&#xff09; 解决方案 引入 H…...

荣耀手机Magic3系列、Magic4系列、Magic5系列、Magic6系列、Magic7系列详情对比以及最新二手价格预测

目录 荣耀Magic系列手机详细对比 最新二手价格预测 性价比分析 总结 以下是荣耀Magic系列手机的详细对比以及最新二手价格预测&#xff1a; 荣耀Magic系列手机详细对比 特性荣耀Magic3系列荣耀Magic4系列荣耀Magic5系列荣耀Magic6系列荣耀Magic7系列处理器骁龙888&#x…...

后盾人JS -- 模块化开发

开发模块管理引擎 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </he…...

CNN卷积神经网络多变量多步预测,光伏功率预测(Matlab完整源码和数据)

代码地址&#xff1a;CNN卷积神经网络多变量多步预测&#xff0c;光伏功率预测&#xff08;Matlab完整源码和数据) 标题&#xff1a;CNN卷积神经网络多变量多步预测&#xff0c;光伏功率预测 一、引言 1.1 研究背景及意义 随着全球能源危机的加剧和环保意识的提升&#xff…...

深入 JVM 虚拟机:字符串常量池演变与 intern() 方法工作原理解析

🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall ︱vue3-element-admin︱youlai-boot︱vue-uniapp-template 🌺 仓库主页: GitCode︱ Gitee ︱ Github 💖 欢迎点赞 👍 收藏 ⭐评论 📝 如有错误敬请纠正! 前言 在 Java 开发中,字符串常量池(String Constant…...

单向/双向,单层/多层RNN输入输出维度问题

单向/双向&#xff0c;单层/多层RNN输入输出维度问题 RNN单层单向RNNRnn CellRnn 双层单向RNN单层双向RNN双层双向RNN RNN 单层单向RNN Rnn Cell 循环神经网络最原始的Simple RNN实现如下图所示: 下面写出单个时间步对应的Rnn Cell计算公式: 如果用矩阵运算视角来看待的话&…...

chromium-mojo

https://chromium.googlesource.com/chromium/src//refs/heads/main/mojo/README.md 相关类&#xff1a;https://zhuanlan.zhihu.com/p/426069459 Core:https://source.chromium.org/chromium/chromium/src//main:mojo/core/README.md;bpv1;bpt0 embedder:https://source.chr…...

ZooKeeper 的典型应用场景:从概念到实践

引言 在分布式系统的生态中&#xff0c;ZooKeeper 作为一个协调服务框架&#xff0c;扮演着至关重要的角色。它的设计目的是提供一个简单高效的解决方案来处理分布式系统中常见的协调问题。本文将详细探讨 ZooKeeper 的典型应用场景&#xff0c;包括但不限于配置管理、命名服务…...

缓存组件<keep-alive>

缓存组件<keep-alive> 1.组件作用 组件, 默认会缓存内部的所有组件实例&#xff0c;当组件需要缓存时首先考虑使用此组件。 2.使用场景 场景1&#xff1a;tab切换时&#xff0c;对应的组件保持原状态&#xff0c;使用keep-alive组件 使用&#xff1a;KeepAlive | Vu…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...