• 移动携手华为发布移动云信息技术融合应用创新产业生态联合体
    4月25日,由中国移动通信集团主办的“云擎未来 智信天下”2023移动云大会在苏州金鸡湖国际会议中心顺利召开。华为技术有限公司高级副总裁曹既斌受邀出席此次大会,并参加了移动云信息技术融合应用创新产业生态联合体发布仪式。移动云信息技术融合应用创新产业生态联合体的发布,标志着中国移动将凝聚合力共同构建开放包容、合作共赢的产业生态。当前,中国移动与华为正迈入新的合作周期,华为也将秉持"一切皆服务"的战略,与移动云强强联合,持续做强技术底座,为产业创造更多价值,为建设数字中国注入澎湃动能。        在本届移动云大会上,华为云计算技术有限公司副总裁黄瑾做了主题为“一切皆服务,共促云产业,深耕数智化”的演讲,阐述了华为云对于产业的思考,业务战略,以及与移动云合作的成果及未来展望。“在数字转型的关键时刻,华为云通过一切皆服务,共创行业新价值,助力企业数智化转型,共建繁荣生态共赢。中国移动和华为在云计算领域的战略合作,取得较好进展,践行“一切皆服务”,把研发投入所产生的技术和产品、数字化转型经验,伙伴的能力,都开放在云上作为云服务,提升竞争力,同时更好的共同服务客户。”
  • [技术干货] 移动携手华为发布移动云信息技术融合应用创新产业生态联合体
            4月25日,由中国移动通信集团主办的“云擎未来 智信天下”2023移动云大会在苏州金鸡湖国际会议中心顺利召开。华为技术有限公司高级副总裁曹既斌受邀出席此次大会,并参加了移动云信息技术融合应用创新产业生态联合体发布仪式。移动云信息技术融合应用创新产业生态联合体的发布,标志着中国移动将凝聚合力共同构建开放包容、合作共赢的产业生态。当前,中国移动与华为正迈入新的合作周期,华为也将秉持"一切皆服务"的战略,与移动云强强联合,持续做强技术底座,为产业创造更多价值,为建设数字中国注入澎湃动能。        在本届移动云大会上,华为云计算技术有限公司副总裁黄瑾做了主题为“一切皆服务,共促云产业,深耕数智化”的演讲,阐述了华为云对于产业的思考,业务战略,以及与移动云合作的成果及未来展望。“在数字转型的关键时刻,华为云通过一切皆服务,共创行业新价值,助力企业数智化转型,共建繁荣生态共赢。中国移动和华为在云计算领域的战略合作,取得较好进展,践行“一切皆服务”,把研发投入所产生的技术和产品、数字化转型经验,伙伴的能力,都开放在云上作为云服务,提升竞争力,同时更好的共同服务客户。”
  • [其他] Dfiy联网搜索工具
    dify版本支持:≥v1.0(我环境1.4)1.停止dify运行(在dify/docker目录下)docker-compose down2.导入公钥(将公钥上传至服务器)mkdir docker/volumes/plugin_daemon/public_keyscp dify_key_pair.public.pem docker/volumes/plugin_daemon/public_keys3.修改环境变量配置(/dify/docker/docker-compose.yaml)vi /dify/docker/docker-compose.yaml具体添加修改内容如下:THIRD_PARTY_SIGNATURE_VERIFICATION_ENABLED: trueTHIRD_PARTY_SIGNATURE_VERIFICATION_PUBLIC_KEYS: /app/storage/public_keys/your_key_pair.public.pem4.plugin daemon时间同步同上修改环境变量配置- /etc/localtime:/etc/localtime:ro # 同步时间保存退出5.启动difydocker-compose up -d6.登录dify准备导入工具登陆后点击“插件”点击“安装插件”→“本地插件”选择“baidu_search.signed.difypkg”等待安装成功可查看docker日志观察安装是否存在问题(一般问题会出现在pip拉取依赖失败-自行解决)查看docker实例“dify-plugin-daemon”的ID或名称docker psdocker lofgs -f [容器名称或 ID]备注:如若安装不成功联系我获取插件代码。可能存在问题:导入提示不良签名(需要重新生成签名,通过作者获取插件源文件重新打包)三方签名生成:(自行参考第三方签名验证 - Dify Docs),发不了url检测不过。依赖安装不成功,自行进入实例“dify-plugin-daemon”,虚拟环境中手动安装依赖或者采用国内pip源进行安装。
  • DeepSeek-R1-0528 已全新发布,完全不输Claude 4!
    DeepSeek-R1于2025年5月29日在huggingface、modelscope开源了最新的DeepSeek-R1-0528这次根据官方说法,是小范围升级,目前实测来看,知识库更新到2025年3月,而且在某些任务下DeepSeek-R1的思考时间可能有30-60分钟livecodebench排行榜分数也不错华为云也即将上线新版本DeepSeek-R1-0528模型,大家可以在升级后去体验最新的模型!
  • [其他] 华为云BMS部署Qwen3-32b
    华为云 BMS 部署 Qwen3-32b 实战指南在人工智能蓬勃发展的当下,大模型的应用愈发广泛。Qwen3-32b 作为一款备受关注的大模型,在华为云 BMS 上的部署过程是很多开发者关心的焦点。本文将基于详细的部署文档,为大家呈现华为云 BMS 部署 Qwen3-32b 的完整流程,助力大家顺利搭建起属于自己的大模型服务环境。一、华为云 BMS 配置概览在开始部署之前,先来了解一下本次部署所使用的华为云 BMS 配置。具体配置如下:机型:physical.kat2e.48xlarge.8.280t.ei.c002CPU:Kunpeng 920 (4*48Core@2.6GHz),强大的多核心处理器为模型运行提供了稳定的计算基础。内存:24*64GB DDR4 RAM,充足的内存空间确保模型在处理大量数据时能够流畅运行。本地磁盘:3*7.68TB NVMe SSD,高速的存储设备可以快速读写模型数据和相关文件。扩展配置:2100GE + 8200GE,高速网络配置为数据传输提供了有力保障。镜像:HCE2.0-Arm-64bit-for-Snt9A2-BareMetal-with-24.1.0-7.5.0.3.220-CANN7.6.0.1.220,该镜像为本次部署提供了特定的运行环境。二、环境准备工作1.1 检查 NPU 设备SSH 登录到机器后,首要任务是检查 NPU 设备的状态。通过运行以下命令来获取 NPU 设备信息:查看 NPU 卡状态:npu-smi info,此命令可以在每个实例节点上运行,查看 NPU 卡的具体运行情况。确认总卡数:npu-smi info -l | grep Total,通过该命令确认对应卡数是否已经成功挂载。查看驱动和固件版本:npu-smi info -t board -i 1 | egrep -i "software|firmware",如果在执行这些命令时出现错误,可能是 NPU 设备没有正常安装,或者 NPU 镜像被其他容器挂载。此时需要先正常安装固件和驱动,或者释放被挂载的 NPU,确保 NPU 设备能够正常工作。1.2 检查与安装 dockerdocker 是部署过程中不可或缺的工具,接下来检查 docker 是否已经安装。可以通过docker -v命令查看 docker 的版本信息,如果尚未安装,则需要运行以下命令进行安装:yum install -y docker-engine.aarch64 docker-engine-selinux.noarch docker-runc.aarch64安装完成后,还需要配置 IP 转发,以实现容器内的网络访问。执行sysctl -p | grep net.ipv4.ip_forward命令查看net.ipv4.ip_forward配置项的值,如果为 1,则说明已经配置好,可跳过此步骤;若不为 1,则执行以下命令进行配置:sed -i's/net\\.ipv4\\.ip\_forward=0/net\\.ipv4\\.ip\_forward=1/g' /etc/sysctl.conf sysctl -p | grep net.ipv4.ip\_forward1.3 获取基础镜像为了确保部署的稳定性和兼容性,建议使用官方提供的镜像来部署推理服务。镜像地址可以通过特定方式获取,获取后使用docker pull命令进行下载,例如:官方镜像目前为对外公布敬请期待!!!docker pull quay.io/ascend/vllm-ascend:v0.8.5rc1(这个镜像理论上可行)1.4 获取权重文件权重文件是 Qwen3-32b 模型运行的关键数据,需要通过解决方案实践 —OBS 极速同步模型权重文件,将其导入到自己的 OBS 中,然后再下载到服务器中。具体下载步骤如下:获取权重文件方式:https://www.huaweicloud.com/solution/implementations/replicating-model-weight-files-across-obs-buckets.html1.4.1 权重文件下载到服务器使用wget命令下载obsutil工具:wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_arm64.tar.gz解压下载的文件:tar -xzvf obsutil_linux_arm64.tar.gz进入解压后的目录:cd obsutil_linux_arm64_5.7.3/赋予obsutil执行权限:chmod 755 obsutil配置obsutil:./obsutil config -i=ak -k=sk -e=obs.cn-north-9.myhuaweicloud.com下载权重文件:./obsutil cp obs:/bucket-test/Qwen3-32B /home/Qwen3-32B -r -f三、启动部署流程2.1 启动容器在完成环境准备后,就可以启动容器了。启动容器需要使用一系列命令参数,具体如下:docker run -itd \ --device=/dev/davinci0 \ --device=/dev/davinci1 \ --device=/dev/davinci2 \ --device=/dev/davinci3 \ --device=/dev/davinci4 \ --device=/dev/davinci5 \ --device=/dev/davinci6 \ --device=/dev/davinci7 \ -v /etc/localtime:/etc/localtime \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /etc/ascend_install.info:/etc/ascend_install.info \ --device=/dev/davinci_manager \ --device=/dev/devmm_svm \ --device=/dev/hisi_hdc \ -v /var/log/npu/:/usr/slog \ -v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi \ -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ -v ${dir}:${container_work_dir} \ --net=host \ --name ${container_name} \ ${image_id} \ /bin/bash 这里以一个实际示例来说明:docker run -itd --restart=always --device=/dev/davinci0 --device=/dev/davinci1 --device=/dev/davinci2 --device=/dev/davinci3 --device=/dev/davinci4 --device=/dev/davinci5 --device=/dev/davinci6 --device=/dev/davinci7 -v /etc/localtime:/etc/localtime -v /usr/local/Ascend/driver:/usr/local/Ascend/driver -v /etc/ascend\_install.info:/etc/ascend\_install.info --device=/dev/davinci\_manager --device=/dev/devmm\_svm --device=/dev/hisi\_hdc -v /var/log/npu/:/usr/slog -v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /home/Qwen3-32B:/home/Qwen3-32B --net=host --name qwen3-32b 628e52638d5d /bin/bash2.2 进入容器与资源评估容器启动成功后,使用docker exec -it -u ma-user ${container_name} /bin/bash命令进入容器。进入容器后,首先运行npu-smi info命令评估推理资源,检查 NPU 设备信息可用的卡数,同时确认卡是否被占用、端口是否被占用以及是否有对应运行的进程。然后根据实际情况配置需要使用的 NPU 卡,例如,当有 8 张卡时,可以通过export ASCEND_RT_VISIBLE_DEVICES=0,7命令进行配置。2.3 启动大模型最后一步就是启动大模型了,使用以下命令:nohup python -m vllm.entrypoints.openai.api_server --model /home/Qwen3-32B \ --max-num-seqs=256 \ --max-model-len=131072 \ --max-num-batched-tokens=131072 \ --tensor-parallel-size=8 \ --block-size=128 \ --host=0.0.0.0 \ --port=8080 \ --gpu-memory-utilization=0.9 \ --num-scheduler-steps=8 \ --trust-remote-code \ --enforce-eager \ > /home/Qwen3-32B/logs/vllm_api_server.log 2>&1 & 通过以上步骤,我们就完成了华为云 BMS 部署 Qwen3-32b 的全部过程。在实际部署过程中,可能会遇到各种各样的问题,需要大家根据具体情况进行排查和解决。希望本文能够对大家有所帮助,让大家在大模型部署的道路上少走弯路,顺利搭建起自己的人工智能服务环境。
  • gradio缺少frpc_linux_amd64_v0.3文件处理方法
    使用 gradio 框架的时候可能遇到以下错误警告:Could not create share link. Missing file: /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.3.  Please check your internet connection. This can happen if your antivirus software blocks the download of this file. You can install manually by following these steps:  1. Download this file: https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_linux_amd64 2. Rename the downloaded file to: frpc_linux_amd64_v0.3 3. Move the file to this location: /usr/local/lib/python3.10/dist-packages/gradio这个错误信息表明 Gradio 在尝试创建一个共享链接时,缺少了一个必要的文件 frpc_linux_amd64_v0.3。这个文件是 Gradio 用来建立本地服务器和外部网络之间的连接的工具。解决方法根据错误信息,你可以按照以下步骤手动解决这个问题:下载文件:打开浏览器,访问以下链接下载文件: https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_linux_amd64重命名文件:将下载的文件重命名为 frpc_linux_amd64_v0.3。移动文件:将重命名后的文件移动到以下目录:/usr/local/lib/python3.10/dist-packages/gradio设置文件权限(如果需要):你可能需要给这个文件设置可执行权限。可以使用以下命令:chmod +x /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.3重新运行 Gradio:完成以上步骤后,重新运行你的 Gradio 应用程序,看看问题是否解决。其他注意事项网络连接:确保你的网络连接稳定,特别是在下载文件时。权限问题:如果你在移动文件或设置权限时遇到问题,可能需要使用 sudo 命令来提升权限。完整命令如下:# 下载文件 wget https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_linux_amd64 # 检查文件是否下载成功 ls -l frpc_linux_amd64 # 重命名文件 mv frpc_linux_amd64 frpc_linux_amd64_v0.3 # 确保目标目录存在 sudo mkdir -p /usr/local/lib/python3.10/dist-packages/gradio # 复制文件到目标目录 sudo cp frpc_linux_amd64_v0.3 /usr/local/lib/python3.10/dist-packages/gradio # 设置文件权限 sudo chmod +x /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.3验证是否成功运行以下命令检查文件是否已正确放置并具有可执行权限:ls -l /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.3输出应类似于:-rwxr-xr-x 1 root root XXXX Oct 10 12:34 /usr/local/lib/python3.10/dist-packages/gradio/frpc_linux_amd64_v0.3重新启动项目后,就能正常运行了。
  • 胶囊神经网络解析
    胶囊神经网络(Capsule Neural Network,简称 CapsNet)是由深度学习领域的先锋之一 Geoffrey Hinton 等人提出的神经网络结构,旨在改善传统卷积神经网络(CNN)在处理空间层次结构能力上的不足,并提升对图像中物体姿态(pose)、方向、位置变化等感知不变性的建模能力。为什么要提出胶囊网络?传统 CNN 在图像分类等任务中取得了巨大成功,但存在一些局限性:丢失空间信息:经过池化操作(如最大池化)会丢失物体在图像中的空间结构。对小形变不鲁棒:CNN 对图像的旋转、平移、缩放敏感,降维后容易损失重要空间信息。模块性差:由一系列不可解释的神经元组成的黑盒,缺乏结构上的语义模块化。Hinton 提出 胶囊网络 正是为了解决这些限制,特别是对图像中对象的层次结构与姿态感知进行更精确的建模。胶囊网络的基本概念什么是 Capsule?胶囊 是一组神经元的集合。每个胶囊通过其向量输出表示某个特征的存在程度(magnitude) 以及该特征的各种属性(如位置、方向、比例等)。比如,一个胶囊可以识别“眼球”这一特征,并通过输出向量来表示其位置、角度、大小等。胶囊网络的核心创新1. 动态路由(Dynamic Routing)传统神经网络中,信号从一层向下一层做前向传播。胶囊网络采用“动态路由”算法,通过迭代机制选择哪些胶囊应该被上层胶囊激活,从而实现一种“模块化”的信息流。目标:让父胶囊通过子胶囊的信息组合,采用“加权投票”的方式决定上层胶囊的输出。动态路由算法思路如下:初始阶段,设置预测输出向量(子胶囊到父胶囊的预测)。计算“耦合系数”,表示上层胶囊优先选择哪些下层胶囊的信息。使用耦合系数进行加权求和,确定上层胶囊的输出。迭代更新耦合系数,直到收敛或达成稳定。这是一种“自底向上”的注意力机制,类似于注意力机制中的“投票”过程。胶囊网络的结构层次(以 CapsNet 为例)以最经典的图像分类任务中使用的结构为例:卷积层 + 池化层(和 CNN 类似)Primary Capsules(原始胶囊层):输入为卷积层的高维激活图。输出为胶囊输出的向量表示(如 [8 维向量]),每个胶囊对应某种简单位姿的特征(如边缘、角点等)。Digit Capsules(数字胶囊层):上一层原始胶囊输出链接到 Digit Capsules。使用动态路由算法进行信息聚合。每个 Digit Capsule 表示一个类别的胶囊(例如 10 个分别表示 0~9)。Margin Loss 作为目标函数:根据 Digit Capsule 输出向量(长度)来计算样本预测的类别得分。示例:CapsNet 的结构流程以 MNIST 手写数字数据集为例,CapsNet 流程如下:Input Layer(图像 28 x 28)Convolutional Layer(卷积 + ReLU)PrimaryCaps(编码低层特征,比如边缘)输出 32 个胶囊,每个胶囊 8 尺度的向量。DigitCaps(使用动态路由算法,编码高层对象)10 个胶囊,每个对应一个数字类。Length of Capsule Output as Class Probability向量的长度代表该类别出现的可能性。Marginal Loss(损失函数)根据标签,最小化目标类别的向量长度,最大化非目标类别的长度。示例代码(简单 CapsNet 模型)# 初始卷积层 conv1 = Conv2D(...)(input) # PrimaryCaps 层 primary_caps = PrimaryCapsulation(...) # 输出形状如 (None, 32, 6, 6, 8) # DigitCaps 采用 Dynamic Routing digit_caps = DigitCapsulation(...) # 输出 10 个胶囊,每个 16 尺度 # 根据 capsule 的输出长度预测分类 output = Length()(digit_caps) # Loss Function: margin loss loss = MarginLoss()([output, labels]) 总结项目说明胶囊网络一种结构感知性更强的神经网络结构核心思想用向量表示特征,用动态路由传递信息优势更强的姿态建模、空间不变性、类人层次结构感知不足训练复杂、工业落地较少适用领域小样本图像识别、姿态估计、可解释AI等胶囊网络相关论文《Dynamic Routing Between Capsules》作者:Sara Sabour, Nicholas Frosst, Geoffrey Hinton年份:2017提出了经典的 CapsNet 架构论文链接 (arXiv)
  • 元学习详解
    “元学习”(Meta Learning,也称为“学会学习”)是一种机器学习的高级方法,它的核心思想是让模型学会如何学习,而不是仅仅学会某个具体任务。与传统的机器学习方法不同,元学习不仅关注在具体问题上的性能,更关注模型的泛化能力与学习能力,使其能够更高效地学习新任务。元学习的定义元学习(Meta-Learning) 是一种机制或框架,允许模型从多个任务中提取共同的学习策略或结构信息,进而帮助模型在**新任务(未见过的任务)**上快速而有效地进行学习。通俗地说,元学习是“教模型如何学习”,而非“教模型怎么做某个具体任务”。元学习的关键概念概念说明任务(Task)一个具体的训练目标,例如分类一个图像、预测商品销量等。元任务(Meta-Task)在多个任务中学习跨任务的模式,例如图像分类的元任务就是让模型学会识别图像中物体的一般特征。样本任务(Training Tasks)用于训练模型学会如何学习的任务集合,是元学习的训练数据。目标任务(Target Task)学习完元任务后,部署或测试模型的目标任务,即新出现的、数据量少或未知的任务。元学习的应用场景小样本学习(Few-shot Learning):构建一个模型,可以在只有少量样本的情况下,快速掌握新类别。举例:MAML、ProtoNet、Reptile 等。超参数学习(Hyperparameter Optimization):让模型学会找出最优秀的超参数组合,而非手动调参。神经网络架构搜索(Neural Architecture Search, NAS):让模型学会什么样的网络结构最有助于某个任务。强化学习中的策略迁移:在多个环境中训练一个元策略,用于快速适应新环境。典型元学习算法算法说明MAML(Model-Agnostic Meta-Learning)一种模型无关的元学习方法,通过为模型参数寻找一个“好起点”,使其对新任务只需少量学习即可取得良好效果。ProtoNet基于原型网络的元学习,通过学习类别原型来进行小样本分类。Reptile采用参数多任务更新的方式,避免显式优化元目标函数。Meta-SGD元学习优化器,让模型不仅学习参数,还学习学习率等元参数。元学习与传统机器学习的区别项目传统机器学习元学习目标解决单一任务从多个任务中学习一种通用学习方式数据量每个任务需大量数据每个任务可小样本训练泛化性泛化能力依赖任务训练数据通过元学习提升对新任务的泛化能力模型更新每次任务模型重新训练一次训练学习如何有效学习元学习的优势显著提升小样本任务的性能减少训练时间和数据量依赖适用于多任务、多领域学习促进迁移学习与强化学习的结合元学习的挑战需要大量训练任务(元训练集)计算成本高,训练复杂度高难以设计合适的元目标函数泛化能力仍存在不确定性总结元学习是一种“学习如何学习”的方法,通过模型在多个任务中的训练,提取和泛化出一种学习能力。它非常适合那些数据稀缺、任务多样、训练效率要求高的场景。元学习是连接传统机器学习和人类学习能力的桥梁,是AI向更智能方向发展的重要一步。
  • Ollama模型处理性能统计全解析
    当使用 ollama run xxxx --verbose 命令运行模型时,Ollama 会在每次回答完成后输出一组详细的性能统计信息,这些信息可以帮助理解模型在处理当前任务时的计算效率、资源消耗、瓶颈分析等关键性能指标。示例输出:total duration: 53.816488258s load duration: 17.007831ms prompt eval count: 2048 token(s) prompt eval duration: 1.129753292s prompt eval rate: 1812.79 tokens/s eval count: 1480 token(s) eval duration: 52.632442857s eval rate: 28.12 tokens/s 各项指标详解:total duration: 53.816488258s总处理时间从模型初始化、加载到输入处理、响应生成的整个流程所花费的总时间(单位为秒)用于评估整体延迟,适合对比不同输入或模型之间的性能表现load duration: 17.007831ms模型加载时间Ollama 加载模型(包含计算图、权重、初始化)所需的时间(单位为毫秒)如果之前已加载过该模型,该值可能为 0 或极短prompt eval count: 2048 token(s)Prompt 环境的 tokens 数量表示输入提示(Prompt)在模型上下文窗口中的 token 总数token 数量越多,处理时间、内存占用等通常也会增加prompt eval duration: 1.129753292sPrompt 相关计算耗时模型对 prompt 的 token 进行处理和编码所消耗的时间用于解析用户输入的上下文,预计算状态prompt eval rate: 1812.79 tokens/sPrompt 处理速度prompt eval count / prompt eval duration 的结果该值越高,说明模型处理输入文本越快eval count: 1480 token(s)生成内容的 tokens 数量表示模型在此次响应中生成的内容 token 数量(即输出长度)通常对应回答的字数eval duration: 52.632442857s生成内容的计算时间模型生成每个 token 所花费的总时间评估响应生成效率,判断是否被“卡顿”、“延迟”等性能问题影响eval rate: 28.12 tokens/s生成 token 的速度eval count / eval duration 的结果反映模型生成内容时的速度。该值越低,表示 response 生成越慢总结:指标含义相关性total duration整个流程耗时延迟表现load duration模型加载耗时启动性能prompt eval count输入 tokens 数量上下文大小prompt eval duration输入处理耗时输入解析prompt eval rate输入解析速度Input 处理效率eval count输出 tokens 数量回答长度eval duration生成答案耗时响应生成延迟eval rate生成速度实时性 / 响应效率这些参数可以帮助分析:模型在不同输入下的处理能力优化输入提示的长度或结构判断性能瓶颈(如响应生成慢)比较不同模型或不同版本的性能差异
  • llama-cli运行报错,缺少libllama.so解决方案
    llama-cli -h llama-cli: error while loading shared libraries: libllama.so: cannot open shared object file: No such file or directory这个错误信息表示在运行 llama-cli 可执行文件时,缺少动态链接库 libllama.so,也就是说系统找不到这个 .so 文件。解决方案如下:查找并安装 libllama.so确认是否从源码编译了项目?llama.cpp 项目默认编译后的 libllama.so 是动态库文件。成功编译后,libllama.so 应该会在 llama.cpp/build/bin/ 目录中生成。加载动态库的路径:要让系统找到 libllama.so,需要将其路径加入 LD_LIBRARY_PATH,如:export LD_LIBRARY_PATH=/path/to/your/build:$LD_LIBRARY_PATH 运行 llama-cli:llama-cli -h如果安装到了系统路径(如 `/usr/local/lib`),你也可以使用 `sudo make install` 安装到系统目录。如果不确定 libllama.so 位置可以尝试在整个磁盘中搜索:find / -name "libllama.so" 2>/dev/null # 全盘搜索 检查编译后的路径(如 llama.cpp/build/bin/libllama.so )。补充:永久设置 LD_LIBRARY_PATH如果经常使用这个库文件,可以将其加到 shell 配置文件中(如 ~/.bashrc 或 ~/.zshrc):export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH 然后运行:source ~/.bashrc # 或 source ~/.zshrc 总结问题解决方式缺少 libllama.so编译源码或指定路径系统未加载 .so设置 LD_LIBRARY_PATH
  • nvmlDeviceGetNvLinkRemoteDeviceType符号未定义解决方法
    在使用GPU云服务器执行项目代码时会遇到Can't find nvmlDeviceGetNvLinkRemoteDeviceType: /home/opt/gpuproxy/lib64/libnvidia-ml.so.1: undefined symbol: nvmlDeviceGetNvLinkRemoteDeviceType这个错误通常是由于PyTorch在尝试调用NVIDIA管理库(NVML)中的某个函数时,发现该函数不可用或未定义。具体来说,错误信息指出nvmlDeviceGetNvLinkRemoteDeviceType这个符号未定义,系统中安装的NVML库版本可能不支持这个函数,或者库文件本身有问题。可能的原因和解决方法:NVML库版本不匹配:nvmlDeviceGetNvLinkRemoteDeviceType 是NVML库中的一个较新函数,可能在你使用的NVML库版本中不存在。解决方法:确保你安装了NVIDIA驱动版本匹配的NVML库。可以尝试更新NVIDIA驱动和CUDA工具包到最新版本。库文件损坏或不完整:如果libnvidia-ml.so.1文件损坏或不完整,可能会导致某些符号未定义。解决方法:重新安装NVIDIA驱动和CUDA工具包,确保所有库文件完整且未损坏。环境变量问题:如果你有多个版本的NVIDIA驱动或CUDA安装,可能会导致环境变量指向错误的库文件。解决方法:检查你的LD_LIBRARY_PATH环境变量,确保它指向正确的库路径。你可以通过以下命令查看当前的环境变量设置:echo $LD_LIBRARY_PATH 如果路径不正确,可以手动设置:export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH PyTorch与CUDA版本不兼容:你使用的PyTorch版本可能与当前安装的CUDA版本不兼容。解决方法:检查PyTorch和CUDA的版本兼容性,并根据需要升级或降级PyTorch或CUDA。系统更新问题:如果你的系统最近进行了更新,可能会导致某些库文件被覆盖或删除。解决方法:重新安装NVIDIA驱动和CUDA工具包,确保所有依赖项都正确安装。具体步骤:检查NVIDIA驱动和CUDA版本:nvidia-smi nvcc --version更新NVIDIA驱动和CUDA:访问NVIDIA官网下载并安装最新的驱动和CUDA工具包。重新安装PyTorch:使用与CUDA版本匹配的PyTorch安装命令重新安装PyTorch。例如:pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126检查环境变量:确保LD_LIBRARY_PATH指向正确的库路径。重启系统:在完成上述步骤后,重启系统以确保所有更改生效。
  • WARNING: The script xxxxx is installed in '/home/service/.local/bin' which is not on PATH.解决方案
    使用服务器的时候出现下面警告WARNING: The script xxxxx is installed in '/home/service/.local/bin' which is not on PATH.Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.这条警告信息的意思是:你使用 pip 安装了一个名为 xxxxx 的 Python 包,它包含了一些可执行脚本(例如命令行工具),这些脚本被安装到了目录 /home/service/.local/bin 中。但这个目录不在你的系统环境变量 PATH 中,所以你在终端中直接运行这些脚本时,系统可能找不到它们。更详细解释xxxxx 是一个可执行命令:安装后会生成一些可以在终端运行的脚本文件(比如 xxxxx` 命令)。安装路径为 /home/service/.local/bin:这是用户本地安装的默认路径(通常通过 pip install --user 或者当前用户权限安装产生的路径)。未加入 PATH 环境变量:这意味着你不能在任意位置直接输入 xxxxx 来运行它,除非你手动进入该目录或者加上完整路径。解决方法方法一:将该路径添加到 PATH 环境变量中(推荐)你可以将 /home/service/.local/bin 添加到你的 PATH 中,这样就可以在任何地方运行 xxxxx 命令了。操作步骤:打开你的 shell 配置文件,通常是:Bash 用户:~/.bashrcZsh 用户:~/.zshrc在文件末尾添加一行:export PATH="/home/service/.local/bin:$PATH" 保存文件并生效更改:source ~/.bashrc # 或 source ~/.zshrc 验证是否成功:echo $PATH 总结如果你不确定自己用的是哪个 shell,可以用 echo $SHELL 查看。有些 Linux 系统默认不会把 .local/bin 加入 PATH,这就是为什么你会看到这个警告。
  • [案例共创] 【案例共创】基于华为开发者空间使用 FastAPI 构建 MCP 天气查询服务
    基于华为开发者空间使用 FastAPI 构建 MCP 天气查询服务案例介绍本案例将指导您在华为云开发者空间中使用 FastAPI 框架构建一个功能完整的 MCP(Model Context Protocol)天气查询服务。通过本案例,您将学会如何将传统的 REST API 转换为 MCP 工具,让 AI 模型能够实时获取天气信息并与用户进行智能交互。技术栈后端框架: FastAPI协议标准: MCP (Model Context Protocol)天气数据: 和风天气 API认证方式: JWT (JSON Web Token)客户端: Cherry Studio部署环境: 华为云开发者空间学习目标掌握 MCP 协议的基本概念和实现方式实现 AI 模型与外部服务的实时数据交互体验华为云开发者空间的开发便利性以及 Maas DeepSeek 模型服务的无缝集成案例内容1. 概述1.1 案例背景在传统的 AI 对话系统中,模型只能基于训练数据回答问题,无法获取实时信息。MCP 协议的出现解决了这一痛点,它允许 AI 模型通过标准化的接口调用外部工具和服务,获取实时数据。本案例选择天气查询作为示例,因为:实时性强: 天气数据需要实时获取,体现 MCP 的价值应用广泛: 天气查询是用户常见需求技术完整: 涵盖 API 调用、数据处理、错误处理等完整流程易于扩展: 可以基于此案例扩展更多功能1.2 适用对象企业用户:关注业务自动化、提升开发效率、集成AI能力,适合希望快速构建智能工具、提升企业数字化水平的团队。个人开发者:具备一定Python基础(或能借助 AI 辅助编程),关注技术创新与个人成长,适合希望掌握MCP协议、API开发与AI集成的开发者。高校学生:有编程基础(或能借助 AI 辅助编程),关注AI应用实践与创新能力培养,适合希望参与AI+云平台项目实训、提升综合开发能力的学生群体。1.3 案例时间准备阶段: 30 分钟(环境配置、账号注册)开发阶段: 20 分钟(代码编写、调试)测试阶段: 10 分钟(功能验证、MCP 集成)总计时间: 约 60 分钟1.4 案例流程环境准备API 注册项目初始化依赖安装核心代码开发JWT 认证实现天气接口开发MCP 集成本地测试客户端配置端到端验证1.5 资源总览本案例预计花费0元。资源类型名称/说明免费额度/价格备注开发资源华为云开发者空间0元免费注册使用Maas DeepSeek 模型服务0元免费额度,按需扩展和风天气开发者账号0元免费额度,1000次/天Cherry Studio 客户端0元开源免费API 配额和风天气免费版0元1000次/天Maas DeepSeek 模型服务0元免费额度注意事项:本案例基于免费额度,如需更高配额请自行申请。API 可能存在调用次数限制,超出免费额度需付费或申请扩容。2. 环境配置2.1 开发者空间配置步骤 1: 登录华为云领取免费云主机访问 华为云官网,注册并登录华为云账号点击“开发者->开发者空间”,进入。如果没有领取过云主机可以点击"立即体验"进行开通。步骤 2: 配置云主机访问开发者空间工作台, 点击"配置云主机"配置基础资源: CPU架构为 ARM,规格为 4 vCPUs 8 GB,操作系统为 Ubuntu ,镜像为 Ubuntu 24.04 Server定制版等待环境初始化完成后点击"打开云主机-进入桌面",打开云主机桌面2.2 MaaS 领取 DeepSeek步骤 1: 访问华为云 MaaS 服务在华为云控制台搜索"MaaS",点击进入 Maas 控制台区域选择西南-贵阳一,在左侧菜单栏,选择在线推理 > 预置服务 > 免费服务,选择DeepSeek-R1-32K模型,点击领取额度,领取200万免费token。步骤 2: 获取API地址和模型名称领取后点击调用说明*,可以获取到对应的API地址、模型名称。步骤 3: 获取 API 密钥点击“API Key管理-创建API Key“”,自定义标签和描述,点击确定创建API Key。(⚠️注意:API Key仅会在新建后显示一次,请及时复制并妥善保存,若API Key丢失,请新建API Key。)至此,我们获取到了所需的 API 地址、模型名称和 API 密钥。2.3 安装 Cherry Studio下载安装:使用云主机中的Firefox访问 [Cherry Studio 官网] https://cherry-studio.com根据操作系统下载对应版本,如 **Cherry-Studio-1.3.12-arm64.AppImage给下载的文件添加执行权限chmod +x Cherry-Studio-1.3.12-arm64.AppImage安装运行相关依赖sudo apt update sudo apt install zlib1g zlib1g-dev sudo add-apt-repository universe sudo apt install libfuse2运行安装包./Cherry-Studio-1.3.12-arm64.AppImage --no-sandbox基础配置:配置 DeepSeek 模型连接测试模型连接是否正常2.4 和风天气 API 注册(可选)💡 提示: 教程代码中已包含测试用的 API 配置,如无法使用请自行注册注册流程:访问 和风天气开发者平台 https://id.qweather.com/注册开发者账号并完成邮箱验证进入开发服务控制台创建项目并获取必要的认证信息获取配置信息:PROJECT_ID: 项目标识符KEY_ID: JWT 密钥标识PRIVATE_KEY: JWT 签名私钥WEATHER_API_HOST: API 服务地址访问[https://id.qweather.com/] https://id.qweather.com/ 注册账号,初次使用需绑定手机号和邮箱。注册成功后,点击菜单中的 开发服务控制台,进入开发控制台。点击项目管理-创建项目,创建一个项目,如 fastapi, 创建之后此步骤获得PROJECT_ID。点击项目如fastapi,进入项目详情页并点击创建凭据开始创建 JWT 凭据,需先在本地生成公私钥对再创建,创建之后此步骤获得PRIVATE_KEY和KEY_ID。(PS: 虽然 APIKey 更简单,但推荐使用JSON Web Token (JWT)的认证方式获得更高等级的安全性以及不受限的API请求)点击设置, 从开发者信息中获取 API Host,也就是WEATHER_API_HOST至此,我们天气查询 MCP 中需要用到的配置信息已经全部获取。3. 项目搭建3.1 项目初始化使用CodeArts IDE for Python创建项目工程或者使用脚本创建项目目录:mkdir qweather-mcp cd qweather-mcp安装依赖包:pip install fastapi uvicorn fastapi-mcp pyjwt cryptography requests -i https://repo.huaweicloud.com/repository/pypi/simple创建项目结构:qweather-mcp/ ├── main.py # 主程序文件 ├── requirements.txt # 依赖列表 ├── config.py # 配置文件(可选) └── README.md # 项目说明3.2 核心代码实现详细的代码实现包括:FastAPI 应用初始化JWT 认证机制天气 API 封装MCP 协议集成错误处理机制3.3 关键功能模块JWT 认证模块: 实现安全的 API 访问控制天气查询模块: 封装和风天气 API 调用MCP 集成模块: 将 REST API 转换为 MCP 工具数据模型模块: 定义请求和响应的数据结构3.4 代码实现我们创建 main.py 文件,并编写代码。3.4.1 引入依赖from fastapi import FastAPI, HTTPException, Depends, Query, status from fastapi.security import HTTPBearer from pydantic import BaseModel, Field from typing import List, Optional, Dict, Any import jwt import time import requests from datetime import datetime import base64 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.backends import default_backend # 引入 fastapi_mcp from fastapi_mcp import FastApiMCP, AuthConfig3.4.2 配置天气服务 APIapp = FastAPI(title="天气查询API") # 配置项 - 在实际应用中应从环境变量或配置文件中读取 # PRIVATE_KEY = """YOUR_PRIVATE_KEY""" # PROJECT_ID = "YOUR_PROJECT_ID" # KEY_ID = "YOUR_KEY_ID" # WEATHER_API_HOST = "your_api_host" PRIVATE_KEY = "MC4CAQAwBQYDK2VwBCIEIG2wMZga50X1YDHmR8jkE5TGKNXpriFPXXCT/kgwZkcT" PROJECT_ID = "3MKPRE78RN" KEY_ID = "T8B7JMU7AY" WEATHER_API_HOST = "ky6mte27bb.re.qweatherapi.com" 3.4.3 定义响应 Model# 响应模型 class Location(BaseModel): name: str id: str lat: str lon: str adm2: str adm1: str country: str tz: str utcOffset: str isDst: str type: str rank: str fxLink: str class LocationResponse(BaseModel): code: str location: List[Location] class WeatherNow(BaseModel): obsTime: str temp: str feelsLike: str icon: str text: str wind360: str windDir: str windScale: str windSpeed: str humidity: str precip: str pressure: str vis: str cloud: str dew: str class ReferSources(BaseModel): sources: List[str] license: List[str] class WeatherResponse(BaseModel): code: str updateTime: str fxLink: str now: WeatherNow refer: ReferSources class JWTRequest(BaseModel): expiry_seconds: Optional[int] = 900 # 默认15分钟 custom_claims: Optional[dict] = None # 允许添加自定义声明 class WeatherQuery(BaseModel): city: str = Field(..., description="城市名,如:北京") 3.4.4 JWT 令牌生成# 解码并加载私钥 try: _der_key_bytes = base64.b64decode(PRIVATE_KEY) PRIVATE_KEY = serialization.load_der_private_key( _der_key_bytes, password=None, backend=default_backend() ) except Exception as e: print(f"关键错误:无法从配置加载EdDSA私钥。错误: {e}") PRIVATE_KEY = None if PRIVATE_KEY is None: raise RuntimeError(f"关键错误:EdDSA私钥加载失败,应用无法启动。错误: {e}") # 令牌缓存(简单实现,生产环境应使用Redis等缓存系统) token_cache = { "token": None, "expires_at": 0 } # Bearer Token 安全头 token_auth_scheme = HTTPBearer() EXPECTED_BEARER_TOKEN = "huaweicloud" # 定义期望的固定Token # Bearer Token 验证依赖 async def verify_bearer_token(token_payload = Depends(token_auth_scheme)): if token_payload.credentials != EXPECTED_BEARER_TOKEN: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证凭据", headers={"WWW-Authenticate": "Bearer"}, ) return token_payload.credentials # 生成JWT令牌 def generate_jwt(expiry_seconds: int = 900): current_time = int(time.time()) # 构建标准JWT载荷 payload = { 'iat': current_time - 30, # 颁发时间(提前30秒,避免时钟偏差问题) 'exp': current_time + expiry_seconds, # 过期时间 'sub': PROJECT_ID # 主题(项目ID) } # JWT头部 headers = { 'kid': KEY_ID # 密钥ID } if PRIVATE_KEY is None: raise ValueError("JWT生成失败: 私钥未初始化或加载失败。") try: # 生成JWT encoded_jwt = jwt.encode(payload, PRIVATE_KEY, algorithm='EdDSA', headers=headers) # 更新缓存 token_cache["token"] = encoded_jwt token_cache["expires_at"] = current_time + expiry_seconds - 60 # 提前1分钟过期,确保安全 return encoded_jwt except Exception as e: raise ValueError(f"JWT生成失败: {str(e)}") # 获取有效的JWT令牌(如果缓存中有有效令牌则使用缓存,否则生成新令牌) def get_valid_token(): current_time = int(time.time()) # 检查缓存中的令牌是否有效 if token_cache["token"] and token_cache["expires_at"] > current_time: return token_cache["token"] # 生成新令牌 return generate_jwt() @app.post("/generate-jwt", operation_id="generate_jwt", tags=["JWT"]) async def create_jwt( request: JWTRequest = JWTRequest() ): """ 生成JWT令牌 - 使用EdDSA算法签名 - 默认有效期为15分钟 - 可以添加自定义声明 """ try: encoded_jwt = generate_jwt(request.expiry_seconds) current_time = int(time.time()) return { "jwt": encoded_jwt, "expires_at": datetime.fromtimestamp(current_time + request.expiry_seconds).isoformat(), "issued_at": datetime.fromtimestamp(current_time - 30).isoformat(), "valid_for_seconds": request.expiry_seconds } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) 3.4.5 天气查询接口# 发送HTTP请求到天气API,并处理gzip压缩 def fetch_weather_api(endpoint: str, params: Dict[str, Any]): # 获取有效的JWT令牌 token = get_valid_token() headers = { 'Authorization': f'Bearer {token}', 'Accept-Encoding': 'gzip' # 我们请求gzip压缩 } url = f"https://{WEATHER_API_HOST}/{endpoint}" try: response = requests.get(url, headers=headers, params=params) # 检查响应状态 if response.status_code != 200: # 如果是401或403,尝试刷新令牌并重试 if response.status_code in [401, 403]: # 强制生成新令牌 new_token = generate_jwt() # 更新请求头 headers['Authorization'] = f'Bearer {new_token}' # 重试请求 response = requests.get(url, headers=headers, params=params) # 如果还是失败,则抛出异常 if response.status_code != 200: raise HTTPException( status_code=response.status_code, detail=f"天气API请求失败: HTTP {response.status_code}" ) else: raise HTTPException( status_code=response.status_code, detail=f"天气API请求失败: HTTP {response.status_code}" ) # 依赖 requests 库自动处理Gzip解压缩,并直接解析JSON # 旧的Gzip处理逻辑已被移除 return response.json() except requests.exceptions.JSONDecodeError as e: # 如果响应不是有效的JSON(即使在解压缩后),则捕获此特定错误 raise HTTPException(status_code=500, detail=f"天气API响应解析失败: 无效的JSON内容 - {str(e)}") except HTTPException: # 重新抛出已捕获的HTTPException,以便FastAPI处理 raise except Exception as e: # 捕获其他潜在错误 raise HTTPException(status_code=500, detail=f"天气API请求时发生未知错误: {str(e)}") @app.get("/city/lookup", response_model=LocationResponse, operation_id="lookup_city", tags=["天气查询"]) async def lookup_city( location: str = Query(..., description="城市名称,如:北京") ): """ 根据城市名称查询位置ID - 返回城市的详细信息和位置ID - 位置ID用于后续天气查询 """ try: data = fetch_weather_api("geo/v2/city/lookup", {"location": location}) return data except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.get("/weather/now", response_model=WeatherResponse, operation_id="get_weather_now", tags=["天气查询"]) async def get_weather_now( location: str = Query(..., description="位置ID,如:101010100") ): """ 获取指定位置的实时天气 - 需要提供位置ID - 返回当前天气详情 """ try: data = fetch_weather_api("v7/weather/now", {"location": location}) return data except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/weather/by-city", response_model=WeatherResponse, operation_id="get_weather_by_city", tags=["天气查询"]) async def get_weather_by_city( query: WeatherQuery ): """ 一站式查询城市天气 - 只需提供城市名 - 自动查询位置ID并获取天气 """ try: # 先查询城市ID location_data = fetch_weather_api("geo/v2/city/lookup", {"location": query.city}) # 检查是否找到城市 if location_data.get("code") != "200" or not location_data.get("location"): raise HTTPException(status_code=404, detail=f"找不到城市: {query.city}") # 获取第一个匹配城市的ID location_id = location_data["location"][0]["id"] # 查询天气 weather_data = fetch_weather_api("v7/weather/now", {"location": location_id}) return weather_data except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e)) 3.4.6 主程序及 MCP 集成@app.get("/") async def root(): """主页 - 提供API简介""" return { "message": "天气查询API", "endpoints": [ "/generate-jwt - 生成JWT令牌", "/city/lookup - 根据城市名查询位置ID", "/weather/now - 根据位置ID查询当前天气", "/weather/by-city - 一站式查询城市天气" ], "docs": "/docs 查看完整API文档" } # mcp 实现 mcp = FastApiMCP( app, name="My Weather MCP", description="天气查询API", include_operations=["get_weather_by_city"], # 只公开 get_weather_by_city 接口作为 MCP tool auth_config=AuthConfig(dependencies=[Depends(verify_bearer_token)]) # MCP 使用 Bearer Token 验证 ) mcp.mount() # 启动服务器的命令(在命令行中运行): # uvicorn main:app --reload if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8009) 4. 测试验证4.1 本地服务测试启动服务:python main.pyAPI 文档验证:访问 http://localhost:8009/docs测试各个接口功能验证数据格式正确性我们可以使用浏览器访问 http://localhost:8009/docs 来查看并测试API。4.2 MCP 客户端集成Cherry Studio 配置:服务名称: FastAPI 天气查询连接类型: 服务器发送事件(SSE)服务地址: http://localhost:8009/mcp认证头: Authorization=Bearer huaweicloud功能验证:配置 MCP 服务连接验证工具列表显示测试天气查询功能对比启用前后的效果在 Cherry Studio 中配置好的 MCP 服务,我们可以直接在 MCP 客户端中使用 get_weather_by_city 接口:名称:FastAPI 天气查询类型:服务器发送事件(SSE)URL:http://localhost:8009/mcp请求头:Authorization=Bearer zishu.co在工具中,我们可以看到get_weather_by_city接口已经添加到MCP服务中。新建一个话题,先不开启 MCP 服务器设置,直接输入北京天气怎么样,我们会发现大模型无法实时获取天气。接着我们启用 MCP 服务,并输入北京天气怎么样,我们会发现大模型可以实时获取天气。get_weather_by_city tool 被成功调用,并返回了天气信息。5. 总结5.1 技术收获通过本案例,您将掌握:MCP 协议: 理解 AI 模型与外部工具的交互机制FastAPI 开发: 快速构建高性能 Web APIJWT 认证: 实现安全的 API 访问控制第三方 API 集成: 学会封装和调用外部服务错误处理: 构建健壮的服务应用5.2 应用价值实时数据获取: AI 模型可以获取最新的天气信息用户体验提升: 提供准确、及时的天气查询服务技术架构: 为更复杂的 MCP 应用奠定基础可扩展性: 可以轻松添加更多天气相关功能如果想要拓展更多功能,您可以动手试试看!
  • [案例共创] 【案例共创】基于华为开发者空间构建MCP Server与DeepSeek大模型的教育辅助应用
    基于华为开发者空间构建MCP Server与DeepSeek大模型的教育辅助应用前言随着人工智能技术的飞速发展,大语言模型(LLM)在教育领域的应用价值日益凸显。本文将详细介绍如何利用华为开发者空间和DeepSeek大模型,基于MCP(Model Context Protocol)协议构建一个教育辅助工具——“智慧作业批改助手”,旨在帮助教师高效批改学生作业并提供个性化的学习建议。应用场景与方案应用场景"智慧作业批改助手"主要针对中小学教师,解决以下痛点:教师批改作业工作量大,耗时长批改标准难以统一,主观性强难以为每位学生提供个性化的学习建议缺乏对学生作业数据的系统分析能力解决方案利用华为开发者空间提供的云主机和开发环境,部署MCP Server服务,通过华为云MaaS平台提供的DeepSeek-R1大模型的强大理解和生成能力,实现作业智能批改功能。使用的华为云产品及开源框架华为开发者空间:提供云主机和开发环境CodeArts IDE for Cangjie:开发工具华为云MaaS平台:提供DeepSeek-R1模型服务MCP协议框架:实现模型与工具的标准化连接Flask:构建Web应用后端Vue.js:构建前端界面应用功能展示我们的"智慧作业批改助手"具有以下核心功能:作业批改:自动批改学生作业,包括计算题、填空题、简答题和作文等多种题型错误分析:识别学生作业中的错误,并给出详细的错误分析学习建议:根据学生的作业表现,生成个性化的学习建议进度跟踪:记录学生的学习进度和问题点数据分析:统计和分析班级整体的学习情况,帮助教师调整教学策略开发过程详解步骤一:准备开发环境首先,登录华为云官网,领取华为开发者空间资源。访问华为开发者空间领取链接,完成注册和认证后获取云主机和开发资源。图4: 华为开发者空间资源领取页面登录分配的云主机,使用预置的CodeArts IDE for Cangjie开发工具。# 连接云主机 ssh user@your_cloud_host_ip # 检查环境配置 python --version node --version步骤二:领取DeepSeek免费Token登录华为云MaaS控制台进入贵阳一区域的MaaS控制台点击控制台左侧"模型推理"点击右侧的"旧版服务"找到DeepSeek相关模型,点击"领取"领取成功后记录Token信息,后续开发中将使用图5: DeepSeek Token领取流程步骤三:MCP Server搭建MCP (Model Context Protocol)是连接AI模型与各种数据源和工具的标准化协议。在我们的项目中,MCP Server充当中间层,处理前端请求并与DeepSeek模型通信。创建项目目录结构:mkdir -p education_assistant/{app,static,templates} cd education_assistant安装必要的Python依赖:pip install flask requests pandas numpy matplotlib mcp-server deepseek-api创建MCP Server配置文件mcp_config.py:# mcp_config.py MCP_CONFIG = { "server": { "host": "0.0.0.0", "port": 8000 }, "tools": [ { "name": "homework_grading", "description": "Grade student homework and provide feedback", "parameters": { "type": "object", "properties": { "subject": { "type": "string", "description": "Academic subject (math, chinese, english, etc.)" }, "grade_level": { "type": "string", "description": "Student grade level (e.g., Grade 3, Junior 1)" }, "question": { "type": "string", "description": "The homework question" }, "student_answer": { "type": "string", "description": "Student's answer to the question" }, "standard_answer": { "type": "string", "description": "Standard answer for reference" } }, "required": ["subject", "question", "student_answer"] } }, { "name": "learning_recommendation", "description": "Generate personalized learning recommendations based on student performance", "parameters": { "type": "object", "properties": { "student_name": { "type": "string", "description": "Name of the student" }, "subject": { "type": "string", "description": "Academic subject" }, "performance_data": { "type": "string", "description": "JSON string containing performance data" }, "weak_areas": { "type": "array", "items": { "type": "string" }, "description": "List of identified weak areas" } }, "required": ["subject", "performance_data"] } }, { "name": "class_analytics", "description": "Analyze class performance data and generate insights", "parameters": { "type": "object", "properties": { "class_id": { "type": "string", "description": "Class identifier" }, "subject": { "type": "string", "description": "Academic subject" }, "performance_data": { "type": "string", "description": "JSON string containing class performance data" } }, "required": ["class_id", "performance_data"] } } ] } 创建MCP Server启动脚本mcp_server.py:# mcp_server.py import os import json import requests import time # 用于演示 # --- Placeholder for MCP_CONFIG --- MCP_CONFIG = { "server_host": "127.0.0.1", "server_port": 8080, "log_level": "INFO", } # --- Placeholder for MCPServer --- class MCPServer: def __init__(self, config): self.config = config self.tools = {} print(f"MCP 服务器已使用配置初始化: {self.config}") def register_tool(self, tool): if hasattr(tool, 'name') and callable(getattr(tool, 'execute', None)): self.tools[tool.name] = tool print(f"工具 '{tool.name}' 已注册。") else: print(f"注册工具失败:工具缺少 'name' 属性或 'execute' 方法。") def start(self): print("MCP 服务器正在启动...") print(f"已注册的工具: {list(self.tools.keys())}") print( f"MCP 服务器模拟监听于 {self.config.get('server_host', 'localhost')}:{self.config.get('server_port', 8000)}") print("这是一个占位符服务器。实际的流式处理需要一个支持流的网络框架 (如 FastAPI, Flask with SSE, WebSockets)。") print("要停止这个模拟阻塞,请按 Ctrl+C。") try: while True: # 模拟阻塞 time.sleep(1) except KeyboardInterrupt: print("\nMCP 服务器(占位符)已停止。") def _handle_request_stream(self, tool_name, params): """ 模拟处理流式请求。 在真实的服务器中,这将通过网络协议将数据流式传输给客户端。 这里我们仅在控制台打印。 """ if tool_name in self.tools: tool = self.tools[tool_name] # 检查 execute 方法是否为生成器 if not hasattr(tool.execute, '__iter__'): # 简易检查,更准确的是 inspect.isgeneratorfunction print(f"错误: 工具 '{tool_name}' 的 execute 方法不是一个生成器,无法进行流式处理。") return print(f"\n流式执行工具 '{tool_name}',参数: {params}") full_response_chunks = [] try: for chunk in tool.execute(params): # 调用工具的 execute 生成器 if isinstance(chunk, dict) and "error" in chunk: # 处理工具内部错误 print(f"工具错误: {chunk['error']}") break if isinstance(chunk, str): # 假设流式数据是字符串块 print(chunk, end="", flush=True) # 实时打印块 full_response_chunks.append(chunk) else: # 如果块不是字符串,可以根据需要处理或打印 print(f"\n[接收到非字符串块: {type(chunk)} - {chunk}]", end="", flush=True) print("\n--- 流式响应结束 ---") # 尝试将收集到的块组合并解析为JSON(如果原始意图是JSON) # 这部分取决于工具的流式输出是否最终能构成一个有效的JSON full_response_str = "".join(full_response_chunks) try: final_json = json.loads(full_response_str) print("\n最终组合并解析的 JSON:") print(json.dumps(final_json, ensure_ascii=False, indent=2)) return {"status": "success_stream_completed", "data": final_json, "raw_stream": full_response_str} except json.JSONDecodeError: print("\n警告: 收集到的流式响应无法解析为JSON。") print(f"原始流式输出: {full_response_str[:500]}...") # 打印部分原始输出 return {"status": "success_stream_completed_non_json", "raw_stream": full_response_str} except Exception as e: print(f"\n执行工具 '{tool_name}' 流式传输时出错: {e}") return {"status": "error_streaming", "message": str(e)} else: print(f"未找到工具: '{tool_name}'") return {"status": "error_tool_not_found", "message": f"Tool '{tool_name}' not found."} # DeepSeek API配置 DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY", "yourApiKey") DEEPSEEK_API_URL = "https://maas-cn-southwest-2.modelarts-maas.com/v1/infers/8a062fd4-7367-4ab4-a936-5eeb8fb821c4/v1/chat/completions" class BaseTool: def _call_deepseek_api_stream(self, prompt): """ 调用DeepSeek API并流式返回内容块。 这是一个生成器。 """ headers = { "Content-Type": "application/json", "Authorization": f"Bearer {DEEPSEEK_API_KEY}" } data = { "model": "DeepSeek-R1", "messages": [{"role": "user", "content": prompt}], "temperature": 0.3, "max_tokens": 1000, # 对于流式,max_tokens 仍然很重要 "stream": True # <--- 改为 True } api_response = None try: # 使用 stream=True 进行请求 api_response = requests.post(DEEPSEEK_API_URL, headers=headers, json=data, stream=True, verify=False) api_response.raise_for_status() for line in api_response.iter_lines(): if line: decoded_line = line.decode('utf-8') if decoded_line.startswith("data: "): json_str = decoded_line[len("data: "):] if json_str.strip() == "[DONE]": # print("\n[API Stream DONE signal received]") # 调试信息 break try: chunk_json = json.loads(json_str) # 提取内容,根据实际API响应结构调整 # 对于 OpenAI 兼容的 Chat Completions API 流: # chunk_json["choices"][0]["delta"]["content"] delta = chunk_json.get("choices", [{}])[0].get("delta", {}) content_chunk = delta.get("content") if content_chunk: yield content_chunk # 如果需要处理 role 或其他 delta 信息 # role_chunk = delta.get("role") # if role_chunk: # yield f"[ROLE: {role_chunk}]" # 示例 except json.JSONDecodeError: print(f"\n[无法解析流中的JSON块: {json_str}]") continue # 跳过无法解析的块 except KeyError: # 有时API流可能包含没有'content'的delta(例如,只有role的第一个delta) # print(f"\n[流块中缺少预期的键: {json_str}]") continue # print("\n[API _call_deepseek_api_stream finished yielding]") # 调试信息 except requests.exceptions.HTTPError as e: error_message = f"DeepSeek API HTTP 错误: {e}" if api_response is not None: error_message += f"\n响应状态码: {api_response.status_code}\n响应内容: {api_response.text}" print(error_message) yield {"error": "api_http_error", "details": error_message} # 将错误作为流的一部分传递 except requests.exceptions.RequestException as e: error_detail = f"DeepSeek API 请求错误: {e}" print(error_detail) yield {"error": "api_request_error", "details": error_detail} except Exception as e: # 其他意外错误 error_detail = f"处理API流时发生意外错误: {e}" print(error_detail) yield {"error": "api_stream_processing_error", "details": error_detail} class HomeworkGradingTool(BaseTool): def __init__(self): self.name = "homework_grading" def execute(self, params): # 现在这是一个生成器 subject = params.get("subject") question = params.get("question") student_answer = params.get("student_answer") standard_answer = params.get("standard_answer", "") grade_level = params.get("grade_level", "") prompt = f""" 作为一名专业教师,请批改以下{subject}作业: 年级: {grade_level} 问题: {question} 学生答案: {student_answer} 标准答案: {standard_answer} 请提供以下内容: 1. 评分(满分100分) 2. 错误分析(如有) 3. 改进建议 4. 针对该问题的知识点解析 请严格按照以下JSON格式返回,不要包含JSON块标记(```json ... ```): {{ "score": 分数, "error_analysis": "错误分析", "suggestions": "改进建议", "explanation": "知识点解析" }} """ # _call_deepseek_api_stream 是一个生成器,所以我们需要迭代它 # 并且 execute 也将是一个生成器 # print(f"\n[HomeworkGradingTool.execute] 调用 _call_deepseek_api_stream with prompt:\n{prompt[:100]}...") # 调试信息 for chunk in self._call_deepseek_api_stream(prompt): yield chunk # 将从API获得的每个块直接yield出去 class LearningRecommendationTool(BaseTool): def __init__(self): self.name = "learning_recommendation" def execute(self, params): # 生成器 student_name = params.get("student_name", "学生") subject = params.get("subject") performance_data = params.get("performance_data") weak_areas = params.get("weak_areas", []) prompt = f""" 作为一名教育顾问,请为{student_name}提供针对{subject}科目的个性化学习建议。 学生表现数据: {performance_data} 弱点领域: {', '.join(weak_areas) if weak_areas else '未指定'} 请提供以下内容: 1. 建议的学习重点 2. 推荐的学习资源 3. 学习计划安排 4. 提升方法建议 请严格按照以下JSON格式返回,不要包含JSON块标记(```json ... ```): {{ "focus_areas": ["重点领域1", "重点领域2", ...], "resources": ["资源1", "资源2", ...], "schedule": "学习计划安排", "methods": ["方法1", "方法2", ...] }} """ for chunk in self._call_deepseek_api_stream(prompt): yield chunk class ClassAnalyticsTool(BaseTool): def __init__(self): self.name = "class_analytics" def execute(self, params): # 生成器 class_id = params.get("class_id") subject = params.get("subject") performance_data = params.get("performance_data") prompt = f""" 作为一名教育数据分析专家,请分析以下{subject}科目的班级({class_id})表现数据。 性能数据: {performance_data} 请提供以下分析: 1. 班级整体表现概述 2. 主要问题点和常见错误 3. 班级表现的强项和弱项 4. 针对教师的教学建议 请严格按照以下JSON格式返回,不要包含JSON块标记(```json ... ```): {{ "overview": "整体表现概述", "common_issues": ["问题1", "问题2", ...], "strengths": ["强项1", "强项2", ...], "weaknesses": ["弱项1", "弱项2", ...], "teaching_suggestions": ["建议1", "建议2", ...] }} """ for chunk in self._call_deepseek_api_stream(prompt): yield chunk def main(): # 创建工具实例 homework_grading_tool = HomeworkGradingTool() learning_recommendation_tool = LearningRecommendationTool() class_analytics_tool = ClassAnalyticsTool() # 创建MCP服务器并注册工具 server = MCPServer(MCP_CONFIG) server.register_tool(homework_grading_tool) server.register_tool(learning_recommendation_tool) server.register_tool(class_analytics_tool) # --- 测试流式工具调用 --- print("\n--- 开始流式工具测试 ---") if DEEPSEEK_API_KEY == "yourApiKey": print("警告: DEEPSEEK_API_KEY 未配置。API 调用将使用占位符 Token,很可能会失败。") print("请设置 DEEPSEEK_API_KEY 环境变量或在代码中直接修改其值以进行有效测试。") # 测试作业批改工具 (流式) test_grading_params = { "subject": "语文", "question": "请解释成语“画蛇添足”的含义,并造一个句子。", "student_answer": "画蛇添足就是画蛇的时候多画了脚,意思是多此一举。他这种做法真是画蛇添足。", "standard_answer": "画蛇添足比喻做了多余的事,非但无益,反而不合适。也比喻虚构事实,无中生有。例句:这篇文章的结尾画蛇添足,反而削弱了主题。", "grade_level": "五年级" } # 使用服务器的模拟处理方法 server._handle_request_stream(homework_grading_tool.name, test_grading_params) # 你可以取消注释以下代码来测试其他工具 # test_recommendation_params = { # "student_name": "小红", # "subject": "历史", # "performance_data": "对朝代顺序和重要事件记忆模糊", # "weak_areas": ["宋朝经济文化", "明清政治制度"] # } # server._handle_request_stream(learning_recommendation_tool.name, test_recommendation_params) print("\n--- 流式工具测试结束 ---") # 启动模拟阻塞的服务器。实际应用中,这会是一个真正的网络服务器。 # 如果你只想运行上面的测试而不启动阻塞服务器,可以注释掉 server.start() print("\n现在将启动模拟的阻塞服务器。按 Ctrl+C 停止。") server.start() if __name__ == "__main__": main() 设置DeepSeek API密钥环境变量:export DEEPSEEK_API_KEY="your_deepseek_token_here" 步骤四:构建Web应用后端创建Flask应用作为我们的Web后端,与MCP Server通信:创建app.py:# app.py from flask import Flask, request, jsonify, render_template import requests import json import os import pandas as pd import matplotlib.pyplot as plt import io import base64 app = Flask(__name__) # MCP Server配置 MCP_SERVER_URL = "http://localhost:8000" @app.route('/') def index(): return render_template('index.html') @app.route('/api/grade', methods=['POST']) def grade_homework(): data = request.json # 构造MCP请求 mcp_request = { "tool": "homework_grading", "params": { "subject": data.get("subject"), "grade_level": data.get("grade_level"), "question": data.get("question"), "student_answer": data.get("student_answer"), "standard_answer": data.get("standard_answer", "") } } # 调用MCP Server response = requests.post(f"{MCP_SERVER_URL}/execute", json=mcp_request) if response.status_code == 200: return jsonify(response.json()) else: return jsonify({"error": "Failed to process request"}), 500 @app.route('/api/recommend', methods=['POST']) def get_recommendations(): data = request.json # 构造MCP请求 mcp_request = { "tool": "learning_recommendation", "params": { "student_name": data.get("student_name", ""), "subject": data.get("subject"), "performance_data": json.dumps(data.get("performance_data")), "weak_areas": data.get("weak_areas", []) } } # 调用MCP Server response = requests.post(f"{MCP_SERVER_URL}/execute", json=mcp_request) if response.status_code == 200: return jsonify(response.json()) else: return jsonify({"error": "Failed to process request"}), 500 @app.route('/api/class-analytics', methods=['POST']) def analyze_class(): data = request.json # 构造MCP请求 mcp_request = { "tool": "class_analytics", "params": { "class_id": data.get("class_id"), "subject": data.get("subject"), "performance_data": json.dumps(data.get("performance_data")) } } # 调用MCP Server response = requests.post(f"{MCP_SERVER_URL}/execute", json=mcp_request) if response.status_code == 200: result = response.json() # 生成可视化图表 if "performance_data" in data: performance_data = data.get("performance_data") if isinstance(performance_data, list) and len(performance_data) > 0: # 创建DataFrame df = pd.DataFrame(performance_data) # 生成成绩分布图 plt.figure(figsize=(10, 6)) plt.hist(df.get('score', []), bins=10, alpha=0.7, color='blue') plt.title('班级成绩分布') plt.xlabel('分数') plt.ylabel('学生数量') plt.grid(True, alpha=0.3) # 转换为base64图像 buffer = io.BytesIO() plt.savefig(buffer, format='png') buffer.seek(0) img_str = base64.b64encode(buffer.read()).decode('utf-8') plt.close() result['score_distribution_chart'] = img_str return jsonify(result) else: return jsonify({"error": "Failed to process request"}), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) 步骤五:构建前端界面使用Vue.js构建前端界面:创建templates/index.html:<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>智慧作业批改助手</title> <link href="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.10/theme-chalk/index.css" rel="stylesheet"> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.10/index.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.min.js"></script> <style> body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; background-color: #f5f7fa; margin: 0; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding: 20px; } .header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #eaeaea; } .header h1 { color: #409EFF; } .tab-content { padding: 20px 0; } .result-card { margin-top: 20px; border: 1px solid #ebeef5; border-radius: 4px; padding: 20px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .score { font-size: 24px; font-weight: bold; color: #409EFF; } .chart-container { margin-top: 20px; text-align: center; } </style> </head> <body> <div id="app"> <div class="container"> <div class="header"> <h1>智慧作业批改助手</h1> <p>基于华为云开发者空间与DeepSeek大模型构建的教育辅助工具</p> </div> <el-tabs v-model="activeTab"> <!-- 作业批改标签页 --> <el-tab-pane label="作业批改" name="grading"> <div class="tab-content"> <el-form :model="gradingForm" label-width="100px"> <el-form-item label="学科"> <el-select v-model="gradingForm.subject" placeholder="选择学科"> <el-option label="数学" value="数学"></el-option> <el-option label="语文" value="语文"></el-option> <el-option label="英语" value="英语"></el-option> <el-option label="物理" value="物理"></el-option> <el-option label="化学" value="化学"></el-option> </el-select> </el-form-item> <el-form-item label="年级"> <el-select v-model="gradingForm.grade_level" placeholder="选择年级"> <el-option label="小学一年级" value="小学一年级"></el-option> <el-option label="小学二年级" value="小学二年级"></el-option> <el-option label="小学三年级" value="小学三年级"></el-option> <el-option label="小学四年级" value="小学四年级"></el-option> <el-option label="小学五年级" value="小学五年级"></el-option> <el-option label="小学六年级" value="小学六年级"></el-option> <el-option label="初中一年级" value="初中一年级"></el-option> <el-option label="初中二年级" value="初中二年级"></el-option> <el-option label="初中三年级" value="初中三年级"></el-option> <el-option label="高中一年级" value="高中一年级"></el-option> <el-option label="高中二年级" value="高中二年级"></el-option> <el-option label="高中三年级" value="高中三年级"></el-option> </el-select> </el-form-item> <el-form-item label="题目"> <el-input type="textarea" v-model="gradingForm.question" rows="3" placeholder="请输入题目内容"></el-input> </el-form-item> <el-form-item label="标准答案"> <el-input type="textarea" v-model="gradingForm.standard_answer" rows="3" placeholder="请输入标准答案(可选)"></el-input> </el-form-item> <el-form-item label="学生答案"> <el-input type="textarea" v-model="gradingForm.student_answer" rows="5" placeholder="请输入学生答案"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="gradeHomework" :loading="loading.grading">批改作业</el-button> </el-form-item> </el-form> <!-- 批改结果展示 --> <div v-if="gradingResult" class="result-card"> <h3>批改结果</h3> <div> <span>得分:</span> <span class="score">{{ gradingResult.score }}</span> </div> <el-divider></el-divider> <div> <h4>错误分析</h4> <p>{{ gradingResult.error_analysis }}</p> </div> <div> <h4>改进建议</h4> <p>{{ gradingResult.suggestions }}</p> </div> <div> <h4>知识点解析</h4> <p>{{ gradingResult.explanation }}</p> </div> </div> </div> </el-tab-pane> <!-- 学习建议标签页 --> <el-tab-pane label="学习建议" name="recommendation"> <div class="tab-content"> <el-form :model="recommendationForm" label-width="100px"> <el-form-item label="学生姓名"> <el-input v-model="recommendationForm.student_name" placeholder="请输入学生姓名"></el-input> </el-form-item> <el-form-item label="学科"> <el-select v-model="recommendationForm.subject" placeholder="选择学科"> <el-option label="数学" value="数学"></el-option> <el-option label="语文" value="语文"></el-option> <el-option label="英语" value="英语"></el-option> <el-option label="物理" value="物理"></el-option> <el-option label="化学" value="化学"></el-option> </el-select> </el-form-item> <el-form-item label="表现数据"> <el-input type="textarea" v-model="recommendationForm.performance_data_raw" rows="5" placeholder="请输入学生表现数据,如:最近5次考试成绩为85,92,78,88,90"></el-input> </el-form-item> <el-form-item label="弱点领域"> <el-tag :key="index" v-for="(tag, index) in recommendationForm.weak_areas" closable :disable-transitions="false" @close="handleWeakAreaClose(tag)"> {{tag}} </el-tag> <el-input class="input-new-tag" v-if="inputWeakAreaVisible" v-model="inputWeakArea" ref="saveWeakAreaInput" size="small" @keyup.enter.native="handleWeakAreaInputConfirm" @blur="handleWeakAreaInputConfirm"> </el-input> <el-button v-else class="button-new-tag" size="small" @click="showWeakAreaInput">+ 添加弱点领域</el-button> </el-form-item> <el-form-item> <el-button type="primary" @click="getRecommendations" :loading="loading.recommendation">生成学习建议</el-button> </el-form-item> </el-form> <!-- 学习建议结果展示 --> <div v-if="recommendationResult" class="result-card"> <h3>学习建议</h3> <div> <h4>学习重点</h4> <el-tag v-for="(item, index) in recommendationResult.focus_areas" :key="index" type="success" style="margin-right: 5px;">{{ item }}</el-tag> </div> <div> <h4>推荐资源</h4> <ul> <li v-for="(resource, index) in recommendationResult.resources" :key="index">{{ resource }}</li> </ul> </div> <div> <h4>学习计划安排</h4> <p>{{ recommendationResult.schedule }}</p> </div> <div> <h4>提升方法</h4> <el-tag v-for="(method, index) in recommendationResult.methods" :key="index" type="info" style="margin-right: 5px; margin-bottom: 5px;">{{ method }}</el-tag> </div> </div> </div> </el-tab-pane> <!-- 班级分析标签页 --> <el-tab-pane label="班级分析" name="analytics"> <div class="tab-content"> <el-form :model="analyticsForm" label-width="100px"> <el-form-item label="班级ID"> <el-input v-model="analyticsForm.class_id" placeholder="请输入班级ID"></el-input> </el-form-item> <el-form-item label="学科"> <el-select v-model="analyticsForm.subject" placeholder="选择学科"> <el-option label="数学" value="数学"></el-option> <el-option label="语文" value="语文"></el-option> <el-option label="英语" value="英语"></el-option> <el-option label="物理" value="物理"></el-option> <el-option label="化学" value="化学"></el-option> </el-select> </el-form-item> <el-form-item label="班级数据"> <el-input type="textarea" v-model="analyticsForm.performance_data_raw" rows="10" placeholder="请输入班级表现数据,格式为JSON数组"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="analyzeClass" :loading="loading.analytics">分析班级数据</el-button> <el-button @click="generateSampleData">生成示例数据</el-button> </el-form-item> </el-form> <!-- 班级分析结果展示 --> <div v-if="analyticsResult" class="result-card"> <h3>班级分析结果</h3> <div> <h4>整体表现概述</h4> <p>{{ analyticsResult.overview }}</p> </div> <div class="chart-container" v-if="analyticsResult.score_distribution_chart"> <h4>成绩分布图</h4> <img :src="'data:image/png;base64,' + analyticsResult.score_distribution_chart" alt="成绩分布图"> </div> <div> <h4>常见问题</h4> <el-collapse> <el-collapse-item v-for="(issue, index) in analyticsResult.common_issues" :key="index" :title="'问题 ' + (index + 1)"> <div>{{ issue }}</div> </el-collapse-item> </el-collapse> </div> <div> <h4>班级强项</h4> <el-tag v-for="(strength, index) in analyticsResult.strengths" :key="index" type="success" style="margin-right: 5px; margin-bottom: 5px;">{{ strength }}</el-tag> </div> <div> <h4>班级弱项</h4> <el-tag v-for="(weakness, index) in analyticsResult.weaknesses" :key="index" type="danger" style="margin-right: 5px; margin-bottom: 5px;">{{ weakness }}</el-tag> </div> <div> <h4>教学建议</h4> <el-steps direction="vertical"> <el-step v-for="(suggestion, index) in analyticsResult.teaching_suggestions" :key="index" :title="'建议 ' + (index + 1)" :description="suggestion"></el-step> </el-steps> </div> </div> </div> </el-tab-pane> </el-tabs> </div> </div> <script> new Vue({ el: '#app', data() { return { activeTab: 'grading', loading: { grading: false, recommendation: false, analytics: false }, gradingForm: { subject: '', grade_level: '', question: '', student_answer: '', standard_answer: '' }, gradingResult: null, recommendationForm: { student_name: '', subject: '', performance_data_raw: '', weak_areas: [] }, inputWeakAreaVisible: false, inputWeakArea: '', recommendationResult: null, analyticsForm: { class_id: '', subject: '', performance_data_raw: '' }, analyticsResult: null } }, methods: { gradeHomework() { if (!this.gradingForm.subject || !this.gradingForm.question || !this.gradingForm.student_answer) { this.$message.error('请填写必要的信息!'); return; } this.loading.grading = true; axios.post('/api/grade', this.gradingForm) .then(response => { this.gradingResult = response.data; this.loading.grading = false; }) .catch(error => { console.error('Error grading homework:', error); this.$message.error('批改作业失败,请稍后重试'); this.loading.grading = false; }); }, getRecommendations() { if (!this.recommendationForm.subject || !this.recommendationForm.performance_data_raw) { this.$message.error('请填写必要的信息!'); return; } try { // 转换表现数据为结构化数据 let performanceData; if (this.recommendationForm.performance_data_raw.includes('[') || this.recommendationForm.performance_data_raw.includes('{')) { // 尝试解析为JSON performanceData = JSON.parse(this.recommendationForm.performance_data_raw); } else { // 解析为逗号分隔的分数列表 const scores = this.recommendationForm.performance_data_raw.split(',').map(s => parseInt(s.trim())); performanceData = { scores: scores, average: scores.reduce((a, b) => a + b, 0) / scores.length }; } const requestData = { student_name: this.recommendationForm.student_name, subject: this.recommendationForm.subject, performance_data: performanceData, weak_areas: this.recommendationForm.weak_areas }; this.loading.recommendation = true; axios.post('/api/recommend', requestData) .then(response => { this.recommendationResult = response.data; this.loading.recommendation = false; }) .catch(error => { console.error('Error getting recommendations:', error); this.$message.error('获取学习建议失败,请稍后重试'); this.loading.recommendation = false; }); } catch (e) { this.$message.error('表现数据格式错误,请检查后重试'); console.error('Data parsing error:', e); } }, analyzeClass() { if (!this.analyticsForm.class_id || !this.analyticsForm.subject || !this.analyticsForm.performance_data_raw) { this.$message.error('请填写必要的信息!'); return; } try { // 解析班级表现数据 const performanceData = JSON.parse(this.analyticsForm.performance_data_raw); const requestData = { class_id: this.analyticsForm.class_id, subject: this.analyticsForm.subject, performance_data: performanceData }; this.loading.analytics = true; axios.post('/api/class-analytics', requestData) .then(response => { this.analyticsResult = response.data; this.loading.analytics = false; }) .catch(error => { console.error('Error analyzing class data:', error); this.$message.error('分析班级数据失败,请稍后重试'); this.loading.analytics = false; }); } catch (e) { this.$message.error('班级数据格式错误,请检查后重试'); console.error('Data parsing error:', e); } }, handleWeakAreaClose(tag) { this.recommendationForm.weak_areas.splice(this.recommendationForm.weak_areas.indexOf(tag), 1); }, showWeakAreaInput() { this.inputWeakAreaVisible = true; this.$nextTick(_ => { this.$refs.saveWeakAreaInput.$refs.input.focus(); }); }, handleWeakAreaInputConfirm() { let inputValue = this.inputWeakArea; if (inputValue) { this.recommendationForm.weak_areas.push(inputValue); } this.inputWeakAreaVisible = false; this.inputWeakArea = ''; }, generateSampleData() { // 生成示例班级数据 const students = 30; const sampleData = []; for (let i = 1; i <= students; i++) { const score = Math.floor(Math.random() * 40) + 60; // 60-100的随机分数 sampleData.push({ student_id: `S${i.toString().padStart(3, '0')}`, name: `学生${i}`, score: score, completed_time: Math.floor(Math.random() * 40) + 20, // 20-60分钟的完成时间 error_count: Math.floor((100 - score) / 10) // 错误数量与分数相关 }); } this.analyticsForm.performance_data_raw = JSON.stringify(sampleData, null, 2); } } }); </script> </body> </html> 步骤六:项目启动与配置创建启动脚本start_services.sh:#!/bin/bash # 设置环境变量 export DEEPSEEK_API_KEY="your_deepseek_token_here" # 启动MCP Server echo "Starting MCP Server..." python mcp_server.py & MCP_PID=$! # 等待MCP Server启动 sleep 5 # 启动Flask应用 echo "Starting Flask application..." python app.py & FLASK_PID=$! # 注册退出处理函数 function cleanup { echo "Shutting down services..." kill $FLASK_PID kill $MCP_PID } # 捕获SIGINT信号(Ctrl+C) trap cleanup SIGINT echo "Services started successfully!" echo "MCP Server running with PID: $MCP_PID" echo "Flask App running with PID: $FLASK_PID" echo "Access the application at http://localhost:5000" echo "Press Ctrl+C to stop all services." # 等待用户中断 wait设置脚本执行权限:chmod +x start_services.sh步骤七:应用部署与测试在华为开发者空间云主机上启动服务:./start_services.sh使用浏览器访问应用(通过云主机的公网IP):http://<your_cloud_host_ip>:5000进行功能测试:在"作业批改"标签页中,输入学科、年级、题目及学生答案,测试批改功能在"学习建议"标签页中,输入学生信息和表现数据,测试个性化建议生成功能在"班级分析"标签页中,使用自动生成的示例数据,测试班级分析功能开发过程中的关键点与技术细节1. MCP协议的应用MCP (Model Context Protocol)是一个标准化协议,它允许应用程序以统一的方式向大语言模型提供上下文和工具。在本项目中,我们通过MCP协议实现了三个核心工具:homework_grading: 用于批改作业和提供反馈learning_recommendation: 用于生成个性化学习建议class_analytics: 用于分析班级整体表现这些工具通过标准化的接口与DeepSeek大模型进行交互,使得应用架构更加清晰和可扩展。2. DeepSeek大模型的调用我们通过华为云MaaS平台提供的API接口调用DeepSeek-R1模型。关键代码示例:def _call_deepseek_api(self, prompt): headers = { "Content-Type": "application/json", "X-Auth-Token": DEEPSEEK_API_KEY } data = { "model": "deepseek-r1-mainstream", "messages": [{"role": "user", "content": prompt}], "temperature": 0.3, "max_tokens": 1000 } response = requests.post(DEEPSEEK_API_URL, headers=headers, json=data) return response.json().get("choices", [{}])[0].get("message", {}).get("content", "") 这段代码封装了对DeepSeek模型的调用,通过设置适当的temperature参数(0.3)使模型输出更加稳定和准确,特别适合教育场景下的精确评估需求。3. 数据可视化实现为了更直观地展示班级分析结果,我们使用了Python的matplotlib库生成图表,并通过base64编码将图表嵌入到前端页面中:# 生成成绩分布图 plt.figure(figsize=(10, 6)) plt.hist(df.get('score', []), bins=10, alpha=0.7, color='blue') plt.title('班级成绩分布') plt.xlabel('分数') plt.ylabel('学生数量') plt.grid(True, alpha=0.3) # 转换为base64图像 buffer = io.BytesIO() plt.savefig(buffer, format='png') buffer.seek(0) img_str = base64.b64encode(buffer.read()).decode('utf-8') 这种方法避免了保存和管理图片文件的复杂性,直接将图表数据嵌入到API响应中。4. 前端与后端交互前端使用Vue.js和Element UI组件库构建用户界面,通过axios进行API调用:axios.post('/api/grade', this.gradingForm) .then(response => { this.gradingResult = response.data; this.loading.grading = false; }) .catch(error => { console.error('Error grading homework:', error); this.$message.error('批改作业失败,请稍后重试'); this.loading.grading = false; }); 这种前后端分离的架构使得应用更加灵活,易于维护和扩展。实际应用与拓展方向实际应用场景"智慧作业批改助手"可以在以下场景中发挥作用:中小学教师日常教学:帮助教师减轻批改作业的负担,提高批改效率和质量在线教育平台:集成到现有的在线教育平台,为学生提供即时反馈家庭教育:帮助家长评估孩子的学习情况,提供有针对性的辅导教育研究:为教育研究者提供数据分析工具,挖掘教育规律拓展方向项目还可以向以下方向拓展:多模态支持:添加对图片、音频等多模态内容的处理能力,如识别手写作业、口语评测等知识图谱集成:构建学科知识图谱,提供更精准的知识点分析和学习路径推荐个性化学习计划:基于学生历史表现数据,自动生成个性化学习计划和练习题教师协作功能:添加教师之间的协作功能,共享教学资源和经验家校互动:增加家长端功能,促进家校互动和信息共享总结通过华为开发者空间和DeepSeek大模型,我们成功构建了一个基于MCP协议的教育辅助应用"智慧作业批改助手"。该应用充分发挥了大语言模型在教育领域的应用价值,为教师提供了高效的作业批改工具和数据分析能力。本项目不仅展示了华为云生态资源的强大能力,也为开发者提供了一个完整的AI应用构建实例。通过MCP协议,我们实现了模型与工具的标准化连接,使应用架构更加清晰和可扩展。随着AI技术的不断发展和教育需求的不断变化,我们相信这类智能教育辅助工具将有更广阔的应用空间和更大的发展潜力。参考资料华为开发者空间官方文档[MCP协议文档][DeepSeek模型API文档][Flask官方文档][Vue.js官方文档][Element UI组件库文档]我正在参加【案例共创】第4期 基于华为开发者空间+仓颉/DeepSeek/MCP完成应用构建开发实践 https://bbs.huaweicloud.com/forum/thread-02127182415062274055-1-1.html
  • [技术干货] 基于开发者空间,部署Cherry Studio+高德地图MCP Server构建出行规划助手
    一、概述1. 案例介绍Cherry Studio是一款多模型对话、知识库管理、AI绘画、翻译等功能于一体的全能AI助手平台。Cherry Studio高度自定义的设计、强大的扩展能力和友好的用户体验,使其成为专业用户和AI爱好者的理想选择。无论是零基础用户还是开发者,都能在Cherry Studio中找到合适自己的AI功能,提升工作效率和创造力。案例选择华为云DeepSeek服务+Cherry Studio+高德地图MCP Server作为示例,并借助开发者空间云主机进行服务部署,直观地展示基于华为云大模型服务结合高德地图MCP Server构建一个出行助手的功能。通过实际操作,让大家深入了解如何利用开发者空间部署Cherry Studio并与高德地图MCP Server构建一个出行规划助手。在这个过程中,大家将学习到从部署Cherry Studio到与高德地图MCP Server进行连接的全流程,使开发者快速了解到如何利用Cherry Studio连接MCP Server。2. 适用对象企业、个人开发者、高校学生3. 案例时间本案例总时长预计30分钟。4. 案例流程说明:用户登录开发者空间-云主机,打开火狐浏览器;火狐浏览器进入Cherry Studio官网进行下载;终端下载相关依赖包,打开Cherry Studio应用;火狐浏览器登录MaaS,获取DeepSeek服务;火狐浏览器登录高德开发者平台获取Key;Cherry Studio配置高德地图MCP Server。5. 资源总览本案例预计花费0元。资源名称规格单价(元)时长(分钟)华为开发者空间-云主机4vCPUs | 8GB | ARM | Ubuntu030二、环境配置1. 开发者空间配置面向广大开发者群体,华为开发者空间提供一个随时访问的“开发桌面云主机”、丰富的“预配置工具集合”和灵活使用的“场景化资源池”,开发者开箱即用,快速体验华为根技术和资源。如果还没有领取开发者空间云主机,可以参考免费领取云主机文档领取。领取云主机后可以直接进入华为开发者空间工作台界面,点击打开云主机 > 进入桌面连接云主机。2. 部署Cherry Studio1. 打开云主机桌面火狐浏览器。2. 在浏览器中输入Cherry Studio网址:https://www.cherry-ai.com/进入Cherry Studio首页。3. 点击下载客户端,进入下载页面。4. 进入到下载页面后,下滑,选择Linux系统ARM架构的系统安装包点击下载。5. 下载完毕后,右上角点击在文件夹中显示。6. 打开所在文件夹后,鼠标右击打开终端。7. 在终端输入以下命令进行相关依赖包的下载。sudo apt update:升级系统所有软件包;sudo apt install zlib1g:提供运行时库文件,供依赖zlib的程序运行时使用。sudo apt updatesudo apt install zlib1g再将以下命令复制到终端进行依赖包的下载。sudo apt install zlib1g-dev:提供开发所需文件,供编译程序时使用。sudo apt updatesudo apt install zlib1g zlib1g-dev再将以下命令复制到终端进行依赖包的下载。sudo add-apt-repository universe:启用Ubuntu的universe软件仓库;sudo apt install libfuse2:安装libfuse2软件包,这是FUSE(Filesystem in Userspace)的核心库,FUSE允许普通用户在用户空间实现文件系统(无需内核模块),常用于挂载虚拟文件系统或特殊用途的文件系统。sudo add-apt-repository universesudo apt install libfuse28. 将以下命令复制到终端,对Cherry Studio安装包进行加权。chmod +x Cherry Studio软件包名称9. 将以下命令复制到终端启动Cherry Studio。./Cherry Studio软件包的名称 --no-sandbox3. MaaS获取模型服务1. 进入到Cherry Studio后点击左下角设置->模型服务->OpenRouter。2. 在浏览器输入https://console.huaweicloud.com/modelarts/?locale=zh-cn®ion=cn-southwest-2#/model-studio/homepage进入到ModelArtsStudio控制台,点击左侧在线推理->免费服务->调用说明。3. 点击API Key管理,获取API Key。4. 点击左上角创建API Key,并输入标签和描述后点击确定,会获取到创建的API Key。5. 将获取到的API Key复制到OpenRouter中的API密钥中。将API地址复制到OpenRouter中的API地址中。(注:在复制API地址时将chat/completions删除)6. 点击下方添加,将DeepSeek-V3复制到模型名称中,再点击添加模型。7. 模型添加完毕后点击右上方检测,选择DeepSeek-V3点击确定,检测成功会出现连接成功字样。到此,Cherry Studio配置完成。4. 获取高德地图MCP Server1. 打开火狐浏览器,输入高德地图开放平台网址:https://lbs.amap.com/,进入高德地图开放平台,点击右上角登录,进行账号登录。2. 登录完毕后,点击右上角控制台,进入控制台页面。3. 点击左侧应用管理->我的应用->创建新应用,并输入应用名称和应用类型,再点击创建。(首次登录会提示注册成为开发者,请按照指引完善信息)4. 点击添加Key,按照截图完成选项点击提交,提交完毕后会获得到Key。至此,高德地图MCP Server获取完毕。一、功能实现1. MCP Server功能实现1. 打开Cherry Studio设置页面,点击MCP服务器->右上角红色三角感叹号,安装MCP相对应的依赖包。注:安装完毕后需要重新启动Cherry Studio,重启后红色三角感叹号变成绿色对钩表示安装成功。2. 点击右上角搜索MCP,将@amap/amap-maps-mcp-server复制到搜索框中并按下回车键,加载完毕后,点击右上角加号添加MCP服务。3. 添加完毕后,返回MCP服务器列表点击右侧设置进行MCP服务器设置。将名称修改为高德地图MCP,环境变量修改为步骤2.4中获取的key,点击保存。4. 保存完成后,点击左上角助手->上方模型选择->OpenRouter DeepSeek-V3。设置完毕后,在聊天框中点击MCP服务器->高德地图MCP。在聊天框中进行询问:使用高德地图MCP,规划从深圳出发去西安的3天旅游计划。这里可以看得出,DeepSeek-V3调用高德地图MCP Server中的工具来进行旅游规划。注:回复速度较慢,请耐心等待。至此,开发者空间部署Cherry Studio+高德地图MCP Server构建出行规划助手已全部完成。
总条数:3403 到第
上滑加载中