.Net接入AzureOpenAI、OpenAI、通义千问、智谱AI、讯飞星火、文心一言大语言模型。
前言
现在在网上搜索.NET接入大模型的帖子很少,有些官方案例只提供java和python的SDK,所以有了这篇.Net的接入大模型文章,目前仅实现对话模型的调用。
这里仅举例通义千问,其他模型实现可以参考Gi他Hub 对您有帮助的话帮忙点个star
个人博客:FaceMan' Blog 。
Github:FaceMan' GitHub 。
实现方式
1. 创建IModelExtensionsChatCompletionService对话服务,规范对话服务应实现的接口。
``` csharp
public interface IModelExtensionsChatCompletionService
{
/// <summary>
/// 对话
/// </summary>
/// <param name="chatHistory">对话历史</param>
/// <param name="settings">参数配置</param>
/// <param name="kernel">SK的kernel</param>
/// <param name="cancellationToken">是否取消</param>
/// <returns></returns>
Task<ChatMessageContent> GetChatMessageContentsAsync(ChatHistory chatHistory, OpenAIPromptExecutionSettings settings = null, Kernel kernel = null, CancellationToken cancellationToken = default);
/// <summary>
/// 流式对话
/// </summary>
/// <param name="chatHistory">对话历史</param>
/// <param name="settings">参数配置</param>
/// <param name="kernel">SK的kernel</param>
/// <param name="cancellationToken">是否取消</param>
/// <returns></returns>
IAsyncEnumerable<string> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, OpenAIPromptExecutionSettings settings = null, Kernel kernel = null, CancellationToken cancellationToken = default);
}
```
2. 创建ModelClient类做数据解析
```
public class ModelClient : IDisposable
{
internal readonly HttpClient HttpClient = null!;
public ModelClient(string apiKey, ModelType modelType, HttpClient? httpClient = null)
{
HttpClient = httpClient ?? new HttpClient();
switch (modelType)
{
case ModelType.ZhiPu:
int expirationInSeconds = 3600; // 设置过期时间为1小时
apiKey = GenerateJwtToken(apiKey, expirationInSeconds);
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
break;
case ModelType.QianWen:
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
break;
case ModelType.XunFei:
break;
}
QianWen = new QianWenClient(this);
ZhiPu = new ZhiPuClient(this);
XunFei = new XunFeiClient(this);
WenXin = new WenXinClient(this);
}
public QianWenClient QianWen { get; set; }
public ZhiPuClient ZhiPu { get; set; }
public XunFeiClient XunFei { get; set; }
public WenXinClient WenXin { get; set; }
/// <summary>
/// 处理基础HTTP客户端。
/// </summary>
public void Dispose() => HttpClient.Dispose();
/// <summary>
/// 数据流转换器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="response">响应体</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
internal static async Task<T> ReadResponse<T>(HttpResponseMessage response, CancellationToken cancellationToken)
{
if (!response.IsSuccessStatusCode)
{
throw new Exception(await response.Content.ReadAsStringAsync());
}
try
{
var debug = await response.Content.ReadAsStringAsync();
return (await response.Content.ReadFromJsonAsync<T>(options: null, cancellationToken))!;
}
catch (Exception e) when (e is NotSupportedException or System.Text.Json.JsonException)
{
throw new Exception($"未能将以下json转换为: {typeof(T).Name}: {await response.Content.ReadAsStringAsync()}", e);
}
}
/// <summary>
/// 讯飞星火 数据流转换器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="receivedMessage"></param>
/// <returns></returns>
public static XunFeiResponseWrapper ReadResponse<T>(string receivedMessage)
{
XunFeiResponseWrapper response = JsonConvert.DeserializeObject<XunFeiResponseWrapper>(receivedMessage);
return response;
}
/// <summary>
/// 智谱生成JWT令牌
/// </summary>
/// <param name="apiKey"></param>
/// <param name="expSeconds"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
internal string GenerateJwtToken(string apiKey, int expSeconds)
{
// 分割API Key以获取ID和Secret
var parts = apiKey.Split('.');
if (parts.Length != 2)
{
throw new ArgumentException("Invalid API key format.");
}
var id = parts[0];
var secret = parts[1];
// 创建Header信息
var header = new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)), SecurityAlgorithms.HmacSha256))
{
{"sign_type", "SIGN"}
};
// 创建Payload信息
long currentMillis = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var payload = new JwtPayload
{
{"api_key", id},
{"exp", currentMillis + expSeconds * 1000},
{"timestamp", currentMillis}
};
// 生成JWT Token
var token = new JwtSecurityToken(header, payload);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
```
3. 定义ModelType区分不同模型供应商
```
public enum ModelType
{
[Description("通义千问")]
[EnumName("通义千问")]
QianWen = 1,
[Description("智谱AI")]
[EnumName("智谱AI")]
ZhiPu,
[Description("科大讯飞")]
[EnumName("科大讯飞")]
XunFei,
[Description("文心一言")]
[EnumName("文心一言")]
WenXin,
}
```
4. 以通义千问为例,创建QianWenChatCompletionService类继承IModelExtensionsChatCompletionService
```
public class QianWenChatCompletionService : IModelExtensionsChatCompletionService
{
private readonly string _apiKey;
private readonly string _model;
public QianWenChatCompletionService(string key, string model)
{
_apiKey = key;
_model = model;
}
/// <summary>
/// 对话
/// </summary>
/// <param name="chatHistory"></param>
/// <param name="settings"></param>
/// <param name="kernel"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<ChatMessageContent> GetChatMessageContentsAsync(ChatHistory chatHistory, OpenAIPromptExecutionSettings settings = null, Kernel kernel = null, CancellationToken cancellationToken = default)
{
var histroyList = new List<ChatMessage>();
ChatParameters chatParameters = null;
foreach (var item in chatHistory)
{
var history = new ChatMessage()
{
Role = item.Role.Label,
Content = item.Content,
};
histroyList.Add(history);
}
if (settings != null)
{
chatParameters = new ChatParameters()
{
TopP = settings != null ? (float)settings.TopP : default,
MaxTokens = settings != null ? settings.MaxTokens : default,
Temperature = settings != null ? (float)settings.Temperature : default,
Seed = settings.Seed != null ? (ulong)settings.Seed : default,
Stop = settings != null ? settings.StopSequences : default,
//RepetitionPenalty = (float)settings.FrequencyPenalty,
//TopK = (int)settings.PresencePenalty
};
}
ModelClient client = new(_apiKey, ModelType.QianWen);
QianWenResponseWrapper result = await client.QianWen.GetChatMessageContentsAsync(_model, histroyList, chatParameters, cancellationToken);
var message = new ChatMessageContent(AuthorRole.Assistant, result.Output.Text);
return message;
}
/// <summary>
/// 流式对话
/// </summary>
/// <param name="chatHistory"></param>
/// <param name="settings"></param>
/// <param name="kernel"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async IAsyncEnumerable<string> GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, OpenAIPromptExecutionSettings settings = null, Kernel kernel = null, CancellationToken cancellationToken = default)
{
var histroyList = new List<ChatMessage>();
ChatParameters chatParameters = null;
foreach (var item in chatHistory)
{
var history = new ChatMessage()
{
Role = item.Role.Label,
Content = item.Content,
};
histroyList.Add(history);
}
if (settings != null)
{
chatParameters = new ChatParameters()
{
TopP = settings != null ? (float)settings.TopP : default,
MaxTokens = settings != null ? settings.MaxTokens : default,
Temperature = settings != null ? (float)settings.Temperature : default,
Seed = settings.Seed != null ? (ulong)settings.Seed : default,
Stop = settings != null ? settings.StopSequences : default,
};
}
ModelClient client = new(_apiKey, ModelType.QianWen);
await foreach (string item in client.QianWen.GetStreamingChatMessageContentsAsync(_model, histroyList, chatParameters, cancellationToken))
{
yield return item;
}
}
}
```
其中,OpenAIPromptExecutionSettings 和 ChatHistory 来自于SK框架,ChatParameters属于自定义的参数类,因为每家模型供应商都不一样。
5. ChatParameters的代码
```
public record ChatParameters
{
/// <summary>
/// 结果的格式-“text”为旧文本版本,“message”为OpenAI兼容消息。
/// <para>对于语言模型,此字段必须是中的“text”,而不是VL模型中使用的字段</para>
/// </summary>
[JsonPropertyName("result_format")]
public string? ResultFormat { get; set; }
/// <summary>
/// 随机数生成器的种子,用于控制模型生成的随机性。
/// 使用相同的种子允许模型输出的再现性。
/// <para>此字段为可选字段。默认值为1234。</para>
/// </summary>
[JsonPropertyName("seed")]
public ulong? Seed { get; set; }
/// <summary>
/// 限制要生成的令牌数量。限制设置了最大值,但不能保证
/// 确切地说,将生成那么多令牌。此字段是可选的。
/// <para>qwen turbo和qwen max longcontext的最大值和默认值为1500。</para>
/// <para>qwen max、qwen-max-1201和qwen plus的最大值和默认值为2048。</para>
/// </summary>
[JsonPropertyName("max_tokens")]
public int? MaxTokens { get; set; }
/// <summary>
/// 细胞核取样的概率阈值。以0.8的值为例,
/// 仅保留累积概率总和大于或等于0.8的令牌。
/// <para>取值范围为(0,1.0)。取值越大,随机性越高</para>
/// <para>值越小,随机性越低。此字段是可选的.</para>
/// <para>默认值为0.8。请注意,该值不应大于或等于1.</para>
/// </summary>
[JsonPropertyName("top_p")]
public float? TopP { get; set; }
/// <summary>
/// 要采样的候选集的大小。例如,当设置为50时,只有前50个令牌
/// 将考虑进行采样。此字段是可选的。较大的值会增加随机性;
/// 较小的值会增加确定性。注意:如果top_ k为null或大于100,
/// 没有使用topk策略,只有topp是有效的。默认值为null。
/// </summary>
[JsonPropertyName("top_k")]
public int? TopK { get; set; }
/// <summary>
/// 为减少模型生成中的冗余而应用重复的惩罚。
/// 值为1.0表示没有惩罚。此字段是可选的。
/// <para>默认值为1.1。</para>
/// </summary>
[JsonPropertyName("repetition_penalty")]
public float? RepetitionPenalty { get; set; }
/// <summary>
/// 控制文本生成的随机性和多样性程度。
/// 高温度值会降低概率分布的峰值、
/// 允许选择更多低概率词,从而产生更多样化的输出。
/// <para>
/// 低温度值会增加峰度,使高概率词更有可能被选中、
/// 从而使输出结果更加确定。此字段为可选项。
/// 数值范围为 [0, 2)。系统默认值为 1.0。
/// </para>
/// </summary>
[JsonPropertyName("temperature")]
public float? Temperature { get; set; }
/// <summary>
/// 指定生成后应停止模型进一步输出的内容。
/// <para>这可以是一个字符串或字符串列表、一个标记 ID 列表或一个标记 ID 列表。
/// <para>例如,如果将 stop 设置为 "hello",则在生成 "hello "之前停止生成;</para
/// <para>如果设置为[37763, 367],则在生成相当于 "Observation "的标记 ID 之前停止生成。
/// <para>
/// 注意,此字段为可选字段,列表模式不支持字符串和标记 ID 混合使用;</para> <para>
/// /// 注意,此字段为可选字段,列表模式不支持字符串和令牌 ID 混合使用。
/// </para>
/// </summary>
[JsonPropertyName("stop")]
public object? Stop { get; set; }
///<summary>
///控制在生成过程中是否考虑搜索结果。
///<para>注意:启用搜索并不保证会使用搜索结果</para>
///<para>
///如果启用了搜索,则模型会将搜索结果视为提示的一部分以潜在地生成包括结果的文本。
///</para>
///<para>此字段为可选字段,默认为false</段落>
///</summary>
[JsonPropertyName("enable_search")]
public bool? EnableSearch { get; set; }
/// <summary>
///控制是否启用增量输出模式。
///<para>
///默认值为false,这意味着后续输出将包含已完成的段。
///当设置为true时,将激活增量输出模式,并且后续输出将不包含
///之前的片段。完整的输出将需要由用户逐步构建。
///</para>
///此字段是可选的,仅适用于流输出模式。
/// </summary>
[JsonPropertyName("incremental_output")]
public bool? IncrementalOutput { get; set; }
/// <summary>
/// 使用同步调用时,此参数应当设置为 fasle 或者省略。表示模型生成完所有内容后一次性返回所有内容。
/// 如果设置为 true,模型将通过标准 Event Stream ,逐块返回模型生成内容。Event Stream 结束时会返回一条data: [DONE] 消息。
/// </summary>
[JsonPropertyName("stream")]
public bool Stream { get; set; }
/// <summary>
/// 智谱: do_sample 为 true 时启用采样策略,do_sample 为 false 时采样策略 temperature、top_p 将不生效
/// </summary>
[JsonPropertyName("do_sample")]
public bool DoSample { get; set; }
/// <summary>
/// 文心
/// 通过对已生成的token增加惩罚,减少重复生成的现象。
/// 说明:(1)值越大表示惩罚越大。
/// (2)默认1.0,取值范围:[1.0, 2.0]。
/// </summary>
[JsonPropertyName("penalty_score")]
public float? PenaltyScore { get; set; }
/// <summary>
/// 文心
/// 模型人设,主要用于人设设定。
/// 例如,你是xxx公司制作的AI助手,
/// 说明:(1)长度限制1024个字符
/// (2)如果使用functions参数,不支持设定人设system
/// </summary>
[JsonPropertyName("system")]
public string System { get; set; }
/// <summary>
/// 文心
/// 强制关闭实时搜索功能,默认false,表示不关闭
/// </summary>
[JsonPropertyName("disable_search")]
public bool DisableSearch { get; set; }
/// <summary>
/// 文心
/// 是否开启上角标返回
/// </summary>
[JsonPropertyName("enable_citation")]
public bool EnableCitation { get; set; }
/// <summary>
/// 文心
/// 鉴权参数
/// </summary>
public string Token { get; set; }
}
```
6. 创建QianWenClienthttp请求类
```
public class QianWenClient
{
/// <summary>
/// 基础请求地址
/// </summary>
private readonly string baseUrl = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
internal QianWenClient(ModelClient parent)
{
Parent = parent;
}
internal ModelClient Parent { get; }
public async Task<QianWenResponseWrapper> GetChatMessageContentsAsync(string model, IReadOnlyList<ChatMessage> messages, ChatParameters? parameters = null, CancellationToken cancellationToken = default)
{
HttpRequestMessage httpRequest = new(HttpMethod.Post, baseUrl)
{
Content = JsonContent.Create(QianWenRequestWrapper.Create(model, new
{
messages,
}, parameters), options: new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}),
};
HttpResponseMessage resp = await Parent.HttpClient.SendAsync(httpRequest, cancellationToken);
return await ModelClient.ReadResponse<QianWenResponseWrapper>(resp, cancellationToken);
}
public async IAsyncEnumerable<string> GetStreamingChatMessageContentsAsync(string model,
IReadOnlyList<ChatMessage> messages,
ChatParameters? parameters = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
HttpRequestMessage httpRequest = new(HttpMethod.Post, baseUrl)
{
Content = JsonContent.Create(QianWenRequestWrapper.Create(model, new
{
messages,
}, parameters), options: new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}),
};
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
httpRequest.Headers.TryAddWithoutValidation("X-DashScope-SSE", "enable");
using HttpResponseMessage resp = await Parent.HttpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
if (!resp.IsSuccessStatusCode)
{
throw new Exception(await resp.Content.ReadAsStringAsync());
}
string lastText = string.Empty; // 记录上一次返回的数据
using StreamReader reader = new(await resp.Content.ReadAsStreamAsync(), Encoding.UTF8);
while (!reader.EndOfStream)
{
if (cancellationToken.IsCancellationRequested) throw new TaskCanceledException();
string? line = await reader.ReadLineAsync();
if (line != null && line.StartsWith("data:"))
{
string data = line["data:".Length..];
if (data.StartsWith("{\"code\":"))
{
throw new Exception(data);
}
var result = JsonSerializer.Deserialize<QianWenResponseWrapper>(data)!;
// 获取新增加的部分数据并返回
int commonPrefixLength = 0;
while (commonPrefixLength < lastText.Length && commonPrefixLength < result.Output.Text.Length && lastText[commonPrefixLength] == data[commonPrefixLength])
{
commonPrefixLength++;
}
// 获取新增加的文本部分并返回
string newText = result.Output.Text;
string addedText = newText.Substring(lastText.Length);
lastText = newText;
yield return addedText;
}
}
}
}
```
7. 创建QianWenResponseWrapper基本响应类
/// <summary>
/// 用于映像请求异步任务的通用基本响应类。
/// </summary>
public record QianWenResponseWrapper
{
/// <summary>
/// The identifier corresponds to each individual request.
/// </summary>
[JsonPropertyName("request_id")]
public string RequestId { get; init; }
/// <summary>
/// The processed task status response associated with the respective request.
/// </summary>
[JsonPropertyName("output")]
public QianWenChatOutput Output { get; init; }
/// <summary>
/// Usage of the request.
/// </summary>
[JsonPropertyName("usage")]
public QianWenChatTokenUsage? Usage { get; init; }
}
/// <summary>
/// 聊天请求的令牌使用情况。
/// </summary>
public record QianWenChatTokenUsage
{
/// <summary>
/// 输出消息的令牌计数。
/// </summary>
[JsonPropertyName("output_tokens")]
public int OutputTokens { get; init; }
/// <summary>
/// 输入消息的令牌计数。
/// </summary>
[JsonPropertyName("input_tokens")]
public int InputTokens { get; init; }
}
/// <summary>
/// 聊天请求的输出。
/// </summary>
public record QianWenChatOutput
{
/// <summary>
/// 模型的输出内容。
/// </summary>
[JsonPropertyName("text")]
public string Text { get; init; }
/// <summary>
/// 有3种情况:
/// <list type="bullet">
/// <item><c>null</c>正在生成</item>
/// <item><c>stop</c> 停止了</item>
/// <item><c>length</c> 文本太长</item>
/// </list>
/// </summary>
[JsonPropertyName("finish_reason")]
public string FinishReason { get; init; }
}
8. 创建QianWenRequestWrapper请求包装器
/// <summary>
/// 请求包装器
/// </summary>
public record QianWenRequestWrapper
{
public static QianWenRequestWrapper<TInput, TParameters> Create<TInput, TParameters>(string model, TInput input, TParameters? parameters = default) => new()
{
Model = model ?? throw new ArgumentNullException(nameof(model)),
Input = input ?? throw new ArgumentNullException(nameof(input)),
Parameters = parameters,
};
public static QianWenRequestWrapper<TInput, object> Create<TInput>(string model, TInput inputPrompt) => new()
{
Model = model ?? throw new ArgumentNullException(nameof(model)),
Input = inputPrompt ?? throw new ArgumentNullException(nameof(inputPrompt)),
};
}
public record QianWenRequestWrapper<TInput, TParameters> : QianWenRequestWrapper
{
[JsonPropertyName("model")]
public string Model { get; set; }
[JsonPropertyName("input")]
public TInput Input { get; init; }
[JsonPropertyName("parameters")]
public TParameters? Parameters { get; init; }
}
9. 调用
QianWenChatCompletionService chatgpt = new("你的key", "模型名称:例如qwen-turbo");
ChatHistory historys = new ChatHistory();
historys.AddSystemMessage("你是一个c#编程高手,你将用代码回答我关于.net编程的技术问题,下面是我的第一个问题:");
historys.AddUserMessage("用c#写一个冒泡排序");
// 流式调用
await foreach (string item in chatgpt.GetStreamingChatMessageContentsAsync(historys))
{
Console.Write(item);
}
//普通调用
var result = await chatgpt.GetChatMessageContentsAsync(historys);
Console.WriteLine(result);
.Net接入AzureOpenAI、OpenAI、通义千问、智谱AI、讯飞星火、文心一言大语言模型。的更多相关文章
- 阿里版ChatGPT:通义千问pk文心一言
随着 ChatGPT 热潮卷起来,百度发布了文心一言.Google 发布了 Bard,「阿里云」官方终于也宣布了,旗下的 AI 大模型"通义千问"正式开启测试! 申请地址:http ...
- 文心一言,通营销之学,成一家之言,百度人工智能AI大数据模型文心一言Python3.10接入
"文心"取自<文心雕龙>一书的开篇,作者刘勰在书中引述了一个古代典故:春秋时期,鲁国有一位名叫孔文子的大夫,他在学问上非常有造诣,但是他的儿子却不学无术,孔文子非常痛心 ...
- 慕零的黑夜-头条-第一期(必问)[导读:]1.CSDN必问赏金流向何方 2.CSDN必问偷偷做的手脚 3.CSDN必问靠谱吗 4.关于钱于回答的平衡问题:一美元拍卖骗局 作者:qq3461896724
本期是关于CSDN 必问 (biwen.csdn.net)的内容,欢迎评论 文末,文中插入有 小姐姐 img(附py代码,1.49G) + coding资料 哟~~~ 你看到这篇很可能是在提问.推荐加 ...
- 已废弃_CSDN慕零的黑夜-头条-第一期(必问)[导读:]1.CSDN必问赏金流向何方 2.CSDN必问偷偷做的手脚 3.CSDN必问靠谱吗 4.关于钱于回答的平衡问题:一美元拍卖骗局qq3461896724
[本文有已知的链接差错,懒得改了] 本期是关于CSDN 必问 (biwen.csdn.net)的内容,欢迎评论文末,文中插入有 小姐姐 img(附py代码,1.49G) + coding资料 哟~~~ ...
- Android 讯飞语音听写SDK快速接入(附空指针解决和修改对话框文字方法)
1.账号准备工作 首先要有一个讯飞的账号啦,为后面申请APPID.APPKey等东西做准备.顺带一提:讯飞对不同认证类型用户开 放的SDK的使用次数是有不同的,详情如下图. 账号申请完成后,需要去你自 ...
- Android 应用接入广点通统计API 方案
官方给你参考文档,很扯淡,是c++和python脚本: 安卓java代码接入如下: package com.edaixi.util; import java.io.UnsupportedEncodin ...
- 【Python千问 2】Python核心编程(第二版)-- 欢迎来到Python世界
1.1 什么是Python 继承了传统编译语言的强大性和通用性,同时也借鉴了简单脚本和解释语言的易用性. 1.2 起源 来源于某个项目,那些程序员利用手边现有的工具辛苦工作着,他们设想并开发了更好的解 ...
- 【Python千问 1】Python核心编程(第二版)导读
第一章 欢迎来到Python世界 什么是Python Python的起源 Python的特点 下载Python 安装Python 运行Python Python文档 比较Python(与其它语言的比较 ...
- 【Java千问】你了解代理模式吗?
代理模式详解 1 什么是代理模式? 一句话描述:代理模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式. 详细描述: 1.理论基础-代理模式是设计原则中的“开闭原则 ...
- bzoj千题计划304:bzoj3676: [Apio2014]回文串(回文自动机)
https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include& ...
随机推荐
- 我的小程序之旅六:微信公众号授权登录(适用于H5小程序)
实现步骤 1 第一步:用户同意授权,获取code 2 第二步:通过code换取网页授权access_token 3 第三步:刷新access_token(如果需要) 4 第四步:拉取用户信息(需sco ...
- centos7 安装vmware tool 遇到遇到 kernel-headers 问题修复
安装 vmware tool 步骤 1. cp VMwareTools-10.3.25-20206839.tar.gz 到 用户目录下 2. tar zxf VMwareTools-10.3.25-2 ...
- 2021-07-21 vue插槽
说明 为什么要有插槽? 是为了方便优雅地在父组件中向子组件传递向子组件传递dom结构. 代码处理 子组件 该子组件的组件名为ChildComponent: <template> <d ...
- flutter——android报错Manifest merger failed : Attribute application@allowBackup value=(false)
与这个https://www.cnblogs.com/MaiJiangDou/p/13848658.html 报错类似. 报错: Manifest merger failed : Attribute ...
- go-ini解析ini文件
文档 https://github.com/go-ini/ini https://ini.unknwon.io/docs/intro/getting_started go get -u gopkg.i ...
- React 组件之样式
无论你的梦想有多么高远,记住,一切皆有可能. 我们从前面的学习知道一个 React 组件不仅仅只包含 DOM 结构的,还应该样式和 Javascript 逻辑的.这里我们学习下如何构建 CSS 样式. ...
- jupyter notebook更改默认工作目录
jupyter notebook默认配置路径:C:\Users\Administrator\.jupyter\jupyter_notebook_config.py 如果找不到配置文件,可以生成一个 j ...
- 【LeetCode哈希表#3】快乐数(set)
快乐数 力扣题目链接(opens new window) 编写一个算法来判断一个数 n 是不是快乐数. 「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程 ...
- Isito 入门(二):Istio 的部署
本教程已加入 Istio 系列:https://istio.whuanle.cn 目录 2,部署 Istio 安装 Helm 部署 istio-base 部署 istiod 部署 istio-ingr ...
- 使用Kubernetes搭建带有ik分词的Elasticsearch集群
创建好带有Ik分词的es镜像,并上传到镜像仓库中,创建镜像可参考链接中的文档 https://www.cnblogs.com/hi-lijq/p/16895206.html 编写es_cluster- ...