news 2026/9/7 16:00:16

MCP与CLI的本质区别:如何用Python将本地脚本改造成MCP Server

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP与CLI的本质区别:如何用Python将本地脚本改造成MCP Server

最近在技术社群里看到不少讨论,有人问“AI 编程工具已经有 CLI 了,为什么还要用 MCP?”也有人觉得“MCP 不是 SaaS 平台才需要的协议吗,跟普通开发者有什么关系?”

这两个问题其实都踩中了同一个认知误区:CLI 和 MCP 解决的是不同层面的问题,它们从来不是替代关系;而 MCP 的价值也绝不止于 SaaS 系统对接,本地开发工具、私有化部署、甚至单机脚本,都能通过 MCP 获得全新的自动化能力。

本文会先厘清 CLI 与 MCP 的本质区别,再通过一个本地文件批量整理的实战案例,演示如何在不依赖任何 SaaS 平台的前提下,把一个普通 Python 脚本改造成 MCP Server。读完你会明白:MCP 不是“大厂专属”的协议,它完全可以是普通开发者日常工具箱里的一把新钥匙。

1. 背景:CLI 与 MCP 为什么总被放在一起比较

1.1 三者常被混淆的现实背景

过去一年,AI 编程工具的形态快速迭代。从早期的 ChatGPT 网页对话框,到 GitHub Copilot 插件,再到 Claude Code、Codex CLI 这类终端工具,开发者的交互方式一直在变。近期热词里频繁出现 “codex cli”“claude cli”“cursor cli” 以及各种 “MCP server” 的讨论,本身就说明大家正在接触这些新概念。

CLI 的全称是 Command-Line Interface,即命令行界面。它让用户通过在终端输入命令来操作软件。对开发者来说git commitnpm installdocker compose up都是典型的 CLI 用法。

MCP 的全称是 Model Context Protocol,即模型上下文协议。它最初由 Anthropic 在 2024 年提出并开源,目标是为 AI 模型与外部工具、数据源之间建立一套标准化的通信方式。简单来说,MCP 定义了“AI 应用如何发现工具、如何调用工具、工具结果如何返回”的规范。

除了这两个概念,SaaS(Software as a Service,软件即服务)也经常出现在相关讨论里。很多 SaaS 产品为了接入 AI 能力,会把自身 API 封装成 MCP Server,这正是“MCP 是 SaaS 专属”这一误解的来源之一。

1.2 混淆的三个典型来源

第一个来源是产品形态的相似性。Claude Code 和 Codex CLI 都提供了终端交互能力,用户可以在终端里让 AI 读取代码、执行测试、提交代码。这种“在终端里驱动 AI”的使用体验,让不少人以为“CLI 已经能做很多事了,还需要 MCP 干嘛”。

第二个来源是生态推广的错位。一些云服务商、SaaS 平台在宣传 MCP 时,主要强调“一键接入我们的产品能力”。这给普通开发者造成了一种印象:MCP 是平台方的事情,跟本地脚本和自研工具关系不大。

第三个来源是概念命名上的误导。MCP 里有 “Server”“Client”“Tool” 这些词,很容易让人联想到传统的客户端/服务器架构,进一步加深了“这是企业级、云端的协议”的错觉。

1.3 为什么现在值得弄清这个问题

MCP 协议正在快速成为 AI 工具链的事实标准之一。OpenAI 的 Codex 已经宣布支持 MCP,Claude Desktop、Cursor、Trae 等客户端也陆续接入 MCP 生态。如果开发者能准确理解 MCP 的定位,就能在合适的场景下用它串联自己的本地工具链,而不是被动等 SaaS 厂商提供现成的 MCP Server。

更重要的是,MCP 的本地化、私有化能力被严重低估。下面我们会看到,一个普通的本地 Python 脚本,只需要少量改造就能变成标准的 MCP Server,让 AI 客户端像调用内置功能一样调用它。

2. CLI 与 MCP 的本质区别

2.1 CLI 解决的是“人如何操作工具”的问题

CLI 的核心交互对象是人。它通过命令、参数、标准输入输出,让人能精确控制一个程序。git log --oneline -5表示查看最近 5 条提交记录,这条命令的每个部分都有明确含义,输入之后由人读取输出结果并判断下一步操作。

CLI 的特点可以总结为:

  • 需要人来组织命令序列和执行时机。
  • 输出格式通常面向人类阅读,而不是面向机器解析。
  • 功能边界完全由程序自身定义,没有统一的能力发现机制。
  • 调用方和被调用方需要约定好参数格式和返回格式。

在 AI 编程工具中,CLI 的价值在于:AI 可以像人一样在终端里执行命令、读取输出、判断结果,从而完成编译、测试、部署等操作。但这里有一个隐蔽的限制——AI 调用 CLI 时,本质上是在“模拟人类操作”,每次调用都需要告诉它有哪些命令、参数什么含义、输出怎么解析。这个过程既不标准化,也不够健壮。

2.2 MCP 解决的是“AI 如何发现和调用工具”的问题

MCP 的核心交互对象是 AI 模型。它定义了一套协议,让 AI 客户端可以动态获取服务器提供了哪些工具、每个工具的输入输出 schema 是什么、应该如何在上下文中调用。

MCP 的关键设计包括:

  • 能力发现:客户端通过tools/list获取服务器支持的所有工具及其参数定义。
  • 标准化调用:客户端通过tools/call携带结构化参数调用某个工具,服务器返回结构化结果。
  • 资源访问:除了工具调用,MCP 还支持资源(Resources)的读取,比如文档、数据库 schema、配置文件。
  • 提示词模板:服务器可以定义可复用的提示词模板,帮助 AI 在特定任务中采用更合理的策略。
  • 传输方式:MCP 支持 stdio(标准输入输出,适合本地进程)和 Streamable HTTP(适合远程服务)等多种传输方式。

从这个角度看,MCP 更像是“AI 世界的 USB 接口”——它规定了一套统一的插拔标准,让不同的 AI 客户端能连接不同的工具服务,而不需要针对每个工具单独做适配。

2.3 核心对比:为什么 CLI 不能替代 MCP

对比维度CLIMCP
交互对象AI 模型
调用方式命令 + 参数协议化工具调用
能力发现无,依赖文档或记忆通过 tools/list 自动发现
结果格式面向人类阅读结构化、适合模型继续推理
错误处理依赖退出码和输出协议定义错误码与结构化错误信息
本地支持天然支持支持 stdio 本地传输,天然支持
远程支持需要额外封装支持 HTTP 传输,标准化程度高
生态适配每个 AI 工具需单独适配主流 AI 客户端逐渐统一支持

从上表能清楚看到,CLI 和 MCP 的定位是互补的。CLI 适合“人 + AI 模拟人去操作终端程序”的场景;MCP 适合“AI 通过标准化协议直接获取数据和调用能力”的场景。一个成熟的 AI 工具链里,两者完全可以同时存在——AI 既可以通过 MCP 获取结构化数据,也可以通过 CLI 执行底层的程序命令。

2.4 一个直观的类比

把 AI 想象成一个新入职的实习生。

CLI 方式是:你给实习生一份厚厚的命令手册,告诉他“你把这段命令敲进终端,看着输出自己判断”。遇到命令报错、输出格式变化、参数拼写错误,实习生需要反复翻手册,效率很低。

MCP 方式是:实习生有一个万能工具台,每个工具都贴着清晰的标签,写明输入什么、返回什么。实习生扫一眼就能知道“当前环境提供了 5 个工具,分别能干什么”,按标签调用即可。

显然,在需要 AI 自主完成复杂任务时,MCP 的体验更接近“开箱即用”,而 CLI 更依赖外围的说明书和上下文。

3. MCP 绝不局限在 SaaS 场景

3.1 SaaS 场景只是 MCP 生态的冰山一角

当前 MCP 生态中,曝光度最高的确实是各类 SaaS 产品——设计工具、项目管理平台、数据分析服务等纷纷推出自己的 MCP Server。例如设计协作平台蓝湖推出 MCP 服务后,用户可以在 AI 助手中直接获取设计稿信息,Cursor 等客户端也能连接蓝湖 MCP 读取设计资源。

这给很多人的印象是:MCP 是 SaaS 平台对外开放能力的一种新方式。这种理解本身没错,但过于片面。从协议设计的初衷来看,MCP 是一个非常通用的工具调用规范,它既不知道也不关心工具背后跑在云上还是本地、是商业服务还是开源脚本。

3.2 本地与私有化场景同样适合 MCP

MCP 的 stdio 传输模式,天然支持本地进程。这意味着:

  • 本地数据库可以通过 MCP Server 暴露结构化查询能力给 AI。
  • 本地文件系统可以通过 MCP Server 提供文件读写、搜索、分类能力。
  • 开发工具链如 Git、Docker、构建脚本,都可以封装成 MCP 工具。
  • 企业内部私有系统(如自研运维平台、内部知识库)可以安全地通过 MCP 接入 AI,而不需要把数据提交到任何第三方 SaaS。

举几个现实中的例子。运行安全监控平台 Wazuh 的团队可以搭建 Wazuh MCP 服务器,让 AI 助手直接查询告警信息;游戏开发团队可以用 Unity MCP 让 AI 在编辑器环境中读取场景对象、执行操作;设计工具 Figma 社区也出现了 Open Figma MCP 这类插件,让 AI 能获取设计稿信息。这些场景的共同点是:数据高度敏感或格式高度专用,不适合也不需要通过 SaaS 平台中转

3.3 MCP 的“三不”原则

要真正理解 MCP 的定位,可以记住这三个特点:

  1. 不绑定云端:MCP 同时支持本地 stdio 和远程 HTTP 传输,本地优先。
  2. 不绑定特定 AI:OpenAI、Anthropic、开源社区都在支持 MCP,它是开放标准的一部分。
  3. 不绑定商业产品:任何人写一个 Python 脚本,按照协议暴露成 MCP Server,就能被任何支持 MCP 的客户端调用。

4. 实战:用 Python 将本地脚本改造成 MCP Server

接下来我们用一个完整的例子,演示如何把本地文件整理脚本改造成 MCP Server。这个案例不需要任何 SaaS 服务,只用 Python 就能跑通。

4.1 需求与设计

需求很简单:本地有一个 Download 文件夹,里面堆满了各种类型的文件。我们希望 AI 客户端能通过 MCP 工具完成两件事:

  • 统计文件夹中的文件类型分布。
  • 按文件类型将文件移动到对应的子文件夹中。

上述需求对应两个 MCP 工具:count_files_by_typeorganize_files_by_type

设计上我们选择 FastMCP 库来实现,因为它抽象层级合理,既能体现 MCP 的核心流程,又不需要手写协议细节。版本方面,MCP 生态迭代较快,本文示例以 fastmcp 2.x 版本为例,实际使用时请以你安装到的版本为准,API 如有微调按官方文档调整即可。

4.2 环境准备

示例环境建议如下:

  • 操作系统:Windows / macOS / Linux 均可。
  • Python 版本:3.10 及以上。
  • 主要依赖:fastmcp、mcp。
# 创建虚拟环境(强烈推荐,避免依赖冲突) python -m venv .venv # Windows 激活虚拟环境 .venv\Scripts\activate # macOS / Linux 激活虚拟环境 source .venv/bin/activate # 安装依赖 pip install fastmcp mcp

如果安装过程中网络较慢,可以更换为国内 pip 镜像源,例如:

pip install fastmcp mcp -i https://pypi.tuna.tsinghua.edu.cn/simple

4.3 项目结构

local-mcp-server/ ├── .venv/ # 虚拟环境目录 ├── file_tools.py # MCP Server 主文件 └── test_data/ # 测试文件夹 ├── report.pdf ├── photo.png ├── notes.txt └── archive.zip

test_data 文件夹模拟一个杂乱的文件目录,用于验证 MCP 工具效果。

4.4 编写 MCP Server 核心代码

创建file_tools.py,完整代码如下:

# 文件路径:local-mcp-server/file_tools.py """ 一个用于本地文件整理的 MCP Server 示例。 通过 FastMCP 暴露两个工具: 1. count_files_by_type —— 统计目录下文件类型分布 2. organize_files_by_type —— 按文件类型移动到子文件夹 """ from pathlib import Path from fastmcp import FastMCP # 创建 MCP Server 实例,名称会显示在 AI 客户端的工具列表中 mcp = FastMCP("Local File Tools") # 用于测试的默认目录,可在实例化时自定义 DEFAULT_TARGET_DIR = Path("test_data") # 常见的文件类型 -> 分类名称映射 TYPE_CATEGORY_MAP = { ".pdf": "documents", ".doc": "documents", ".docx": "documents", ".txt": "documents", ".md": "documents", ".png": "images", ".jpg": "images", ".jpeg": "images", ".gif": "images", ".webp": "images", ".zip": "archives", ".rar": "archives", ".7z": "archives", ".tar": "archives", ".gz": "archives", ".py": "code", ".js": "code", ".ts": "code", ".java": "code", ".go": "code", ".html": "code", ".css": "code", } def _resolve_dir(target_dir: str | Path) -> Path: """解析目标目录,支持字符串和 Path,返回绝对路径。""" path = Path(target_dir) if not path.is_absolute(): path = Path.cwd() / path return path def _category_for_suffix(suffix: str) -> str: """根据文件后缀返回分类名称,未映射的后缀统一归入 others。""" return TYPE_CATEGORY_MAP.get(suffix.lower(), "others") @mcp.tool() def count_files_by_type(target_dir: str = "test_data") -> dict: """ 统计指定目录下的文件类型分布。 Args: target_dir: 要统计的目录路径,默认 test_data。 Returns: 包含各类别文件数量的字典。 """ base = _resolve_dir(target_dir) if not base.exists() or not base.is_dir(): return {"error": f"目录不存在或不是文件夹: {base}"} counts: dict[str, int] = {} for item in base.iterdir(): if item.is_file(): category = _category_for_suffix(item.suffix) counts[category] = counts.get(category, 0) + 1 return {"target_dir": str(base), "counts": counts} @mcp.tool() def organize_files_by_type(target_dir: str = "test_data", dry_run: bool = True) -> dict: """ 按文件类型将文件移动到对应的子文件夹。 Args: target_dir: 要整理的目录路径,默认 test_data。 dry_run: 为 True 时只预览将如何移动,不实际执行;为 False 时真正移动文件。 Returns: 移动操作的执行结果。 """ base = _resolve_dir(target_dir) if not base.exists() or not base.is_dir(): return {"error": f"目录不存在或不是文件夹: {base}"} if not dry_run: # 确认非 dry_run 操作前,要求目录必须存在且当前用户有写权限 if not base.is_dir(): return {"error": f"目标目录不存在: {base}"} moved: list[dict] = [] skipped: list[str] = [] for item in base.iterdir(): if not item.is_file(): continue category = _category_for_suffix(item.suffix) target_subdir = base / category if dry_run: moved.append({ "file": item.name, "category": category, "target": str(target_subdir / item.name), "action": "preview" }) continue # 目标子文件夹不存在时创建,存在则忽略 target_subdir.mkdir(exist_ok=True) destination = target_subdir / item.name if destination.exists(): skipped.append(item.name) continue item.rename(destination) moved.append({ "file": item.name, "category": category, "target": str(destination), "action": "moved" }) result = { "target_dir": str(base), "dry_run": dry_run, "moved_count": len(moved), "skipped_count": len(skipped), } if dry_run: result["preview"] = moved result["message"] = "预览模式,使用 dry_run=False 真正执行移动" else: result["moved_files"] = moved result["skipped_files"] = skipped return result if __name__ == "__main__": # 以 stdio 方式运行 MCP Server,交给 AI 客户端启动 mcp.run(transport="stdio")

代码做了几处关键设计,下面逐一解释。

@mcp.tool()装饰器是注册工具的核心入口。被它装饰的函数会自动成为 MCP 协议中的 Tool,函数签名里的参数和 docstring 会被解析为工具的描述和参数 schema。因此写清楚 docstring 非常重要,AI 模型会依赖这些描述决定何时调用工具、传什么参数。

count_files_by_type实现了类型分布统计。它遍历目标目录,只看文件不文件夹,按后缀映射出分类,累计数量后返回结构化字典。返回结果会直接进入 AI 的上下文,模型可以据此判断目录里都有什么类型的文件。

organize_files_by_type实现了移动整理。这里特别引入了dry_run参数,默认值是True,即默认只预览不执行。原因很简单:任何可能批量修改本地文件的操作,都应该先预览、后执行。AI 工具调用不像人手动操作那样谨慎,如果模型误调用了移动函数,轻则文件乱序,重则覆盖目标文件。通过 dry_run 机制,能大大降低出错概率。

实际执行移动时,代码会先创建目标分类子文件夹,如果目标文件已经存在则跳过而不是覆盖。这里选择了最保守的策略,宁可跳过也不覆盖,最大程度保护数据安全。

mcp.run(transport="stdio")表示以标准输入输出方式启动 MCP Server。这种方式适合被 AI 客户端以子进程方式拉起,Claude Desktop、Codex CLI 等支持 stdio 传输的客户端都可以直接连接。

4.5 连接到 AI 客户端运行与验证

启动 MCP Server 后,需要连接到支持 MCP 的客户端中。以 Codex CLI 为例(Codex 是目前支持 MCP 的 AI 编程工具之一),可以通过配置文件注册本地 MCP Server。不同版本的配置格式可能有差异,以下是一个参考片段:

{ "mcpServers": { "local-file-tools": { "command": "python", "args": ["/absolute/path/to/file_tools.py"], "cwd": "/absolute/path/to/local-mcp-server" } } }

实际操作时,请把路径替换为你本机的真实路径。启动 Codex CLI 后,在对话中输入类似“帮我看一下 test_data 目录里有哪些类型的文件”,模型会自动识别出可用的 MCP 工具,尝试调用count_files_by_type并返回统计结果。

如果暂时不想对接 AI 客户端,也可以先写一个简单的 Python 测试脚本,直接调用工具函数验证逻辑:

# 文件路径:local-mcp-server/test_tools.py from file_tools import count_files_by_type, organize_files_by_type # 统计类型分布 result = count_files_by_type("test_data") print("统计结果:", result) # 预览移动方案 preview = organize_files_by_type("test_data", dry_run=True) print("预览移动条目数:", preview["moved_count"]) for item in preview["preview"]: print(f" {item['file']} -> {item['target']}")

预期输出类似:

统计结果: {'target_dir': '/path/to/local-mcp-server/test_data', 'counts': {'documents': 1, 'images': 1, 'archives': 1, 'others': 1}} 预览移动条目数: 4 report.pdf -> test_data/documents/report.pdf photo.png -> test_data/images/photo.png notes.txt -> test_data/documents/notes.txt archive.zip -> test_data/archives/archive.zip

确认预览结果符合预期后,再执行真正的整理:

result = organize_files_by_type("test_data", dry_run=False) print("实际移动结果:", result)

这时候 test_data 下面会出现 documents、images、archives 等子文件夹,原文件按分类进入对应目录。

这个例子简单但意义不小:原本一个只能靠人手动执行的 Python 脚本,改造成 MCP Server 后,就变成了 AI 能自主发现、动态调用的标准工具。这就是 MCP 在本地开发场景下的核心价值。

5. 常见报错与排查思路

接入 MCP 和 CLI 的过程中,有几个高频问题经常出现在各大技术社区的热搜词里。下面整理成一张排查表,并针对较复杂的场景做补充说明。

问题现象常见原因解决思路
ChatGPT 启动时提示 “Unable to locate the Codex CLI binary”AI 客户端找不到 Codex CLI 可执行文件路径在客户端配置中显式设置 Codex CLI 路径,确保已安装且 PATH 正确
Claude Code 提示 “Could not locate the Claude CLI on Path”Claude CLI 未安装或 PATH 环境变量未生效重新安装 CLI,检查which claude/where claude输出
MCP Server 启动后客户端提示连接失败Server 启动路径错误、依赖未安装、Python 虚拟环境未激活在终端手动运行python file_tools.py观察是否有报错
MCP 工具调用后返回超时stdio 模式下 Server 进程阻塞、工具逻辑耗时长检查工具函数是否死循环或等待输入,必要时加日志定位
文件移动后目标路径仍不对相对路径在不同工作目录下解析不一致使用绝对路径或在配置中设置固定 cwd
移动文件时提示目标已存在目标目录里已有同名文件确认 skip 策略是否符合预期,避免直接覆盖

补充说明两个排查技巧:

第一个技巧是“先手动跑通再对接客户端”。无论接入哪个 AI 客户端,最好先在本机终端手动运行 MCP Server 命令。如果你手动运行时都能看到报错,那客户端连接失败的原因就一目了然;如果手动运行正常,再重点检查客户端侧的路径配置和启动参数。

第二个技巧是“多看日志”。MCP 协议调用失败时,客户端界面上的错误信息往往很抽象。可以给工具函数临时加上print输出或使用 Python 的logging模块,将运行过程中的关键信息打印到标准错误流,便于定位问题在哪一步。

6. 最佳实践与工程建议

6.1 工具设计要面向 AI 的思维方式

给 MCP 写工具时,一个常见误区是按照“人使用函数”的方式设计参数。但 MCP 工具的实际调用者是 AI 模型,它没有你的业务直觉。因此:

  • 每个工具职责要单一,避免一个函数做太多事。
  • 参数命名要清晰,尽量使用通用的英文单词。
  • 返回值要结构化,最好包含状态、数据和必要的提示信息。
  • docstring 里要写清“这个工具是干什么的”“参数什么含义”“什么时候适合使用”。

6.2 安全边界要提前设计

MCP 让 AI 有了调用工具的能力,这本身是强大的,也带来了风险。如果你的 MCP Server 会修改文件、执行命令、访问数据库,建议做到:

  • 默认只读优先,写操作用dry_run或确认机制兜底。
  • 路径校验,防止 AI 传入意外的绝对路径覆盖系统关键文件。
  • 最小权限原则,本地进程尽量使用普通用户权限运行。
  • 远程 MCP Server 必须做鉴权,不要把内部 MCP Server 暴露在公网。

6.3 日志与可观测性

MCP Server 通常是常驻进程或者被子进程拉起的,排查问题时如果没有任何日志会非常痛苦。建议在工具函数里合理记录日志,至少覆盖:工具被调用、参数关键值、执行结果、异常堆栈。

import logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger("file-tools-mcp") # 在工具函数内部 logger.info("count_files_by_type called, target_dir=%s", target_dir)

6.4 不要把 CLI 和 MCP 对立起来

最后想强调的工程建议是:同一个项目里,CLI 和 MCP 完全可以共存。CLI 适合“人主动发起、逐条执行”的场景;MCP 适合“AI 在上下文里自主决策、按需调用”的场景。

在搭建 AI 工具链时,可以先梳理当前工作流里有哪些重复性操作,再判断:

  • 如果操作需要人来确认每一步,保留 CLI 或 GUI 流程。
  • 如果操作结果是结构化的、AI 需要基于结果继续推理,那就封装成 MCP Server。
  • 如果一个内部系统后续可能要接入多个 AI 客户端,封装 MCP 的收益会远大于逐家适配。

以本文的本地文件整理工具为例,它既可以通过 MCP 被 AI 调用,也可以再写一个简单的命令行入口供人手动执行。两种方式各有优劣,但它们在工程上是互补的关系,不是非此即彼的选择题。

7. 从 MCP 到 AI 工具链的下一步

MCP 的影响力正在从 SaaS 平台扩展到本地开发、私有化部署、游戏引擎、安全分析、工业软件等各个领域。围绕 MCP 的热门讨论也从“什么是 MCP”逐渐转向“怎么把 MCP 用在自己的系统里”。

对于普通开发者,接下来的学习路径可以这样规划:

  • 先掌握 MCP 的基本概念、工具/资源/提示词三类能力。
  • 用 FastMCP 或其他 SDK 写几个本地工具示例,理解 stdio 与 HTTP 传输的区别。
  • 深入学习工具调度、错误处理、鉴权等进阶主题。
  • 在实践中观察 AI 模型是如何根据工具描述决定调用策略的,并持续优化工具设计。

从长远看,MCP 这类标准化协议的价值在于:它让 AI 的能力边界从“对话”扩展到了“行动”。当越来越多的本地工具和内部系统通过 MCP 向 AI 开放能力,开发者的工作方式也会随之改变。现在动手改造自己的第一个 MCP Server,就是走进这个新阶段最简单的一步。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 15:58:44

NFC吉他项链:碰一下秒切歌的智能周边全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 15:57:34

Flutter在OpenHarmony上的表单实战:发起组队功能完整实现

前几篇写完项目的整体骨架、路由和房间列表之后,总算要碰一个真正需要“动手填”的界面了。这一篇我们聚焦剧本杀组队App里最核心的入口:发起组队表单。说实话,在很多教程里表单通常被一笔带过,好像就是几个输入框堆在一起而已&am…

作者头像 李华
网站建设 2026/9/7 15:57:32

Unity自定义Inspector显示名:用CustomLabel特性实现字段名与代码解耦

前阵子项目里策划丢过来一个问题:“你这个 speed 到底是移动速度还是攻击速度?我在Inspector里改来改去总怕改错。”我打开编辑器一看,字段名是 m_MoveSpeed ,确实是直接暴露出来了。代码命名规范要求字段带前缀没问题&#x…

作者头像 李华
网站建设 2026/9/7 15:57:06

从UMD到gRPC:AI训练中GPU指令翻译官的服务化实战

搞AI训练的哥们儿应该都有这种感觉:模型写起来不难,难的是让计算真正跑到GPU上。这一层“让计算跑起来”的衔接,通常不是AI框架直接操作显卡,而是框架调用GPU驱动,驱动再把算子请求翻译成硬件指令。今天聊的就是这条链…

作者头像 李华