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

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…...

【docker】Failed to allocate manager object, freezing:兼容兼容 cgroup v1 和 v2

参考大神让系统同时兼容 cgroup v1 和 v2 要解决你系统中只挂载了 cgroup v2 但需要兼容 cgroup v1 的问题,可以通过以下几步来使系统同时兼容 cgroup v1 和 cgroup v2。这样 Docker 和其他服务就可以正常工作了。步骤 1:更新 Grub 配置,启用兼容模式 编辑 GRUB 配置来启用同…...

详解策略模式

引言 实现一个目标往往有多种方式&#xff0c;比如从上海到北京&#xff0c;可以选择高铁、火车、飞机、自驾等等。同样实现一个功能我们可能也有多种方法&#xff0c;把这些方法封装为算法&#xff0c;根据不同的需求选择不同的算法&#xff08;策略&#xff09;&#xff0c;让…...

2025影视泛目录站群程序设计_源码二次开发新版本无缓存刷新不变实现原理

1. 引言 本设站群程序计书旨在详细阐述苹果CMS泛目录的创新设计与实现&#xff0c;介绍无缓存刷新技术、数据统一化、局部URL控制及性能优化等核心功能&#xff0c;以提升网站访问速度和用户体验。 2. 技术概述 2.1 无缓存刷新技术 功能特点&#xff1a; 内容不变性&#x…...

【RabbitMQ】RabbitMQ的下载安装及使用

安装RabbitMQ 下载网站&#xff1a;https://www.rabbitmq.com/docs/install-windows 点击后&#xff0c;会直接定位到依赖介绍位置&#xff0c;告诉你需要安装Erlang 下载Erlang Erlang也是一种编程语言&#xff0c;只是比较小众&#xff0c;但其拥有极为出色的性能 这个网站是…...

Stylelint 如何处理 CSS 预处理器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

Word中Ctrl+V粘贴报错问题

Word中CtrlV粘贴时显示“文件未找到&#xff1a;MathPage.WLL”的问题 Word的功能栏中有MathType&#xff0c;但无法使用&#xff0c;显示灰色。 解决方法如下&#xff1a; 首先找到MathType安装目录下MathPage.wll文件以及MathType Commands 2016.dotm文件&#xff0c;分别复…...

jmeter逻辑控制器9

1&#xff0c;简单控制器2&#xff0c;录制控制器3&#xff0c;循环控制器4&#xff0c;随机控制器5&#xff0c;随机顺序控制器6&#xff0c;if控制器7&#xff0c;模块控制器8&#xff0c;Include控制器9&#xff0c;事物控制器本文永久更新地址: 1&#xff0c;简单控制器 不…...

uniapp mqttjs 小程序开发

在UniApp中集成MQTT.js开发微信小程序时&#xff0c;需注意平台差异、协议兼容性及消息处理等问题。以下是关键步骤与注意事项的综合指南&#xff1a; 一、环境配置与依赖安装 安装MQTT.js 推荐使用兼容性较好的版本&#xff1a;mqtt4.1.0&#xff08;H5和小程序兼容性最佳&…...

GitHub Copilot Agent 模式系统提示词

系统提示词 你是一名 AI 编程助手。 当被问及你的名字时&#xff0c;你必须回答“GitHub Copilot”。请严格且完整地遵循用户的要求。 遵守微软内容政策。 避免涉及侵犯版权的内容。如果有人要求你生成有害、仇恨、种族主义、性别歧视、淫秽、暴力或与软件工程完全无关的内容&…...

【设计模式】【行为型模式】模板方法模式(Template Method)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f4eb; 欢迎V&#xff1a; flzjcsg2&#xff0c;我们共同讨论Java深渊的奥秘 &#x1f…...

w200基于spring boot的个人博客系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…...

React 生命周期函数详解

React 组件在其生命周期中有多个阶段&#xff0c;每个阶段都有特定的生命周期函数&#xff08;Lifecycle Methods&#xff09;。这些函数允许你在组件的不同阶段执行特定的操作。以下是 React 组件生命周期的主要阶段及其对应的生命周期函数&#xff0c;并结合了 React 16.3 的…...

docker grafana安装

mkdir /root/grafana-storage chmod 777 -R /root/grafana-storage docker run -d -p 3000:3000 --namedocker-apisix-grafana-1 --network docker-apisix_apisix -v /root/grafana-storage:/var/lib/grafana grafana/grafana:9.1.0 浏览器访问&#xff1a; http://192.…...

H5+CSS+JS制作好看的轮播图

先来看效果 点击下方按钮可以做到平滑切换轮播&#xff0c;轮播图片可以根据自定义随心变化。 先来看一下页面代码结构 <div class"container"><div class"lunbo-wrap"><div id"slide"></div><div class"butto…...

aio-pika 快速上手(Python 异步 RabbitMQ 客户端)

目录 简介官方文档如何使用 简介 aio-pika 是一个 Python 异步 RabbitMQ 客户端。5.0.0 以前 aio-pika 基于 pika 进行封装&#xff0c;5.0.0 及以后使用 aiormq 进行封装。 https://github.com/mosquito/aio-pikahttps://pypi.org/project/aio-pika/ pip install aio-pika官…...

表单与交互:HTML表单标签全面解析

目录 前言 一.HTML表单的基本结构 基本结构 示例 二.常用表单控件 文本输入框 选择控件 文件上传 按钮 综合案例 三.标签的作用 四.注意事项 前言 HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础&#xff0c;其中表单&#xff08;<form>&…...

非递减子序列(力扣491)

这道题的难点依旧是去重&#xff0c;但是与之前做过的子集类问题的区别就是&#xff0c;这里是求子序列&#xff0c;意味着我们不能先给数组中的元素排序。因为子序列中的元素的相对位置跟原数组中的相对位置是一样的&#xff0c;如果我们改变数组中元素的顺序&#xff0c;子序…...

Python基础-元组tuple的学习

在 Python 中&#xff0c;元组&#xff08;tuple&#xff09;是一种不可变的序列类型&#xff0c;允许存储不同类型的元素。元组非常类似于列表&#xff08;list&#xff09;&#xff0c;但与列表不同的是&#xff0c;元组一旦创建&#xff0c;就不能修改其内容。 1 元组的创建…...

Vue与Konva:解锁Canvas绘图的无限可能

前言 在现代Web开发中&#xff0c;动态、交互式的图形界面已成为提升用户体验的关键要素。Vue.js&#xff0c;作为一款轻量级且高效的前端框架&#xff0c;凭借其响应式数据绑定和组件化开发模式&#xff0c;赢得了众多开发者的青睐。而当Vue.js邂逅Konva.js&#xff0c;两者结…...

如何修改DNS解析?

DNS(域名系统)就像互联网的“电话簿”&#xff0c;负责将我们输入的网址转换为计算机能够理解的IP地址。如果DNS解析出现问题&#xff0c;访问网站就会受到影响。那我们该如何修改DNS解析呢?接下来&#xff0c;我们就来介绍一下这个话题。 为什么要修改DNS解析? 使用默认的…...

go语言文件和目录

打开和关闭文件 os.Open()函数能够打开一个文件&#xff0c;返回一个*File 和一个 err。操作完成文件对象以后一定要记得关闭文件。 package mainimport ("fmt""os" )func main() {// 只读方式打开当前目录下的 main.go 文件file, err : os.Open(".…...

Solidity09 Solidity构造函数和修饰器

文章目录 一、构造函数二、修饰器三、OpenZeppelin的Ownable标准实现四、Remix 演示示例五、代码示例 这一讲&#xff0c;我们将用合约权限控制&#xff08; Ownable&#xff09;的例子介绍 Solidity语言中构造函数&#xff08; constructor&#xff09;和独有的修饰器&…...

ES6 Map 数据结构是用总结

1. Map 基本概念 Map 是 ES6 提供的新的数据结构&#xff0c;它类似于对象&#xff0c;但是"键"的范围不限于字符串&#xff0c;各种类型的值&#xff08;包括对象&#xff09;都可以当作键。Map 也可以跟踪键值对的原始插入顺序。 1.1 基本用法 // 创建一个空Map…...

使用wpa_supplicant和wpa_cli 扫描wifi热点及配网

一&#xff1a;简要说明 交叉编译wpa_supplicant工具后会有wpa_supplicant和wpa_cli两个程序生产&#xff0c;如果知道需要连接的wifi热点及密码的话不需要遍历及查询所有wifi热点的名字及信号强度等信息的话&#xff0c;使用wpa_supplicant即可&#xff0c;否则还需要使用wpa_…...

json转excel,在excel内导入json, json-to-excel插件

简介 JSON 转 Excel 是一款 Microsoft Excel 插件&#xff0c;可将 JSON 数据转换为 Excel 表格。 要求 此插件适用于以下环境&#xff1a;Excel 2013 Service Pack 1 或更高版本、Excel 2016 for Mac、Excel 2016 或更高版本、Excel Online。 快速开始 本快速开始指南适用…...

大模型应用与实战:专栏概要与内容目录

文章目录 大模型应用与实战&#x1f4da; 核心内容模块一、大模型推理与部署1.1 推理框架应用实践1.2 框架源码深度解析1.3 高并发部署优化1.4 国产化平台适配 二、Agent框架专题2.1 Langchain系列2.2 Qwen-Agent系列2.3 Dify应用实践2.4 框架对比与迁移 三、微调技术研究3.1 微…...

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

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

Arbess基础教程-创建流水线

Arbess(谐音阿尔卑斯) 是一款开源免费的 CI/CD 工具&#xff0c;本文将介绍如何使用 Arbess 配置你的第一条流水线&#xff0c;以快速入门上手。 1. 创建流水线 根据不同需求来创建不同的流水线。 1.1 配置基本信息 配置流水线的基本信息&#xff0c;如分组&#xff0c;环境&…...

科普书《从一到无穷大》的科普知识推翻百年集论

科普书《从一到无穷大》的科普知识推翻百年集论 黄小宁 “我们给两组无穷大数列中的各个数一一配对&#xff0c;如果最后这两组都一个不剩&#xff0c;这两组无穷大就是相等的&#xff1b;如果有一组还有些数没有配出去&#xff0c;这一组就比另一组大些&#xff0c;或者说强些…...