不用第三方库调用DeepSeek

news2025/4/17 19:58:53

又双叒叕很久不写博客,今天吐一口老曹。

一、为啥干这个

        之前在修改OJ的时候,本着少修改多收益的原则,用Python写了一些DeepSeek的调用,真的很简单,用拉下来OpenAI按照官方文档复制粘贴就可以。接口文档页面(首次调用 API | DeepSeek API Docs)上至今有三种:curl(哦天哪,我在捣鼓ubuntu的时候好像用这个)、python(这个看着脸熟……当时敲的就是,那没事了)、node.js(dot是后加的,不然还真不认识)。

        但是,随着需求越搞越多,顺手就祭起冷门编程语言VB.NET了。完成了各种远程多Judge服务器之后,发现".py"越看越不顺眼。于是乎,来吧NuGet一下……emmmmm……两下、三四五六下,He,Tui~~~

        然后,再次祭起冷门语言VB.NET。因为之前就是多线程在搞一些队列呀、JudgeAPI呀,所以这个线程突然就顺手了。众所周知,我也不知道到底OpenAI支持多少参数,咱也搜不到,咱也不敢搜。所以,就比着能找到的就行了。并且,为了凑字数,成员定义的非常啰嗦:

Imports System
Imports System.Net.Http
Imports System.Text
Imports System.Threading.Tasks
Imports System.Text.Json
Imports System.Text.Json.Serialization
Imports System.Net
Imports System.Threading

Public Class DeepSeekClient
    Private ReadOnly _sharedHttpClient As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _apiBaseUrl As String
    Private _model As String = "deepseek-chat"
    Private _temperature As Single = 0.1
    Private _maxTokens As Integer = 2048
    Private _top_p As Single = 0.2
    Private _stream As Boolean = False

    ''' <summary>
    ''' 初始化 DeepSeek 客户端(使用共享 HttpClient)
    ''' </summary>
    ''' <param name="sharedHttpClient">共享的 HttpClient 实例</param>
    ''' <param name="apiKey">API 密钥</param>
    ''' <param name="apiBaseUrl">API 基础地址 (可选,默认为官方地址)</param>
    Public Sub New(sharedHttpClient As HttpClient, apiKey As String, Optional apiBaseUrl As String = "https://api.deepseek.com/")
        If sharedHttpClient Is Nothing Then
            Throw New ArgumentNullException(NameOf(sharedHttpClient))
        End If
        If String.IsNullOrWhiteSpace(apiKey) Then
            Throw New ArgumentException("APIKey不能为空。", NameOf(apiKey))
        End If

        _sharedHttpClient = sharedHttpClient
        _apiKey = apiKey
        _apiBaseUrl = apiBaseUrl.TrimEnd("/"c)

        ' 如果 HttpClient 还没有 Authorization 头,则添加
        If Not _sharedHttpClient.DefaultRequestHeaders.Contains("Authorization") Then
            _sharedHttpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}")
        End If
    End Sub

    ''' <summary>
    ''' 设置模型名称
    ''' </summary>
    Public Property Model As String
        Get
            Return _model
        End Get
        Set(value As String)
            If String.IsNullOrWhiteSpace(value) Then
                Throw New ArgumentException("模型名称不能为空。")
            End If
            _model = value
        End Set
    End Property

    ''' <summary>
    ''' 设置温度参数 (0-2)
    ''' </summary>
    Public Property Temperature As Single
        Get
            Return _temperature
        End Get
        Set(value As Single)
            If value < 0 OrElse value > 2 Then
                Throw New ArgumentOutOfRangeException(NameOf(Temperature), "温度值应在[0,2]区间")
            End If
            _temperature = value
        End Set
    End Property

    ''' <summary>
    ''' 设置最大令牌数
    ''' </summary>
    Public Property MaxTokens As Integer
        Get
            Return _maxTokens
        End Get
        Set(value As Integer)
            If value <= 0 Then
                Throw New ArgumentOutOfRangeException(NameOf(MaxTokens), "最大Token数不应小于0。")
            End If
            _maxTokens = value
        End Set
    End Property

    ''' <summary>
    ''' 生成多样性控制
    ''' </summary>
    Public Property Top_p As Single
        Get
            Return _top_p
        End Get
        Set(value As Single)
            If value < 0 OrElse value > 1 Then
                Throw New ArgumentOutOfRangeException(NameOf(Temperature), "Top_p应在[0,1]区间。")
            End If
            _top_p = value
        End Set
    End Property

    ''' <summary>
    ''' 是否流式响应
    ''' </summary>
    Public Property Stream As Boolean
        Get
            Return _stream
        End Get
        Set(value As Boolean)
            _stream = value
        End Set
    End Property


    ''' <summary>
    ''' 发送消息到 DeepSeek API 并获取响应
    ''' </summary>
    ''' <param name="userMessage">用户消息内容</param>
    ''' <returns>API 响应内容</returns>
    Public Async Function SendMessageAsync(systemMessage As String, userMessage As String, timeout As TimeSpan) As Task(Of String)
        If String.IsNullOrWhiteSpace(systemMessage) Then
            Throw New ArgumentException("系统消息不能为空。", NameOf(systemMessage))
        End If
        If String.IsNullOrWhiteSpace(userMessage) Then
            Throw New ArgumentException("用户消息不能为空。", NameOf(userMessage))
        End If
        If timeout = Nothing Then
            Throw New ArgumentException("超时时间不能为空。", NameOf(timeout))
        End If
        ' 创建请求体对象
        Dim requestBody = New DeepSeekRequest With {
            .Model = _model,
            .Messages = New List(Of ChatMessage) From {
                New ChatMessage With {.Role = "system", .Content = systemMessage},
                New ChatMessage With {.Role = "user", .Content = userMessage}
            },
            .Temperature = _temperature,
            .MaxTokens = _maxTokens,
            .Top_p = Top_p,
            .Stream = False
        }

        ' 使用 System.Text.Json 序列化
        Dim jsonContent = JsonSerializer.Serialize(requestBody)
        Dim content = New StringContent(jsonContent, Encoding.UTF8, "application/json")
        Try
            ' 创建带有超时设置的请求
            Dim request = New HttpRequestMessage(HttpMethod.Post, $"{_apiBaseUrl}/chat/completions") With {
                .Content = content
            }

            ' 使用 CancellationTokenSource 实现单次请求超时
            Using cts = New CancellationTokenSource(timeout)

                Dim response = Await _sharedHttpClient.SendAsync(request, cts.Token)
                Dim responseContent = Await response.Content.ReadAsStringAsync()

                If Not response.IsSuccessStatusCode Then
                    HandleApiError(response.StatusCode, responseContent)
                End If

                ' 使用 System.Text.Json 反序列化
                Dim responseObject = JsonSerializer.Deserialize(Of DeepSeekResponse)(responseContent)
                Return responseObject?.Choices?.FirstOrDefault()?.Message?.Content
            End Using
        Catch ex As TaskCanceledException When Not ex.CancellationToken.IsCancellationRequested
            Throw New TimeoutException("请求超时。", ex)
        Catch ex As OperationCanceledException
            Throw New TimeoutException("由于超时,请求被取消。", ex)
        Catch ex As HttpRequestException
            Throw New Exception("发送请求时出错。", ex)
        End Try
    End Function

    ''' <summary>
    ''' 处理 API 错误响应
    ''' </summary>
    Private Sub HandleApiError(statusCode As HttpStatusCode, responseContent As String)
        Dim errorMessage As String = "发生未知错误"

        Try
            If Not String.IsNullOrEmpty(responseContent) Then
                Dim errorResponse = JsonSerializer.Deserialize(Of DeepSeekErrorResponse)(responseContent)
                errorMessage = If(errorResponse?.Error?.Message, errorMessage)
            End If
        Catch
            ' 如果 JSON 解析失败,使用默认错误消息
        End Try

        Select Case statusCode
            Case HttpStatusCode.BadRequest ' 400
                Throw New DeepSeekApiException("错误请求:" & errorMessage, statusCode)
            Case HttpStatusCode.Unauthorized ' 401
                Throw New DeepSeekApiException("身份验证失败。请检查您的API密钥。", statusCode)
            Case HttpStatusCode.PaymentRequired ' 402
                Throw New DeepSeekApiException("余额不足。请给您的帐户充值。", statusCode)
            Case CType(422, HttpStatusCode) ' 422
                Throw New DeepSeekApiException("参数错误。" & errorMessage, statusCode)
            Case CType(429, HttpStatusCode) ' 429
                Throw New DeepSeekApiException("超出速率限制。请降低您的请求频率。", statusCode)
            Case HttpStatusCode.InternalServerError ' 500
                Throw New DeepSeekApiException("服务器错误。请稍后再试。", statusCode)
            Case HttpStatusCode.ServiceUnavailable ' 503
                Throw New DeepSeekApiException("服务器繁忙。请稍后再试。", statusCode)
            Case Else
                Throw New DeepSeekApiException($"API 错误 ({statusCode}): {errorMessage}", statusCode)
        End Select
    End Sub
End Class

' 请求和响应数据模型
Public Class DeepSeekRequest
    <JsonPropertyName("model")>
    Public Property Model As String

    <JsonPropertyName("messages")>
    Public Property Messages As List(Of ChatMessage)

    <JsonPropertyName("temperature")>
    Public Property Temperature As Single

    <JsonPropertyName("top_p")>
    Public Property Top_p As Single

    <JsonPropertyName("max_tokens")>
    Public Property MaxTokens As Integer

    <JsonPropertyName("stream")>
    Public Property Stream As Boolean
End Class

Public Class ChatMessage
    <JsonPropertyName("role")>
    Public Property Role As String

    <JsonPropertyName("content")>
    Public Property Content As String
End Class

Public Class DeepSeekResponse
    <JsonPropertyName("choices")>
    Public Property Choices As List(Of Choice)
End Class

Public Class Choice
    <JsonPropertyName("message")>
    Public Property Message As ChatMessage
End Class

Public Class DeepSeekErrorResponse
    <JsonPropertyName("error")>
    Public Property [Error] As ApiError
End Class

Public Class ApiError
    <JsonPropertyName("message")>
    Public Property Message As String

    <JsonPropertyName("type")>
    Public Property Type As String

    <JsonPropertyName("code")>
    Public Property Code As String
End Class

''' <summary>
''' DeepSeek API 异常类
''' </summary>
Public Class DeepSeekApiException
    Inherits Exception

    Public ReadOnly Property StatusCode As HttpStatusCode

    Public Sub New(message As String, statusCode As HttpStatusCode)
        MyBase.New(message)
        Me.StatusCode = statusCode
    End Sub

    Public Sub New(message As String, statusCode As HttpStatusCode, innerException As Exception)
        MyBase.New(message, innerException)
        Me.StatusCode = statusCode
    End Sub
End Class

        以上就是DeepSeek写的调用自己的代码(添加了几个属性、增加了几个参数、翻译了几段英文错误说明)之后的样子。

        接下来就是调用咯:

    ' 全局共享的 HttpClient
    Private Shared ReadOnly HttpClientInstance As New HttpClient()

    Async Function TestDeepSeekClient(modeName As String, systemMessage As String, userMessage As String, timeOut As TimeSpan) As Task(Of (Boolean, String))
        Try
            ' 初始化客户端(使用共享的 HttpClient)
            Dim apiKey = "sk-差点得去换APIKEY- -!!!"
            Dim client = New DeepSeekClient(HttpClientInstance, apiKey)

            ' 自定义参数 (可选)
            client.Model = modeName
            client.Temperature = 0.1
            client.MaxTokens = 1024
            client.Top_p = 0.2
            client.Stream = False
            ' 发送消息
            Dim response = Await client.SendMessageAsync(systemMessage, userMessage, timeOut)
            Return (True, response)
        Catch ex As DeepSeekApiException
            Return (False, $"API Error ({ex.StatusCode}): {ex.Message}")
        Catch ex As Exception
            Return (False, $"Error: {ex.Message}")
        End Try
    End Function

        最后,就是调用的调用:

    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        '"deepseek-chat"  "deepseek-reasoner"
        Dim result = Await TestDeepSeekClient("deepseek-chat", "你是一个AI。", "你的名字是?", TimeSpan.FromSeconds(600))
        If result.Item1 Then
            Debug.Print(result.Item2)
        Else
            Debug.Print("发生错误:" & result.Item2)
        End If
        Debug.Print("完成。")
    End Sub

        好了,到此为止吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2330922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Proximal Policy Optimization (PPO)

2.1 策略梯度方法 策略梯度方法计算策略梯度的估计值并将其插入到随机梯度上升算法中。最常用的梯度估计器的形式如下&#xff1a; g ^ E t [ ∇ θ log ⁡ π θ ( a t ∣ s t ) A ^ t ] (1) \hat{g} \mathbb{E}_t \left[ \nabla_{\theta} \log \pi_{\theta}(a_t | s_t) \h…

微信小程序:动态表格实现,表头单元格数据完全从data中获取,宽度自定义,自定义文本框,行勾选,样式效果,横向滚动表格(解决背景色不足的问题)等

一、样式效果 二、代码 1、wxml <view class"line flex flex-center"><view class"none" wx:if"{{info.length 0}}">暂无料号</view><view wx:else class"table-container"><!-- 动态生成表头 -->&…

python-Leetcode 65.搜索旋转排序数组

题目&#xff1a; 整数数组nums按升序排列&#xff0c;数组中的值互不相同 在传递给函数之前&#xff0c;nums在预先未知的某个小标K上进行了旋转&#xff0c;使数组变为[nums[k], nums[k1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]&#xff0c;小标从0开始计数。…

Django学习记录-1

Django学习记录-1 虽然网上教程都很多&#xff0c;但是感觉自己记录一下才属于自己&#xff0c;之后想找也方面一点&#xff0c;文采不佳看的不爽可绕道。 参考贴 从零开始的Django框架入门到实战教程(内含实战实例) - 01 创建项目与app、加入静态文件、模板语法介绍&#xff…

K8s私有仓库拉取镜像报错解决:x509 certificate signed by unknown authority

前言 在Kubernetes环境中使用自签名证书的私有Harbor镜像仓库时&#xff0c;常会遇到证书验证失败的问题。本文将详细讲解如何解决这个常见的证书问题。 环境信息&#xff1a; Kubernetes版本&#xff1a;1.28.2容器运行时&#xff1a;containerd 1.6.20私有仓库&#xff1a…

LabVIEW 长期项目开发

LabVIEW 凭借其图形化编程的独特优势&#xff0c;在工业自动化、测试测量等领域得到了广泛应用。对于长期运行、持续迭代的 LabVIEW 项目而言&#xff0c;其开发过程涵盖架构设计、代码管理、性能优化等多个关键环节&#xff0c;每个环节都对项目的成功起着至关重要的作用。下面…

蓝桥杯真题——好数、R格式

目录 蓝桥杯2024年第十五届省赛真题-好数 【模拟题】 题目描述 输入格式 输出格式 样例输入 样例输出 提示 代码1&#xff1a;有两个案例过不了&#xff0c;超时 蓝桥杯2024年第十五届省赛真题-R 格式 【vector容器的使用】 题目描述 输入格式 输出格式 样例输入…

Windows版-RabbitMQ自动化部署

一键完成Erlang环境变量配置&#xff08;ERLANG_HOME系统变量&#xff09;‌ 一键完成RabbitMQ环境变量配置&#xff08;RabbitMQ系统变量&#xff09;‌ 实现快速安装部署RabbitMQ PS&#xff1a; 需提前下载安装&#xff1a; - otp_win64_25.0.exe (Erlang) - rabbit…

openEuler24.03 LTS下安装Flink

目录 Flink的安装模式下载Flink安装Local模式前提条件解压安装包启动集群查看进程提交作业文件WordCount持续流WordCount 查看Web UI配置flink-conf.yaml简单使用 关闭集群 Standalone Session模式前提条件Flink集群规划解压安装包配置flink配置flink-conf.yaml配置workers配置…

从零开始学java--泛型(1)

泛型 学生成绩可能是数字类型&#xff0c;也可能是字符串类型&#xff0c;如何存放可能出现的两种类型呢&#xff1a; public class Score {String name;String id;Object value; //因为Object是所有类型的父类&#xff0c;因此既可以存放Integer也能存放Stringpublic Score…

【正点原子】STM32MP135去除SD卡引脚复用,出现 /dev/mmcblk1p5 not found!

如果在设备树中直接注释掉 sdmmc1 节点&#xff0c;就会导致系统启动时识别不到真正的 eMMC 设备&#xff0c;进而挂载失败&#xff0c;爆出 /dev/mmcblk1p5 not found 的问题。 正点原子STM32MP135开发板Linux核心板嵌入式ARM双千兆以太网CAN 正确操作是“放空”而不是“删光…

CrystalDiskInfo电脑硬盘监控工具 v9.6.0中文绿色便携版

前言 CrystalDiskInfo是一个不用花钱的硬盘小帮手软件&#xff0c;它可以帮你看看你的电脑硬盘工作得怎么样&#xff0c;健不健康。这个软件能显示硬盘的温度高不高、还有多少地方没用、传输东西快不快等等好多信息。用了它&#xff0c;你就能很容易地知道硬盘现在是什么情况&…

详解模型蒸馏,破解DeepSeek性能谜题

大家好&#xff0c;不少关注 DeepSeek 最新动态的朋友&#xff0c;想必都遇到过 “Distillation”&#xff08;蒸馏&#xff09;这一术语。本文将介绍模型蒸馏技术的原理&#xff0c;同时借助 TensorFlow 框架中的实例进行详细演示。通过本文&#xff0c;对模型蒸馏有更深的认识…

园区网拓扑作业

作业要求&#xff1a; 需求&#xff1a; 需求分析&#xff1a; 1.按照图示的VLAN及IP地址需求&#xff0c;完成相关配需&#xff1a;VLAN 2、3、20、30 已分配子网&#xff0c;需在交换机上创建 VLAN 并配置三层接口作为网关。确保各 VLAN 内设备能互通&#xff0c;跨 VLAN 通…

隔行换色总结

功能效果展示&#xff1a; 第一种思路&#xff1a; 使用数组&#xff0c;将数组的内容渲染到页面上&#xff0c;序号也就是将数组的下标输出到第一个td上&#xff0c;将数组的内容输出到第二个td上&#xff0c;&#xff08;使用拼接字符串&#xff09; 具体操作&#xff1a; …

使用Docker Desktop进行本地打包和推送

使用Docker Desktop进行本地打包和推送 一、Docker Desktop配置二、IDEA配置1.下载Docker插件2.在“Settings”中&#xff0c;配置“Docker”3.选择“Docker Registry”&#xff0c;配置远程仓库。 三、POM配置 一共有三个地方需要配置 一、Docker Desktop配置 在Docker Deskt…

Redis主从复制:告别单身Redis!

目录 一、 为什么需要主从复制&#xff1f;&#x1f914;二、 如何搭建主从架构&#xff1f;前提条件✅步骤&#x1f4c1; 创建工作目录&#x1f4dc; 创建 Docker Compose 配置文件&#x1f680; 启动所有 Redis&#x1f50d; 验证主从状态 &#x1f4a1; 重要提示和后续改进 …

ORM、Mybatis和Hibernate、Mybatis使用教程、parameterType、resultType、级联查询案例、resultMap映射

DAY21.1 Java核心基础 ORM Object Relationship Mapping 对象关系映射 面向对象的程序到—关系型数据库的映射 比如java – MySQL的映射 ORM框架就是实现这个映射的框架 Hibernate、Mybatis、MybatisPlus、Spring Data JPA、Spring JDBC Spring Data JPA的底层就是Hiber…

C#调用Lua方法1+C#调用Lua方法2,3

xLua中Lua调用C#代码 原因&#xff1a;C#实现的系统&#xff0c;因为Lua可以调用&#xff0c;所以完全可以换成Lua实现&#xff0c;因为Lua可以即时更改&#xff0c;即时运行&#xff0c;所以游戏的代码逻辑就可以随时更改。 实现和C#相同效果的系统&#xff0c;如何实现&#…

NO.77十六届蓝桥杯备战|数据结构-单调队列|质量检测(C++)

什么是单调队列&#xff1f; 单调队列&#xff0c;顾名思义&#xff0c;就是存储的元素要么单调递增要么单调递减的队列。注意&#xff0c;这⾥的队列和普通的队列不⼀样&#xff0c;是⼀个双端队列。单调队列解决的问题 ⼀般⽤于解决滑动窗⼝内最⼤值最⼩值问题&#xff0c;以…