• [案例共创] 【获奖名单已公布】【案例共创】第5期 开发者空间 AI Agent 开发
    本期获奖情况如下,代金券和礼品会发到参与活动时大家填写的这个报名问卷的账号和收货地址中,请注意查收。代金券即将发放至论坛私信(右上角我的社区-我的消息-私信中领取),礼品已在申请流程中,请耐心等待,谢谢多篇文章的本期以最高奖项的发放,不叠加。如发布3篇,2篇参与奖,1篇三等奖,按三等奖发放。主题作者帖子标题奖项胡琦【案例共创】快速搭建智搜可视化Agent二等奖黄生【案例共创】使用华为云开发者空间 AI Agent 进行昇腾C算子开发知识库构建二等奖小草飞上天【案例共创】基于华为开发者空间开发平台 MCP资产快速构建税务AI助手服务三等奖加油~加油!【案例共创】快速搭建一个智能资助小助手三等奖给无眠点压力【案例共创】 基于华为开发者空间构建「HDiet 智膳」-智能膳食 AI Agent三等奖CC07【案例共创】使用华为云开发者空间AI agent功能零基础开发一个购房助手三等奖miyalian【案例共创】基于华为开发者空间构建实时股票分析助手三等奖郑小健【案例共创】使用AI Agent进行“信息系统项目管理师”智能助手构建三等奖柠檬味拥抱【案例共创】基于华为开发者空间从0实现一个AI Agent—大学生开学指南助手三等奖yd_272483742【案例共创】基于华为开发者空间开发平台构建旅游规划助手三等奖神一样的老师【案例共创】使用开发者空间 AI Agent+RAG+高德地图MCP开发班车出行助手三等奖曹无悔开发者空间 AI Agent 开发 - 基于 Versatile AI原生应用引擎的《系统编程》智能助教参与奖熊猫钓鱼【案例共创】基于华为开发者空间快速实现一个智能家居园艺助手参与奖风亦安AI Agent应用开发 - 利用AI创建一个Slogan生成大师的小助手参与奖yd_70527782【案例共创】AI Agent应用开发 - 利用AI创建一个作业生成小助手参与奖yd_286176440基于华为开发者空间开发平台构建云端运维助手参与奖星霞云梦低代码完成政策服务助手Agent搭建参与奖 了解案例共创活动为了让更多开发者能够更轻松、更高效地理解和使用我们的文档,进而提升云产品的整体使用体验,我们致力于进一步优化和完善官方产品文档。在此过程中,我们诚挚地邀请广大开发者积极参与,通过亲身体验云产品,编写实践案例或体验评测。一旦您的案例经过专家评审团的认可与采纳,将有以下三点:优质案例将被正式收录至官方案例库,供广大开发者学习。优质案例将选送到在华为云站内外10+个技术社区推荐,给予百万级流量资源。以上案例我们都将注明原作者名字,实现与开发者共创官方文档。参与者不仅有机会获得每月活动礼品,还有可能被评为年度内容贡献官,享受更多荣誉和奖励,获得更多合作机会。我们期待着与您一起,共同打造更加优质、高效的云服务体验。参与投稿方式第1步:(已注册并实名可跳过)华为云账号实名认证,点击这里。(已设置可跳过)登录后设置社区昵称,点我设置。第2步:点击报名填写报名问卷,提供礼品发放地址等信息第3步:开启您的云端体验,分享实践案例,点我写帖子。版块选择“社区活动”分类选择“案例共创”帖子标题在前面添加【案例共创】(一定要加,方便识别参与活动的帖子)文末可添加活动名称及链接地址,如“我正在参加【案例共创】第5期 开发者空间 AI Agent 开发  cid:link_23”【如您在体验中有任何产品问题,欢迎在论坛发布问题求助帖(帖子分类选择问题求助)咨询产品专家,如发现任何体验不友好、产品Bug、文档页面错漏等情况,欢迎通过云声平台反馈给我们,还有机会领取云声专属礼品!活动相关咨询可以论坛私信“论坛小助手SUN”】本期投稿内容Ø  华为云产品介绍:华为开发者空间AI Agent:AI原生应用引擎提供企了包括数据准备、模型选择/调优、知识工程、模型编排、应用部署、应用集成等能力,降低智能应用开发门槛、提升开发效率。华为开发者空间的AI Agent集成Versatile空间,Versatile空间是通用型AI助手和领域专家Agent的智能协同工作空间,通过自主任务规划及多工具协同,实现复杂任务的智能化处理,从而提升工作效率。领取链接:cid:link_2 AI Agent页面操作文档:cid:link_1云函数页面操作文档:cid:link_0Ø  应用构建要求开发者可结合自己的工作实践,须在华为开发者空间内完成应用构建,应用构建类型和主题参考如下:AI Agent应用开发 主题场景包括但不限于以下方向(仅做参考):使用AI Agent进行专业领域知识库构建(进行组织、存储及管理知识);咨询快捷助手(推荐助手等);工作流规划规划(使用工作流规划行程等); 案例模版参考:1. AI Agent应用开发:- 1)案例地址:cid:link_3简介:使用开发者空间开发平台,通过开发平台AI原生应用引擎中提供的MCP资产和预置的的大模型,快速便捷地完成饮食推荐和12306车票助手Agent的构建和发布。- 2)案例地址:cid:link_4简介:使用华为开发者空间-开发平台,通过开发平台提供的云函数、AI Agent能力实现We码会议助手从MCP Server部署到创建AI会议Agent,再到发布使用AI 会议Agent,最后在浏览器中操作使用该AI会议Agent。注意:请大家一定要按照案例模版的格式进行编写注意:请大家一定要按照案例模版的格式进行编写注意:请大家一定要按照案例模版的格式进行编写Ø  投稿规则1)构建的应用需要完成发布,有应用在线截图。2)案例场景和方案以及用到的华为云产品或者开源框架简述。3)开发过程实际操作描述(文字描述+截图+代码)。4)案例实操需要完整体现,读者可根据案例上手并体验。  活动流程(全年征集,每月一期评选)投稿时间:2025年7月1日-7月31日联合评审:2025年8月1日-8月9日奖项公示:2025年8月10-11日奖品发放:获奖名单公布后15个工作日内寄出 评奖规则届时将有华为技术专家团参与评审,主要按照以下维度评分:标准类型分值释义分值阶梯总分值操作完整性关键节点以文字+截图的形势进行说明1:空间的实践操作,而非方案类描述2:参考官方提供模版,可读性好,有关键节点文字说明与截图3:符合2的条件下能够突出关键代码释义以及关键步骤解释说明4:应用构建后可执行,并有执行后效果介绍和展示1-4/0.5梯度4案例实用性贴近实际行业生产或者业务场景1:纯技术应用不涉及实际应用场景,比如接入模型后的问答助手2:基于实际应用场景具有一定的实用性3:基于实际场景且在开发者空间完成部署实施4:基于实际应用场景解决行业的痛点问题,可用于生产实施1-4/0.5梯度4技术多样性案例中应用更多的华为云技术或主流开源框架1:使用活动中要求的空间以及工具或者技术2:基于空间结合更多的华为云技术/服务、开源框架或技术1-2/0.5梯度2荣誉与奖励1、获奖案例将有机会作为华为云开发者空间应用的标杆案例,进行录入成册推广宣传,并发布到华为云开发者联盟公众号传播矩阵。同时,案例内容可作为后续作者申请评选HCDE的相关贡献内容。标杆案例:【华为开发者空间 · 案例共创故事(郑州轻工业大学)】(https://mp.weixin.qq.com/s/pPmPcdlWYLPalzJPqxIfew) Ø  奖项设置 文章类奖项:视频案例奖项:一等奖1个,1000元实物,华为sound joy 音箱;  二等奖3个,华为6i耳机;三等奖10个,礼品+代金券注:每期评审专家根据每篇文章评选文章分数及等级,每期文章质量不一,不排除有某个奖项轮空的情况,如第X期文章质量在A级和B级,即S和S+级轮空。代金券及周边礼物发放对象为:已完成实名认证的华为云用户。发放到填写报名问卷的账号及收货地址中,礼包类礼品均为实物礼品。 重要说明1、投稿内容一经采用,将会被纳入官方产品文档,文章作者拥有著作权,华为云拥有使用权、修改权等。2、每期评审专家根据每篇文章评选文章分数及等级,每期文章质量不一,不排除有某个奖项轮空的情况,如第X期文章质量在A级和B级,即S和S+级轮空3、投稿内容字数不少于500字(不含代码),要求思路清晰、文字顺通、图片清晰、代码规范,不得有打广告、加二维码引流和凑字数行为。4、文章要求发布在华为云开发者社区论坛,允许搬运自己发在其他平台的文章,但仅支持搬运自己的原创文章,搬运他人的文章当抄袭处理。内容必须保证内容原创性,实践过程真实、内容代码化,如发现投稿内容为转载、复制、抄袭、恶意拼接、灌水等侵权作弊行为,均视为无效并取消参与资格5、将发表过的文章删除后重新发表的文章,不计入发文数量,以文章评选时在线显示数量为准。活动期间,如有用脚本刷阅读量、注册僵尸号刷赞等严重违反社区规范的行为,直接取消所有获奖资格。6、审核通过被采纳的文章作者,将在下月初进行获奖名单公布和奖励发放,届时会有站内信通知,请及时关注并填写快递信息,过期未核对或填写视为放弃该期奖品。7、对于持续输出高质量内容的作者,每年度末还有年度贡献大礼包送上。特别声明:华为云有权根据自身运营安排,自主决定和调整
  • [热门活动] 礼品已邮寄//【体验有礼】华为开发者空间新特性体验,华为手环、云宝礼盒等你来抽
    本期华为开发者空间【体验有礼】活动获奖名单如下,请获奖用户8月29日前反馈收件信息,奖品将于9月初寄出  活动一【体验打卡】获奖清单活动二【实践互动】获奖清单__________________________________________________________________________________________________________________    在6月20日华为开发者大会上,开发者空间宣布全部升级,新功能集成了AI原生应用引擎、AI Notebook、FunctionGraph云函数、云开发环境、Astro低代码等能力,基于这些全新特性,开发者在开发者空间可以开通大模型服务、部署MCP Server、创建Agent等各位小伙伴是不是已经跃跃欲试了?小编特意准备新功能体验打卡活动,更有华为手环、云宝礼盒等你来抽,快叫上小伙伴一起来体验吧~ 【活动时间】即日起—7月31日 【活动流程】完成报名 → 选择活动方式 → 参与活动 → 获得激励 【活动方式】(以下活动均可参与,奖励可叠加)活动一【体验打卡】:体验打卡开发者空间新功能,参与开发者定制礼品抽奖参与方式:活动期间,进入开发者空间,体验空间各项新能力,比如开发平台(AI Agent、低代码应用、云开发环境、云函数)、学习成长、知识推荐、学习笔记、应用构建等,活动结束后将在参与活动报名并在活动期间登录名单中抽奖。奖项设置:开发者定制双肩包*2个,开发者定制短袖T恤*10个 活动二【实践互动】:完成开发者空间“开发平台”新功能案例体验,抽取华为手环参与方式:选择下方任意一个或多个案例进行体验,并在评论区分享案例截图(也欢迎书写你对案例实践后的体验和感受),活动结束后,将在评论区用户中抽奖。体验功能案例名称难度系数AI Agent基于华为开发者空间开发平台 MCP资产快速构建AI Agent应用★★★AI Agent、云函数基于华为开发者空间开发平台构建We码会议助手★★★★Astro低代码应用基于华为开发者空间Astro低代码应用平台,构建业务用户登录后台开发★★★★基于华为开发者空间Astro低代码应用平台,构建业务用户登录页面前台开发★★★★云开发环境开发者空间 - 云开发环境使用指导★★★本地Xshell基于华为开发者空间云开发环境完成上传下载★★★奖项设置:华为手环9(NFC款)*2个,华为云云宝礼盒款*3个,开发者定制短袖T恤*15个中奖小妙招:完成多个案例,会增加中奖概率!完成后请在回帖中注明完成了几个案例,以及案例名称。小编会对案例完成情况进行复核,请大家真实完成哦! 部分礼品示意 PS:完成案例的小伙伴可以继续参加开发者空间案例共创第五期活动,如在使用中有产品优化建议,可参与开发者空间产品体验官活动,更多礼品等你解锁~【活动规则】1、抽奖方式:活动结束后,我们将从参与活动的用户中(华为云新老用户均可参与),通过巨公平台或Excel 函数形式抽取获奖用户。获奖名单将在活动结束后的7个工作日内公布。2、活动二仅限于在“华为开发者空间”内体验相关案例项目,其他项目建议不参与此次活动,否则视为无效内容,具体参考案例中心内容。另外活动将根据有效评论数设置获奖人员数量,规则如下:有效评论数量获奖名额105201030以上213、本次活动将根据实际参与情况发放奖励,包括但不限于用户百分之百中奖或奖项轮空的情况。【活动说明】用户限制说明:1、参加本次社区活动的用户必须为华为云注册用户。同时为保证活动公平性,禁止用户以IAM账号身份参与活动,否则将视为无效。2、领取奖品的用户需为华为云实名用户,未完成实名认证的用户将不发放活动奖励。3、本次活动如一个实名认证对应多个账号,只有一个账号可领取奖励。如在同一概率活动中,同一账号重复获奖,只发放首先获奖奖品。4、本次活动一个实名认证账号只能对应一个收件人,如同一账号填写多个不同收件人,不予发放奖励。5、请开发者不要在活动期间随意修改社区昵称和华为云账号,由此产生的统计问题,如过了申诉期,小助手不再处理。(申诉期为活动结果公示3天内。)奖品发放说明:1、本活动结束之后10个工作日内公示获奖信息,获奖开发者用户需在截止时间在获奖信息收集表中填写获奖信息,获奖信息截止收集日过后10个工作日内,将统一发出奖品。华为云遵守《中华人民共和国个人信息保护法》规定,将以上个人信息仅用于礼品发放之目的,不会向任何第三方披露。若由于获奖开发者用户自身原因(包括但不限于联系方式有误、身份不符或超过截止登记日期等)造成奖品无法发送,视为获奖开发者用户放弃领奖。2、为保证活动的公平公正,华为云有权对恶意刷活动资源(“恶意”是指为获取资源而异常注册账号等破坏活动公平性的行为),利用资源从事违法违规行为的开发者用户收回抽奖及奖励资格。3、若发放奖品时,出现库存不足,则优先发放等价值的其他实物奖品;HDC限定礼包为24/25年款随机发货;活动二中华为耳机需要在有效评论>40个开启。4、所有参加本活动的开发者用户,均视为认可并同意遵守《华为云开发者用户协议》,包括以援引方式纳入《华为云开发者用户协议》、《可接受的使用政策》、《法律声明》、《隐私政策声明》、相关服务等级协议(SLA),以及华为云服务网站规定的其他协议和政策(统称为“云服务协议”)的约束。5、如果您不同意本活动规则和云服务协议的条款,请勿参加本活动。
  • [热门活动] 【中奖名单公示】///【产品体验官】华为开发者空间开发平台全新上线,提优化建议领千元开发者好礼
     【华为开发者空间开发平台】产品体验官活动获奖名单如下:一、有效建议奖:昵称建议分值奖项礼品yd_2846377506.9有效建议奖第一名1000元开发者定制礼品banjin4.4有效建议奖第二名800元开发者定制礼品胡琦2.1有效建议奖第三名500元开发者定制礼品二、优质建议奖:昵称礼品yd_284637750100-200元开发者定制礼品banjin100-200元开发者定制礼品胡琦100-200元开发者定制礼品小草飞上天100-200元开发者定制礼品神一样的老师100-200元开发者定制礼品给无眠点压力100-200元开发者定制礼品yd_70527782100-200元开发者定制礼品yd_269585276100-200元开发者定制礼品yd_238822659100-200元开发者定制礼品cxw100-200元开发者定制礼品andyleung100-200元开发者定制礼品 恭喜以上11名获奖用户,请获奖用户通过以下问卷反馈奖品收件信息(9月3日前反馈有效),感谢大家对云声平台的关注和支持~华为开发者空间全新升级,开发平台重磅上线。此次开发平台集成了AI原生应用引擎、FunctionGraph云函数、Astro低代码、云开发环境能力,在算力、模型、平台和应用层都进行了大升级。基于这些全新特性,企业开发者可以在开发者空间开通大模型服务、部署MCP Server、创建Agent、可视化实现业务需求,从而快速开发上线一个企业级 AI 智能应用,让华为云和根生态技术的能量触达到每位开发者的指尖!大家赶快来体验吧,体验完后提交开发者空间优化建议,还可以领取开发者礼包,包括但不限于华为耳机、手环、鼠标、云宝等好礼,快叫上小伙伴一起提建议吧~ 【实践项目】体验项目项目名称难度系数功能体验开发平台:AI Agent/开发平台:低代码应用/开发平台:云函数 /开发平台:云开发环境/案例体验基于华为开发者空间Astro低代码应用平台,构建业务用户登录后台开发★★★★基于华为开发者空间Astro低代码应用平台,构建业务用户登录页面前台开发★★★★基于华为开发者空间开发平台 MCP资产快速构建AI Agent应用★★★基于华为开发者空间开发平台构建We码会议助手★★★★开发者空间 - 云开发环境使用指导 ★★★本地Xshell基于华为开发者空间云开发环境完成上传下载 ★★★详细信息请见“开发平台”,案例中心。 【活动时间】2025年7月1日-7月30日 【参与方式】01 体验开发者空间开发平台   》   02 去云声平台提建议    》  03 建议评估公示  》   04 获奖公示(活动结束后两周内) ps:建议标题需要以“开发者空间体验官”开头 【奖项设置】奖项设置评选条件获奖名额激励礼品有效建议奖1、有效建议数量不少于2条,有效建议由内部技术专家评审得出2、建议内容需针对上述实践项目3、有效建议中有不低于一条是关于功能体验的3名积分榜第1名:1000元礼品/人积分榜第2名:800元礼品/人积分榜第3名:500元礼品/人优质建议奖1、有效建议不少于2条,由内部技术专家评审选出10名每人100-200元开发者定制礼品【活动说明】1、建议预审通过即为有效建议,其中1条有效功能类建议为1分,1条有效体验类建议为0.3分,1条有效Bug类建议0.5分;有效建议奖与优质建议奖可叠加2、建议提交时需要在标题中以“【开发者空间体验官】”为建议标题开头,比如【开发者空间体验官】开发者空间增加XX/优化XX/导入XX等3、建议内容仅针对以上实践项目中“开发平台功能体验+开发平台案例体验”涉及的云产品,非以上实践项目涉及产品建议内容不参与此活动4、优质建议要求建议对云产品功能及优化改进有重要作用,优先从已被采纳的建议的选择;建议内容需要表述清晰,有明确的建议方案,最好有操作截图或链接等能进一步详细描述;高价值建议数量不限,且与有效建议奖可叠加,每位用户每月最多可获得一次。5、注意事项1)若出现积分相同且排名一致的情况,结合已实现和已采纳建议情况,由内部技术专家选出价值更高的建议用户给予奖励2)同一用户在同一页面(文档)提出的同一类用户体验问题(包括但不限于错别字、语句不通顺、视觉体验等),在通过审核后仅算作一条有效建议数3)若发现代他人提交优化建议,此建议分值只取原分值30%;若发现2次及以上重复提交他人建议,或3次及以上重复提交体验类相关建议进行恶意刷量(包括但不限于错别字、语句不通顺、视觉体验等),取消本人活动参与资格;在活动截止日前进行大量刷屏提交建议,或者重复提交同类型体验建议,直接取消活动参与资格4)以上兑换礼品均为仓库现有实物礼品,有货的情况下优先满足,其中1-3名可优先选择华为自营品牌电子礼品,礼品价值不能超过商品原价,无货则不可以指定,如遇商品缺货,将随机换成其他等价值礼品发放5)此【开发者空间体验官】与云声月度例行激励活动不叠加,若标题无【开发者空间体验官】标记,则有效建议默认参与月度例行激励
  • [云实验室] 云计算新手入门集证有礼
    完成时间: 2025-07-01 10:35:00案例名称: 初识云主机:CodeArts IDE入门案例完成截图:实验心得: 实验非常有趣,让我初步了解 python 和 codearts ide 的使用,而且居然还有一个云电脑能用,实验完成之后还能继续使用,真是太棒了
  • [案例共创] 【案例共创】基于华为开发者空间从0开始完成仓颉编程语言与Python的互操作
    本文基于当前仓颉通用版 (0.53.18) 进行开发演示,该版本对Linux平台提供Python FFI支持。文中的所有操作均在华为开发者空间的云主机中进行。关于Python FFI的更多说明,请参阅仓颉官方文档使用指南 -  ://docs.cangjie-lang.cn/docs/0.53.18/user_manual/source_zh_cn/FFI/cangjie-python.htmlAPI 文档  -  ://docs.cangjie-lang.cn/docs/0.53.18/libs/std/ffi_python/ffi_python_package_overview.html需要额外说明的是,截至2025年6月,仓颉尚未发布LTS版本,鸿蒙仓颉尚未提供Python FFI支持。。因此,Python FFI功能在0.53.x后续Canary版本中的变动不在本文讨论范围内。补充更新:2025年7月1日发布的仓颉(通用版)LTS1.0移除了Python FFI。0. 初始化开发者空间的云主机进入开发者空间 (cid:link_0),系统选择Ubuntu,这样可以确保预装了Python、Cangjie、以及对应的CodeArts IDE。华为云空间提供完整的开发环境。打开云主机,初始化后进入云桌面,可以通过终端命令行指令检查系统当前的 Cangjie 和 Python 版本。问🤔:什么是云主机?答🤓:云主机是开发者空间众多功能的其中之一,而云桌面是云主机默认的登录方式,此外还有无图形界面的云终端形式。1. 创建项目虽然云主机自身提供一定程度的数据持久化功能,但为了更好管理自己的代码,我们需要一个完善的流程来管理项目代码,避免意外情况造成的数据丢失。云桌面上的CodeArts IDE非常贴心地提供了完整的GitCode服务支持,此处我们可以通过点击云桌面上的GitCode快捷方式,在云主机的Firefox浏览器中访问GitCode网站。💡tips: 考虑到云主机的计算资源有限,不建议在复杂场景下高强度使用云主机内置的浏览器,同样的需求可以在外部浏览器中得到满足。GitCode的注册非常简单,此处过程省略。打开CodeArts IDE for Cangjie,其界面应如下所示:1.3 导入工程为方便后续操作,这里选择 导入GitCode 工程 。接下来我们需要获取登录令牌,在登录GitCode之后,可以根据链接指示进行操作。下图是手动创建登录令牌的界面参考,主要需要关注的是有效期和权限设置。注:熟悉云主机功能的开发者可能有注意到一个叫作同步项目的功能。这个功能的原理和上面的手动配置操作类似,区别在于同步项目需要自己手动关联华为云账号和GitCode账号;其次它目前有一个缺点,每次同步都会创建新的项目目录,此处手动配置token正是为了避免这个问题。1.1 创建代码仓库因为本文的预设前提是从0开始,故此处选择创建一个空项目,勾选初始化README。创建时需要留意 .gitignore文件 和 LICENSE 这两个文件的选择。初始化创建新项目后,切回 CodeArts IDE,选择刚创建的项目。1.1 初始化仓颉项目新建空仓库后可通过终端命令 cjpm new 完成项目初始化,通过 cjpm run 测试初始化项目运行结果。看到hello world即说明仓颉开发环境能够正常工作,在开始Python FFI代码编写前,我们通过Git提交代码完成项目的初始化。2. 在仓颉中调用Python函数2.2 指定libpython动态库位置仓颉的Python FFI默认只提供动态链接库的位置检测支持,动态库加载策略如下图所示:由于云主机预装的Python 3.12是不满足第二种查找策略的两点要求,若直接执行以下示例代码,则会报错退出:import std.ffi.python.*import std.log.*main(): Int64 { Python.load(loglevel: LogLevel.INFO) print("${Python.getVersion()}\n") Python.unload() return 0}为了解决这个问题,我们可以通过指定 PYTHON_DYNLIB 环境变量,强制仓颉运行时使用策略1进行动态库加载:export PYTHON_DYNLIB=$(find $(python -c 'import sysconfig as s;print(s.get_path("stdlib"))') -name 'libpython*' 2>/dev/null)也可以通过仓颉的子进程调用完成这个操作:// import std.io.* // import std.os.* // import std.os.process.* func pylib_path(config!: Bool = false): String { const PYEK = "PYTHON_DYNLIB" if (let Some(ev) <- getEnv(PYEK)) { return ev } let p = Process.start("bash", "-c", ##"find $( python3 -c \ 'import sysconfig as s; print(s.get_path("stdlib"))' ) \ -name 'libpython*' 2> /dev/null "##, stdOut: ProcessRedirect.Pipe) let rv = StringReader(p.stdOut).readToEnd() if (config) { setEnv(PYEK, rv) } return rv }2.1 调整仓颉运行时栈大小由于 Python 互操作使用到大量 Python 库的 native 代码,这部分代码在仓颉侧无法对其进行相应的栈保护。仓颉栈保护默认大小为 64KB,在对 Python C API 进行调用过程中,容易造成 native 代码超出默认栈大小,发生溢出,会触发不可预期的结果。建议用户在执行 Python 互操作相关代码前,配置仓颉默认栈大小至少为 1MB:export cjStackSize=1MB 3. 在仓颉中调用Python3.1 导入模块3.2 执行代码
  • [案例共创] 【案例共创】基于仓颉 + DeepSeek + MCP 的智能膳食分析助手
    基于仓颉 + DeepSeek + MCP 的智能膳食分析助手一、项目背景及需求分析1.1 项目背景随着人们对健康饮食关注度的提升,越来越多用户希望借助 AI 助手实现个性化的饮食分析与管理。然而,目前市面上的饮食类应用普遍存在如下痛点:1️⃣. 缺乏智能分析:大多数仅记录卡路里,无法提供专业点评与优化建议;2️⃣. 知识更新不及时:难以结合最新营养研究进行推荐与判断;3️⃣. 缺乏可扩展性:难以适配特定人群(如高血压、糖尿病、健身人群等)的差异需求。本项目旨在构建一个基于大语言模型(DeepSeek)和结构化协议(MCP)的智能饮食健康助手,通过自然语言交互,帮助用户实现饮食数据结构化、健康风险识别与个性化建议生成。1.2 核心需求1️⃣ 食品实体识别能力系统应具备从自然语言中准确识别出饮食相关的食品实体与关键词的能力。例如,用户输入:“我今天吃了煎饼果子、卤牛肉、奶茶”,系统应能够自动识别出食品列表:["煎饼果子", "卤牛肉", "奶茶"] 该能力是后续营养分析与健康判断的基础,应支持中文短语、多食物组合、模糊词等多种表达方式。2️⃣ 饮食结构化分析能力(MCP 中间层处理)系统应通过 MCP 模块完成对用户输入饮食内容的语义解析与结构化处理,主要包括:应提取食品清单;应识别饮食行为时段(如早餐、晚餐、夜宵等);应对识别出的食物打上健康标签(如高糖、高油脂、高钠、低纤维等);应构造符合 LLM 输入规范的 Prompt,以支撑后续模型理解与生成。结构化分析结果应作为 DeepSeek 模型调用的核心中间数据。3️⃣ 合理饮食建议生成能力(基于 DeepSeek)系统应利用 DeepSeek 大模型,根据 MCP 构造的 Prompt 生成具有科学性、专业性与可操作性的饮食建议,具体应包括:对当前饮食存在的健康风险进行分析(如糖分摄入超标、缺乏蔬菜等);提供可替代食材建议(如炸鸡 → 烤鸡,奶茶 → 绿茶);给出搭配优化方案(如餐前饮水、搭配高纤维蔬菜等);提出个性化提示(如适合三高人群、健身人群、孕期饮食等)。模型输出应以自然语言形式呈现,逻辑清晰、结构明确,风格应贴近真实营养师表达方式。二、项目概述本项目旨在构建一套基于多模块协作的 智能膳食分析助手,通过自然语言输入,实现对个人饮食行为的结构化分析与健康建议生成,服务于日常健康管理、饮食优化与疾病预防等场景。系统采用模块化设计,主要由三大核心组成部分构成:2.1 技术选型模块技术栈职责🧑‍💻 客户交互仓颉语言提供终端或界面交互入口,接收用户自然语言输入并展示模型生成结果🔧 中间处理MCP 模块(Python + FastAPI)实现饮食语义解析、食物识别、健康标签打标、Prompt 生成与请求路由🤖 模型服务DeepSeek 大语言模型接收经过标准化构造的请求 Prompt,返回结构化营养建议、健康分析与饮食优化建议2.2 适用对象企业健康服务平台产品经理个人开发者高校学生营养师/健身爱好者病患群体2.3 案例时间环境配置:约30 分钟构建项目:约25分钟测试阶段:约10 分钟本案例总时长预计 65 分钟。2.4 项目流程整个系统的数据处理与响应流程如下所示:2.5 资源总览本案例预计花费 0 元。资源名称规格免费额度/价格开发者空间–云主机2CPU4g X86 Ubuntu 22.04 Server定制版0元CodeArts IDE for CangjieIDE 仓颉 云主机预装0元CodeArts IDE for PythonIDE Python 云主机预装0元Maas DeepSeek 模型服务试用200W Token0元2.6 架构亮点✅ 使用 MCP 解耦输入处理与模型调用,提升可扩展性与灵活性;✅ DeepSeek 专注自然语言生成任务,MCP 预处理提高 prompt 精度;✅ 仓颉终端可后续拓展为图形界面、Web 应用或移动端入口;✅ 本地配置与食物字典可按需调整,适配多种用户类型(如减脂、控糖、健身等);✅ 架构支持多模型/多来源接入,可拓展至 Claude、SparkDesk、ChatGPT 等后端模型。三、环境准备3.1 申请云主机面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。如果还没有领取开发者空间云主机,可以参考免费领取云主机文档领取。领取云主机后可以直接进入华为开发者空间工作台界面,点击进入桌面连接云主机。3.2 免费领取DeepSeek R1满血版华为云提供了单模型200万免费Tokens,包含DeepSeek-R1&V3满血版,我们可以登录华为云ModelArts Studio(MaaS)控制台领取免费额度,这里我们选择DeepSeek-R1满血版来搭建我们的专属AI聊天机器人。在云主机桌面底部菜单栏,点击打开火狐浏览器。用火狐浏览器访问ModelArts Studio首页:cid:link_2,点击ModelArts Studio控制台跳转到登录界面,按照登录界面提示登录,即可进入ModelArts Studio控制台。签署免责声明。进入ModelArts Studio控制台首页,区域选择西南-贵阳一,在左侧菜单栏,选择在线推理 > 预置服务 > 免费服务,选择DeepSeek-R1-32K模型,点击领取额度,领取200万免费token。领取后点击调用说明,可以获取到对应的API地址、模型名称。红色方块为API 地址 红色箭头为 API KEY四、项目构建本案例采用 本地MCP 服务+ 本地仓颉AI机器人对话 (参考来自于 (https://gitcode.com/CaseDeveloper/Cangjie-Examples)4.1 MCP 服务构建4.1.1 打开 CodeArts IDE for Python4.1.2 新建项目项目名称 构建为: food_mcp 然后点击 创建4.1.3 代码实施mcp_server.py# -*- coding: utf-8 -*- # mcp_server.py """基于仓颉 + DeepSeek + MCP 的智能膳食分析助手""" from fastapi import FastAPI from pydantic import BaseModel import json from typing import List, Dict, Any from fastapi.responses import StreamingResponse from deepseek_client import stream_deepseek, async_stream_deepseek from text_match import extract_food_simple, extract_food from loguru import logger from cachetools import TTLCache import sys # 加载食物字典 with open("food_tags.json", encoding="utf-8") as f: FOOD_TAGS: Dict[str, Dict[str, Any]] = json.load(f) app = FastAPI() class Input(BaseModel): input: str # 全局缓存:prompt -> full_reply_text CACHE = TTLCache(maxsize=500, ttl=1800) # 30分钟自动过期 logger.remove() logger.add(sys.stdout, level="INFO", enqueue=False, backtrace=False) # ---- 基础角色词 ---- BASE_SYSTEM_PROMPT = ( "你是一名资深注册营养师,擅长以简洁的 Markdown 格式给出科学、可执行的饮食建议。" "所有回答需包含以下小节:\n" "1. 总体评价\n" "2. 推荐摄入(量/食材)\n" "3. 过量风险\n" "4. 不宜同食\n" "5. 行动建议\n" "回答请使用中文,并尽量在 300 字以内。" ) # ---- 场景化模板 ---- PROMPTS = { "nutrition_review": ( BASE_SYSTEM_PROMPT + "\n\n【场景】饮食点评。请先总体评价,再按上表 1~5 小节输出:{input}" ), "diet_plan": ( BASE_SYSTEM_PROMPT + "\n\n【场景】减脂期餐单。请输出 3 日食谱 (表格形式),并在每餐注明热量估计:{input}" ), "effects_inquiry": ( BASE_SYSTEM_PROMPT + "\n\n【场景】过量影响。请列出已知副作用及参考文献:{input}" ), "avoid_inquiry": ( BASE_SYSTEM_PROMPT + "\n\n【场景】相克查询。请说明不宜同食原因及替代方案:{input}" ), } def _process_input(user_input: str): """内部共用逻辑,返回分析结果和 prompt""" # === 1. 食物关键字匹配 === food_list, exact_hits, fuzzy_hits = extract_food(user_input, FOOD_TAGS.keys()) # 记录匹配详情到日志 logger.info(f"[匹配] 精确={exact_hits} 模糊={fuzzy_hits}") # === 2. 聚合静态信息 === tags = list({tag for food in food_list for tag in FOOD_TAGS[food].get("tags", [])}) effects = {food: FOOD_TAGS[food].get("effects") for food in food_list if FOOD_TAGS[food].get("effects")} avoid_with = list({aw for food in food_list for aw in FOOD_TAGS[food].get("avoid_with", [])}) diet_type = list({dt for food in food_list for dt in FOOD_TAGS[food].get("diet_type", [])}) # === 3. 根据关键词判定 mode === mode = "nutrition_review" if any(k in user_input for k in ["减肥", "减脂", "低卡", "少油", "瘦身", "个人食谱"]): mode = "diet_plan" elif any(k in user_input for k in ["吃多了", "过量", "上火", "副作用", "影响"]): mode = "effects_inquiry" elif any(k in user_input for k in ["不能一起", "相克", "不宜同食", "一起吃"]): mode = "avoid_inquiry" new_prompt_base = PROMPTS[mode].format(input=user_input) # === 4. 拼接静态附加信息 === notes = [] if effects: notes.append("【过量风险】" + ";".join(f"{food}:{desc}" for food, desc in effects.items())) if avoid_with: notes.append("【不宜同食】" + "、".join(avoid_with)) extra_info = ";".join(notes) if extra_info: new_prompt_base += ( "\n\n以下为已知静态信息(请务必先列出【过量风险】与【不宜同食】两个小节,并完整引用下列内容,否则视为回答不完整):" f"{extra_info}" ) # 要求模型以 JSON 输出,配合 response_format new_prompt = new_prompt_base + "\n请使用 markdown bullet list 输出建议。" return { "food_list": food_list, "tags": tags, "effects": effects, "avoid_with": avoid_with, "diet_type": diet_type, "mode": mode, "routed_input": new_prompt, } @app.post("/mcp") async def route_prompt(data: Input): user_input = data.input result = _process_input(user_input) return result @app.post("/mcp_stream") async def route_prompt_stream(data: Input): user_input = data.input result = _process_input(user_input) async def event_generator(): meta_json = json.dumps({k: v for k, v in result.items() if k != 'routed_input'}, ensure_ascii=False) # 发送 Meta 事件 yield f"event: meta\ndata: {meta_json}\n\n" prompt_key = result["routed_input"] # 若缓存命中,直接按20字符切片发送缓存内容 if prompt_key in CACHE: logger.info("[缓存] 命中") cached_text = CACHE[prompt_key] # SSE data 行不能包含裸换行,需逐行加前缀 payload_lines = [f"data: {line}" for line in cached_text.split("\n")] payload_block = "\n".join(payload_lines) yield f"event: token\n{payload_block}\n\n" yield "event: done\ndata: [DONE]\n\n" return logger.info("[缓存] 未命中") collected_chunks = [] async for chunk in async_stream_deepseek(prompt_key): collected_chunks.append(chunk) yield f"event: token\ndata: {chunk}\n\n" # 缓存完整内容 full_text = "".join(collected_chunks) CACHE[prompt_key] = full_text # 结束事件 yield "event: done\ndata: [DONE]\n\n" return StreamingResponse(event_generator(), media_type="text/event-stream") deepseek_client.py# deepseek_client.py # 基于 DeepSeek API 的客户端 import requests import json from typing import Generator import httpx # 从 config.json 读取配置 with open("config.json", encoding="utf-8") as f: config = json.load(f) DEEPSEEK_API_KEY = config["DEEPSEEK_API_KEY"] DEEPSEEK_URL = config["DEEPSEEK_URL"] MODEL_NAME = config["MODEL_NAME"] def call_deepseek(prompt): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {DEEPSEEK_API_KEY}" } payload = { "model": MODEL_NAME, "messages": [ {"role": "user", "content": prompt} ] } response = requests.post(DEEPSEEK_URL, headers=headers, json=payload) result = response.json() return result["choices"][0]["message"]["content"] # 新增:流式推理生成器 def stream_deepseek(prompt) -> Generator[str, None, None]: """Yield generated content chunks from DeepSeek API using SSE.""" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {DEEPSEEK_API_KEY}" } payload = { "model": MODEL_NAME, "messages": [ {"role": "user", "content": prompt} ], "stream": True, "max_tokens": 2048, "stream_options": {"include_usage": True}, } # 使用 stream=True 触发增量输出 response = requests.post(DEEPSEEK_URL, headers=headers, json=payload, stream=True) # iter_lines 将保持连接并逐行读取 for line in response.iter_lines(decode_unicode=True): if not line: continue # DeepSeek/SSE 行以 "data: " 开头 if line.startswith("data: "): data_str = line[6:] # 结束标志 if data_str.strip() == "[DONE]": break # 解析 JSON,获取增量内容 try: data_json = json.loads(data_str) # usage 块 choices 为空,跳过 choices = data_json.get("choices", []) if not choices: continue delta = choices[0]["delta"].get("content", "") if delta: yield delta except json.JSONDecodeError: # 忽略无法解析的片段 continue # 异步流式生成器 async def async_stream_deepseek(prompt): """异步生成器,从 DeepSeek API 生成内容(SSE)。""" headers = { "Content-Type": "application/json", "Authorization": f"Bearer {DEEPSEEK_API_KEY}" } payload = { "model": MODEL_NAME, "messages": [ {"role": "user", "content": prompt} ], "stream": True, "max_tokens": 2048, "stream_options": {"include_usage": True}, } async with httpx.AsyncClient(timeout=None) as client: async with client.stream("POST", DEEPSEEK_URL, headers=headers, json=payload) as resp: async for line in resp.aiter_lines(): if not line: continue if line.startswith("data: "): data_str = line[6:] if data_str.strip() == "[DONE]": break try: data_json = json.loads(data_str) choices = data_json.get("choices", []) if not choices: continue delta = choices[0]["delta"].get("content", "") if delta: yield delta except json.JSONDecodeError: continue test_pipeline.py# -*- coding: utf-8 -*- # test_pipeline.py import requests from deepseek_client import call_deepseek import json from requests.exceptions import ChunkedEncodingError def test_diet_analysis(): user_input = "中午吃了炸鸡和奶茶,晚上吃了牛肉火锅" # 调用 MCP mcp_resp = requests.post("http://localhost:8001/mcp", json={"input": user_input}) mcp_data = mcp_resp.json() print("MCP 返回:", mcp_data) # 调用 DeepSeek reply = call_deepseek(mcp_data["routed_input"]) print("DeepSeek 回复:", reply) def test_diet_plan(): user_input = "我想要一个一周减肥计划,主要食物包括香菇、鸡胸肉和燕麦" mcp_resp = requests.post("http://localhost:8001/mcp", json={"input": user_input}) mcp_data = mcp_resp.json() print("MCP 返回:", mcp_data) reply = call_deepseek(mcp_data["routed_input"]) print("DeepSeek 回复:", reply) def test_effects_inquiry(): user_input = "香菇吃多了会怎样?" mcp_resp = requests.post("http://localhost:8001/mcp", json={"input": user_input}) mcp_data = mcp_resp.json() print("MCP 返回:", mcp_data) reply = call_deepseek(mcp_data["routed_input"]) print("DeepSeek 回复:", reply) def test_avoid_inquiry(): user_input = "牛奶和虾能一起吃吗?" mcp_resp = requests.post("http://localhost:8001/mcp", json={"input": user_input}) mcp_data = mcp_resp.json() print("MCP 返回:", mcp_data) reply = call_deepseek(mcp_data["routed_input"]) print("DeepSeek 回复:", reply) def test_stream(): user_input = "我晚上吃什么比较好呢? 中午吃了炸鸡和奶茶" try: resp = requests.post( "http://localhost:8001/mcp_stream", json={"input": user_input}, stream=True, timeout=None, ) print("Stream start →", resp.status_code) meta_printed = False for line in resp.iter_lines(decode_unicode=True): if not line: continue # SSE: 可能含 event: xxx if line.startswith("event: "): current_event = line[7:] # 下一行应该是 data: continue if not line.startswith("data: "): continue data = line[6:] # current_event 默认为 token current_event = locals().get("current_event", "token") if current_event == "meta": try: meta_obj = json.loads(data) pretty_meta = json.dumps(meta_obj, ensure_ascii=False) print("Meta:", pretty_meta) except json.JSONDecodeError: print("Meta:", data) meta_printed = True print("\n--- AI 回复 ---\n", end="") continue if current_event == "done": print("\n✅ Stream done") break # 其他 token 输出 print(data, end="", flush=True) except ChunkedEncodingError: # 服务器已发送完毕但提前关闭连接,可忽略 print("\n⚠️ 连接提前关闭,已接收全部内容。") finally: try: resp.close() except Exception: pass if __name__ == "__main__": # test_diet_analysis() # test_diet_plan() # test_effects_inquiry() # test_avoid_inquiry() test_stream() text_match.py# -*- coding: utf-8 -*- # text_match.py import jieba import difflib from typing import Iterable, List, Set, Tuple def extract_food( user_text: str, food_vocab: Iterable[str], fuzzy_threshold: float = 0.8, ) -> Tuple[List[str], List[str], List[str]]: """根据用户输入提取食物关键词。 1. 先使用 jieba 分词命中精确词。 2. 对分词结果做模糊匹配,解决同义词/花式写法。 Args: user_text: 用户原始输入字符串。 food_vocab: 食物静态数据库的键集合。 fuzzy_threshold: SequenceMatcher 相似度阈值 (0-1)。 Returns: 去重后的食物列表,按出现顺序返回。 """ vocab_set: Set[str] = set(food_vocab) tokens = jieba.lcut(user_text, cut_all=False) hits: List[str] = [] exact_hits: List[str] = [] fuzzy_hits: List[str] = [] added: Set[str] = set() for token in tokens: # 精确匹配 if token in vocab_set and token not in added: hits.append(token) exact_hits.append(token) added.add(token) continue # 模糊匹配 for food in vocab_set: if food in added: continue if difflib.SequenceMatcher(None, token, food).ratio() >= fuzzy_threshold: hits.append(food) fuzzy_hits.append(food) added.add(food) break return hits, exact_hits, fuzzy_hits # 向后兼容的简化接口 def extract_food_simple(user_text: str, food_vocab: Iterable[str], fuzzy_threshold: float = 0.8) -> List[str]: """返回仅 food list 的旧版接口,供其他模块调用。""" return extract_food(user_text, food_vocab, fuzzy_threshold)[0] food_tags.json{ "炸鸡": { "tags": ["高油脂", "高热量"], "effects": "经常食用可能导致能量过剩、血脂升高", "avoid_with": ["啤酒"], "diet_type": ["增重期", "周末放纵"], "recipe_hint": "可使用空气炸锅,减少50%油脂摄入" }, "奶茶": { "tags": ["高糖", "高热量"], "effects": "过量摄入易导致胰岛素抵抗、体重增加", "avoid_with": [], "diet_type": ["偶尔犒劳"], "recipe_hint": "选择低糖或无糖版本,减少珍珠" }, "可乐": { "tags": ["高糖"], "effects": "长期高糖饮料摄入可增加蛀牙及肥胖风险", "avoid_with": ["咖啡"], "diet_type": ["偶尔犒劳"], "recipe_hint": "选择零度可乐或气泡水替代" }, "卤牛肉": { "tags": ["高钠", "高蛋白"], "effects": "高钠摄入可能升高血压", "avoid_with": [], "diet_type": ["增肌期"], "recipe_hint": "配合蔬菜食用平衡营养" }, "牛肉火锅": { "tags": ["高油脂", "高钠"], "effects": "高盐高油易导致水肿", "avoid_with": ["冰啤酒"], "diet_type": ["增重期"], "recipe_hint": "控制汤底油脂和盐分,搭配蔬菜" }, "蔬菜汤": { "tags": ["低热量", "健康推荐"], "effects": "一般安全", "avoid_with": [], "diet_type": ["减脂期", "日常维稳"], "recipe_hint": "少盐少油" }, "白灼虾": { "tags": ["高蛋白", "健康推荐"], "effects": "嘌呤偏高,痛风患者需控制", "avoid_with": ["维生素C高的水果"], "diet_type": ["增肌期", "减脂期"], "recipe_hint": "控制蘸料用量" }, "香菇": { "tags": ["高纤维", "低脂"], "effects": "过量可能导致胀气", "avoid_with": ["寒性食物"], "diet_type": ["减脂期", "日常维稳"], "recipe_hint": "烹饪前泡发充分" }, "燕麦": { "tags": ["高纤维", "低GI"], "effects": "摄入过多可能引起腹胀", "avoid_with": [], "diet_type": ["减脂期", "增肌期"], "recipe_hint": "配合蛋白质食物提高饱腹" }, "糙米": { "tags": ["高纤维", "低GI"], "effects": "富含植酸,影响矿物质吸收,建议与其他谷物搭配", "avoid_with": [], "diet_type": ["减脂期", "日常维稳"], "recipe_hint": "提前浸泡12小时再煮" }, "炸薯条": { "tags": ["高油脂", "高热量"], "effects": "反式脂肪酸摄入风险", "avoid_with": ["汽水"], "diet_type": ["偶尔犒劳"], "recipe_hint": "可使用空气炸锅替代油炸" } , "牛油果": { "tags": ["高脂肪", "高纤维", "健康推荐"], "effects": "脂肪含量高,减脂期注意总热量", "avoid_with": [], "diet_type": ["增肌期", "日常维稳"], "recipe_hint": "搭配全麦面包或沙拉" }, "酸奶": { "tags": ["高蛋白", "益生菌"], "effects": "乳糖不耐人群可能腹胀", "avoid_with": ["高糖谷物"], "diet_type": ["减脂期", "增肌期", "日常维稳"], "recipe_hint": "选择无糖或低糖版本" }, "披萨": { "tags": ["高油脂", "高热量"], "effects": "高钠高脂,注意摄入频次", "avoid_with": ["可乐"], "diet_type": ["周末放纵"], "recipe_hint": "选择薄底、加多蔬菜版本" }, "红薯": { "tags": ["低GI", "高纤维", "健康推荐"], "effects": "过量可致腹胀", "avoid_with": [], "diet_type": ["减脂期", "日常维稳"], "recipe_hint": "蒸煮可保留营养" }, "鲑鱼": { "tags": ["高蛋白", "Omega-3"], "effects": "富含EPA/DHA,有助心血管健康", "avoid_with": ["富含草酸蔬菜"], "diet_type": ["增肌期", "日常维稳"], "recipe_hint": "推荐清蒸或空气炸锅" }, "绿茶": { "tags": ["低热量", "抗氧化"], "effects": "空腹或过量可致胃刺激", "avoid_with": ["牛奶"], "diet_type": ["减脂期", "日常维稳"], "recipe_hint": "餐后一小时饮用更佳" }, "巧克力": { "tags": ["高糖", "高脂"], "effects": "摄入过多易长痘和增重", "avoid_with": [], "diet_type": ["偶尔犒劳"], "recipe_hint": "优选 70% 以上黑巧" }, "能量饮料": { "tags": ["高糖", "咖啡因"], "effects": "过量可能导致心悸、睡眠障碍", "avoid_with": ["咖啡"], "diet_type": ["比赛备战", "偶尔提神"], "recipe_hint": "控制每日总咖啡因 < 400mg" } } config.json{ "DEEPSEEK_API_KEY": "刚刚申请的Key", "DEEPSEEK_URL": "申请得到的Url", "MCP_PORT": 8001, "MODEL_NAME": "DeepSeek-R1" } requirements.txtfastapi uvicorn requests httpx jieba cachetools loguru4.1.4 安装依赖点击 终端pip install -r requirements.txt -i https://mirrors.huaweicloud.com/repository/pypi/simple安装成功 示例图 如果显示仍然缺少依赖 请关闭ide 重新打开即可4.2 调试 MCP终端上执行命令uvicorn mcp_server:app --port 8001 运行成功示例图下一步 新建终端 点加号执行 测试程序python test_pipeline.py执行测试成功示例图 这里我们就可以看到 流式响应的一个效果然后我们最小化 这个 CodeArts IDE for Python4.3 仓颉交互AIChat(魔改MCP)打开 仓颉编辑器4.3.1 新建项目我们这里名称就叫 food_bot 然后点击 创建4.3.2 代码实施config.json{ "model": "", "api_key": "", "base_url": "http://localhost:8001/mcp_stream", "system_prompt": "" } cjpm.toml[dependencies] [package] cjc-version = "0.53.13" compile-option = "" description = "nothing here" link-option = "" name = "food_bot" output-type = "executable" src-dir = "" target-dir = "" version = "1.0.0" package-configuration = {} src/main.cjpackage food_bot import std.console.Console import std.collection.ArrayList func cli_chat(env_info: EnvInfo, stream!: Bool) { println("\n欢迎使用智能膳食分析助手,输入exit退出") var history: ArrayList<(String, String)> = ArrayList<(String, String)>() while (true) { print("Input: ") var prompt: String = "" match(Console.stdIn.readln()) { case Some(str1: String) => prompt=str1 case None => continue } if (prompt == "exit" || prompt == "exit()") { break } if (prompt == "clear") { history.clear() println("Output: 已清理历史对话信息。") continue } // 根据配置或命令行参数判断是否启用流式模式 var use_stream: Bool = stream if (!use_stream && env_info.base_url.endsWith("/mcp_stream")) { use_stream = true } print("ChatBox: ") let response_option: Option<String> = if (use_stream) { stream_chat(prompt, env_info, history) } else { chat(prompt, env_info, history) } match (response_option) { case Some(response: String) => println("${response}") history.append((prompt, response)) case None => println("发生错误,正在重试一次...") let retry_option = if (use_stream) { stream_chat(prompt, env_info, history) } else { chat(prompt, env_info, history) } match(retry_option) { case Some(resp2: String) => println("${resp2}") history.append((prompt, resp2)) case None => println("重试失败,即将退出") break } } } } main(args: Array<String>): Int64 { let env_info = load_env_info() var stream: Bool = false if (args.size == 1) { if (args[0] == "--stream") { stream = true } else { println("参数无效,仅支持 --stream 以开启流式输出") } } cli_chat(env_info, stream: stream) return 0 } env_info.cjpackage food_bot import encoding.json.stream.* import std.fs.File import std.fs.Path import std.io.ByteArrayStream public class EnvInfo <: JsonDeserializable<EnvInfo> & JsonSerializable { public let model: String // 模型名称 public let api_key: String // api密钥 public let base_url: String // 调用接口路径 public let system_prompt: String // 预置系统提示词 public init(model: String, api_key: String, base_url: String, system_prompt: String) { this.model = model this.api_key = api_key this.base_url = base_url this.system_prompt = system_prompt } public static func fromJson(r: JsonReader): EnvInfo { var temp_model: String = "" var temp_api_key: String = "sk-xxx" var temp_base_url: String = "http://xxx.xxx.xxx/v1" var temp_system_prompt: String = "You are a helpful assistant." while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "model" => temp_model = r.readValue<String>() case "api_key" => temp_api_key = r.readValue<String>() case "base_url" => temp_base_url = r.readValue<String>() case "system_prompt" => temp_system_prompt = r.readValue<String>() case _ => () } } r.endObject() break case _ => throw Exception() } } return EnvInfo(temp_model, temp_api_key, temp_base_url, temp_system_prompt) } public func toJson(w: JsonWriter): Unit { w.startObject() w.writeName("model").writeValue(this.model) w.writeName("api_key").writeValue(this.api_key) w.writeName("base_url").writeValue(this.base_url) w.writeName("system_prompt").writeValue(this.system_prompt) w.endObject() w.flush() } } public func save_env_info(): Unit { // 该函数用于测试EnvInfo类的序列化为json的能力,顺便生成一个env_sample.json样本做为参考 let env_path = Path("env_sample.json") if (File.exists(env_path)) { File.delete(env_path) } let file = File.create(env_path) let env_info = EnvInfo( "xxxx", "sk-xxxxxx", "http://xxx.xxx.xxx/v1/chat/completions", "You are a helpful assistant." ) var byte_stream = ByteArrayStream() var json_writer = JsonWriter(byte_stream) let write_config = WriteConfig.pretty json_writer.writeConfig = write_config env_info.toJson(json_writer) file.write(byte_stream.readToEnd()) println("`env_sample.json` save ok") file.close() } public func load_env_info(): EnvInfo { // 用于加载配置文件 let env_path = Path("config.json") if (!File.exists(env_path)) { throw Exception("The config file not exists, please check again") } let file = File.openRead(env_path) let file_str: Array<UInt8> = file.readToEnd() var byte_stream = ByteArrayStream() byte_stream.write(file_str) let json_reader = JsonReader(byte_stream) let env_info: EnvInfo = EnvInfo.fromJson(json_reader) file.close() // println("model: ${env_info.model}") // println("api_key: ${env_info.api_key}") // println("base_url: ${env_info.base_url}") // println("system_prompt: ${env_info.system_prompt}") return env_info } chat.cjpackage food_bot import encoding.json.stream.* import net.http.ClientBuilder import net.http.HttpHeaders import net.http.HttpRequestBuilder import net.tls.TlsClientConfig import net.tls.CertificateVerifyMode import std.collection.ArrayList import std.io.ByteArrayStream import std.time.Duration import std.unicode.UnicodeExtension // for String.trim() // ===== 可配置常量 ===== public let READ_TIMEOUT_SECONDS: Int64 = 300 // 长轮询 SSE 建议 300 秒 // =========================== public enum RoleType { User | Assistant | System } public func role_type_to_str(role: RoleType): Option<String> { return match(role) { case RoleType.User => Some("user") case RoleType.Assistant => Some("assistant") case RoleType.System => Some("system") } } public func str_to_role_type(role_option_str: Option<String>): RoleType { return match(role_option_str) { case Some(str) => match(str) { case "user" => RoleType.User case "assistant" => RoleType.Assistant case "system" => RoleType.System case _ => RoleType.Assistant } case None => RoleType.Assistant } } public struct Message<: JsonDeserializable<Message> & JsonSerializable { public let role: RoleType public var content: String public init(role: RoleType, content: String) { this.role = role this.content = content } public static func fromJson(r: JsonReader): Message { var temp_role: Option<String> = None var temp_content: String = "" while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match(n) { case "role" => temp_role = r.readValue<Option<String>>() case "content" => temp_content = r.readValue<String>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Message") } } let role_type: RoleType = str_to_role_type(temp_role) return Message(role_type, temp_content) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("role").writeValue<Option<String>>(role_type_to_str(this.role)) w.writeName("content").writeValue<String>(this.content) w.endObject() w.flush() } } public struct ChatRequest <: JsonSerializable { private let model: String private let messages: ArrayList<Message> private let max_tokens: Int64 private let stream: Bool public init( model: String, messages: ArrayList<Message>, max_tokens: Int64, stream: Bool ) { // construction function with messages this.model = model this.messages = messages this.max_tokens = max_tokens this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, max_tokens: Int64, stream: Bool ){ // construction function with prompt and system_prompt this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = max_tokens this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, stream: Bool ){ // construction function with prompt and default arguments this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = 2000 this.stream = stream } public func toJson(w: JsonWriter) { w.startObject() w.writeName("model").writeValue<String>(this.model) w.writeName("messages").writeValue<ArrayList<Message>>(this.messages) w.writeName("max_tokens").writeValue<Int64>(this.max_tokens) w.writeName("stream").writeValue<Bool>(this.stream) w.endObject() w.flush() } } public struct Choice <: JsonDeserializable<Choice> & JsonSerializable { public let index: Int32 public let message: Option<Message> public let delta: Option<Message> public let finish_reason: Option<String> public let logprobs: Option<Float64> // dashscope for qwen need public init( index: Int32, message: Option<Message>, delta: Option<Message>, finish_reason: Option<String>, logprobs: Option<Float64> ) { this.index = index this.message = message this.delta = delta this.finish_reason = finish_reason this.logprobs = logprobs } public static func fromJson(r: JsonReader): Choice { var temp_index: Int32 = -1 var temp_message: Option<Message> = None var temp_delta: Option<Message> = None var temp_finish_reason: Option<String> = None var temp_logprobs: Option<Float64> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "index" => temp_index = r.readValue<Int32>() case "message" => temp_message = r.readValue<Option<Message>>() case "delta" => temp_delta = r.readValue<Option<Message>>() case "finish_reason" => temp_finish_reason = r.readValue<Option<String>>() case "logprobs" => temp_logprobs = r.readValue<Option<Float64>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Choice") } } return Choice(temp_index, temp_message, temp_delta, temp_finish_reason, temp_logprobs) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("index").writeValue<Int32>(this.index) w.writeName("message").writeValue<Option<Message>>(this.message) w.writeName("delta").writeValue<Option<Message>>(this.delta) w.writeName("finish_reason").writeValue<Option<String>>(this.finish_reason) w.writeName("logprobs").writeValue<Option<Float64>>(this.logprobs) w.endObject() w.flush() } } public struct Usage <: JsonDeserializable<Usage> & JsonSerializable { public let prompt_tokens: UInt64 public let completion_tokens: UInt64 public let total_tokens: UInt64 public init(prompt_tokens: UInt64, completion_tokens: UInt64, total_tokens: UInt64) { this.prompt_tokens = prompt_tokens this.completion_tokens = completion_tokens this.total_tokens = total_tokens } public static func fromJson(r: JsonReader): Usage { var temp_prompt_tokens: UInt64 = 0 var temp_completion_tokens: UInt64 = 0 var temp_total_tokens: UInt64 = 0 while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "prompt_tokens" => temp_prompt_tokens = r.readValue<UInt64>() case "completion_tokens" => temp_completion_tokens = r.readValue<UInt64>() case "total_tokens" => temp_total_tokens = r.readValue<UInt64>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Usage") } } return Usage(temp_prompt_tokens, temp_completion_tokens, temp_total_tokens) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("prompt_tokens").writeValue<UInt64>(this.prompt_tokens) w.writeName("completion_tokens").writeValue<UInt64>(this.completion_tokens) w.writeName("total_tokens").writeValue<UInt64>(this.total_tokens) w.endObject() w.flush() } } public struct ChatResponse <: JsonDeserializable<ChatResponse> { // some api names `id`, and some names `request_id` public let id: Option<String> public let request_id: Option<String> public let system_fingerprint: Option<String> public let model: String public let object: String public let created: UInt64 public let choices: ArrayList<Choice> public let usage: Option<Usage> public init( id: Option<String>, request_id: Option<String>, system_fingerprint: Option<String>, model: String, object: String, created: UInt64, choices: ArrayList<Choice>, usage: Option<Usage> ) { this.id = id this.request_id = request_id this.system_fingerprint = system_fingerprint this.model = model this.object = object this.created = created this.choices = choices this.usage = usage } public static func fromJson(r: JsonReader): ChatResponse { var temp_id: Option<String> = None var temp_request_id: Option<String> = None var temp_system_fingerprint: Option<String> = None var temp_model: String = "" var temp_object: String = "" var temp_created: UInt64 = 0 var temp_choices: ArrayList<Choice> = ArrayList<Choice>([]) var temp_usage: Option<Usage> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "id" => temp_id = r.readValue<Option<String>>() case "request_id" => temp_request_id = r.readValue<Option<String>>() case "system_fingerprint" => temp_system_fingerprint = r.readValue<Option<String>>() case "model" => temp_model = r.readValue<String>() case "object" => temp_object = r.readValue<String>() case "created" => temp_created = r.readValue<UInt64>() case "choices" => temp_choices = r.readValue<ArrayList<Choice>>() case "usage" => temp_usage = r.readValue<Option<Usage>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for ChatResponse") } } return ChatResponse( temp_id, temp_request_id, temp_system_fingerprint, temp_model, temp_object, temp_created, temp_choices, temp_usage ) } } public func get_domain( url: String ): String { var temp_url = url if (temp_url.startsWith("https://")) { temp_url = temp_url["https://".size..] } else if (temp_url.startsWith("http://")) { temp_url = temp_url["http://".size..] } let domain: String = temp_url.split("?")[0].split("/")[0] return domain } public func build_http_client( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)>, stream!: Bool ){ // prepare input data // If we are targeting the custom `/mcp_stream` backend we must send // a very simple JSON body `{ "input": "<prompt>" }` instead of the // OpenAI-style `ChatRequest`. Detect this via URL suffix. let is_mcp_stream = env_info.base_url.endsWith("/mcp_stream") var post_data: Array<UInt8> if (is_mcp_stream) { var local_stream = ByteArrayStream() let local_writer = JsonWriter(local_stream) // { "input": "..." } local_writer.startObject() local_writer.writeName("input").writeValue(prompt) local_writer.endObject() local_writer.flush() post_data = local_stream.readToEnd() } else { var array_stream = ByteArrayStream() let json_writer = JsonWriter(array_stream) let chat_res = ChatRequest( env_info.model, prompt, history, env_info.system_prompt, stream ) chat_res.toJson(json_writer) post_data = array_stream.readToEnd() } // build headers var headers: HttpHeaders = HttpHeaders() if (!is_mcp_stream) { // local backend doesn't require auth header headers.add("Authorization", "Bearer ${env_info.api_key}") } headers.add("Content-Type", "application/json") if (stream) { headers.add("Accept", "text/event-stream") } let request = HttpRequestBuilder() .url(env_info.base_url) .method("POST") .body(post_data) .readTimeout(Duration.second * READ_TIMEOUT_SECONDS) .addHeaders(headers) .build() let client = if (env_info.base_url.startsWith("https")) { var tls_client_config = TlsClientConfig() tls_client_config.verifyMode = CertificateVerifyMode.TrustAll tls_client_config.domain = get_domain(env_info.base_url) ClientBuilder() .tlsConfig(tls_client_config) .build() } else { ClientBuilder().build() } return (request, client) } public func chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: false ) var result_message: Option<String> = None var res_text = "" try { // call api let response = client.send( request ) // read result (support max revice 100k data) let buffer = Array<Byte>(102400, item: 0) let length = response.body.read(buffer) res_text = String.fromUtf8(buffer[..length]) // println("res_text: ${res_text}") var input_stream = ByteArrayStream() input_stream.write(res_text.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { let message = choices[0].message.getOrThrow() // println("message: ${message.content}") result_message = Some(message.content) } else { println("can't found any response") } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${res_text}") } client.close() return result_message } public func stream_chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: true ) let is_mcp_stream = env_info.base_url.endsWith("/mcp_stream") var result_response: String = "" var temp_text2 = "" try { // call api let response = client.send( request ) // read result let buffer = Array<Byte>(10240, item: 0) if (is_mcp_stream) { var done = false var current_event = "" while(!done) { let length = response.body.read(buffer) if (length == 0) { break } let res_text = String.fromUtf8(buffer[..length]) for (line in res_text.split("\n")) { let trimmed = line.trim() if (trimmed.size == 0) { continue } if (trimmed.startsWith("event: ")) { current_event = trimmed["event: ".size..] continue } if (trimmed.startsWith("data: ")) { let data = trimmed["data: ".size..] if (current_event == "token") { // 累积 token,暂不直接打印 result_response = result_response + data } else if (current_event == "ping") { // 服务器心跳,忽略即可 () } else if (current_event == "error") { println("服务器错误: ${data}") done = true result_response = "" // force empty so caller sees None break } else if (current_event == "done") { done = true break } } } if (done) { break } } } else { var finish_reason: Option<String> = None while(finish_reason.isNone() && temp_text2 != "[DONE]") { let length = response.body.read(buffer) let res_text = String.fromUtf8(buffer[..length]) for (temp_text in res_text.split("\n")) { temp_text2 = if (temp_text.startsWith("data: ")) { temp_text["data: ".size..] } else { temp_text } if (temp_text2.size == 0) { continue } if (temp_text2 == "[DONE]") { break } var input_stream = ByteArrayStream() input_stream.write(temp_text2.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { finish_reason = choices[0].finish_reason if (finish_reason.isNone()) { let delta = choices[0].delta.getOrThrow() result_response = result_response + delta.content } } else { println("can't found any response") } } } } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${temp_text2}") } client.close() if (result_response.size > 0) { return Some(result_response) } else { return None } } 4.4 调试AIChat点击 终端执行 命令cjpm run运行成功示例图输入测试语句例如:我中午吃了炸鸡和奶茶还有香菇 我晚上吃点什么比较好呢?切回 CodeArts IDE for Python 切换 终端我们就可以看到 MCP 服务端 接受到请求的日志然后我们切回 仓颉编辑器 查看 这是响应正常 那么我们来测试 缓存 的效果 再次发出这个命令我中午吃了炸鸡和奶茶还有香菇 我晚上吃点什么比较好呢?你会看到它 立刻返回了结果然后我们可以 切换到 Python编辑器 看一下 命中缓存的日志💡这是两次请求1️⃣请求未命中则去请求DeepSeek2️⃣请求相同数据 则命中缓存五、项目总结5.1 MCPMCP,全称 Model Context Protocol,中文叫“模型上下文协议”。你可以把它想象成 AI 的“USB 接口” --让不同的 AI 模型、工具和应用程序能用统一的方式交流。那么我的理解是:它更像是一个适配器来调节各种AI不同的接口 达到一致的效果,让AI的交流更加简单,即使没有身份预设,走MCP是完美的让AI成为你的最佳助手。5.2 项目tag本次案例参考:https://devstation.connect.huaweicloud.com/space/devportal/casecenter/9a5b50615ac44a21af30f8699c81efc6/1https://devstation.connect.huaweicloud.com/space/devportal/casecenter/5a7144a66d1b43e088592ae447d518ed/1https://gitcode.com/CaseDeveloper/Cangjie-Examples简单的实现了 MCP并发(案例教程没有提及 uvicorn mcp_server:app --port 8001 --workers 4 )后续可拓展例如:food_bot 🔜 vue等各种UI 前端 各种适配机器人food_mcp🔜 独立开发为自己更加需要 模式更加契合的"USB" 让AI更加懂你关于缓存方面:缓存可以采用本地文件、redis等更加高性能的方案来替换这样就可以让实现由AI回答 到AI理解到了所表达和呈现的每一个需求点六、活动地址​ 如果对该项目有疑问,可以评论区留言交流~如果喜欢可以点点赞~​ 我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [案例共创] 【案例共创】基于仓颉编程语言+DeepSeek实现 AI 绘画提示词生成智能体
    生成式 AI 技术飞速发展,行业日新月异,AI 设计工具已经能替代设计师一部分工作,但要写出好的提示词也是挺费力的一件事。当前案例将以华为开发者空间为平台,结合仓颉语言+DeepSeek,构建一个专业的 AI 绘画提示词生成智能体,实现理解用户输入的模糊的创作意图,给出高质量的 AI 绘画提示词,以期提高 AI 绘画质量和效率。一、技术栈语言:仓颉开发环境:华为开发者空间IDE:CodeArts IDE for CangjieAI模型:DeepSeek(华为云 MaaS 免费提供)二、实现流程免费领取华为开发者空间云主机使用 CodeArts IDE 创建仓颉项目华为 MaaS 平台免费领取 DeepSeek Tokens仓颉对接 DeepSeek 接口设计提示词生成逻辑模板三、资源总览本案例预计花费总计0元。资源名称规格单价(元)时长(分钟)云主机2vCPU 4GB X86 Ubuntu 或4vCPUs 8GB ARM Ubuntu免费40四、免费领取华为开发者空间云主机注册并登录华为云开发者空间即可免费领取一台云主机,在工作台中点击“打开云主机”可以打开即可进入桌面桌面上点击“CodeArts IDE for Cangjie”的图标即可打开编辑器使用,无需部署环境和安装软件。五、华为 Maas 平台免费领取 DeepSeek Tokens领取 DeepSeek 200w 免费 Tokens,登录MaaS,可进入贵阳一区域的 ModelArts Studio 控制台,然后点击控制台左侧“模型推理”-“在线推理”,点击“预置服务”-“免费服务”,找到想要使用的 DeepSeek 版本后点击“领取”,领取成功后就可以开始体验使用了。然后点击“调用说明”可以获取API地址、模型名称,之前未使用需点击左侧菜单的 API Key 管理创建 API Key,保存 API 地址、模型名称、API Key 备用。六、仓颉对接对接 DeepSeek 开发 AI 智能体进入云主机桌面,右键选择Open Terminal Here打开终端命令窗口,克隆仓颉示例代码仓库。git clone https://gitcode.com/CaseDeveloper/Cangjie-Examples.git使用 CodeArts IDE for Cangjie 打开 AIChat 示例项目。左上点击文件—打开项目,选择前面克隆的示例代码目录 Cangjie-Examples 下的 AIChat 打开修改根目录下的 config.json 配置文件,配置 DeepSeek 的配置信息和提示词,其中model为前面获取到的模型名称、api_key为 API KEY,base_url 为 API 地址,system_prompt 为系统提示词,系统提示词这里填入:你是我的全能助手,你的名字叫仓颉。仓颉编程语言是一款面向全场景智能的新一代编程语言,主打原生智能化、天生全场景、高性能、强安全。主要应用于鸿蒙原生应用及服务应用等场景中,为开发者提供良好的编程体验。须保持回复简洁,回复内容不超过300字。打开 src/main.cj 文件,点击运行按钮,即可看到效果我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [技术干货] 基于华为开发者空间Astro低代码应用平台,构建业务用户登录功能开发
    📋 案例概览📝 背景与简介华为开发者空间是为全球开发者打造的专属云上成长空间,深度整合昇腾AI、鸿蒙、鲲鹏等华为根技术。开发者空间在HDC2025上迎来全面升级,新增AI原生应用引擎、AI Notebook、鸿蒙云手机、FunctionGraph云函数、Astro低代码等核心能力,并在算力、模型、平台、应用层实现全方位优化,助力开发者高效完成从编码到调测的全流程,打造智能AI应用开发新体验。华为开发者空间-低代码应用开发平台是华为云推出的一款可视化应用开发平台,旨在通过"拖拽式"组件和模板化设计,降低开发门槛,提升企业数字化应用构建效率。Astro平台特别适合业务人员与开发者协同创新,能大幅缩短应用交付周期,典型适用于OA审批、数据看板、轻量级业务系统等场景。传统用户登录开发深陷 “高成本、低效率、弱安全” 困局,而华为Astro通过 “预制安全能力+可视化编排+云原生运维” 三位一体,实现:安全零信任:内建金融级防护,杜绝密码泄露风险;部署小时级:拖拽式开发释放IT资源;业务可持续:权限热配置支撑敏捷迭代。本案例将通过华为开发者空间-低代码应用开发平台,为业务应用自定义一个登录页。通过在登录页中输入用户名、密码等登录信息,与系统中存储的业务用户信息进行对比,来验证业务用户的身份,并根据设置的业务用户权限,为业务用户分配相应的资源和访问权限。🔖 案例流程🕹️ 流程说明:领取华为开发者空间,登录华为开发者空间-低代码应用开发平台;新建低代码应用,进入Astro轻应用服务控制台主页,开发应用。✍️ 案例实操📝 业务用户登录后台开发领取华为开发者空间,登录华为开发者空间-低代码应用开发平台;在华为开发者空间-低代码应用开发平台创建低代码应用,配置系统环境变量;创建用户登录、注册脚本,测试登录注册运行,返回参数;关联登录脚本,创建用户登录服务编排;创建公共接口,发布服务。在业务用户登录后台开发的最后,我们创建了一个公共接口。接下来我们将完成业务用户前台的开发,通过调取公共接口进行前后端数据互传,完成业务用户登录功能的开发。📝 业务用户登录前台开发获取自定义登录组件模板,修改配置文件,创建自定义组件;创建高级页面,开发登录页,配置后台公共接口;发布应用,登录成功!!!🌈 案例最终效果
  • [其他问题] 【AR502产品】关于容器到AR502,再到云主机的端口开通策略
    当前IP数据如下:云主机ip 172.17.0.2 .AR502: 172.27.144.70 AR502里面的容器A: 172.17.0.6容器A中启动了nginx,nginx对外暴露的监听端口是7080,在AR502配置了端口映射,由容器的7080映射到AR502的7080nft add rule dnat1 pre ip daddr 172.27.144.70 tcp dport 7080 iif GE4 dnat 172.17.0.6:7080在云主机 可以ping 通 172.27.144.70 ,但是访问http://172.27.144.70:7080 报502.有大神知道原因么,或者怎么解决么,感谢。
  • [案例共创] 【案例共创】基于华为云开发者空间的AI Agent [旅行灵感生成器]智能体
    基于华为云开发者空间的AI Agent [旅行灵感生成器]智能体随着全球化进程的加快和人们生活水平的提高,旅行已成为现代人放松身心、拓宽视野的重要方式。然而,尽管旅行的需求日益增长,很多人仍然在选择目的地、规划行程、设计旅行活动时面临着许多挑战。如何高效、便捷地找到适合自己兴趣与需求的旅行灵感,成为许多旅行者渴望解决的问题。传统的旅行推荐方式通常依赖于静态的旅游指南或有限的用户评价,难以提供个性化、实时且多元化的旅行建议。为了解决这一问题,基于华为云开发者空间,结合先进的AI技术,我们可以构建一个全新的智能体——旅行灵感生成器AI Agent。这一智能体将能够通过分析用户的兴趣、偏好、预算、季节等多个维度,提供量身定制的旅行灵感,助力用户规划个性化的旅行路线和活动。该智能体通过集成华为云的Flexus服务和DeepSeek大模型的强大推理能力,能够精准理解用户需求,生成创意十足的旅行建议。通过与大数据平台、社交媒体数据源、旅游网站等的深度结合,智能体可以实时更新热门景点、文化活动、独特目的地等信息,保证推荐的内容始终与用户的兴趣和全球旅游趋势保持同步。该平台不仅支持快速生成旅行灵感,还能够优化用户的旅行计划,通过分析目的地的天气、最佳出行时间以及当地的热门活动,确保用户能够享受到最佳的旅行体验。华为云开发者空间背景介绍华为云开发者空间是华为为全球开发者打造的一站式云端创新与协作平台,旨在帮助开发者更高效地开发、部署和管理各类AI应用与智能体项目。它集成了华为云强大的计算资源、大模型服务、应用托管能力,以及丰富的低代码/无代码工具,构建起从开发到部署、从测试到运维的完整生态闭环。在AI日益普及的当下,传统应用开发方式面临着复杂的模型管理、高昂的资源成本和部署流程繁琐等挑战。华为云开发者空间通过“低门槛、易集成、快部署”的理念,联合MaaS(Model as a Service)大模型服务平台与Flexus云服务体系,极大地降低了开发者使用大模型构建AI Agent(智能体)的门槛。此外,开发者空间还内嵌支持Dify、LangChain、Streamlit等多种主流AI应用开发框架,提供了灵活的工作流管理、Agent编排能力和丰富的API服务支持,助力开发者快速构建属于自己的AI应用,如智能问答助手、情绪分析助手、面试模拟官等。总的来说,华为云开发者空间不仅是一个技术平台,更是推动AI应用普及、激发开发者创造力的智能创新孵化器。借助它,开发者可以轻松将想法转化为可落地的AI产品,加速AI应用的落地与推广。AI Agent介绍AI Agent(人工智能智能体)是指具备自主感知、理解、推理和执行能力的智能程序实体。它能够基于用户输入、外部环境信息或系统状态,做出智能化的响应和决策,从而完成特定任务或持续交互。这类智能体广泛应用于对话助手、推荐系统、智能问诊、流程自动化、内容生成等场景中。核心特征目标导向性每个AI Agent围绕一个或多个特定目标构建(如智能客服、面试模拟、情绪管理等),具备明确的任务执行逻辑。上下文感知AI Agent能持续跟踪对话上下文、用户历史、环境数据等,实现个性化与情境相关的响应。语言理解与生成能力借助大语言模型(LLM),AI Agent可以高质量地理解自然语言输入并生成符合语境的回复、计划或建议。自主决策与执行部分高级AI Agent具备工具调用与任务链规划能力,可在无监督情况下自动完成较复杂的任务流程。可编排、可集成现代AI Agent通常由多个模块组成,可通过工作流引擎(如Dify、LangChain)进行灵活编排,也能嵌入到Web、App或企业系统中。技术基础大语言模型(如ChatGPT、DeepSeek等)提供强大的语言理解与生成能力,是Agent的“核心大脑”。工具调用接口(Tool Calling)支持Agent调用搜索引擎、数据库、API服务等外部工具,实现复杂任务分解。工作流引擎/框架(如Dify、LangChain)支持多Agent协作与任务编排。记忆系统与知识库提供上下文记忆、知识增强问答等能力,使Agent更加智能和精准。典型应用场景智能问答助手(如客服Bot、政务助手)个性化推荐(如智能菜谱、旅游规划)模拟对话训练(如面试官、语言学习伙伴)企业流程自动化(如审批流程助手、文档生成器)医疗/心理支持(如情绪识别、健康指导)AI Agent 正在成为AI应用的新范式。它不仅具备强大的自然语言交互能力,还融合了自主性、工具链调用能力和知识增强特性,正在改变人类与信息系统之间的交互方式。借助华为云Dify等平台,开发者可以快速搭建并定制自己的AI Agent,加速AI在各行业的落地。旅行灵感生成器AI Agent 搭建1.先进入开发者空间,点击创建Agent2.页面如下。3.基础信息一键解锁个性化旅行方案,从目的地推荐到行程规划,激发您的旅行灵感,打造专属的梦幻旅程。4.模型选择Deepseek5.角色设定角色定义: 作为旅行灵感生成器,你的核心任务是帮助用户一键解锁个性化旅行方案,从推荐目的地到详细行程规划,全面激发用户的旅行灵感,打造专属的梦幻旅程。你需要深入了解用户的偏好、旅行目的和预算,结合全球旅游资源,提供创意且实用的旅行建议,确保每位用户都能获得独一无二的旅行体验。 工具能力: 1. 用户偏好分析: 能够通过问卷或对话方式,精准捕捉用户的旅行偏好、兴趣点和预算范围。 2. 目的地推荐: 根据用户偏好,从全球范围内推荐最适合的目的地,包括热门景点和冷门秘境。 3. 行程规划: 自动生成详细的行程安排,包括交通、住宿、餐饮、景点游览等,确保行程合理且高效。 4. 实时信息更新: 提供目的地的实时天气、交通状况、当地活动等信息,帮助用户做出最佳决策。 5. 个性化建议: 根据用户的特殊需求,提供个性化的旅行建议,如亲子游、蜜月游、探险游等。 6. 预算管理: 帮助用户合理分配旅行预算,提供性价比高的选择,避免超支。 要求与限制: 1. 准确性: 提供的信息必须准确无误,包括目的地信息、行程安排、预算估算等。 2. 个性化: 每个旅行方案都必须高度个性化,贴合用户的独特需求和偏好。 3. 实时性: 及时更新目的地的最新信息,确保用户获取的是最新、最实用的建议。 4. 用户体验: 界面友好,操作简便,确保用户能够轻松使用并获取所需信息。 5. 多样性: 提供多样化的旅行方案,涵盖不同风格和类型,满足不同用户的需求。 6. 安全性: 确保推荐的行程和活动安全可靠,避免用户在旅行中遇到不必要的风险。 6.工具选择7.开场白设置您好!我是旅行灵感生成器,专为您提供个性化旅行方案。从精选目的地到定制行程,我助您激发旅行灵感,打造独一无二的梦幻旅程。告诉我您的喜好,让我们开启美好旅程吧!MCP创建点击创建服务选择Bing插件发布智能体智能体体验如何根据个人兴趣和预算选择理想的旅行目的地"选择理想的旅行目的地就像为自己定制一场专属的冒险,关键在于找到兴趣与预算的完美平衡点。让我用简单易懂的方式为你梳理:先问自己最想体验什么想躺平放松?巴厘岛的白沙滩或瑞士的温泉酒店会是不错选择热爱人文历史?西安的兵马俑或罗马的斗兽场能让你穿越时空追求刺激?新西兰皇后镇的蹦极或菲律宾的潜水值得考虑预算决定了旅行半径5千以内:国内云南/泰国清迈等性价比之选1-2万:日本/欧洲部分国家能玩得舒适无上限?迪拜帆船酒店或大溪地水上屋等你解锁小技巧让选择更轻松淡季出行能省30%-50%预算(比如春节后的三亚)多目的地旅行可优先选择相邻地区(如奥地利+匈牙利)善用比价工具关注机票折扣举个实际例子:如果你喜欢海岛但预算有限,马来西亚的兰卡威比马尔代夫节省一半费用;若钟情欧洲文化又怕贵,波兰克拉科夫比巴黎更经济实惠。需要我根据你的具体喜好(比如偏爱美食/摄影/购物等)和预算金额,帮你筛选几个精准目的地吗?"总结随着人工智能和大模型技术的持续演进,AI Agent已逐步成为提升用户体验和服务效率的重要工具。本文围绕“旅行灵感生成器”智能体的构建,系统展示了如何基于华为云开发者空间,结合Flexus云服务与DeepSeek大模型,打造一款集个性化推荐、实时信息分析与智能行程规划于一体的AI应用。通过详实的搭建流程、角色设定、功能说明以及界面引导,我们不仅实现了一个具备高可用性与可扩展性的旅行推荐助手,更展示了华为云在AI Agent构建领域的强大生态能力。该平台为开发者提供了低门槛、高效率、强兼容的开发体验,极大地加速了AI从技术到应用的落地过程。未来,随着Agent编排能力和大模型推理服务的进一步增强,旅行灵感生成器AI Agent将具备更强的智能性、适应性和创新性,为全球用户带来更具价值的旅行辅助服务,也为AI应用的场景化落地提供了典范范例。我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [HCSD校园沙龙] “华为云HCSD校园公开课”走进新疆大学,为学子点亮科技成长新赛道
    在数字经济蓬勃发展的当下,以人工智能、大数据、云计算等为代表的前沿技术正深度渗透至各行业领域,重塑产业格局。6月12日,由华为云计算技术有限公司主办、新疆大学承办的“华为云HCSD校园公开课”在新疆大学博达校区顺利举行。华为云开发者联盟与云商店部部长王希海、华为云PaaS技术创新实验室主任王千祥、华为云高校生态总监罗静带领技术专家团队走进校园,与新疆大学副校长汪烈军、计算机科学与技术学院党委副书记吴健强、计算机科学与技术学院副院长刘胜全、计算机科学与技术学院副院长夏威、计算机科学与技术学院副教授郑炅及师生代表展开深入交流。▲ 大会现场图深化校企协同合作共筑华为根技术人才生态如今,行业对复合型技术人才的需求正呈现爆发式增长,尤其是从产业发展趋势看,AI大模型落地、算力基础设施建设及行业数字化解决方案创新,亟需高校与企业形成人才培养闭环——既需高校夯实理论根基,更需企业注入技术实践与行业经验。新疆大学计算机科学与技术学院副院长刘胜全在开场致辞中表示,一直以来,新疆大学与华为都保持着良好的合作关系,双方在学科共建、赛事组织及课程改革等方面成果丰硕。华为云也通过名校行、HCSD校园公开课等活动,将华为云的专家和技术带进校园,与老师进行教学研讨、指导学生开展云上实践。未来,双方将继续深化校企合作,协力为同学们创造更好的学习环境,并以华为ICT大赛、华为开发者大赛等为抓手,培养更多适应行业发展需求的创新型人才。▲ 新疆大学计算机科学与技术学院副院长 刘胜全华为云作为全球领先的智能云服务提供商,始终以技术创新驱动产业变革,在AI算力布局、大模型落地及开发者生态构建等领域持续深耕。华为云开发者联盟与云商店部部长王希海也在致辞中表示,依托全栈AI能力与昇腾算力基础设施,华为云打造的CloudMatrix 384超节点集群已成为支撑复杂大模型训推的核心底座,而盘古大模型在30多个行业、400+场景的规模化应用,更彰显了从技术突破到产业赋能的闭环能力。在人才生态建设中,华为云也通过沃土云创高校计划、开发者布道师认证、HCSD校园公开课等多维举措,将前沿技术与行业实践深度融入高校教学体系,与新疆大学等高校共建“产学研用”一体化培养模式。这种以技术需求为导向、以场景实践为纽带的校企协同机制,不仅为数字经济输送了大量实战型人才,更成为推动数实融合的关键引擎。▲ 华为云开发者联盟与云商店部部长 王希海随后,激动人心的校园大使授证仪式举行。新疆大学计算机科学与技术学院党委副书记吴健强、华为云开发者联盟与云商店部部长王希海为校园大使孙海洋、石昌辉同学颁发证书。这些校园大使将成为华为云与学校之间的桥梁,促进技术交流与创新实践活动的开展。他们将积极参与华为云相关活动的组织与推广,带动更多同学了解和应用华为云技术。在现场师生的共同见证下,华为新疆政企副总经理范朝军与新疆大学计算机科学与技术学院副院长刘胜全上台为学校颁发了华为ICT大赛(第九届)2024-2025中国区新疆赛区优秀组织奖与优秀合作伙伴奖。同时,在范朝军与新疆大学计算机科学与技术学院副院长夏威的见证下,在活动上向新疆大学ICT学院捐赠了信创电脑10台。以科技赋能教育创新打造高质量教育体系新范式新疆大学计算机科学与技术学院副教授、华为开发者布道师郑炅以《以赛促学,以赛促教》为题,系统介绍了华为ICT学院的运营模式与赛事组织经验。她提出的以竞赛宣讲、赛道分析、启动报名、培训认证、资源协调、组织参赛、赛事总结为主的“七步法”赛事运营模型形成了完整的人才培养闭环,并向区内高校分享经验,有效推动了区域信息技术教育均衡发展。此外,她还展示了新疆大学携手华为在课程改革方面取得的成果,如开展基于华为鲲鹏云的《大数据导论》课程建设及“国产软件进课堂”教学改革项目,通过将企业技术融入课程,提升学生实践能力。通过以赛促学、以赛促创的创新模式,推动校企协同,成功构建了完善的人才培养生态体系。▲ 新疆大学计算机科学与技术学院副教授、华为开发者布道师 郑炅目前,软件工程正从1.0瀑布模型、2.0敏捷开发演进至3.0智能化阶段,其核心是通过AI大模型重构研发范式。华为云PaaS技术创新实验室主任、华为云智能化研发首席专家王千祥也带来了主题为“软件工程 3.0:大模型驱动的软件研发新范式”的演讲,他指出,华为代码大模型Pangu-Coder在150亿参数规模下可实现HumanEval 测试72.3%通过率,性能超越业界主流模型。在实践层面,华为通过CodeArts盘古助手构建全流程智能辅助生产线,支持从需求分析到运维的全生命周期自动化,代码接纳率提升至32.42%。他强调,大模型与传统程序本质等价,智能化软件需融合确定性符号计算与非确定性概率模型。当前华为已实现多模型协同开发,如CodeR工具在SWE-bench 榜单登顶,未来将通过DevAgent推动研发向Agent主导的自主化演进。▲ 华为云PaaS技术创新实验室主任、华为云智能化研发首席专家 王千祥华为云PaaS高级工程师卢步云以《低代码,高效率:解锁应用开发新技能》为题,以一段快闪视频开场,让同学们了解低代码技术如何降低开发门槛,提升校园开发者创新效率。随后,她系统地介绍了华为云Astro低代码平台的核心服务,Astro Canvas快速构建数据可视化大屏应用,Astro Zero轻松开发轻量级业务应用。结合流程化、可视化的行业案例,她展示了Astro平台如何帮助企业真正实现“事半功倍”的效能提升,让师生们更直观的感受到低代码技术在降本增效中的价值。针对低代码和智能编程这两种技术趋势,她提出了“两者都要”的见解,并且针对不同开发目的和场景提供了学习建议,与师生一起探讨了低代码与AI技术的融合趋势。▲ 华为云PaaS高级工程师 卢步云值得一提的是,在实操环节,卢步云基于华为云Astro Zero轻应用低代码平台,演示如何搭建一个完整的包含前台界面、后端逻辑、和第三方模型对接的自定义聊天助手。将低代码搭建应用的过程进行了拆解,帮助同学们掌握运用低代码工具的思路和方法。华为云Astro低代码平台设计完善,提供丰富多样的前后台资产和持续更新优化的使用指南,在降低开发门槛的同时,也支持开发者做出更专业和复杂的业务应用。DTSE开发者技术专家郭高举以“基于云主机部署 DeepSeek 构建 Agent 智能助手”为主题,演示了如何通过华为开发者空间提供的云主机快速部署大模型服务。该平台为开发者提供一站式环境配置、工具集成及案例指导,支持鲲鹏、昇腾等多技术方向,助力学生低成本体验前沿技术开发。▲ 华为云DTSE开发者技术专家 郭高举在活动期间还召开了教师研讨会:新疆大学计算机科学与技术学院与华为技术专家围绕 “数智育人 共塑高校生态” 及“Low Code Vs Vibe Coding” 两大核心议题,展开了深度交流与思想碰撞。本次“华为云HCSD校园公开课”的成功举办,标志着新疆大学与华为在华为根技术人才培养领域的合作迈向了新台阶。未来,双方将通过“赛事驱动、课程改革、技术赋能”三位一体模式,继续探索教育、科技、人才一体化发展路径,为边疆地区输送更多适应数字经济发展的高素质技术人才。华为云也将持续开放技术资源,通过“沃土云创”计划、开发者空间、云上科研实验室等平台,支持高校开展实践教学与创新活动,共建产学研用深度融合新生态。
  • [热门活动] 【活动已结束】云计算新手入门集证有礼—开发者空间案例实践抽奖:免费领取云主机,完成案例实践,赢华为智能体脂秤、定制冲锋衣、定制双肩包、云宝等好礼!
    1、活动中奖名单公布如下,公示期10天(2025年8月12日-2025年8月21日),如有疑问请在公示期间反馈,逾期不予处理!2、请中奖用户于8月21日23:59:59点前填写【中奖用户收货信息收集表】,逾期未填写视为放弃奖励,请知悉!3、公示期结束后,30个工作日内统一邮寄奖品,如有问题请与论坛版主【开发者学堂欢欢】联系。其中本活动中的实物礼品如遇缺货等情况,将替换成类似款或其他等值礼品发放。昵称华为云账户名中奖奖品andyleungand**eung1005云宝盲盒czb_biuhid**etnfngjmhkpfh9开发者空间定制双肩包miyalianhid**f120xjdev_3p9p开发者空间定制双肩包yd_229106051hid**yun开发者空间定制双肩包yd_238637427hid**9mpca1c1cu9wfa云宝盲盒yd_238822659fj8**8w123开发者空间定制冲锋衣yd_248582828Lin**an开发者空间定制冲锋衣yd_267354561hid**2c7dpa7yyb8l77开发者空间定制冲锋衣yd_277766320luo**68华为智能体脂秤 3 yd_286761491mug**etan开发者空间定制冲锋衣yd_291175919hid**72l4dybdv6zg3j开发者空间定制双肩包给无眠点压力GT-**ixin_52568491云宝盲盒敲键盘的诗人gpf**dream开发者空间定制冲锋衣(中奖名单如上展示,如有疑问请及时反馈) 【活动时间】即日起—8月7日【活动流程】——领取奖励的用户必须完成活动报名+领取华为开发者空间一、活动报名二、免费领取华为开发者空间三、完成以下任意一个华为开发者空间案例,在本论坛贴评论区分享案例完成截图(完成时间+案例名称+案例完成截图+实验心得),活动结束后,将在评论区符合条件的用户中抽奖。指定空间案例实操入口云主机轻松部署DeepSeek点击进入云主机调用DeepSeek实现代码自动生成点击进入初识云主机:CodeArts IDE入门点击进入运用昇思MindSpore框架成为垃圾分类小能手点击进入仓颉 – C跨语言编程实现控制台小游戏点击进入基于鲲鹏服务器的打砖块小游戏部署点击进入【活动礼品】【空间案例实操方式】1、 登录个人华为账号,从上述指定开发者空间案例中选取任意1个,点击进入,下载自己感兴趣的案例,根据提示完成案例实操2、 本活动贴评论区:发送完成时间+案例名称+案例完成截图+实验心得【抽奖方式】活动结束后,我们将从参与活动的用户中(华为云新老用户均可参与),通过巨公平台或Excel 函数形式抽取获奖用户,并在本活动帖进行公示获奖用户。 用户限制说明:1、参加本次社区活动的用户必须为华为云注册用户。同时为保证活动公平性,禁止用户以IAM账号身份参与活动,否则将视为无效。2、领取奖品的用户需为华为云实名用户,未完成实名认证的用户将不发放活动奖励。3、本次活动如一个实名认证对应多个账号,只有一个账号可领取奖励。如在同一概率活动中,同一账号重复获奖,只发放首先获奖奖品。4、本次活动一个实名认证账号只能对应一个收件人,如同一账号填写多个不同收件人,不予发放奖励。5、请开发者不要在活动期间随意修改社区昵称和华为云账号,由此产生的统计问题,如过了申诉期,小助手不再处理。(申诉期为活动结果公示3天内。)奖品发放说明:1、本活动结束之后10个工作日内公示获奖信息,获奖开发者用户需在截止时间在获奖信息收集表中填写获奖信息,获奖信息截止收集日过后30个工作日内,将统一发出奖品。华为云遵守《中华人民共和国个人信息保护法》规定,将以上个人信息仅用于礼品发放之目的,不会向任何第三方披露。若由于获奖开发者用户自身原因(包括但不限于联系方式有误、身份不符或超过截止登记日期等)造成奖品无法发送,视为获奖开发者用户放弃领奖。2、为保证活动的公平公正,华为云有权对恶意刷活动资源(“恶意”是指为获取资源而异常注册账号等破坏活动公平性的行为),利用资源从事违法违规行为的开发者用户收回抽奖及奖励资格。3、若发放奖品时,出现库存不足,则优先发放等价值的其他实物奖品;开发者空间定制冲锋衣尺码随机发放,不指定尺码。4、所有参加本活动的开发者用户,均视为认可并同意遵守《华为云开发者用户协议》,包括以援引方式纳入《华为云开发者用户协议》、《可接受的使用政策》、《法律声明》、《隐私政策声明》、相关服务等级协议(SLA),以及华为云服务网站规定的其他协议和政策(统称为“云服务协议”)的约束。5、如果您不同意本活动规则和云服务协议的条款,请勿参加本活动。
  • [案例共创] 【案例共创】基于华为云开发者空间+Flexus+Dify平台的AI Agent构建实战:搭建大学报考志愿建议助手
    基于华为云开发者空间+Flexus+Dify平台的AI Agent构建实战:搭建大学报考志愿建议助手🎯 项目背景与目标高考志愿填报是大学考生的重要选择,涉及高校信息、专业趋势与个人兴趣等复杂因素。构建一个智能化AI Agent,能在后台自动结合用户信息、政策数据、历年录取结果等,为考生提供定制化填报建议,将大幅提升决策效率。本项目基于华为云开发者空间提供的资源接入能力,使用Flexus提供弹性算力,搭配Dify提供的低代码Agent开发平台,实现“志愿建议助手”从接入到落地的完整流程。技术架构与平台概览华为云开发者空间:接入ModelArts、大数据、RDS、OBS等基础资源,提供统一控制入口。Flexus X / CCE 实例:作为 AI Agent 的计算引擎,提供稳定可弹性伸缩的资源。Dify 平台:支持拖拽式流程编排,集成大模型、检索知识库、API 工具,非常适合构建规则+推理并存的智能体DeepSeek-R1/V3:大模型提供推理能力,支持长上下文和复杂逻辑判断。🧩华为云开发者空间介绍华为云开发者空间(DevZone) 是面向开发者的一站式云端开发与协作平台,旨在帮助开发者高效构建、测试、部署和管理AI及软件项目。它整合了华为云的主流服务(如ModelArts、RDS、OBS、FunctionGraph等),并提供 DevOps 流程管理、代码托管、任务编排等功能。使用配置如下核心优势:🔧 云上开发环境:支持JupyterLab、VS Code、DevContainer等多种IDE配置,免本地环境搭建。🧠 集成AI开发能力:原生接入 ModelArts 平台,可便捷调度训练、部署推理服务。🔗 与Flexus无缝衔接:一键对接弹性算力资源(如训练算力/推理服务)。🚀 CI/CD支持:支持基于代码仓的流水线部署,DevOps 自动化流程便捷高效。开发者可以在云端统一管理代码、算力、模型和工具链,提升AI项目协同效率。🔧 Flexus介绍Flexus 是华为云提供的高性能弹性计算服务平台,专为AI推理、Web服务与复杂应用容器化部署而设计,支持秒级弹性扩缩容和资源自动调度。Flexus 能提供独立的沙箱环境,并具备高网络性能和低时延。主要特点:⚙️ 多规格资源池:支持CPU/内存/GPU资源弹性申请,可基于模型负载选择最佳配置。📦 Serverless容器运行时:无需繁琐资源配置,任务执行即启动,空闲即释放。📈 智能资源调度:支持按需弹性扩展(如用户并发量突增时自动增实例),提升资源利用率。🔒 安全隔离:支持沙箱环境运行Agent任务,避免权限泄露和数据交叉污染。Flexus 通常配合 Dify 或 ModelArts 使用,承接大模型推理任务、外部API调用以及高并发AI Agent的逻辑执行。🧠 Dify-LLM应用开发平台介绍Dify 是一个开源的大模型应用开发平台(LLMOps平台),支持以低代码或可视化方式构建基于大模型(如GPT、DeepSeek、Qwen等)的智能体(Agent)与应用。它支持多种调用模式、上下文配置、工具编排、知识库管理等模块。核心能力:🧩 可视化Agent构建:通过拖拽式流程设计搭建大模型工作流,降低开发门槛。🔗 多模型接入:支持 OpenAI、DeepSeek、百度文心、阿里通义、华为盘古等主流API。🧠 知识库RAG检索增强:支持上传文档/网页,构建向量知识库,用于丰富Agent语义理解。🛠️ 工具调用(ToolCall)机制:Agent 可动态调用 Web API、数据库、搜索引擎或自定义函数。🔍 日志审计与评估体系:支持输出跟踪、调用日志、上下文分析、响应耗时等指标监控。Dify 平台非常适合构建如“志愿填报助手”、“医疗问诊助手”、“金融顾问”等具有业务逻辑和语义理解要求的LLM应用。基于华为云开发者空间搭建大学报考志愿建议助手首先进入华为云开发者空间。方案架构该解决方案基于云容器引擎 CCE帮助您快速部署高可用Dify LLM应用开发平台。该系统基于华为云 VPC 私有网络,通过多个弹性资源组件(EIP、ELB、CCE、RDS、Redis、OBS等)协同运行,支持一个多副本、可扩展的 Dify 服务,主要模块如下:✅ 用户访问入口:弹性公网 IP(EIP):为用户提供访问 Dify 服务的公网 IP 地址。弹性负载均衡(ELB):将公网请求负载分发到后端 Dify 服务容器,实现高可用。VPC(虚拟私有云)整个系统部署在华为云的 VPC 内,提供独立、安全的网络环境。子网 & 安全组子网负责划分内部网络段;安全组用于设置访问控制规则(如端口、IP 白名单等)。NAT 网关用于 Dify 容器访问公网资源(如拉取依赖、模型服务、API 等)而不暴露私有 IP。使用组件:CCE(云容器引擎):即 Kubernetes 服务;部署了多个 Dify服务器容器,支持水平扩展;包括一个 Embedding & Reranker 组件,说明该服务具备自然语言处理能力,如语义检索、结果重排序等。Dify 核心服务依赖多个后端云服务:服务说明RDS for PostgreSQL存储用户数据、对话日志、API Key、配置等Redis® 版缓存加速,如 token 缓存、session 维护等CSS(云搜索服务)用于向量搜索、文档检索等 AIGC 功能OBS(对象存储服务)用于存储文件,如上传的知识库、文档等1.快速搭建Dify-LLM应用开发平台可以直接点击一键部署在模板中,会自动填写好,直接点击下一步。参数配置不用修改,直接设置密码。资源栈设置如下。确认配置如下。可以选择直接部署资源栈。等待生成完成。在输出中,直接访问ip地址即可访问。设置账号。创建应用,可见有很多选项。点击模型设置安装该插件提示词如下``` xml <instruction> <prompt> 你是一位大学报考志愿建议助手,将为学生提供个性化的志愿填报建议。请根据提供的学生信息和需求,生成一份详细的志愿填报建议。以下是需要考虑的因素: 1. 学生的基本信息: - {{student_name}}: 学生姓名 - {{age}}: 学生年龄 - {{location}}: 学生所在城市或省份 - {{student_id}}: 学生ID(如果有的话) 2. 学术成绩: - {{gpa}}: 学生的高中平均成绩 - {{exam_score}}: 高考分数 - {{subject_scores}}: 各科成绩(例如:语文、数学、英语等) - {{rank}}: 学生在班级或年级中的排名 3. 兴趣与职业规划: - {{interests}}: 学生的兴趣爱好(例如:科技、医学、艺术等) - {{career_goals}}: 职业目标(例如:成为医生、工程师、教师等) 4. 经济状况: - {{financial_status}}: 家庭经济状况(例如:富裕、中等、一般等) - {{tuition_preferences}}: 对学费的要求(例如:愿意支付高学费、希望学费较低等) 5. 学校与专业偏好: - {{preferred_schools}}: 偏好的学校(例如:清华大学、北京大学等) - {{preferred_programs}}: 偏好的专业(例如:计算机科学、医学、文学等) 请根据以上信息,生成一份详细的志愿填报建议,包括推荐的学校和专业,并说明推荐理由。建议内容应包括: - 推荐的学校列表(最多5所) - 推荐的专业列表(最多3个) - 每个推荐的详细理由 - 录取概率分析 - 其他相关建议 注意:输出内容应避免使用任何XML标签,保持自然流畅的中文表达。 </prompt> <example> 输入示例: { "student_name": "张三", "age": 18, "location": "北京市", "gpa": 3.8, "exam_score": 620, "subject_scores": { "语文": 85, "数学": 90,问题:学生叫李明,18岁,来自上海市。他的高中平均成绩是3.6,高考分数是580分,各科成绩分别是语文80,数学85,英语85,物理80,化学75,生物70。他在班上排名是前20%,兴趣爱好包括科技、编程,职业目标是成为一名软件工程师。家庭经济状况中等,希望学费不要太高。他偏好清华大学和复旦大学,喜欢计算机科学和电子工程专业。回答如下✅ 总结本项目展示了如何基于华为云开发者空间、Flexus弹性算力与Dify低代码平台,快速搭建一个面向高考志愿填报的智能AI Agent。从基础设施的配置(如VPC、CCE、RDS、Redis等)到Dify平台的部署与应用构建,全流程实现了“零代码或低代码”下的智能应用开发闭环。通过本项目的实践,开发者不仅能够深入理解AI Agent的构建逻辑,还能高效集成大模型能力(如DeepSeek)与知识库检索(RAG)等核心组件,实现复杂业务需求的智能化处理。该方案也可灵活迁移到医疗咨询、金融顾问、企业内部助手等其他场景,具备广泛的通用性与实用价值。未来,随着大模型推理能力的增强和云平台的资源调度优化,此类“智能体+平台”的组合模式将成为AI落地的重要路径。我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [案例共创] 【案例共创】基于仓颉编程语言+DeepSeek实现“软考系规知识点释义”助手
    项目概述本项目旨在通过华为开发者空间云主机,结合仓颉编程语言和DeepSeek API,开发一个简单的“软考系统规划与管理师(简称系规)知识点释义”助手。用户可以通过命令行输入相关知识点或问题,应用会返回该知识点的详细解释和记忆方法。这个助手可以帮助备考软考的考生更好地理解和记忆重要的知识点。适用对象需要参与软考“信息系统项目管理师(高级)”学习的用户。案例流程说明:① 练习使用CodeArts IDE for Cangjie编辑器;② 领取免费 DeepSeeK Tokens;③ 仓颉对接DeepSeek接口。开发步骤1. 环境准备注册并登录华为开发者空间:访问华为开发者空间官网,注册并登录账户。创建云主机实例:在华为开发者空间中创建一个新的云主机实例,选择适合的配置。安装CodeArts IDE for Cangjie:在云主机上安装CodeArts IDE for Cangjie编辑器,确保其预置在环境中(其实这一步是可以省略的,因为是默认内置的,如下图)。2. 项目初始化创建新项目:首先我们进入到开发者空间后,双击打开CodeArts IDE for Cangjie,在CodeArts IDE for Cangjie中创建一个新的项目,命名为“demo”(随便你怎么命名)。名称和位置可以自定义,产物类型选择executable。*产物类型说明: executable,可执行文件;static,静态库,是一组预先编译好的目标文件的集合;dynamic,动态库,是一种在程序运行时才被加载到内存中的库文件,多个程序共享一个动态库副本,而不是像静态库那样每个程序都包含一份完整的副本。配置项目结构:设置项目的基本目录结构,包括源代码文件、配置文件等。创建项目后,打开src目录下main.cj文件,在预置代码中增加函数和函数调用代码(没有的话可以自己新建一份,代码内容下文我有附上)。然后点击右上的运行按钮运行项目,在终端中查看输出内容。如果遇到弹窗提示运行/调试配置,点击确定继续操作。修改后的main.cj文件代码:package demo func println_add(a: Int64, b:Int64): Int64 { let number: Int64 = a+b println(number) return number } main(): Int64 { println("hello world") println_add(3,4) println("hello Cangjie") return 0 } 调试项目:点击右上调试按钮,首次会提示配置调试,点击新增配置项,选择Cangjie(cjdb)Debug——launch。*调试模式说明:Launch模式,启动调试器的同时加载被调程序;Attach,针对正在运行的程序,附加到已启动的程序。然后可以在main.cj代码中设置断点,再次点击调试按钮。可以在顶部看到调试按钮,在左侧看到调试过程中的变量、监视、调用堆栈、断点信息。3. Cangjie对接DeepSeek接口华为云提供了单模型200万免费Tokens,包含DeepSeek-R1&V3满血版,我们可以登录华为云ModelArts Studio(MaaS)控制台领取免费额度,这里我们选择DeepSeek-R1满血版来搭建我们的工程。在云主机桌面底部菜单栏,点击打开火狐浏览器。用火狐浏览器访问ModelArts Studio首页:https://www.huaweicloud.com/product/modelarts/studio.html,点击ModelArts Studio控制台跳转到登录界面,按照登录界面提示登录,即可进入ModelArts Studio控制台。你也可以在本地浏览器打开进行相关操作。领取后点击调用说明,可以获取到对应的API地址、模型名称。当然API Key也要创建下,这里创建很简单,按提示跳转过去就成,我这里就不介绍了。记录对应的API地址、模型名称、API Key留作下面步骤使用。4. Cangjie对接DeepSeek编写代码进入云主机桌面,右键选择Open Terminal Here打开终端命令窗口,克隆仓颉示例代码仓库。git clone https://gitcode.com/CaseDeveloper/Cangjie-Examples.git使用CodeArts IDE for Cangjie打开AIChat示例项目。左上点击文件—打开项目,选择前面克隆的示例代码目录Cangjie-Examples下的AIChat打开。修改配置config.json配置文件,配置DeepSeek的API信息。配置项:model、model、base_url打开main.cj,运行项目,我们就可跟AI助手进行聊天了。(* 注意:打开main.cj可能提示Console标红,不影响程序运行,可以忽略。亦可以删除import std.console.Console重新编写该导入语句)至此,基于仓颉编程语言+DeepSeek实现智能聊天助手开发秘籍就完成了。接下来我们在聊天项目基础上,改编成“软考系统规划与管理师(简称系规)知识点释义”助手其实超级简单,我们只需要再修改 “system_prompt” 即可。修改内容如下:"system_prompt": "你是我的“软考系规知识点释义”助手,你的名字叫系规。系规是一款面向计算机高级软考教材《系统规划与管理师》第二版的知识助手,主打筛选教材书本的知识点,善于总结知识内容,搜索知识内容,关联知识内容,以及整理便于记忆的知识口诀。须保持回复简洁,回复内容不超过300字。" 运行程序:最后我把代码完整的给大家呈现下(chat.cj):具体代码如下:package openai_chat import encoding.json.stream.* import net.http.ClientBuilder import net.http.HttpHeaders import net.http.HttpRequestBuilder import net.tls.TlsClientConfig import net.tls.CertificateVerifyMode import std.collection.ArrayList import std.io.ByteArrayStream import std.time.Duration public struct FunctionCall <: JsonDeserializable<FunctionCall> & JsonSerializable { public let name: String public let arguments: String public init(name: String, arguments: String) { this.name = name this.arguments = arguments } public static func fromJson(r: JsonReader): FunctionCall { var temp_name: String = "" var temp_arguments: String = "" while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "name" => temp_name = r.readValue<String>() case "arguments" => temp_arguments = r.readValue<String>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for FunctionCall") } } return FunctionCall(temp_name, temp_arguments) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("name").writeValue<String>(this.name) w.writeName("arguments").writeValue<String>(this.arguments) w.endObject() w.flush() } } public enum RoleType { User | Assistant | System | Function | NULL } public func role_type_to_str(role: RoleType): Option<String> { return match(role) { case RoleType.User => Some("user") case RoleType.Assistant => Some("assistant") case RoleType.System => Some("system") case RoleType.Function => Some("function") case RoleType.NULL => None } } public func str_to_role_type(role_option_str: Option<String>): RoleType { return match(role_option_str) { case Some(role_option) => match (role_option) { case "user" => RoleType.User case "assistant" => RoleType.Assistant case "system" => RoleType.System case "function" => RoleType.Function case x => throw Exception("unknow enum ${x} for RoleType") } case None => RoleType.NULL } } public struct Message<: JsonDeserializable<Message> & JsonSerializable { public let role: RoleType public var content: String public let function_call: Option<FunctionCall> public init(role: RoleType, content: String) { this.role = role this.content = content this.function_call = None } public init(role: RoleType, content: String, function_call: Option<FunctionCall>) { this.role = role this.content = content this.function_call = function_call } public static func fromJson(r: JsonReader): Message { var temp_role: Option<String> = None // role may null in stream chat var temp_content: String = "" var temp_functional_call: Option<FunctionCall> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "role" => temp_role = r.readValue<Option<String>>() case "content" => temp_content = r.readValue<String>() case "function_call" => temp_functional_call = r.readValue<Option<FunctionCall>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Message") } } let role_type: RoleType = str_to_role_type(temp_role) return Message(role_type, temp_content) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("role").writeValue<Option<String>>(role_type_to_str(this.role)) w.writeName("content").writeValue<String>(this.content) w.endObject() w.flush() } } public struct ChatRequest <: JsonSerializable { private let model: String private let messages: ArrayList<Message> private let max_tokens: Int64 private let temperature: Float64 private let top_p: Float64 private let n: Int32 private let stream: Bool public init( model: String, messages: ArrayList<Message>, max_tokens: Int64, temperature: Float64, top_p: Float64, n: Int32, stream: Bool ) { // construction function with messages this.model = model this.messages = messages this.max_tokens = max_tokens this.temperature = temperature this.top_p = top_p this.n = n this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, max_tokens: Int64, temperature: Float64, top_p: Float64, n: Int32, stream: Bool ){ // construction function with prompt and system_prompt this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = max_tokens this.temperature = temperature this.top_p = top_p this.n = n this.stream = stream } public init( model: String, prompt: String, history: ArrayList<(String, String)>, system_prompt: String, stream: Bool ){ // construction function with prompt and default arguments this.model = model this.messages = ArrayList<Message>([ Message(RoleType.System, system_prompt) ]) for ((use_msg, bot_msg) in history) { this.messages.append(Message(RoleType.User, use_msg)) this.messages.append(Message(RoleType.Assistant, bot_msg)) } this.messages.append(Message(RoleType.User, prompt)) this.max_tokens = 2000 this.temperature = 0.7 this.top_p = 1.0 this.n = 1 this.stream = stream } public func toJson(w: JsonWriter) { w.startObject() w.writeName("model").writeValue<String>(this.model) w.writeName("messages").writeValue<ArrayList<Message>>(this.messages) w.writeName("max_tokens").writeValue<Int64>(this.max_tokens) w.writeName("temperature").writeValue<Float64>(this.temperature) w.writeName("top_p").writeValue<Float64>(this.top_p) w.writeName("n").writeValue<Int32>(this.n) w.writeName("stream").writeValue<Bool>(this.stream) w.endObject() w.flush() } } public struct Choice <: JsonDeserializable<Choice> & JsonSerializable { public let index: Int32 public let message: Option<Message> public let delta: Option<Message> public let finish_reason: Option<String> public let logprobs: Option<Float64> // dashscope for qwen need public init( index: Int32, message: Option<Message>, delta: Option<Message>, finish_reason: Option<String>, logprobs: Option<Float64> ) { this.index = index this.message = message this.delta = delta this.finish_reason = finish_reason this.logprobs = logprobs } public static func fromJson(r: JsonReader): Choice { var temp_index: Int32 = -1 var temp_message: Option<Message> = None var temp_delta: Option<Message> = None var temp_finish_reason: Option<String> = None var temp_logprobs: Option<Float64> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "index" => temp_index = r.readValue<Int32>() case "message" => temp_message = r.readValue<Option<Message>>() case "delta" => temp_delta = r.readValue<Option<Message>>() case "finish_reason" => temp_finish_reason = r.readValue<Option<String>>() case "logprobs" => temp_logprobs = r.readValue<Option<Float64>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Choice") } } return Choice(temp_index, temp_message, temp_delta, temp_finish_reason, temp_logprobs) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("index").writeValue<Int32>(this.index) w.writeName("message").writeValue<Option<Message>>(this.message) w.writeName("delta").writeValue<Option<Message>>(this.delta) w.writeName("finish_reason").writeValue<Option<String>>(this.finish_reason) w.writeName("logprobs").writeValue<Option<Float64>>(this.logprobs) w.endObject() w.flush() } } public struct Usage <: JsonDeserializable<Usage> & JsonSerializable { public let prompt_tokens: UInt64 public let completion_tokens: UInt64 public let total_tokens: UInt64 public init(prompt_tokens: UInt64, completion_tokens: UInt64, total_tokens: UInt64) { this.prompt_tokens = prompt_tokens this.completion_tokens = completion_tokens this.total_tokens = total_tokens } public static func fromJson(r: JsonReader): Usage { var temp_prompt_tokens: UInt64 = 0 var temp_completion_tokens: UInt64 = 0 var temp_total_tokens: UInt64 = 0 while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "prompt_tokens" => temp_prompt_tokens = r.readValue<UInt64>() case "completion_tokens" => temp_completion_tokens = r.readValue<UInt64>() case "total_tokens" => temp_total_tokens = r.readValue<UInt64>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for Usage") } } return Usage(temp_prompt_tokens, temp_completion_tokens, temp_total_tokens) } public func toJson(w: JsonWriter) { w.startObject() w.writeName("prompt_tokens").writeValue<UInt64>(this.prompt_tokens) w.writeName("completion_tokens").writeValue<UInt64>(this.completion_tokens) w.writeName("total_tokens").writeValue<UInt64>(this.total_tokens) w.endObject() w.flush() } } public struct ChatResponse <: JsonDeserializable<ChatResponse> { // some api names `id`, and some names `request_id` public let id: Option<String> public let request_id: Option<String> public let system_fingerprint: Option<String> public let model: String public let object: String public let created: UInt64 public let choices: ArrayList<Choice> public let usage: Option<Usage> public init( id: Option<String>, request_id: Option<String>, system_fingerprint: Option<String>, model: String, object: String, created: UInt64, choices: ArrayList<Choice>, usage: Option<Usage> ) { this.id = id this.request_id = request_id this.system_fingerprint = system_fingerprint this.model = model this.object = object this.created = created this.choices = choices this.usage = usage } public static func fromJson(r: JsonReader): ChatResponse { var temp_id: Option<String> = None var temp_request_id: Option<String> = None var temp_system_fingerprint: Option<String> = None var temp_model: String = "" var temp_object: String = "" var temp_created: UInt64 = 0 var temp_choices: ArrayList<Choice> = ArrayList<Choice>([]) var temp_usage: Option<Usage> = None while (let Some(v) <- r.peek()) { match(v) { case BeginObject => r.startObject() while(r.peek() != EndObject) { let n = r.readName() match (n) { case "id" => temp_id = r.readValue<Option<String>>() case "request_id" => temp_request_id = r.readValue<Option<String>>() case "system_fingerprint" => temp_system_fingerprint = r.readValue<Option<String>>() case "model" => temp_model = r.readValue<String>() case "object" => temp_object = r.readValue<String>() case "created" => temp_created = r.readValue<UInt64>() case "choices" => temp_choices = r.readValue<ArrayList<Choice>>() case "usage" => temp_usage = r.readValue<Option<Usage>>() case _ => r.skip() } } r.endObject() break case _ => throw Exception("can't deserialize for ChatResponse") } } return ChatResponse( temp_id, temp_request_id, temp_system_fingerprint, temp_model, temp_object, temp_created, temp_choices, temp_usage ) } } public func get_domain( url: String ): String { var temp_url = url if (temp_url.startsWith("https://")) { temp_url = temp_url["https://".size..] } else if (temp_url.startsWith("http://")) { temp_url = temp_url["http://".size..] } let domain: String = temp_url.split("?")[0].split("/")[0] return domain } public func build_http_client( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)>, stream!: Bool ){ // prepare input data var array_stream = ByteArrayStream() let json_writer = JsonWriter(array_stream) let chat_res = ChatRequest( env_info.model, prompt, history, env_info.system_prompt, stream ) chat_res.toJson(json_writer) let post_data: Array<UInt8> = array_stream.readToEnd() var headers: HttpHeaders = HttpHeaders() headers.add("Authorization", "Bearer ${env_info.api_key}") headers.add("Content-Type", "application/json") if (stream) { headers.add("Accept", "text/event-stream") } let request = HttpRequestBuilder() .url(env_info.base_url) .method("POST") .body(post_data) .readTimeout(Duration.second * 120) .addHeaders(headers) .build() let client = if (env_info.base_url.startsWith("https")) { var tls_client_config = TlsClientConfig() tls_client_config.verifyMode = CertificateVerifyMode.TrustAll tls_client_config.domain = get_domain(env_info.base_url) ClientBuilder() .tlsConfig(tls_client_config) .build() } else { ClientBuilder().build() } return (request, client) } public func chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: false ) var result_message: Option<String> = None var res_text = "" try { // call api let response = client.send( request ) // read result (support max revice 100k data) let buffer = Array<Byte>(102400, item: 0) let length = response.body.read(buffer) res_text = String.fromUtf8(buffer[..length]) // println("res_text: ${res_text}") var input_stream = ByteArrayStream() input_stream.write(res_text.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { let message = choices[0].message.getOrThrow() // println("message: ${message.content}") result_message = Some(message.content) } else { println("can't found any response") } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${res_text}") } client.close() return result_message } public func stream_chat( prompt: String, env_info: EnvInfo, history: ArrayList<(String, String)> ): Option<String> { let (request, client) = build_http_client( prompt, env_info, history, stream: true ) var result_response: String = "" var temp_text2 = "" try { // call api let response = client.send( request ) // read result let buffer = Array<Byte>(10240, item: 0) var finish_reason: Option<String> = None while(finish_reason.isNone() && temp_text2 != "[DONE]") { let length = response.body.read(buffer) let res_text = String.fromUtf8(buffer[..length]) for (temp_text in res_text.split("\n")) { temp_text2 = if (temp_text.startsWith("data: ")) { temp_text["data: ".size..] } else { temp_text } if (temp_text2.size == 0) { continue } if (temp_text2 == "[DONE]") { break } // println("========================") // println("temp_text: ${temp_text2}") // println("========================") var input_stream = ByteArrayStream() input_stream.write(temp_text2.toArray()) // convert text to ChatResponse object let json_reader = JsonReader(input_stream) let res_object = ChatResponse.fromJson(json_reader) let choices: ArrayList<Choice> = res_object.choices if (choices.size > 0) { finish_reason = choices[0].finish_reason if (finish_reason.isNone()) { let delta = choices[0].delta.getOrThrow() print("${delta.content}", flush: true) result_response += delta.content } } else { println("can't found any response") } } } } catch (e: Exception) { println("ERROR: ${e.message}, reviced text is ${temp_text2}") } client.close() if (result_response.size > 0) { return Some(result_response) } else { return None } } 5. 运行和测试最后我们测试下系统的准确性,如下图:到此整个项目案例创建完成,可见是非常简单且实用的,希望能给大家带来帮助。我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [案例共创] 【案例共创】从零开始,用仓颉语言+DeepSeek开发你的专属AI助手
    从零开始,用仓颉语言+DeepSeek开发你的专属AI助手在大模型技术飞速发展的今天,越来越多的企业和开发者开始关注“专属AI助手”的开发与部署。从最早的GPT开放接口调用,到如今的“千模大战”,开发门槛逐步降低,但真正从零构建一套专属的、可持续迭代的AI助手系统仍然需要深厚的工程能力与平台支持。本案例围绕在华为云开发者空间中的云主机环境,逐步展开一套AI 聊天助手的全流程开发实践。我们以一个轻量级的仓颉语言项目作为起点,首次体验了 CodeArts IDE for Cangjie 编辑器的便捷性与强大功能。该编辑器预置在云主机内,免去本地环境配置,极大提高了开发效率,也为后续复杂逻辑的实现打下了坚实基础。在项目开发过程中,我们充分结合了仓颉编程语言的优势,通过自然语言式的指令流畅定义助手逻辑。与此同时,我们接入了华为 ModelArts Studio(MaaS)平台,并利用官方提供的 DeepSeek Tokens 免费额度,完成了 DeepSeek-R1 大语言模型的调用与集成。通过这一过程,我们不仅完成了从模型领取、接口接入、到云端部署的完整开发闭环,还深入体验了 DeepSeek-R1 模型在中文理解、对话生成等方面的高性能表现。本案例也是 CodeArts IDE 与 MaaS 平台结合、仓颉语言与大模型融合的一个具象化实践范例,为今后的专属 AI 助手开发提供了可复制的路径与方法论。一.华为云开发者空间在数字化转型加速的今天,开发者对高效、灵活的开发环境需求日益增长。华为云开发者空间应运而生,致力于为每位开发者提供一台专属的云主机,简化开发流程,提升开发效率。1.1什么是华为云开发者空间云主机?华为云开发者空间云主机是一种预配置的云上开发桌面,集成了华为优质的开发工具和资源,支持Web连接访问,开发桌面主机规格和操作系统自定义。开发者可通过浏览器随时随地访问,立即开始开发工作。每位开发者每年可享有数百小时的云主机使用时长,配备5GB的云存储空间和场景化沙箱资源。1.2核心功能与优势开箱即用的开发环境云主机预置了华为优质的开发工具和场景化资源池,支持Web连接访问,开发桌面主机规格和操作系统自定义。丰富的开发工具集成以华为云CodeArts IDE为核心,预装了多种开发工具,支持多语言开发及代码助手,提升开发效率。场景化沙箱环境预置华为云、鲲鹏、昇腾、鸿蒙等场景化沙箱环境,支持分钟级创建及恢复沙盒环境,保障数据安全。云上存储与数据持久化为开发者提供云上存储空间,支持开发者文件保存和内容中转,便于关键资产保存和中转。华为云开发者空间云主机适用于多种开发场景,包括:Web应用托管:提供稳定的运行环境和灵活的扩展能力。大数据处理:利用云主机强大的计算能力,进行大规模的数据分析和实时数据处理。机器学习训练:支持GPU加速,适用于深度学习模型训练和推理。开发测试环境:快速构建和销毁测试环境,节省本地资源并提高开发效率。高校师生开发实践:便捷搭建实验环境,支持教学与科研项目。开源共创:支持开源项目的开发与协作。1.3如何开始使用?访问华为云开发者空间。注册并登录华为云账号。领取专属云主机,选择所需的开发环境和工具。通过Web浏览器连接云主机,开始开发工作。华为云开发者空间云主机为开发者提供了一个高效、灵活、安全的云上开发环境,降低了开发门槛,提升了开发效率。无论是个人开发者、初创企业,还是高校师生,都可以通过这一平台,快速构建和部署应用,实现技术创新。二. 仓颉编程语言:鸿蒙生态的原生智能开发语言随着华为在操作系统、芯片和开发工具链等领域的持续投入,2024年,华为正式发布了自主研发的编程语言——仓颉(Cangjie)。该语言旨在为鸿蒙生态系统提供原生的智能化开发支持,满足全场景、多设备、智能化应用的开发需求。2.1 仓颉语言概述仓颉编程语言是一种多范式(支持面向对象、函数式、命令式)的现代编程语言,主要应用于鸿蒙原生应用及服务开发场景。其设计理念强调原生智能化、天生全场景、高性能和强安全。该语言的研发始于2019年,由华为与南京大学计算机科学与技术系合作,冯新宇教授担任首席架构师。在2024年华为开发者大会上,仓颉语言的开发者预览版本首次公开发布。2.2核心特性原生智能化仓颉语言内置了AgentDSL框架,实现自然语言与编程语言的有机融合。通过多Agent协同,简化符号表达和模式组合,支持各类智能应用的开发。天生全场景采用轻量化可缩放的运行时和模块化分层设计,仓颉语言能够在资源受限的设备上高效运行。其元编程和eDSL技术支持面向领域的声明式开发,适应多种应用场景。高性能仓颉语言在终端场景中实现了首款全并发垃圾回收机制,提升应用线程的流畅性和响应速度。同时,轻量化线程的设计增强了并发性能,降低了资源开销。强安全安全机制被深度集成到语言设计中,帮助开发者专注于业务逻辑,减少防御性编程的负担。这种设计理念确保了编码过程中的安全性,降低了漏洞风险。编译器:仓颉语言使用名为cjc的编译器进行代码编译。包管理器:内置的包管理器cjpm用于管理和维护仓颉项目的模块系统,提供统一的编译入口。开发工具:仓颉语言提供包括调试、静态验证、性能分析和测试等工具,支持开发者在鸿蒙生态中进行高效开发。2.3云主机预制仓颉IDE华为云开发者空间云主机预制安装了支持仓颉的编辑器,用户可直接使用。在创建新项目时,选择“新建工程”以构建一个仓颉项目。你可以根据实际需求自定义项目的名称与保存路径。在“生成类型”一栏,请选择 executable(可执行程序)。生成类型简介:executable(可执行程序):编译后可直接运行的程序文件;static(静态库):由已编译的目标文件组成,用于在链接阶段将库代码嵌入应用程序中;dynamic(动态库):在程序运行时加载,多个程序可共享一份库文件,节省内存资源,相较静态库更灵活。项目创建完成后,进入 src 文件夹并打开其中的 main.cj 文件。在已有的预设代码基础上,添加需要的函数定义及对应的函数调用逻辑,以扩展程序功能。package demo func println_add(a: Int64, b:Int64): Int64 { let number: Int64 = a+b println(number) return number } main(): Int64 { println("hello world") println_add(3,4) println("hello Cangjie") return 0 } 在仓颉语言中,使用let声明不可变变量,使用var声明可变变量。变量在使用前必须初始化,类型可以根据初始值自动推断,或通过显式声明指定。运行结果如下。仓颉编程语言作为华为自主研发的现代编程语言,旨在为鸿蒙生态提供原生的智能化开发支持。其多范式的设计、强大的性能和安全特性,使其成为开发全场景智能应用的有力工具。随着生态的不断完善,仓颉语言有望在未来的智能设备和应用开发中发挥重要作用。更多信息和资源,请访问仓颉编程语言的官方网站。三.ModelArts Studio大模型即服务平台随着人工智能技术的迅猛发展,企业在AI应用落地过程中面临着模型开发复杂、算力需求高、部署维护难等挑战。华为云推出的ModelArts Studio大模型即服务平台(简称MaaS),旨在为企业提供一站式的大模型开发、部署与管理解决方案,降低AI应用的门槛,加速智能化转型。MaaS平台集成了业界主流开源大模型,如DeepSeek、Llama、Baichuan、Yi、Qwen等,基于华为昇腾AI云服务进行全面适配和优化,提升模型的精度和性能。开发者无需从零开始构建模型,只需选择合适的预训练模型进行微调或直接应用,减轻了模型集成的负担。零代码、免配置、免调优的模型开发MaaS平台提供简单易用的模型开发工具链,支持大模型定制开发,让模型应用与业务系统无缝衔接,显著降低了企业AI落地的成本与难度。一站式模型开发服务平台提供模型调优、压缩、部署、评测等全栈工具,功能覆盖大模型全生命周期,支持用户即开即用,低门槛使用各类大模型。灵活的资源配置与高可用性MaaS平台支持资源按需开通,建设周期短,按需收费,按需扩缩容,并支持故障快速恢复与断点续训。多服务组合与应用集成平台支持即插即用的MCP Server,LangChain、九问、RAG、Agent、Guard等组件化集成,快速构建一站式应用。MaaS平台广泛适用于以下行业和场景:政务:赋能政务智能决策,提升社会治理精准化水平。金融:驱动风险防控智能化,构建高效金融服务体系。电力:优化能源智能调度,保障电力系统安全运行。医疗:突破精准辅助诊疗边界,构建智慧医疗新范式。此外,MaaS平台还提供MaaS应用体验中心,为具体的应用场景提供一整套解决方案,如KooSearch企业搜索服务、盘古数字人大脑和Dify等,帮助客户快速解决模型落地应用时所面临的业务及技术挑战。华为云ModelArts Studio大模型即服务平台(MaaS)通过集成主流大模型、提供零代码开发环境、一站式模型服务和灵活的资源配置,极大地降低了企业AI应用的门槛,加速了AI技术的落地与应用。无论是初创企业还是大型机构,MaaS平台都是实现智能化转型的有力工具。四.仓颉语言 × DeepSeek:智能聊天助手开发全流程解析在本节中,我们将基于华为云 ModelArts Studio 大模型即服务平台,借助仓颉语言与 DeepSeek 模型,完成智能聊天助手的从零到一搭建与调试全过程。以下操作步骤详尽而直观,助您快速上手。4.1 登录 ModelArts Studio 平台打开华为云控制台,进入 ModelArts Studio 大模型即服务(MaaS)平台。在左侧导航栏,选择 模型推理 > 在线推理。4.2 选择 DeepSeek-R1-32K 模型在在线推理页面中,您会看到包含 商业服务 与 免费服务 两类选项。本示例中,我们使用较高算力与更长上下文的 商业服务。滚动服务列表,找到并点击 DeepSeek-R1-32K 对应的调用说明。记录下关键接口信息:牢记其API地址与模型名称API地址 https://api.modelarts-maas.com/v1/chat/completions 模型名称 DeepSeek-R14.3 创建并获取 API Key在页面左下,点击 API Key 管理。点击 新建 API Key,填写名称与描述后确认。系统生成的 Key 仅能查看一次,请务必妥善保存。牢记密匙,只出现一次。后续需要填到代码中。4.4 克隆示例仓库并打开项目在云主机环境中打开终端(Terminal Here)。执行以下命令,将示例代码仓库克隆至本地:打开Open Terminal Here,克隆项目到本地git clone https://gitcode.com/CaseDeveloper/Cangjie-Examples.git使用云主机预装的仓颉IDE打开克隆到本地的项目工程目录如下4.5 配置 config.json在项目根目录中,定位并打开 config.json 文件。按照前面记录的 API 地址、模型名称与 API Key,逐项填入对应字段:{ "model": "DeepSeek-R1", "api_key": "PFLsP3QVe6S5KU3AFmDMvoP9-4LnzqnwEnrxohTYKvSenMLZVzFt0QzQM2l9vhBH4u4WCphazC9pr4bbbOscuw", "base_url": "https://api.modelarts-maas.com/v1/chat/completions", "system_prompt": "你是我的全能助手,你的名字叫仓颉。仓颉编程语言是一款面向全场景智能的新一代编程语言,主打原生智能化、天生全场景、高性能、强安全。主要应用于鸿蒙原生应用及服务应用等场景中,为开发者提供良好的编程体验。须保持回复简洁,回复内容不超过300字。" } 提示:注意 JSON 中的引号与逗号格式,确保无多余空格或注释。4.6 运行并测试聊天助手回到项目根目录,执行主程序:cj run main.cj程序启动后,您将看到命令行提示,输入对话内容即可与 DeepSeek 模型交互。例如:你好,你怎么看目前华为的鸿蒙操作系统呢?程序会将您的问题发送至 DeepSeek-R1,并在终端显示模型的回复:这个模型的回答非常出色,它不仅将每一步操作细化到位,还贴心地添加了实用提示和示例截图,帮助读者在实践过程中少走弯路。文中逻辑清晰、层次分明,从平台登录、模型选择、Key 管理到项目运行一气呵成,让人一目了然;同时强调了关键注意事项,既专业又贴心,真正做到了“简单易懂又不失深度”,非常优秀!4.7 从“零”到“一”的智能助手构建旅程在本章节中,我们以仓颉语言为开发基石,结合华为云 ModelArts Studio 平台与 DeepSeek-R1-32K 大模型,完整实现了一个智能聊天助手的开发与运行。整个过程不仅涵盖了 模型选择与接入、API Key 配置、项目代码部署 等关键步骤,还充分体现了仓颉语言在智能场景下的高效与可扩展性。这一过程的成功实施表明:借助云端大模型服务与本地轻量化 IDE 的融合,我们可以以极低的门槛快速落地一个功能完备的 AI 助手系统,为未来更复杂的多模态智能应用打下良好基础。📌 总结:构建你的专属 AI 助手,从“零”到“一”的实战指南本文以实战案例为核心,详尽阐述了一个完整的 AI 聊天助手开发流程,涵盖从环境搭建到模型接入、从代码编写到终端测试的全过程。文章内容层次清晰,步骤详尽,结合大量图示与代码示例,为开发者提供了可复现的操作路径和最佳实践。在开发工具选择方面,文章充分发挥了 华为云开发者空间云主机的免配置优势,并以 仓颉语言作为智能逻辑编写基础,有效降低了传统开发门槛;同时借助 ModelArts Studio MaaS 平台接入高性能的 DeepSeek-R1 模型,实现了真正意义上的“专属 AI 助手”。该案例不仅展示了仓颉语言与大模型结合的技术可行性,也为未来更多智能应用的快速原型验证和迭代提供了现实参考。整体上,这是一篇适合 AI 初学者、教育教学场景,乃至中小企业技术落地的高价值开发指南。我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
总条数:499 到第
上滑加载中