18C856202C3B81B900EAE874 /* tts.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 18C8561E2C3B81B800EAE874 /* tts.mp3 */; };
18DB20482C213D730070D571 /* AiMouseAudioRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 18DB20462C213D730070D571 /* AiMouseAudioRecord.m */; };
18DB20492C213D730070D571 /* AiMouseRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 18DB20472C213D730070D571 /* AiMouseRecordManager.m */; };
+ 18EF694A2C44CB1E00AA0A36 /* BleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EF69492C44CB1E00AA0A36 /* BleManager.swift */; };
18FBC2BB2C38E0FC002240AF /* ChatGptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FBC2BA2C38E0FC002240AF /* ChatGptViewController.swift */; };
18FF930C2C39263A0006452C /* cat.png in Resources */ = {isa = PBXBuildFile; fileRef = 18FF930A2C39263A0006452C /* cat.png */; };
F59940004E1C16053FBD42F5 /* Pods_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B555BC40ACD8F31DBCDD1A2 /* Pods_Example.framework */; };
18DB20452C213D730070D571 /* AiMouseAudioRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AiMouseAudioRecord.h; sourceTree = "<group>"; };
18DB20462C213D730070D571 /* AiMouseAudioRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AiMouseAudioRecord.m; sourceTree = "<group>"; };
18DB20472C213D730070D571 /* AiMouseRecordManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AiMouseRecordManager.m; sourceTree = "<group>"; };
+ 18EF69492C44CB1E00AA0A36 /* BleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleManager.swift; sourceTree = "<group>"; };
18FBC2BA2C38E0FC002240AF /* ChatGptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatGptViewController.swift; sourceTree = "<group>"; };
18FF930A2C39263A0006452C /* cat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cat.png; sourceTree = "<group>"; };
4B555BC40ACD8F31DBCDD1A2 /* Pods_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
184FFC482C218FC4006F664D /* PlayerManager.swift */,
184FFC4C2C2199E5006F664D /* SendToolBar.swift */,
180255B92C3BE6BD005AFC5F /* SendRecordTooBar.swift */,
+ 18EF69492C44CB1E00AA0A36 /* BleManager.swift */,
);
path = Tools;
sourceTree = "<group>";
18204BD82C193F9A00F94E69 /* ViewController.swift in Sources */,
18FBC2BB2C38E0FC002240AF /* ChatGptViewController.swift in Sources */,
18B74A242C201CE900FABBA9 /* SttViewController.swift in Sources */,
+ 18EF694A2C44CB1E00AA0A36 /* BleManager.swift in Sources */,
18204BD42C193F9A00F94E69 /* AppDelegate.swift in Sources */,
18B74A262C201EBD00FABBA9 /* ChatViewController.swift in Sources */,
184FFC492C218FC4006F664D /* PlayerManager.swift in Sources */,
setupViews()
//设置平台
MixSdk.shared.setPlatform(.chatGPT, key: "sk-proj-fdifVB2nPBIVcQeegE0AT3BlbkFJsgOXO5mnrmzox8pnSxMV")
+ MixSdk.shared.chatGpt.changeModel(model: "gpt-4.0-turbo")
}
func setupViews(){
textView.scrollRangeToVisible(range)
view.makeToastActivity(.center)
- MixSdk.shared.chatGpt.send(content: text, useModel: "gpt-3.5-turbo") {[weak self] response, error in
+ MixSdk.shared.chatGpt.send(content: text) {[weak self] response, error in
guard let self = self else {return}
self.view.hideToastActivity()
if let model = response,let choices = model.choices.first {
setupViews()
setupChatCallBack()
//设置默认平台
- self.title = "chat-阿里云"
- MixSdk.shared.setPlatform(.aliyun)
+ self.title = "chat-豆包"
+ MixSdk.shared.setPlatform(.doubao)
}
func setupViews(){
@objc func changePlatform(){
let vc = UIAlertController(title: nil, message: "请选择", preferredStyle: .actionSheet)
- let action0 = UIAlertAction(title: "阿里云", style: .default) { action in
- self.title = "chat-阿里云"
- MixSdk.shared.setPlatform(.aliyun)
+ let action0 = UIAlertAction(title: "豆包", style: .default) { action in
+ self.title = "chat-豆包"
+ MixSdk.shared.setPlatform(.doubao)
}
vc.addAction(action0)
- let action1 = UIAlertAction(title: "火山", style: .default) { action in
- MixSdk.shared.setPlatform(.huoshan)
- self.title = "chat-火山"
+ let action1 = UIAlertAction(title: "文心一言", style: .default) { action in
+ MixSdk.shared.setPlatform(.yiyan)
+ self.title = "chat-文心一言"
}
vc.addAction(action1)
- let action2 = UIAlertAction(title: "ChatGPT", style: .default) { action in
+ let action2 = UIAlertAction(title: "KiMi", style: .default) { action in
+ MixSdk.shared.setPlatform(.kimi)
+ self.title = "chat-kimi"
+ }
+ vc.addAction(action2)
+ let action3 = UIAlertAction(title: "ChatGPT", style: .default) { action in
MixSdk.shared.setPlatform(.chatGPT, key: "sk-proj-fdifVB2nPBIVcQeegE0AT3BlbkFJsgOXO5mnrmzox8pnSxMV")
+ MixSdk.shared.chatGpt.changeModel(model: "gpt-4.0-turbo")
self.title = "chatGPT"
}
- vc.addAction(action2)
+ vc.addAction(action3)
- let action3 = UIAlertAction(title: "取消", style: .cancel) { action in
-
+ let action4 = UIAlertAction(title: "取消", style: .cancel) { action in
}
- vc.addAction(action3)
+ vc.addAction(action4)
self.present(vc, animated: true)
}
let content = text
view.makeToastActivity(.center)
// 发送chatgpt
- MixSdk.shared.chatGpt.send(content: content,useModel: "gpt-3.5-turbo") {[weak self] response, error in
+ MixSdk.shared.chatGpt.send(content: content) {[weak self] response, error in
guard let self = self else {return}
self.view.hideToastActivity()
if let model = response,let choices = model.choices.first {
--- /dev/null
+//
+// BleManager.swift
+// Example
+//
+// Created by incar on 2024/7/15.
+//
+
+import Foundation
+import CoreBluetooth
+
+public class IBScanOption {
+ var values: [String : Any] = [:]
+ var serviceUUIDs: [String]? = nil
+
+ /// 允许重复扫描
+ @discardableResult
+ public func allowDuplicates(_ value: Bool) -> Self {
+ values[CBCentralManagerScanOptionAllowDuplicatesKey] = value
+ return self
+ }
+
+ /// 扫描指定服务
+ @discardableResult
+ public func solicited(_ serviceUUIDs: [String]) -> Self {
+ self.serviceUUIDs = serviceUUIDs
+ values[CBCentralManagerScanOptionSolicitedServiceUUIDsKey] = serviceUUIDs.map({ CBUUID(string: $0) })
+ return self
+ }
+
+ public static let `default` = IBScanOption()
+ public static var custom: IBScanOption { IBScanOption() }
+}
+
+
+
+class IBBluetooth: NSObject {
+ static let shared = IBBluetooth()
+
+ private override init() {
+ super.init()
+ centralManager = CBCentralManager(delegate: self, queue: nil)
+ }
+
+ /// 2AE5
+ private var notifyCharacteristic: CBCharacteristic!
+ /// 2AE2
+ private var readCharacteristic: CBCharacteristic!
+ /// 2AE3
+ private var writeCharacteristic: CBCharacteristic!
+ /// 2AE4
+ private var writeBigCharacteristic: CBCharacteristic!
+ /// 2AE0
+ private var writeFileCharacteristic: CBCharacteristic!
+
+ /// 中央管理类,管理蓝牙的扫描连接
+ var centralManager: CBCentralManager!
+
+ /// 扫描到的蓝牙设备集合
+ var devices: [CBPeripheral] = []
+
+
+ /// 当前连接设备
+ var currentConnected: CBPeripheral!
+
+
+ private var option: IBScanOption = .default
+ func scan(_ option: IBScanOption = .default) {
+ self.option = option
+ startScan()
+ }
+
+ /// 重连接设备
+ func reconnected() {
+// guard let connectedPeripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: serviceUUID)]).first else { return }
+// centralManager.connect(connectedPeripheral)
+ }
+
+ /// 开始扫描
+ private func startScan() {
+ if centralManager.state == .poweredOn {
+ print("开始扫描蓝牙设备...")
+ }
+ centralManager.scanForPeripherals(withServices: nil, options: option.values)
+
+// if let connectedPeripheral = centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: serviceUUID)]).first{
+// IBManager.discoverHandler?(currentConnected)
+// }
+ }
+
+ /// 停止扫描
+ func stopScan(){
+ print("停止扫描蓝牙设备...")
+ centralManager.stopScan()
+ }
+
+ /// 连接蓝牙设备
+ ///
+ /// - Parameter peripheral: 蓝牙设备对像
+ func connect(_ peripheral: CBPeripheral) {
+ currentConnected = peripheral
+ centralManager.connect(peripheral)
+ }
+
+ /// 断开与蓝牙设备的连接
+ ///
+ /// - Parameter peripheral: 蓝牙设备对像
+ func cancel(_ peripheral: CBPeripheral) {
+ centralManager.cancelPeripheralConnection(peripheral)
+ }
+
+ /// 发送数据
+ /// - Parameters:
+ /// - sender: 消息类
+ func send() {
+ if currentConnected.state == .disconnected{
+
+ print("手机未连上蓝牙设备!")
+ return
+ }
+
+ }
+
+}
+
+extension IBBluetooth: CBCentralManagerDelegate {
+
+ /// 手机蓝牙状态更新
+ /// - Parameter central: 中央管理类
+ func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ switch central.state {
+ case .unknown:
+ print("unknown")
+ case .resetting:
+ print("resetting")
+ case .unsupported:
+ print("unsupported")
+ case .unauthorized:
+ print("unauthorized")
+ case .poweredOff:
+ print("poweredOff")
+ case .poweredOn:
+ startScan()
+ print("poweredOn")
+ @unknown default:
+ break
+ }
+ }
+
+ /// 发现设备
+ func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
+
+ }
+
+ /// 连接上设备
+ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
+ peripheral.delegate = self
+// peripheral.discoverServices([CBUUID(string: serviceUUID)])
+ print("已连接", currentConnected.name)
+ }
+
+ /// 连接失败
+ func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
+ print("连接失败",error?.localizedDescription ?? "" )
+ currentConnected = nil
+ }
+
+ /// 断开连接
+ func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
+ print("断开连接")
+
+ deinitTimer()
+
+ }
+}
+
+extension IBBluetooth: CBPeripheralDelegate {
+
+ /// 读取到信号
+ func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
+
+ }
+
+ /// 发现服务
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
+ peripheral
+ .services?
+ .forEach {
+ peripheral.discoverCharacteristics(nil, for: $0)
+ }
+ }
+
+ /// 发现服务找那个的特征,然后遍历所有特征订阅
+ func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
+
+ }
+
+ /// 来自蓝牙设备的通知
+ func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
+ // 读取特征的值
+ peripheral.readValue(for: characteristic)
+ }
+
+ /// 蓝牙设备发过来的数据
+ func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
+ if let error = error {
+ print(error)
+ }
+
+ guard let data = characteristic.value else { return }
+
+
+ let uuid = characteristic.uuid.uuidString
+ print( uuid, "->", data.map{String(format: "%02x", $0)}.joined(separator: " "), "Bytes(\(data.count))")
+ }
+
+ /// 写入完成(发送指令完成)
+ func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
+ if let error = error {
+ print(characteristic.uuid.uuidString,error.localizedDescription)
+ }
+
+ }
+}
+
+extension IBBluetooth {
+
+ static var timer: Timer?
+ func startTimer() {
+ deinitTimer()
+
+ Self.timer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false, block: { [unowned self] _ in
+ deinitTimer()
+ })
+// print("创建定时器")
+ }
+
+ func deinitTimer() {
+ guard let _ = Self.timer else { return }
+ Self.timer?.invalidate()
+ Self.timer = nil
+
+// print("销毁定时器")
+ }
+}
import Foundation
-/// ChatGpt
+/// ChatGpt对话回调
public typealias MSChatGptCallBack = (_ response: MSChatGptModel?,_ error:MSChatGptError?) -> Void
-/// ChatGpt
+/// ChatGpt语音识别回调
public typealias MSChatGptTranscriptionCallBack = (_ text: String?,_ error:MSChatGptError?) -> Void
+/// ChatGpt验证key是否有效回调
+public typealias MSChatGptCheckKeyCallBack = (_ isValid: Bool?,_ error:MSChatGptError?) -> Void
+
/// 鉴权管理类
public class MSChatGpt {
private var transcription = MSTranscription()
+ /// gpt3.5-turbo、gpt-4.0-turbo、gpt-4o
+ private var gptModel = "gpt-4.0-turbo"
func getUrl(_ api:String)->String{
return "https://api.openai.com/" + api
}
+
+ /// 语音识别
+ /// - Parameters:
+ /// - filePath: 音频文件,支持格式flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm
+ /// - completeHandler: 结果回调
public func transcription(filePath:String,completeHandler: @escaping MSChatGptTranscriptionCallBack){
guard let aiKey = MixSdk.shared.aiKey else {
Logger.log("请设置key")
}
task.resume()
}
+
+
+ /// 切换chatGpt model
+ /// - Parameter model: gpt3.5-turbo、gpt-4.0-turbo、gpt-4o
+ public func changeModel(model:String){
+ gptModel = model
+ }
+
+
+ /// 验证key是否有效
+ /// - Parameters:
+ /// - key: chatGpt Key
+ /// - completeHandler: 结果回调
+ public func isValidKey(_ key:String ,completeHandler: @escaping MSChatGptCheckKeyCallBack){
+
+ completeHandler(true,nil)
+ }
/// 对话
/// - Parameters:
/// - content: 内容
- /// - model: 使用的模型,例如 gpt-3.5-turbo
/// - completeHandler: 结果回调
- public func send(content:String, useModel model:String,completeHandler: @escaping MSChatGptCallBack){
+ public func send(content:String,completeHandler: @escaping MSChatGptCallBack){
guard let key = MixSdk.shared.aiKey else {
Logger.log("请设置key")
completeHandler(nil,MSChatGptError())
}
let params:[String : Any] = [
- "model": model,
+ "model": gptModel,
"messages": [
[
"role": "user",
// Created by incar on 2024/6/13.
//
+//根据需求, AISDK 还需提供以下三个方法:
+//1.国内模型切换的调用方法(国内模型需求定为文心一言、KIMI、豆包)
+//2.ChatGPT Key 的合规可用的校验方法,返回校验结果
+//3.ChatGPT 模型单独切换的调用方法(gpt3.5-turbo、gpt-4.0-turbo、gpt-4o)
+
import Foundation
// https://apifox.com/apidoc/shared-054b7e2c-cf4b-4e2b-875c-79fda14ddbbf?pwd=xRzzNg7l
/// Ai平台类型
public enum MSPlatform {
- /// 阿里云
- case aliyun
- /// 火山
- case huoshan
-// /// 文心一言
-// case yiyan
-// /// 豆包
-// case doubao
-// /// Kimi
-// case kimi
+// /// 阿里云
+// case aliyun
+// /// 火山
+// case huoshan
+ /// 文心一言
+ case yiyan
+ /// 豆包
+ case doubao
+ /// Kimi
+ case kimi
/// chatGPT
case chatGPT
internal var value:String{
switch self {
- case .aliyun:
- return "aliyun"
- case .huoshan:
+// case .aliyun:
+// return "aliyun"
+// case .huoshan:
+// return "huoshan"
+ case .yiyan:
+ return "yiyan"
+ case .doubao:
return "huoshan"
-// case .yiyan:
-// return "yiyan"
-// case .doubao:
-// return "doubao"
-// case .kimi:
-// return "kimi"
+ case .kimi:
+ return "kimi"
case .chatGPT:
return "chatGPT"
public var logs: [String] { Logger.logs }
/// SDK 版本号
- public let version: String = "1.0.6"
+ public let version: String = "1.0.9"
/// 默认为 false
public var isDebug: Bool = false {
didSet { Logger.isDebug = isDebug }
}
/// ai平台
- public var aiPlatform:MSPlatform = .aliyun
+ public var aiPlatform:MSPlatform = .doubao
/// aikey
public var aiKey:String? = ""
## 介绍
-- 当前版本:v1.0.6
+- 当前版本:v1.0.9
-- 更新内容20240710:
+- 更新内容20240715:
1. 登录注册接口修改
1. 新增chatGpt支持语音转写
--- /dev/null
+## 鉴权
+
+1. 没有使用我们ai能力的,(纽曼)走阿里LP
+2. 使用我们提供ai能力的,走我们的管理平台
+
+
+
+## 海外AI能力
+
+1. asr支持海外市场,提供选择语言
+
+2. openAi账号请求带上模型版本,语音带上语言选择
+
+3. 海外cdn、国外的服务器、国外的能力(openAi)。
+
+
+
--- /dev/null
+# 北科服务器接口确认
+
+
+
+## 全局参数
+
+| 参数值 | 类型 | 说明 | |
+| :------- | :----- | :----- | -------------------------------------- |
+| platform | aliyun | string | 请求引擎 支持(huoshan、文心一言、Kimi) |
+| dt | | string | 设备token,可选 |
+| ut | | string | 用户token,可选 |
+| is_auth | | string | 测试阶段参数 为true、1 开启token 验证 |
+
+## asr
+
+### 实时转写
+
+ws://118.145.158.207:80/asr/ws
+
+请求参数。默认中文引擎,通过lang参数可以切换语言
+
+```json
+{
+ "header": {
+ "event": "StartTranscription",//事件
+ "trace_id": "3fa6b9e6a30b407ea38ad7ae69e6c291"//任务id
+ },
+ "payload": {
+ "format": "pcm",//编码格式
+ "sample_rate": 16000,//采样率
+ "lang": "zh-cn"//语言
+ }
+}
+```
+
+转写结果
+
+```json
+{
+ "status": 20000000,//状态码
+ "msg": "success",//提示语
+ "task_id": "80228c9f62821ffe3f867e1a5042d887",//任务id
+ "event": "SentenceEnd",//事件
+ "result": {
+ "result": "嗯。",//识别结果
+ "is_end": true,//是否结束
+ "sentence_id": "d7e62c6fb0ee847b85d674828797f3f3",//句子id
+ "start_time": 13610,//开始的时间
+ "end_time": 14520//结束的时间
+ }
+}
+```
+
+
+
+### 语音文件识别
+
+http://118.145.158.207/asr/sentence-recognize
+
+请求参数。默认中文引擎,zh-cn 中文/en-us 英文
+
+```json
+{
+ "format": "pcm",//编码格式
+ "sample_rate": 16000,//采样率
+ "lang": "zh-cn"//语言
+}
+```
+
+响应
+
+```json
+{
+ "status": 0,//状态
+ "data": {
+ "result": "识别的文字"//识别文字
+ }
+}
+```
+
+
+
+## Aigc
+
+### 创建任务
+
+http://118.145.158.207/aigc/create-image
+
+请求参数
+
+```json
+{
+ "subject": "生成一个黑色背景的表盘蓝色指针", // type=5 参数不生效
+ // "image_base64": "iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7", // data:image/png;base64,{image_base64} 只要图片内容 //type=0 不需要提交image_base64 type=5 必须提交
+ "type": 0, //0:⽂字⽣图,1: ⽂+图⽣图 5: 图⽣图(⻛格变换)
+ "algorithm": "auto", //算法id可⽤值⻅get-style
+ "width":512,//⽣成的图⽚宽度,type=1时需要是32的倍数。 type=5 参数不生效
+ "height":512,//⽣成的图⽚高度,type=1时需要是32的倍数。 type=5 参数不生效
+ "number":4,//⽣成的图⽚的张数,最多生成4张
+}
+```
+
+响应
+
+```json
+{
+ "status": 0,
+ "data": {
+ "status": "PENDING",//状态
+ "task_id": "1706fbfd-8053-41d6-957f-0465b95e51e6"//任务id
+ }
+}
+```
+
+### 查询任务状态
+
+http://118.145.158.207/aigc/get-result
+
+请求参数
+
+```json
+{ "task_id": "1706fbfd-8053-41d6-957f-0465b95e51e6" }
+```
+
+响应
+
+```json
+{
+ "status": 0,
+ "data": {
+ "status": "SUCCEEDED",//状态
+ "task_id": "27b34e2faa42c40b47d0b941a0a5853a",//任务id
+ "result": [
+ "图片url",//图片url
+ ]
+ }
+}
+```
+
+
+
+## 大模型对话
+
+ws://118.145.158.207:80/chat/ws
+
+请求参数,通过platform切换引擎
+
+```json
+{
+ "platform":"huoshan",//使用模型,支持huoshan、文心一言、Kimi
+ "type":"chat",//固定chat
+ "content":"今天天气怎么样"//对话内容
+}
+```
+
+响应
+
+```json
+{
+ "type":"middle",//状态
+ "content":"预测或者气候变化等相关"//返回的内容
+}
+```
+
--- /dev/null
+# 北科服务器接口问题
+
+## 文档地址
+
+https://apifox.com/apidoc/shared-054b7e2c-cf4b-4e2b-875c-79fda14ddbbf/doc-4686064
+
+## 全局参数
+
+1. 用户token从哪里获取?
+
+ 用户注册,暂时用不上。
+
+2. 设备token、用户token具体在哪使用,需要具体说明。
+
+ 设备token、用户token二选一。使用说明接口文档补充。
+
+## 支持平台
+
+1. 需要支持文心一言、KIMI、豆包。目前只有 阿里和豆包。
+
+ 文心一言、KIMI下周一提供。
+
+ 使用的tts、asr使用阿里的。
+
+## aigc
+
+1. 需要支持4张图片,返回url。目前只返回一张图片,格式是image_base64。
+
+ 之前阿里的缓存24小时。确定改回之前支持4图片的
+
+## 其他
+
+、
+
+1. 服务器地址、接口请求参数、响应内容不稳定。
+2. 服务器每次更新,更新前需要将更新内容通知我们。
\ No newline at end of file