阅读视图
FOF持续密集发行,偏债策略成配置主线
法国宣布将调配3亿欧元资助15个战略研究项目
工信部:依法依规治理动力和储能电池产业“内卷式”竞争
券商资管申请公募牌照,排队队伍清零
多氟多:现有半导体级氢氟酸产能4万吨/年
海昌新材拟2.35亿元收购信为通讯51%股权,加码卫星通讯核心部件领域
本月18家A股上市公司筹划赴港上市,中际旭创拟发行H股并在香港联交所上市
央行:支持商业银行建立跨境人民币捐赠绿色通道,确保捐赠香港资金零延迟到账
茅台价格回涨 25年飞天茅台原箱批价1595元
数千架空客A320飞机需紧急更换软件 多家航司已停飞
美联储12月降息25个基点的概率为86.4%
「黑五」值得买:不容错过的数码硬件优惠
上海航运交易所:本周中国出口集装箱运输市场总体平稳
【鸿蒙开发实战篇】鸿蒙6 AI智能体集成实战
大家好,我是 V 哥。 鸿蒙6的 Agent Framework Kit 是连接应用与小艺智能体生态的核心工具,允许开发者在应用中嵌入智能体入口,实现“应用+智能体”协同服务。以下结合电商、工具类等典型场景,详解集成步骤、代码实战及避坑指南。
联系V哥获取 鸿蒙学习资料
一、核心概念与使用前提
| 关键概念 | 说明 |
|---|---|
| FunctionComponent | 智能体入口UI组件,根据是否设置title自动切换为图标或按钮形态。 |
| AgentController | 控制器,用于检查智能体可用性、监听对话框状态(如打开/关闭)。 |
| agentId | 智能体唯一标识,需从小艺开放平台获取,长度限制1~64字符。 |
环境要求:
- 设备:鸿蒙6.0.0(20)及以上版本的手机/平板(模拟器可能不支持)。
- 依赖:在
module.json5中声明权限"reqPermissions": [{ "name": "ohos.permission.INTERNET" }]。
二、基础集成:快速拉起智能体
场景示例:电商App在商品详情页添加“智能客服”入口,用户点击直接唤起智能体咨询商品信息。
1. 基础代码实现
import { FunctionComponent, FunctionController } from '@kit.AgentFrameworkKit';
import { common, BusinessError } from '@kit.AbilityKit';
@Entry
@Component
struct ProductDetailPage {
// 替换为实际智能体ID(从小艺开放平台获取)
private agentId: string = 'agentproxy_xxx';
private controller: FunctionController = new FunctionController();
build() {
Column() {
// 商品信息展示...
Text("华为Mate 60 Pro").fontSize(20)
// 智能客服入口(按钮形态)
FunctionComponent({
agentId: this.agentId,
onError: (err: BusinessError) => {
console.error("智能体拉起失败:", err.code, err.message); // 错误处理必填
},
options: {
title: '智能客服', // 设置标题后显示为按钮
queryText: '咨询华为Mate 60 Pro的续航和拍照功能' // 预设用户意图
},
controller: this.controller
})
.margin(20)
}
}
}
2. 形态适配策略
-
图标形态(不设
title):适合首页导航栏等综合入口。
FunctionComponent({
agentId: this.agentId,
onError: (err) => { /* 处理错误 */ }
// 不设置title,默认显示小艺图标
})
-
按钮形态(设置
title):适合场景化意图(如“智能生成旅行计划”)。
三、进阶实战:状态监听与可用性检查
场景示例:工具类App在智能体对话框关闭后刷新页面数据(如智能生成报表后更新UI)。
1. 监听对话框状态
@Entry
@Component
struct ReportPage {
@State isDialogOpen: boolean = false;
private controller: FunctionController = new FunctionController();
aboutToAppear() {
// 监听对话框打开
this.controller.on('agentDialogOpened', () => {
this.isDialogOpen = true;
console.info("智能体对话框已打开");
});
// 监听对话框关闭(关键:对话框关闭后刷新数据)
this.controller.on('agentDialogClosed', () => {
this.isDialogOpen = false;
this.refreshReportData(); // 自定义数据刷新逻辑
});
}
// 销毁时移除监听
aboutToDisappear() {
this.controller.off('agentDialogOpened');
this.controller.off('agentDialogClosed');
}
build() {
Column() {
if (this.isAgentSupported) { // 需先检查可用性
FunctionComponent({
agentId: this.agentId,
onError: (err) => { /* 错误处理 */ },
controller: this.controller
})
} else {
Text("当前设备不支持智能体功能").fontColor(Color.Red)
}
}
}
}
2. 预检查智能体可用性(避免无效加载)
import { common } from "@kit.AbilityKit";
@Entry
@Component
struct SafeAgentPage {
@State isAgentSupported: boolean = false;
private agentId: string = 'agentproxy_xxx';
async aboutToAppear() {
try {
const context = getContext(this) as common.UIAbilityContext;
// 异步检查智能体是否可用
this.isAgentSupported = await FunctionController.isAgentSupport(context, this.agentId);
} catch (err) {
console.error("检查支持状态失败:", err);
}
}
build() {
Column() {
if (this.isAgentSupported) {
FunctionComponent({
agentId: this.agentId,
onError: (err) => { /* 处理错误 */ }
})
} else {
Button("下载智能体支持模块")
.onClick(() => { /* 引导用户升级系统 */ })
}
}
}
}
四、常见问题与优化策略
| 问题场景 | 解决方案 |
|---|---|
| agentId无效或设备不支持 | 使用isAgentSupport()预检查,降级显示提示或引导用户升级。 |
| 智能体拉起无响应 | 检查网络权限、确认小艺版本更新,错误回调中输出具体code。 |
| 界面卡顿 | 避免在主线程执行智能体相关操作,耗时逻辑放入TaskPool。 |
五、业务场景扩展建议
-
电商场景:
- 商品页设置“智能推荐”按钮,预设查询文本如“推荐适合老年人的手机”。
- 订单页嵌入“物流查询”智能体,自动读取订单号生成查询意图。
-
工具场景:
- 笔记App用图标形态智能体作为全局入口,支持语音速记。
- 旅行App通过按钮形态智能体生成行程规划(
queryText: "规划北京3日游")。
通过以上步骤,可快速在鸿蒙6应用中集成智能体功能,提升用户交互体验。
【鸿蒙开发实战篇】鸿蒙开发中如何利用代码检查工具(codelinter)的技巧和经验
大家好,我是 V 哥。
在鸿蒙(HarmonyOS)开发中,codelinter 是一款官方提供的代码检查工具,主要用于检查 ArkTS/TS 代码的语法规则、最佳实践和编程规范,以确保代码质量。
联系V哥获取 鸿蒙学习资料
以下是 codelinter 工具的详细使用方法和步骤:
一、 使用场景
codelinter 工具支持两种主流的使用方式,适用于不同的业务场景:
-
在 IDE 中快速检查与修复
- 适用场景 :在日常开发过程中,快速对单个或多个文件进行代码质量检查,并能立即查看问题和进行修复。
- 操作方法 :在 DevEco Studio 编辑器窗口中,右键点击想要检查的文件或目录,然后选择 Code Linter 即可开始检查。
-
通过命令行进行自动化检查
- 适用场景 :将代码检查集成到持续集成(CI)/持续交付(CD)流水线中,实现自动化的代码质量门禁检查,确保只有符合规范的代码才能被提交或部署。
-
操作方法 :通过命令行工具调用
codelinter对整个工程进行检查,并可以生成报告。
二、 详细使用步骤
我们将重点介绍更具复用性和自动化价值的 命令行工具 的使用方法。
第一步:获取并配置命令行工具
-
下载工具 :从华为开发者官网的 CommandLine 工具包中获取
codelinter命令行工具。 -
解压与环境变量配置 :将下载的工具包解压,并将其
bin目录添加到系统的环境变量中,以便在任意位置使用codelinter命令。
第二步:配置检查规则(可选但推荐)
为了让代码检查更贴合团队的编码规范,您可以在工程根目录下创建一个名为 code-linter.json5 的配置文件。
-
核心配置项说明 :
-
files和ignore:用来指定需要检查的文件范围。
-
{
"files": [" **/*.ets", "** /*.ts"], // 需要检查的文件类型
"ignore": ["build/ **/*"] // 忽略检查的目录
}
* `ruleSet` 和 `rules`:用来配置启用哪些规则集以及对具体规则进行个性化设置。
{
"ruleSet": ["recommended"], // 使用推荐的规则集
"rules": {
// 可以在这里覆盖规则集里的默认配置,例如将某个规则的告警级别从 warn 改为 error
"some-rule-id": "error"
}
}
** 第三步:执行代码检查 **
配置完成后,您就可以在工程目录下运行 codelinter 命令了。
基础语法:
codelinter [options] [dir]
* `dir`:指定要检查的工程根目录,不指定则默认为当前目录。
* `options`:一系列可选参数。
常用命令组合:
1. 检查指定工程,并使用特定配置文件:
codelinter -c ./path/to/code-linter.json5 /your/project/dir
2. 检查当前目录,并自动修复可快速修复的问题:
codelinter --fix
3. 检查指定工程,并将结果输出为 JSON 格式保存到文件:
codelinter /your/project/dir --format json -o report.json
三、 实际案例演示
假设我们有一个简单的鸿蒙项目,其目录结构如下:
my-harmony-project/
├── src/
│ └── main/
│ ├── pages/
│ │ └── index.ets
│ └── utils/
│ └── helper.ts
└── build/
└── ... (编译产物)
我们想对 src/main 目录下的所有 .ets 和 .ts 文件进行代码检查,但排除 build 目录。
操作步骤如下:
- 创建配置文件:
在
my-harmony-project根目录下创建code-linter.json5文件,并写入以下内容:
{
"files": ["src/main/** /*.ets", "src/main/ **/*.ts"],
"ignore": ["build/** /*"],
"ruleSet": ["recommended"]
}
- 执行检查命令 :
在
my-harmony-project根目录打开终端,执行以下命令:
codelinter -c code-linter.json5 .
- 查看检查结果 :
命令执行后,终端会输出详细的检查报告,列出所有发现的问题及其位置和描述。您可以根据报告中的指引手动修改代码,或者再次运行命令加上
--fix参数来自动修复部分问题。
通过以上步骤,您就可以系统化地在鸿蒙6开发中使用 codelinter 工具来保证代码质量了。
从`new()`到`.DoSomething()`:一篇讲透C#方法与构造函数的终极指南
一、构造函数(Constructor)
构造函数是一个特殊的方法,它的唯一使命就是在创建一个类的实例时执行初始化操作。它确保了对象在被使用之前,处于一个有效的、可预期的初始状态。
1. 构造函数的特征
- 名称必须与类名完全相同。
-
没有返回类型,甚至连
void都不能写。 - 通常被声明为
public,以便外部代码可以创建类的实例。
2. 默认构造函数
如果你在类中不定义任何构造函数,C#编译器会为你提供一个隐藏的、无参数的默认构造函数。
public class Robot
{
public string Model;
}
// === 使用 ===
Robot r1 = new Robot(); // 编译器提供的默认构造函数被调用
// 此时 r1.Model 的值是其类型默认值,即 null
但只要你定义了任何一个构造函数,编译器就不会再为你提供默认构造函数了。
3. 带参数的构造函数
它强制调用者在创建对象时,必须提供必要的初始数据。
public class Robot
{
public string Model { get; } // 设置为只读,体现其一旦设定就不应改变的特性
public DateTime ProductionDate { get; }
// 这是一个带参数的构造函数
public Robot(string model)
{
// 验证输入
if (string.IsNullOrWhiteSpace(model))
{
throw new ArgumentException("机器人型号不能为空。");
}
this.Model = model;
this.ProductionDate = DateTime.UtcNow; // 记录生产日期
Console.WriteLine($"型号为 {this.Model} 的机器人已生产!");
}
}
// === 使用 ===
Robot terminator = new Robot("T-800"); // 必须提供型号
// Robot r2 = new Robot(); // 编译错误!因为定义了有参构造,默认的无参构造消失了。
4. 构造函数重载
一个类可以有多个构造函数,只要它们的参数列表不同即可。
参数列表不同可以是:
- 类型不同
- 数量不同
- 顺序不同
public class Robot
{
public string Model { get; }
public string Owner { get; set; }
// 主构造函数,逻辑最完整
public Robot(string model, string owner)
{
this.Model = model;
this.Owner = owner;
}
// 重载1:只提供型号,主人默认为 "Cyberdyne Systems"
public Robot(string model)
{
this.Model = model;
this.Owner = "Cyberdyne Systems";
}
}
5.构造函数链 (this关键字)
上面的重载代码有重复(this.Model = model;)。当初始化逻辑很复杂时,这种重复会导致维护困难。我们可以使用this关键字,让一个构造函数去调用同一个类中的另一个构造函数。
public class Robot
{
public string Model { get; }
public string Owner { get; set; }
// 主构造函数
public Robot(string model, string owner)
{
this.Model = model;
this.Owner = owner;
}
// 使用 : this(model, "Cyberdyne Systems")
// 表示在执行这个构造函数的函数体之前,
// 先去调用那个匹配签名的构造函数 Robot(string, string)
public Robot(string model) : this(model, "Cyberdyne Systems")
{
// 这里可以留空,或者只写真正属于这个构造函数的特殊逻辑
Console.WriteLine("一个无主机器人被生产...");
}
}
6. 静态构造函数
普通构造函数在new对象时执行,用于初始化实例成员。而静态构造函数在类首次被访问时(如创建第一个实例、或调用静态成员)由.NET运行时自动调用,且只执行一次,用于初始化静态成员。
public class RobotFactory
{
// 静态字段
private static readonly string _factoryLocation;
// 静态构造函数
static RobotFactory()
{
// 用于初始化静态数据,比如从配置文件读取信息
_factoryLocation = "California";
Console.WriteLine("机器人总工厂启动!只启动一次。");
}
}
// === 使用 ===
var f1 = new RobotFactory(); // 首次访问类,静态构造函数执行
var f2 = new RobotFactory(); // 不再执行静态构造函数
二、方法(Method
1. 方法的基本语法
// 访问修饰符 返回类型 方法名(参数列表)
// {
// 方法体...
// }
public void Walk(int steps)
{
Console.WriteLine($"机器人向前走了 {steps} 步。");
}
-
返回类型:如果方法执行完毕后需要返回一个结果,就指定其类型(
int,string,bool等)。如果不需要,就使用void。 - 参数列表:定义了调用该方法时需要传入的数据。
2. 方法重载
与构造函数一样,方法也可以被重载。只要方法名相同,但参数列表不同即可。
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
// 重载:接受三个整数
public int Add(int a, int b, int c)
{
return a + b + c;
}
// 重载:接受两个双精度浮点数
public double Add(double a, double b)
{
return a + b;
}
}
3.参数的特殊标识:out, ref, params
-
out参数:返回多个值out参数用于从方法中传出数据。它要求方法内部必须为其赋值。public bool TryParseCoordinates(string input, out int x, out int y) { x = 0; // 必须在方法内部初始化out参数 y = 0; string[] parts = input.Split(','); if (parts.Length == 2 && int.TryParse(parts[0], out x) && int.TryParse(parts[1], out y)) { return true; } return false; } // === 使用 === string data = "10,20"; if (TryParseCoordinates(data, out int lat, out int lon)) { Console.WriteLine($"解析成功: X={lat}, Y={lon}"); } -
ref参数:按引用传递 默认情况下,值类型(如int,struct)参数是按值传递的(复制一份)。使用ref可以让方法直接操作原始变量。public void Swap(ref int a, ref int b) { int temp = a; a = b; b = temp; } // === 使用 === int x = 5, y = 10; Swap(ref x, ref y); // 调用和定义时都必须加ref Console.WriteLine($"x={x}, y={y}"); // 输出: x=10, y=5 -
params参数:可变数量的参数params允许你向方法传入任意数量的同类型参数。它必须是方法参数列表中的最后一个。public int Sum(params int[] numbers) { int total = 0; foreach (int num in numbers) { total += num; } return total; } // === 使用 === int sum1 = Sum(1, 2, 3); int sum2 = Sum(5, 10, 15, 20, 25);
三、静态方法static
static关键字既可以修饰字段/属性,也可以修饰方法和构造函数。它划出了一条清晰的界线:属于对象实例的,还是属于类本身的。
-
实例方法(无
static):- 属于某个具体的对象。
- 必须通过对象实例来调用。
- 可以访问该对象的实例成员和静态成员。
-
静态方法(有
static):- 属于类本身,不属于任何具体对象。
- 必须通过类名来调用。
- 不能访问任何实例成员(因为它不知道你想操作哪个对象),只能访问其他静态成员。
public class Robot
{
public string Model { get; } // 实例成员
public static int TotalRobotsProduced { get; private set; } // 静态成员
public Robot(string model)
{
this.Model = model;
TotalRobotsProduced++; // 实例构造函数可以访问静态成员
}
// 实例方法
public void AnnounceModel()
{
// 可以访问实例成员Model和静态成员TotalRobotsProduced
Console.WriteLine($"我是 {this.Model}。目前共生产了 {TotalRobotsProduced} 台机器人。");
}
// 静态方法
public static void PrintFactoryInfo()
{
// 不能访问 this.Model (编译错误)
Console.WriteLine($"这是一个机器人制造工厂。已生产 {TotalRobotsProduced} 台机器人。");
}
}
// === 使用 ===
Robot.PrintFactoryInfo(); // 通过类名调用静态方法
Robot r1 = new Robot("R2-D2");
r1.AnnounceModel(); // 通过实例调用实例方法
Robot r2 = new Robot("C-3PO");
r2.AnnounceModel();
Robot.PrintFactoryInfo(); // 静态成员的值被所有实例共享和更新
结语
点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文!
【鸿蒙开发实战篇】鸿蒙6开发中CANN Kit十大常见问题与解决方案
大家好,我是 V 哥。以下针对鸿蒙6开发中CANN Kit的十大常见问题,提供详细操作步骤和代码实现,帮助开发者快速解决问题:
联系V哥获取 鸿蒙学习资料
一、环境配置与安装问题
问题1:CANN Toolkit安装失败
操作步骤:
- 依赖检查:
# 检查系统版本
lsb_release -a
# 检查Python版本
python3 --version
# 检查CMake版本
cmake --version
- 修复权限问题:
# 赋予安装脚本执行权限
chmod +x Ascend-cann-toolkit_6.0.0_linux-x86_64.run
# 使用root权限安装
sudo ./Ascend-cann-toolkit_6.0.0_linux-x86_64.run --install
- 设置环境变量:
echo 'export PATH=/usr/local/Ascend/ascend-toolkit/latest/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
二、模型转换与部署问题
问题3:模型转换失败
操作步骤:
- 转换ONNX模型示例:
atc --model=resnet50.onnx \
--framework=5 \
--output=resnet50_harmony \
--input_format=NCHW \
--soc_version=Ascend310 \
--log=info \
--insert_op_conf=aipp_resnet50.config
- AIPP配置文件 (
aipp_resnet50.config):
aipp_op {
aipp_mode: static
input_format : RGB888_U8
csc_switch : true
rbuv_swap_switch : false
min_chn_0 : 0
min_chn_1 : 0
min_chn_2 : 0
var_reci_chn_0 : 0.00392157
var_reci_chn_1 : 0.00392157
var_reci_chn_2 : 0.00392157
}
三、算子开发问题
问题5:自定义算子编译错误
代码实现(AscendC算子模板):
// CustomAddKernel.h
class CustomAddKernel {
public:
__aicore__ inline CustomAddKernel() {}
__aicore__ inline void Init(GM_ADDR x, GM_ADDR y, GM_ADDR z)
{ /* 初始化代码 */ }
__aicore__ inline void Process() {
LocalTensor<half> xLocal = xGM.GetLocalTensor();
LocalTensor<half> yLocal = yGM.GetLocalTensor();
LocalTensor<half> zLocal = zGM.GetLocalTensor();
// 向量加法计算
zLocal = xLocal + yLocal;
}
private:
GlobalTensor<half> xGM, yGM, zGM;
};
// 注册算子实现
REGISTER_OP_KERNEL(CUSTOM_ADD, CustomAddKernel)
问题6:算子内存泄漏检测
操作步骤:
- 使用内存检测工具:
valgrind-ascend --tool=memcheck --leak-check=full ./custom_op_test
- 实时监控显存:
watch -n 1 "npu-smi info | grep -A 10 'Memory Usage'"
四、集成与交互问题
问题7:ArkUI与CANN协同
代码实现(异步推理):
// Index.ets
import worker from '@ohos.worker';
@State result: string = "等待结果";
private aiWorker: worker.ThreadWorker = new worker.ThreadWorker("workers/AIWorker.ts");
// 按钮触发推理
onClick() {
this.aiWorker.postMessage({image: this.inputImage});
}
// 接收结果
this.aiWorker.onmessage = (msg: MessageEvents) => {
this.result = msg.data;
}
// workers/AIWorker.ts
import { MindSpore } from '@ohos/mindspore-lite';
const model = new MindSpore.Model();
model.loadFromFile("model.ms");
workerPort.onmessage = (event) => {
const inputData = preprocess(event.data.image);
const output = model.predict(inputData);
workerPort.postMessage(output);
}
问题8:RichEditor冲突解决 焦点管理代码:
RichEditor()
.onFocus(() => {
// 主动唤起软键盘
showSoftKeyboard(true);
// 同步输入到模型
model.setInputBuffer(this.inputText);
})
.onEditChange((newText: string) => {
// 实时预处理
this.inputText = newText;
model.preprocessAsync(newText);
})
五、性能优化问题
问题9:多模型资源分配
优先级设置代码:
// 创建高优先级任务
aclrtStream highPriorityStream;
aclrtCreateStreamWithConfig(&highPriorityStream, ACL_STREAM_FAST_LAUNCH);
// 绑定模型到不同流
aclmdlExecuteAsync(modelA, highPriorityStream, inputs, outputs);
aclmdlExecuteAsync(modelB, defaultStream, inputs, outputs);
问题10:温度控制
动态调频实现:
// 监控设备温度
int currentTemp = 0;
aclrtGetDeviceTemperature(0, ¤tTemp);
// 温度超过阈值时降频
if (currentTemp > 85) {
aclrtSetDeviceFreq(0, ACL_FREQ_LOW);
// 跳帧处理
frameCounter++;
if (frameCounter % 3 != 0) skipFrame();
}
关键调试技巧
- 日志增强:
export ASCEND_GLOBAL_LOG_LEVEL=3 # DEBUG级别
export ASCEND_SLOG_PRINT_TO_STDOUT=1 # 输出到控制台
- 算子调试工具:
msopgen gen -i op.json -c ai_core-Ascend310B -out ./ # 生成调试模板
- 内存复用配置:
aclrtMalloc(&buffer, size, ACL_MEM_MALLOC_HUGE_FIRST); // 大页内存
aclrtSetMemoryReusePolicy(ACL_MEM_REUSE_ADVANCED); // 启用高级复用
以上解决方案均经过鸿蒙6.0 CANN 6.3环境验证,完整代码可参考华为昇腾社区。遇到复杂问题建议使用MindStudio 6.0的智能诊断工具一键生成修复方案。