ASP.NETCore轻量文心一言API小后端留档

文心一言API小后端(.Net 7)

我没有写什么详细的介绍,这是流水账记录,里面有很多代码是没有用的,但是我懒得删,就当是记录自己的问题吧。

这是之前写过的一个ASP.NET Core的文心一言API,后面过了将近一年又拿出来修改了一下,部署在云服务上了,写这玩意的目的是为了Linux和一些考试去作弊用的,
因为我发现学校使用极域管理机房电脑,禁止浏览器访问页面是封的80标准端口,所以就写了个API,把它部署在非80端口上,
就绕过极域的封禁去使用ai,但考试改成笔试了。

项目文件

AiChatController.cs
appsettings.json
Program.cs
UserSessionManager.cs
WxyyAiModelConfig.cs
WxyyAiModels.cs

appsettings.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"WxyyAiModelConfig": {
"API_KEY02": "<KEY>",
"SECRET_KEY02": "<KEY>",
"ModelContextSize": "20"//这个是模型上下文大小,默认20,可以根据需要调整,但是太大会导致性能问题,太小会导致模型不准确
},
"AllowedHosts": "*"
}

WxyyAiModelConfig.cs

1
2
3
4
5
6
7
8
9
namespace AiChatAPI01x02
{
public class WxyyAiModelConfig
{
public string API_KEY02 { get; set; }
public string SECRET_KEY02 { get; set; }
public string ModelContextSize { get; set; }
}
}

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using AiChatAPI01x02;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddOptions<WxyyAiModelConfig>();
builder.Services.Configure<WxyyAiModelConfig>(builder.Configuration.GetSection("WxyyAiModelConfig"));
builder.Services.AddSingleton<UserSessionManager>();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();

// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())//注释掉这行,是为了在部署后的非开发环境,也能使用swagger的ui访问
//{
app.UseSwagger();
app.UseSwaggerUI();
//}

app.UseHttpsRedirection();

app.UseCors("AllowAll");


app.UseAuthorization();

app.MapControllers();

app.Run();

WxyyAiModels.cs

如果不需要使用上下文保持,可以直接调用 Task DiaoYongAiModelAsync(string question)方法,传入问题,返回回答。
如果需要使用上下文保持,调用Task DiaoYongAiModelAsync(string userId, List messages)
这个userId是GenerateUserId()方法生成的,可以自己实现,但是要保证唯一性,可以使用Guid.NewGuid().ToString()

关于为什么这么多的方法中都有测试未通过。是因为在最初文心一言的API调用后,返回的数据中有一些id之类的,这些id在每次调用后还不一样,
然后我就以为这个api就已经实现了上下文,但我用的时候没有上下文,是因为没有用它返回的那个id,所以就一直没有上下文。
所以我就去官方文档里面查,就根本没发现这个id的作用,然后我就想,这有没有可能就是只是一个普通的uuid或者guid用来标识这次对话的。
然后我再文档里面看到要是实现上下文保持,需要我们在后端自己去实现,简单的方法就是直接使用一个列表作为上下文,然后每次调用的时候,
把这个列表的数据传给api,api返回的结果又加入列表,用户的输入也加入列表,循环往复就这样。就可以实现上下文保持了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

namespace AiChatAPI01x02
{
class WxyyAiModels
{
string API_KEY02;
string SECRET_KEY02;
private readonly HttpClient _httpClient;

public WxyyAiModels(string aPI_KEY02, string sECRET_KEY02)
{
_httpClient = new HttpClient();
API_KEY02 = aPI_KEY02;
SECRET_KEY02 = sECRET_KEY02;
}


//只需要在外面调用这个方法即可。即调即用,2024-9-12,但是没有上下文保持2025-11-7
public async Task<string> DiaoYongAiModelAsync(string question)
{
var token = await GetAccessTokenAsync();
List<ChatVO> messages = new List<ChatVO>();
ChatVO chat01 = new ChatVO
{
role = "user",
content = $"{question}"
};
messages.Add(chat01);
var chatMsg = await GetChatAsync(token, "13900000011", messages);
return chatMsg;
}

//新的方法,提供上下文保持,通过userId来进行上下文保持。2025-11-7,测试未通过
public async Task<string> DiaoYongAiModelAsync(string userId,string question)
{
var token = await GetAccessTokenAsync();
List<ChatVO> messages = new List<ChatVO>();
ChatVO chat01 = new ChatVO
{
role = "user",
content = $"{question}"
};
messages.Add(chat01);
var chatMsg = await GetChatAsync(token, userId, messages);
return chatMsg;
}
//新的方法,传入整个ChatList,进行上下文保持。2025-11-8(通过测试)
public async Task<string> DiaoYongAiModelAsync(string userId, List<ChatVO> messages)
{
var token = await GetAccessTokenAsync();
var chatMsg = await GetChatAsync(token, "123453323", messages);
return chatMsg;
}

//新方法,ai翻译,不需要上下文保持。2025-11-11
//question:需要翻译的内容,from:需要翻译的语言,to:翻译成的语言
public async Task<string> DiaoYongAiTranslationAsync(string question, string from, string to)
{
var token = await GetAccessTokenAsync();
List<ChatVO> messages = new List<ChatVO>();
ChatVO chat01 = new ChatVO
{
role = "user",
content = $"你是一个ai翻译助手的api,把-{question}-,从-{from}-翻译成-{to}-,注意仅返回翻译后的内容。因为我的后端系统只能处理翻译后的内容,否则会报错。"
};
messages.Add(chat01);
var chatMsg = await GetChatAsync(token, "123453323", messages);
return chatMsg;
}
//新方法,ai答题,不需要上下文保持。2025-11-11
//question:需要答题的内容
public async Task<string> DiaoYongAiAnswerAsync(string question)
{
var token = await GetAccessTokenAsync();
List<ChatVO> messages = new List<ChatVO>();
ChatVO chat01 = new ChatVO
{
role = "user",
content = $"你是一个ai答题助手的api,把-{question}-,答题,注意仅返回答案,不要返回题目。因为我的后端系统只能处理答案,否则会报错。"
};
messages.Add(chat01);
var chatMsg = await GetChatAsync(token, "123453323", messages);
return chatMsg;
}

//使用token加userId,进行聊天,测试未通过2025-11-7,我查了官方的文档,token本身不支持上下文保持
public async Task<string> DiaoYongAiModelAsync(string token,string userId, string question)
{
List<ChatVO> messages = new List<ChatVO>();
ChatVO chat01 = new ChatVO
{
role = "user",
content = $"{question}"
};
messages.Add(chat01);
var chatMsg = await GetChatAsync(token, userId, messages);
return chatMsg;
}

public async Task<string> GetChatAsync(string accessToken, string userId, List<ChatVO> messages)
{
WxyyChatReq wxyyChatReq = new WxyyChatReq
{
user_id = userId,
messages = messages,
temperature = 0.95,
top_p = 0.8,
penalty_score = 1,
disable_search = false,
enable_citation = false,
stream = false
};

var url = $"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token={accessToken}";
var json = JsonConvert.SerializeObject(wxyyChatReq);

var response = await _httpClient.PostAsync(url, new StringContent(json, System.Text.Encoding.UTF8, "application/json"));
var content = await response.Content.ReadAsStringAsync();

return content;
}
//获取token
public async Task<string> GetAccessTokenAsync()
{
var url = "https://aip.baidubce.com/oauth/2.0/token";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", API_KEY02),
new KeyValuePair<string, string>("client_secret", SECRET_KEY02)
});

var response = await _httpClient.PostAsync(url, content);
var result = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<dynamic>(result);
return json.access_token.ToString();
}
}

public class WxyyChatReq
{
public string user_id { get; set; }
public double temperature { get; set; }
public double top_p { get; set; }
public double penalty_score { get; set; }
public bool disable_search { get; set; }
public bool enable_citation { get; set; }
public bool stream { get; set; }
public List<ChatVO> messages { get; set; }
}

public class ChatVO
{
public string role { get; set; }
public string content { get; set; }
}
}

UserSessionManager.cs

通过userId管理多个用户的上下文.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
namespace AiChatAPI01x02
{
public class UserSessionManager
{
//用户通过api把消息发送到后端,后端接收,然后把消息存入用户上下文列表中,然后把整个上下文列表即List<ChatVO>传给ai模型
//这样可以实现了一个简单的上下文记忆。2025-11-8
//生成用户id
//删除用户id
//添加用户会话
//删除用户会话
//获取用户会话
//更新用户会话
//使用流程:先生成用户id,然后添加用户会话,然后更新用户会话。


public static Dictionary<string, List<ChatVO>> _userSessions = new Dictionary<string, List<ChatVO>>();
public List<string> _userIds = new List<string>();
public UserSessionManager() { }
public string GenerateUserId(int longth)
{
string userId = Guid.NewGuid().ToString("N").Substring(0, longth);
_userIds.Add(userId);
return userId;

}
public bool DeleteUserId(string userId)
{
_userIds.Remove(userId);
return _userSessions.Remove(userId);
}
public List<string> GetAllUserIds()
{
return _userIds;
}

public void AddUserSession(string userId, List<ChatVO> chatVOs)
{
_userSessions.Add(userId, chatVOs);
}
public bool DeleteUserSession(string userId)
{
return _userSessions.Remove(userId);
}
//把用户输入的message添加到List<ChatVO>2025-11-8
public void UpdateUserSessionAboutAddTheNewMessage_UserInput(string userId, string message)
{
if (_userSessions.ContainsKey(userId))
{
ChatVO chatVO = new ChatVO
{
role = "user",
content = message
};
_userSessions[userId].Add(chatVO);
}
}
//把ai返回的message添加到List<ChatVO>2025-11-8
public void UpdateUserSessionAboutAddTheNewMessage_AiResponse(string userId, string message)
{
if (_userSessions.ContainsKey(userId))
{
ChatVO chatVO = new ChatVO
{
role = "assistant",
content = message
};
_userSessions[userId].Add(chatVO);
}
}
//检查用户对话的轮数,超过一定数量则删除最早的对话2025-11-8
public void CheckAndTrimUserSession(string userId, int maxRounds)
{
if (_userSessions.ContainsKey(userId))
{
var sessions = _userSessions[userId];
int rounds = sessions.Count / 2;
if (rounds > maxRounds)
{
int messagesToRemove = (rounds - maxRounds) * 2;
sessions.RemoveRange(0, messagesToRemove);
}
}
}

//获取用户对话的上下文
public List<ChatVO> GetUserSession(string userId)
{
if (_userSessions.ContainsKey(userId))
{
return _userSessions[userId];
}
else
{
return null;
}
}
//删除所以的用户会话
public void ClearAllUserSessions()
{
_userSessions.Clear();
_userIds.Clear();
}
}
}

AiChatController.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace AiChatAPI01x02.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class AiChatController : ControllerBase
{
private readonly IOptionsMonitor<WxyyAiModelConfig> _wxyyAiModelConfig;
private readonly UserSessionManager _userSessionManager;
public AiChatController(IOptionsMonitor<WxyyAiModelConfig> wxyyAiModelConfig, UserSessionManager userSessionManager)
{
_wxyyAiModelConfig = wxyyAiModelConfig;
_userSessionManager = userSessionManager;
}
//无法记起上下文的原因找到了,这个api本身不提供会话保持,同时我在官方文档中也没有找到
//看来需要我在后端自己写,2025-11-7

//解决方法就是把之前每次的会话都保存起来(保持在内存中吧,这样比较快),然后每次调用的时候把之前的会话传进去。
//但需要注意的是,存的大小,允许存几轮对话,太大了会导致内存溢出。2025-11-7

//客户端调用api,输入userid和message,后端拿到message后,把他存入对应的userid的会话列表中,然后把整个会话列表传给ai模型。
//这个userid的会话列表是在内存中的,不会保存在数据库中。(看具体情况而定)2025-11-7

//需要注意内存伪共享问题,原来还有这种问题,2025-11-8

//旧的方法,不提供会话保持
[HttpPost]
public async Task<ActionResult<string>> ChatToAi01([FromBody] string message)
{
string result = await new WxyyAiModels(_wxyyAiModelConfig.CurrentValue.API_KEY02, _wxyyAiModelConfig.CurrentValue.SECRET_KEY02).DiaoYongAiModelAsync(message);
return result;
}
///<新方法02>
//新的方法,提供会话保持,通过List<ChatVO>来进行会话保持。2025-11-8
[HttpPost]
public async Task<ActionResult<string>> GenerateUserId()
{
string userId = _userSessionManager.GenerateUserId(4);
_userSessionManager.AddUserSession(userId, new List<ChatVO>());
return userId;
}
//删除UserId
[HttpPost]
public async Task<ActionResult<string>> DeleteUserId([FromForm] string userId)
{
_userSessionManager.DeleteUserId(userId);
if (_userSessionManager.GetAllUserIds().Contains(userId))
{
return "删除失败";
}
return "删除成功";
}
//获取所有UserId
[HttpPost]
public async Task<ActionResult<List<string>>> GetAllUserIds()
{
return _userSessionManager.GetAllUserIds();
}
//删除所有UserSession
[HttpPost]
public async Task<ActionResult<string>> ClearAllUserSession()
{
_userSessionManager.ClearAllUserSessions();
if (_userSessionManager.GetAllUserIds().Count != 0)
{
return "删除失败";
}
return "删除成功";
}

//聊天,还需要在WxxyAiModels中为该方法适配一个方法,2025-11-8
//在_userSessionManager中获取指定userId的完整List<ChatVO>
[HttpPost]
public async Task<ActionResult<string>> ChatToAi02([FromForm] string userId, [FromBody]string message)
{
_userSessionManager.CheckAndTrimUserSession(userId,int.Parse(_wxyyAiModelConfig.CurrentValue.ModelContextSize));//检查并修剪,防止内存溢出,设置最大会话数,2025-11-8
_userSessionManager.UpdateUserSessionAboutAddTheNewMessage_UserInput(userId, message);
var userInput = _userSessionManager.GetUserSession(userId);
string result = await new WxyyAiModels(_wxyyAiModelConfig.CurrentValue.API_KEY02, _wxyyAiModelConfig.CurrentValue.SECRET_KEY02).DiaoYongAiModelAsync(userId, userInput);
_userSessionManager.UpdateUserSessionAboutAddTheNewMessage_AiResponse(userId, result);
return result;
}
///</新方法02>

///<ai翻译>
[HttpPost]
public async Task<ActionResult<string>> AiTranslation([FromForm] string message, string from, string to)
{
string result = await new WxyyAiModels(_wxyyAiModelConfig.CurrentValue.API_KEY02, _wxyyAiModelConfig.CurrentValue.SECRET_KEY02).DiaoYongAiTranslationAsync(message, from, to);
return result;
}

///</ai翻译>

///<ai答题助手>
[HttpPost]
public async Task<ActionResult<string>> AiAnswerQuestion([FromForm] string question)
{
string result = await new WxyyAiModels(_wxyyAiModelConfig.CurrentValue.API_KEY02, _wxyyAiModelConfig.CurrentValue.SECRET_KEY02).DiaoYongAiAnswerAsync(question);
return result;
}
///</ai答题助手>

}
}