Token导航 LogoToken导航TokenDH.com
开发敏感数据github未标认证来源可访问许可证需确认审计异常

deepseek-integrationDeepSeek 集成

Agent Skill

deepseek-integration 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

222

周安装

9

GitHub Stars

1

下载量

70
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:deepseek-integration(DeepSeek 集成)
来源仓库:https://github.com/cacr92/wereply
仓库路径:skills/deepseek-integration
安装命令:
npx skills add https://github.com/cacr92/wereply --skill deepseek-integration
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/cacr92/wereply --skill deepseek-integration

简介

deepseek-integration 提供 DeepSeek API 与 reqwest HTTP 客户端的深度集成指南,支持流式响应与错误处理。

  • 适用于需要将 AI 生成能力嵌入到 Rust 应用中的开发者,强调连接池与令牌安全管理。
  • 包含非流式 JSON 与 Server-Sent Events 两种返回格式的配置示例。
  • 使用前请准备好有效的 DeepSeek API 密钥,并确保运行环境支持 Rust 3.12+。
  • 涉及外部 API 调用时应注意配额限制与超时设置,避免阻塞主流程。

SKILL.md

DeepSeek Integration Skill

Expert guidance for integrating DeepSeek API with reqwest HTTP client, streaming responses, and error handling.

Overview

WeReply uses DeepSeek API to generate reply suggestions:

  • HTTP Client: reqwest with connection pooling
  • API Endpoint: https://api.deepseek.com/v1/chat/completions
  • Authentication: API key via Bearer token
  • Response Format: JSON (non-streaming) or Server-Sent Events (streaming)
  • Configuration: API key stored in system keychain

HTTP Client Configuration

Reqwest Client Setup

use reqwest::{Client, ClientBuilder};
use std::time::Duration;
use std::sync::Arc;

pub struct DeepSeekClient {
    client: Arc<Client>,
    api_key: String,
    api_endpoint: String,
}

impl DeepSeekClient {
    pub fn new(api_key: String) -> anyhow::Result<Self> {
        let client = ClientBuilder::new()
            .pool_max_idle_per_host(10)  // 连接池最大空闲连接数
            .timeout(Duration::from_secs(30))  // 请求超时30秒
            .connect_timeout(Duration::from_secs(10))  // 连接超时10秒
            .build()?;

        Ok(Self {
            client: Arc::new(client),
            api_key,
            api_endpoint: "https://api.deepseek.com/v1/chat/completions".to_string(),
        })
    }

    pub fn with_custom_endpoint(mut self, endpoint: String) -> Self {
        self.api_endpoint = endpoint;
        self
    }
}

Connection Pooling Best Practices

// ✓ 共享 Client 实例(连接池复用)
pub struct AppState {
    deepseek_client: Arc<DeepSeekClient>,
}

// ✗ 每次创建新 Client(无连接池复用)
pub async fn bad_example() {
    let client = DeepSeekClient::new(api_key).unwrap();  // 不要这样做
}

API Request Pattern

Basic Request/Response

use serde::{Deserialize, Serialize};

#[derive(Serialize)]
pub struct ChatCompletionRequest {
    model: String,
    messages: Vec<ChatMessage>,
    #[serde(skip_serializing_if = "Option::is_none")]
    temperature: Option<f32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    max_tokens: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    stream: Option<bool>,
}

#[derive(Serialize, Deserialize)]
pub struct ChatMessage {
    role: String,  // "system", "user", "assistant"
    content: String,
}

#[derive(Deserialize)]
pub struct ChatCompletionResponse {
    id: String,
    model: String,
    choices: Vec<ChatChoice>,
    usage: Usage,
}

#[derive(Deserialize)]
pub struct ChatChoice {
    index: u32,
    message: ChatMessage,
    finish_reason: String,
}

#[derive(Deserialize)]
pub struct Usage {
    prompt_tokens: u32,
    completion_tokens: u32,
    total_tokens: u32,
}

Making API Requests

impl DeepSeekClient {
    pub async fn generate_completion(
        &self,
        messages: Vec<ChatMessage>,
    ) -> anyhow::Result<ChatCompletionResponse> {
        let request = ChatCompletionRequest {
            model: "deepseek-chat".to_string(),
            messages,
            temperature: Some(0.7),
            max_tokens: Some(1000),
            stream: Some(false),
        };

        let response = self.client
            .post(&self.api_endpoint)
            .header("Authorization", format!("Bearer {}", self.api_key))
            .header("Content-Type", "application/json")
            .json(&request)
            .send()
            .await?;

        // 检查 HTTP 状态码
        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await.unwrap_or_default();
            return Err(anyhow!(
                "DeepSeek API 请求失败: {} - {}",
                status,
                error_text
            ));
        }

        // 解析响应
        let completion = response.json::<ChatCompletionResponse>().await?;

        Ok(completion)
    }
}

Streaming Response Pattern

Event Stream Processing

use futures::stream::StreamExt;
use serde_json;

impl DeepSeekClient {
    pub async fn generate_completion_stream(
        &self,
        messages: Vec<ChatMessage>,
        on_chunk: impl Fn(String) + Send + 'static,
    ) -> anyhow::Result<String> {
        let request = ChatCompletionRequest {
            model: "deepseek-chat".to_string(),
            messages,
            temperature: Some(0.7),
            max_tokens: Some(1000),
            stream: Some(true),  // 启用流式响应
        };

        let response = self.client
            .post(&self.api_endpoint)
            .header("Authorization", format!("Bearer {}", self.api_key))
            .header("Content-Type", "application/json")
            .json(&request)
            .send()
            .await?;

        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await.unwrap_or_default();
            return Err(anyhow!(
                "DeepSeek API 请求失败: {} - {}",
                status,
                error_text
            ));
        }

        let mut stream = response.bytes_stream();
        let mut full_content = String::new();
        let mut buffer = String::new();

        while let Some(chunk_result) = stream.next().await {
            let chunk = chunk_result?;
            let chunk_str = String::from_utf8_lossy(&chunk);

            buffer.push_str(&chunk_str);

            // 处理 SSE 格式:data: {...}\n\ndata: {...}\n\n
            for line in buffer.lines() {
                if line.starts_with("data: ") {
                    let json_str = &line[6..];  // 去掉 "data: " 前缀

                    if json_str == "[DONE]" {
                        break;
                    }

                    if let Ok(chunk_data) = serde_json::from_str::<StreamChunk>(json_str) {
                        if let Some(choice) = chunk_data.choices.first() {
                            if let Some(content) = &choice.delta.content {
                                full_content.push_str(content);
                                on_chunk(content.clone());
                            }
                        }
                    }
                }
            }

            buffer.clear();
        }

        Ok(full_content)
    }
}

#[derive(Deserialize)]
struct StreamChunk {
    choices: Vec<StreamChoice>,
}

#[derive(Deserialize)]
struct StreamChoice {
    delta: Delta,
    finish_reason: Option<String>,
}

#[derive(Deserialize)]
struct Delta {
    #[serde(skip_serializing_if = "Option::is_none")]
    content: Option<String>,
}

Tauri Event Integration

use tauri::Manager;

#[tauri::command]
#[specta::specta]
pub async fn generate_suggestions_stream(
    context_messages: Vec<String>,
    style: String,
    state: State<'_, AppState>,
    app_handle: tauri::AppHandle,
) -> ApiResponse<String> {
    let client = state.deepseek_client.clone();

    // 构建消息
    let messages = build_chat_messages(context_messages, style);

    // 流式生成,实时发送到前端
    match client.generate_completion_stream(messages, move |chunk| {
        // 发送 chunk 到前端
        app_handle.emit_all("suggestion-chunk", chunk).ok();
    }).await {
        Ok(full_text) => api_ok(full_text),
        Err(e) => api_err(format!("生成建议失败: {}", e)),
    }
}

Error Handling and Retry

Exponential Backoff Retry

use tokio::time::{sleep, Duration};

impl DeepSeekClient {
    pub async fn generate_completion_with_retry(
        &self,
        messages: Vec<ChatMessage>,
        max_retries: u32,
    ) -> anyhow::Result<ChatCompletionResponse> {
        let mut retry_count = 0;
        let mut backoff_ms = 1000;  // 初始退避时间 1 秒

        loop {
            match self.generate_completion(messages.clone()).await {
                Ok(response) => return Ok(response),
                Err(e) => {
                    retry_count += 1;

                    if retry_count > max_retries {
                        return Err(anyhow!(
                            "DeepSeek API 调用失败,已重试 {} 次: {}",
                            max_retries,
                            e
                        ));
                    }

                    // 判断是否可重试的错误
                    if !is_retryable_error(&e) {
                        return Err(e);
                    }

                    tracing::warn!(
                        error = %e,
                        retry_count = retry_count,
                        backoff_ms = backoff_ms,
                        "DeepSeek API 调用失败,等待重试"
                    );

                    // 指数退避
                    sleep(Duration::from_millis(backoff_ms)).await;
                    backoff_ms = (backoff_ms * 2).min(30000);  // 最大退避 30 秒
                }
            }
        }
    }
}

fn is_retryable_error(error: &anyhow::Error) -> bool {
    let error_msg = error.to_string().to_lowercase();

    // 网络错误、超时、429 (Rate Limit)、5xx 服务器错误可重试
    error_msg.contains("timeout")
        || error_msg.contains("network")
        || error_msg.contains("429")
        || error_msg.contains("500")
        || error_msg.contains("502")
        || error_msg.contains("503")
        || error_msg.contains("504")
}

Error Classification

#[derive(Debug, thiserror::Error)]
pub enum DeepSeekError {
    #[error("API 密钥无效")]
    InvalidApiKey,

    #[error("请求超时")]
    Timeout,

    #[error("请求频率超限")]
    RateLimitExceeded,

    #[error("服务不可用")]
    ServiceUnavailable,

    #[error("网络错误: {0}")]
    NetworkError(String),

    #[error("响应解析错误: {0}")]
    ParseError(String),

    #[error("未知错误: {0}")]
    Unknown(String),
}

impl DeepSeekClient {
    fn classify_error(status_code: u16, body: &str) -> DeepSeekError {
        match status_code {
            401 => DeepSeekError::InvalidApiKey,
            408 | 504 => DeepSeekError::Timeout,
            429 => DeepSeekError::RateLimitExceeded,
            500 | 502 | 503 => DeepSeekError::ServiceUnavailable,
            _ => DeepSeekError::Unknown(body.to_string()),
        }
    }
}

Message Construction Pattern

Building Chat Messages

pub fn build_chat_messages(
    context_messages: Vec<String>,
    style: &str,
) -> Vec<ChatMessage> {
    let mut messages = Vec::new();

    // 系统提示词
    let system_prompt = format!(
        "你是一个微信回复建议助手。请根据聊天上下文,用{}风格生成3条简短的回复建议。\
        每条建议不超过50字,自然流畅,符合微信聊天习惯。",
        get_style_description(style)
    );

    messages.push(ChatMessage {
        role: "system".to_string(),
        content: system_prompt,
    });

    // 聊天上下文
    let context = context_messages.join("\n");
    messages.push(ChatMessage {
        role: "user".to_string(),
        content: format!("聊天记录:\n{}\n\n请生成3条回复建议:", context),
    });

    messages
}

fn get_style_description(style: &str) -> &str {
    match style {
        "formal" => "正式、礼貌",
        "friendly" => "亲切、友好",
        "humorous" => "幽默、轻松",
        _ => "自然、随和",
    }
}

Response Parsing

pub fn parse_suggestions(response_text: &str) -> Vec<String> {
    // DeepSeek 可能返回带编号的列表,如:
    // 1. 建议1
    // 2. 建议2
    // 3. 建议3

    let mut suggestions = Vec::new();

    for line in response_text.lines() {
        let trimmed = line.trim();

        // 匹配带编号的建议(如 "1. xxx" 或 "1) xxx")
        if let Some(content) = trimmed.strip_prefix(|c: char| c.is_numeric()) {
            if let Some(content) = content.strip_prefix(". ").or(content.strip_prefix(") ")) {
                suggestions.push(content.trim().to_string());
            }
        }
    }

    // 如果没找到带编号的建议,尝试按换行分割
    if suggestions.is_empty() {
        suggestions = response_text
            .lines()
            .map(|l| l.trim().to_string())
            .filter(|l| !l.is_empty())
            .collect();
    }

    suggestions
}

Configuration Management

API Key from System Keychain

use keyring::Entry;

pub fn get_deepseek_api_key() -> anyhow::Result<String> {
    let entry = Entry::new("wereply", "deepseek_api_key")?;
    entry.get_password()
        .context("未找到 DeepSeek API 密钥,请在设置中配置")
}

pub fn set_deepseek_api_key(api_key: &str) -> anyhow::Result<()> {
    let entry = Entry::new("wereply", "deepseek_api_key")?;
    entry.set_password(api_key)?;
    Ok(())
}

Environment Variable Fallback

use std::env;

pub fn get_api_endpoint() -> String {
    env::var("DEEPSEEK_API_ENDPOINT")
        .unwrap_or_else(|_| "https://api.deepseek.com/v1/chat/completions".to_string())
}

pub fn get_model_name() -> String {
    env::var("DEEPSEEK_MODEL")
        .unwrap_or_else(|_| "deepseek-chat".to_string())
}

Testing

Mock HTTP Client

#[cfg(test)]
mod tests {
    use super::*;
    use mockito::{mock, server_url};

    #[tokio::test]
    async fn test_generate_completion() {
        // 模拟 DeepSeek API 响应
        let _m = mock("POST", "/v1/chat/completions")
            .with_status(200)
            .with_header("content-type", "application/json")
            .with_body(r#"{
                "id": "chatcmpl-123",
                "model": "deepseek-chat",
                "choices": [{
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": "1. 好的,谢谢!\n2. 收到,马上处理。\n3. 了解,没问题。"
                    },
                    "finish_reason": "stop"
                }],
                "usage": {
                    "prompt_tokens": 20,
                    "completion_tokens": 30,
                    "total_tokens": 50
                }
            }"#)
            .create();

        let client = DeepSeekClient::new("test_api_key".to_string())
            .unwrap()
            .with_custom_endpoint(server_url());

        let messages = vec![
            ChatMessage {
                role: "user".to_string(),
                content: "测试消息".to_string(),
            },
        ];

        let response = client.generate_completion(messages).await.unwrap();

        assert_eq!(response.model, "deepseek-chat");
        assert_eq!(response.choices.len(), 1);
        assert!(response.choices[0].message.content.contains("好的"));
    }

    #[tokio::test]
    async fn test_retry_on_error() {
        // 模拟失败后成功的情况
        let _m1 = mock("POST", "/v1/chat/completions")
            .with_status(503)
            .with_body("Service Unavailable")
            .create();

        let _m2 = mock("POST", "/v1/chat/completions")
            .with_status(200)
            .with_body(r#"{"id": "test", "choices": []}"#)
            .create();

        let client = DeepSeekClient::new("test_key".to_string())
            .unwrap()
            .with_custom_endpoint(server_url());

        let messages = vec![];
        let result = client.generate_completion_with_retry(messages, 3).await;

        assert!(result.is_ok());
    }
}

Performance Optimization

Request Timeout Configuration

pub struct DeepSeekConfig {
    pub connect_timeout_secs: u64,
    pub request_timeout_secs: u64,
    pub max_retries: u32,
}

impl Default for DeepSeekConfig {
    fn default() -> Self {
        Self {
            connect_timeout_secs: 10,
            request_timeout_secs: 30,
            max_retries: 3,
        }
    }
}

Batch Request Processing

use futures::future::join_all;

pub async fn generate_multiple_suggestions(
    &self,
    requests: Vec<Vec<ChatMessage>>,
) -> Vec<anyhow::Result<ChatCompletionResponse>> {
    // 并发处理多个请求
    let futures = requests.into_iter().map(|messages| {
        let client = self.clone();
        async move {
            client.generate_completion(messages).await
        }
    });

    join_all(futures).await
}

Security Best Practices

API Key Validation

pub fn validate_api_key(api_key: &str) -> anyhow::Result<()> {
    if api_key.is_empty() {
        return Err(anyhow!("API 密钥不能为空"));
    }

    if !api_key.starts_with("sk-") {
        return Err(anyhow!("API 密钥格式错误"));
    }

    if api_key.len() < 20 {
        return Err(anyhow!("API 密钥长度不足"));
    }

    Ok(())
}

Logging Without Sensitive Data

use tracing::info;

pub async fn log_api_call(
    &self,
    message_count: usize,
    response_length: usize,
) {
    info!(
        message_count = message_count,
        response_length = response_length,
        "DeepSeek API 调用成功"
    );
    // ❌ 不要记录 API 密钥或消息内容
}

When to Use This Skill

Activate this skill when:

  • Integrating DeepSeek API
  • Implementing AI-powered features
  • Working with HTTP clients (reqwest)
  • Handling streaming responses
  • Implementing retry logic
  • Managing API keys securely
  • Optimizing API performance
  • Testing API integrations

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.22%
按下载量换算23

Claude

31.61%
按下载量换算22

Cursor

18.72%
按下载量换算13

Gemini CLI

9.36%
按下载量换算7

安全审计

Gen Agent Trust Hub

通过

Socket

未通过

Snyk

可疑

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills