SwiftUI + AI Agent + Skill:构建iOS智能工作台的实践指南
开工之前先说明一下这个 Side Project 我起名就叫 SwiftUI-Agent-Skill名字比较直白SwiftUI 负责界面Agent 负责对话和决策Skill 承载具体执行力。我最近一直在折腾它目标是做一个跑在 iOS 上的轻量 AI 工作台不是那种只能聊天的套壳应用而是让 AI 真正能“动手办事”——比如算个表达式、查个备忘录、处理一段文本这些都是通过可插拔的 Skill 技能模块完成的。如果你也在研究 SwiftUI 和 AI Agent 怎么结合或者想把手上的 LLM API 接入自己 App 里做成工具链这篇文章应该能帮到你。我会把项目的整体架构、Skill 协议的设计、Agent 循环的实现以及我在开发中踩过的一堆坑一次性倒出来。后面涉及代码的部分我都会贴关键实现尽量做到你打开 Xcode 就能跟着写。1. 项目概览与核心思路1.1 这个项目到底在干什么SwiftUI-Agent-Skill 从用户视角看就是一个带聊天界面的助手但它和普通聊天应用最本质的区别在于用户输入一句话之后Agent 会判断是否需要调用工具如果需要就解析出结构化参数调度对应的 Skill 执行再把执行结果反馈给模型最终生成一段自然的回答。举个例子你在输入框里敲“帮我算一下 (237)*2 等于多少”普通的聊天 App 要么直接答错要么把问题丢给大模型让它在文本里瞎猜。但这个项目里Agent 会识别出这是数学计算于是触发 calculator 这个 Skill由本地的代码真正执行表达式计算拿到结果后再组织语言回复你。整个过程不是一次性完成的而是一个“模型提出工具调用 - 程序执行工具 - 模型总结结果”的循环。这个小项目的范围我有意控制得很小不做账号系统不做云端同步所有配置和技能元数据都存本地。MVP 只需要支持 3 到 5 个技能、多轮上下文、流式输出和技能热注册。这种边界设置让我能在几周内把核心链路跑通而不是把时间花在无关紧要的周边功能上。对想复现的人来说这个规模刚好看得完、改得动。1.2 为什么是 SwiftUI、Agent、Skill 这三样组合先说 SwiftUI。做这种对话型界面SwiftUI 的声明式语法和状态驱动模型是天然匹配的。消息列表无非就是一组数据模型 ScrollView ForEach新增一条消息、更新某条消息的流式文本都只需要改状态视图自动刷新。比起 UIKit 手写 diff 和 cell 管理开发效率高很多。然后是 Agent。这里说的 Agent 不是一个很玄的概念在我的实现里它就是一个调度核心维护消息历史、构造包含工具描述的请求、解析模型返回的工具调用、调用 Skill、把结果回填给模型。它解决的核心问题就是“什么时候该用哪个技能”本质上是一个带工具调用能力的对话循环。最后是 Skill。如果你做过传统工具型 App应该体会过功能越来越多、if else 越堆越乱的痛苦。Skill 把每个能力封装成独立的插件遵循同一个协议Agent 只依赖协议不依赖具体实现。新增一个技能就是新增一个结构体不需要改 Agent 的代码也不需要动界面层。这三样组合起来正好形成“界面层 决策层 执行层”的清晰分工。1.3 目标读者与预期收获这篇文章适合三类人一是对 SwiftUI 开发有一定基础、想尝试 AI 应用的同学二是已经在调 LLM API、想把它从单轮对话升级成工具调用模式的人三是纯粹对“可插拔技能系统”感兴趣、想看协议怎么设计的人。如果你是完全没写过 Swift 的小白也不用太紧张我会尽量把每一步讲清楚但你最好先知道 SwiftUI 的基本概念比如 View、State、ObservableObject否则 UI 部分可能有点吃力。读完这篇文章你至少能搭建一个运行在 iOS 上的最小 Agent 闭环并且理解如何扩展属于自己的技能。2. 整体架构与技能体系设计2.1 目录结构与模块划分项目代码结构我花了不少心思核心原则是“依赖方向从外向内界面不直接依赖技能实现”。直接看目录SwiftUI-Agent-Skill/ ├── App/ │ ├── AgentSkillApp.swift │ └── RootView.swift ├── Core/ │ ├── Agent/ │ │ ├── Agent.swift │ │ ├── AgentContext.swift │ │ └── AgentStep.swift │ ├── Skill/ │ │ ├── Skill.swift │ │ ├── SkillContext.swift │ │ ├── SkillResult.swift │ │ └── SkillRegistry.swift │ ├── LLM/ │ │ ├── LLMProvider.swift │ │ ├── LLMTool.swift │ │ └── LLMResponse.swift │ ├── Models/ │ │ ├── ChatMessage.swift │ │ └── ChatRole.swift │ └── Services/ │ └── TokenCounter.swift ├── Features/ │ ├── Chat/ │ │ ├── ChatView.swift │ │ ├── MessageBubbleView.swift │ │ └── InputBarView.swift │ └── SkillStore/ │ ├── SkillListView.swift │ └── SkillDetailView.swift └── Resources/ └── Skills/ └── builtin-skills.jsonCore 层不依赖 SwiftUI这样无论是给 Agent 写单元测试还是以后把同一套逻辑搬到 macOS 或 watchOS都不需要动核心代码。Features 层只通过协议和 Core 打交道比如说 ChatView 不知道 calculator 怎么实现它只看到一个 [ChatMessage] 数组在更新。这种分层在实际开发里帮了大忙。有一次我重构技能注册逻辑改动只涉及 SkillRegistry 和对应测试界面层完全没有动。如果一开始就把逻辑写在 View 里这种重构根本不敢碰。2.2 Skill 协议把能力变成插件的核心所有技能都遵循同一个协议这是整个项目的基石。第一版我用的是 class 继承后来发现根本不需要Swift 的 protocol struct 已经够用。我的 Skill 协议长这样public protocol Skill: Identifiable, Sendable { var id: String { get } var name: String { get } var description: String { get } var parameterSchema: String { get } func execute(context: SkillContext) async throws - SkillResult }每个字段都有它的意义。id 是技能唯一标识注册和调用时都靠它name 和 description 是给 Agent 看的尤其是 description它决定了模型在什么场景下会选中这个技能我后面会专门讲怎么写 description 更有效parameterSchema 是一个 JSON Schema 字符串用来告诉模型这个技能需要哪些参数、参数类型是什么execute 是实际执行的入口。为什么用 JSON Schema 而不是硬编码参数列表因为 LLM 本身擅长解析文本给一个结构化 schema它才能返回符合要求的 JSON 参数。这个设计是 Agent 工具调用的标配也是我参考常见大模型工具协议总结出来的通用做法。SkillContext 是一个轻量结构体包含当前对话 id、用户原始输入、解析后的 arguments以及一个可以访问 LLMProvider 的引用。SkillResult 更简单就是执行结果文本和可选附件。public struct SkillContext: Sendable { public let conversationID: UUID public let userInput: String public let arguments: [String: Any] public let provider: LLMProvider } public struct SkillResult: Sendable { public let output: String public let attachments: [Attachment] }这里有个经验arguments 的取值类型最好统一。模型有时候会把数字返回成字符串所以我在解析后做了一层归一化把“23”转成 23。别小看这一步因为这个坑我至少踩了三次。2.3 Agent 调度核心与上下文管理Agent 是这个项目的大脑我把它设计成一个MainActorclass因为它需要更新 UI 绑定的状态比如当前正在流式输出的文本、最新消息列表。Agent 内部维护一个消息数组和技能注册表核心方法是 reply(to:)。我简化后的实现思路是MainActor final class Agent { private var messages: [ChatMessage] [] private var skills: [String: any Skill] [:] private let provider: LLMProvider private let maxIterations 5 func register(_ skill: any Skill) { skills[skill.id] skill } func reply(to userInput: String) async throws - String { messages.append(.user(userInput)) for _ in 0..maxIterations { let response try await provider.complete( messages: messages, tools: skillSchemas() ) if let toolCall response.toolCall { let text: String do { text try await runSkill(toolCall) } catch { text 技能执行失败\(error.localizedDescription) } messages.append(.toolResult(toolCall.id, text)) } else { let text response.text ?? messages.append(.assistant(text)) return text } } throw AgentError.maxIterationsReached } private func runSkill(_ toolCall: LLMToolCall) async throws - String { guard let skill skills[toolCall.name] else { throw AgentError.skillNotFound(toolCall.name) } let arguments try JSONSerialization.jsonObject( with: Data(toolCall.arguments.utf8) ) as? [String: Any] ?? [:] let context SkillContext( conversationID: conversationID, userInput: latestUserInput, arguments: arguments, provider: provider ) let result try await skill.execute(context: context) return result.output } }这个循环最大的特点是“让模型决定下一步”。模型返回工具调用我就执行工具模型返回正常文本我就把文本拿给用户。maxIterations 用来兜底防止模型陷入“反复调用同一个工具”的死循环。5 次是我试下来的平衡点太少会中断复杂任务太多会浪费 token 和时间。上下文管理上我保留了完整的消息历史包括工具调用的入参和结果。这不是为了界面上都显示而是让模型在后续推理时能“看到”自己之前做过什么。工具结果消息一般不会直接展示给用户但必须在发给模型的消息列表里。2.4 状态管理与数据流界面上所有可见的东西都来自一个 AgentStore我把它设计成 ObservableObject里面持有 Agent 实例和当前显示的消息数组。Agent 一旦产生新消息或工具调用步骤就更新 store 的 published 属性SwiftUI 视图随之刷新。状态流我用一个简单的枚举表示enum ChatPhase: Equatable { case idle case thinking case streaming(text: String) case awaitingTool(toolName: String) }为什么需要等待工具状态因为工具执行期间模型已经给出了一个部分响应用户应该看到“Agent 正在调用计算器”这样的反馈而不是界面毫无反应。这个状态看起来简单但能极大提升使用体验不然工具执行超过两秒用户会以为 App 卡死了。数据流是单向的View 只能调用 store 里的方法store 再去调 AgentAgent 通过回调把新状态推回 store。我不会在 View 里直接改消息数组这是保证复杂状态不失控的关键。3. 核心功能实现与实操过程3.1 工程初始化与基础依赖我先用 Xcode 新建了 iOS App最低版本设到 iOS 16。原因很简单NavigationStack 和 AsyncStream 在 iOS 16 就能用而 Observable 要 iOS 17。我想让这个项目能覆盖更多人的设备所以用 ObservableObject 这套成熟方案。依赖管理直接用 Swift Package Manager不需要第三方库。有人可能会问为什么不引入某个大模型的官方 SDK我的想法是把 LLM 调用抽象成 LLMProvider 协议具体实现可以随时换。这样既能避免被单一服务绑定也方便后面做单元测试时用 mock provider。LLMProvider 协议核心就两个方法一个是非流式 complete返回完整文本另一个是流式 completeStream返回 AsyncStream。具体业务代码里我写了一个基于 URLSession 的实现把请求体拼成主流模型服务都兼容的格式工具调用列表放在 tools 字段里。public protocol LLMProvider: Sendable { func complete( messages: [ChatMessage], tools: [LLMTool] ) async throws - LLMResponse func completeStream( messages: [ChatMessage], tools: [LLMTool] ) async throws - AsyncStreamString }这个抽象让 Agent 完全不知道底层模型 API 长什么样。后面我想换个更便宜的模型服务只需要新增一个 Provider不用动 Agent。3.2 实现第一个 Skill计算器理论讲完实操开始。我拿计算器技能当第一个例子因为它逻辑简单又能完整演示协议怎么落地。完整算数求值我用 NSExpression 实现表达式解析交给系统但安全问题必须注意我只接受白名单字符过滤掉字母和分号。struct CalculatorSkill: Skill { let id calculator let name 计算器 let description 执行四则运算支持 - * / 和括号。当用户需要数学计算或数字运算时使用。 let parameterSchema { type: object, properties: { expression: { type: string, description: 待计算的数学表达式例如 (237)*2 } }, required: [expression] } func execute(context: SkillContext) async throws - SkillResult { guard let expression context.arguments[expression] as? String else { throw SkillError.missingArgument(expression) } let sanitized expression.filter { -*/0123456789(). .contains($0) } let expressionObj NSExpression(format: sanitized) guard let result expressionObj.expressionValue(with: nil, context: nil) as? Double, result.isFinite else { throw SkillError.invalidExpression } return SkillResult(output: String(format: %g, result)) } }description 字段我写得很具体特别强调“当用户需要数学计算时使用”。你可能会觉得这句话是废话但实践下来模型判断工具调用时非常依赖这种语义信号。如果你写“帮助用户”,它可能在用户问天气时也调用计算器因为意图描述太模糊。打安全补丁也要注意NSExpression 虽然好用但直接传用户输入会存在命令注入风险。过滤字符是最粗粒度的防护真正的项目里我还会用表达式解析器做白名单校验而不是靠拦截字符串。3.3 Agent 循环与函数调用解析跑通 Agent 循环的关键在于正确解析工具调用。主流服务返回的结构一般是这样的当模型决定调用工具响应里会包含一个 toolCalls 数组每个元素有 id、function.name、function.arguments。arguments 是一个 JSON 字符串。我踩过的第一个大坑就是用不匹配的模型去解析。有些模型不支持工具调用你传 tools 参数它也会忽略然后直接返回一段文字。所以我在 LLMResponse 里设计了一个toolCall可选值解析失败就当普通文本处理绝不报警。另外流式工具调用有一个很恶心的坑异步流返回的是一个个增量 chunk工具调用的名称和参数可能分散在多个 chunk 里。我的处理方式是先拼完整流再解析 JSON。也就是说流式场景下工具调用阶段不逐字输出而是等工具执行完再一次性显示结果。这个设计让 UI 实现简单很多。Agent 循环里还要处理工具执行失败的情况。我不让错误直接传给用户而是把错误信息包装成一条 tool result 消息返回给模型。例如“技能执行失败除以零”。模型看到这条信息后通常会自动修正参数重新调用或者向用户解释。这种机制比直接崩溃友好得多。3.4 SwiftUI 界面实现与流式更新聊完调度看 UI。聊天界面由三块组成消息列表、输入栏、状态提示条。消息列表用 ScrollViewReader 包裹新消息出现时自动滚动到底部。每条消息用一个 MessageBubbleView 渲染根据 role 区分左对齐还是右对齐。流式更新是这里最容易搞砸的地方。我最开始的实现是每收到一个 chunk 就 append 到字符串然后直接更新 store。这是一个高频小更新看起来没问题但消息一多就开始卡顿。后来我改成节流每 0.05 秒只刷新一次 UI中间来的 chunk 合并到一起。具体实现用 AsyncStream 很顺手final class AgentStore: ObservableObject { Published var messages: [ChatMessage] [] Published var streamingText: String Published var phase: ChatPhase .idle func send(_ text: String) { let message ChatMessage(role: .user, content: text) messages.append(message) phase .thinking Task { do { let reply try await agent.reply(to: text) messages.append(.assistant(reply)) phase .idle } catch { messages.append(.assistant(出错了\(error.localizedDescription))) phase .idle } } } }流式版本的 send 我改用了 agent.streamReply(to:)内部是一个 AsyncStreamstore 在 for await 循环里把文本累积到 streamingText等到流结束后再落成正式消息。这样 View 在流式阶段就显示 streamingText结束后显示 messages.last。Published var streamingText和Published var messages分开是关键因为消息数组每次 append 都会触发完整的列表 diff如果流式期间频繁 append列表性能会很差。流式文本只影响一个 Text 视图开销小得多。3.5 技能注册与内置技能列表技能怎么进到系统里我做了两层。第一层是内置技能清单resources 里放了一个 builtin-skills.json启动时加载文件读取每个技能的 id、name、description、schema并实例化对应的 Skill。第二层是运行时注册用户可以在设置页打开一个 Skill触发 registry.register(skill)。SkillRegistry 内部维护两个字典一个是注册表[String: any Skill]另一个是元信息[String: SkillMeta]。注册表用于实际调用元信息用于列表展示和给 Agent 构造 tools。final class SkillRegistry { private var skills: [String: any Skill] [:] private(set) var meta: [String: SkillMeta] [:] func register(_ skill: any Skill, enabled: Bool true) { skills[skill.id] skill meta[skill.id] SkillMeta(id: skill.id, name: skill.name, description: skill.description, enabled: enabled) } func enabledToolSchemas() - [LLMTool] { meta.filter(\.value.enabled).map { LLMTool(name: $0.key, description: $0.value.description, schema: ...) } } }这个注册表最大的好处是Agent 每次请求只带上启用的技能禁用掉不用的技能可以省 token。例如用户不常用天气那天气技能就不进 tools模型也不会把它当成候选。这个筛选逻辑对 token 成本控制非常有效。4. 踩坑实录与问题排查技巧4.1 流式刷新导致 SwiftUI 卡顿前面提到流式文本节流这里详细说说现象。第一次集成流式输出时我在 for await 里每收到一个 chunk 就更新 Published最后模拟器上所有文本都正常但真机上滚动列表时明显掉帧。为什么因为每次更新都触发整个 body 重新求值列表里的每一行都要重新 diff尤其是长对话场景几百条消息全量 diff 的成本不可忽略。解决办法有三步第一流式期间只更新 streamingText不 append 新消息第二对 streamingText 的赋值做时间节流保证刷新频率不高于每秒 20 次第三给消息列表的 ForEach 加上稳定的 id避免视图因为位置变化被重建。我这里做了一个小工具函数用 Date 判断距上次刷新是否超过 50ms不足则缓存到 pendingText到时间再合并更新。代码不复杂但效果立竿见影真机帧率从明显卡顿变成 60fps 无压力。4.2 Actor 隔离与 Swift 6 并发问题把项目迁移到 Swift 6 语言模式时编译期报了一堆 actor 隔离错误。典型问题在于 Skill 协议里的execute(context:)是异步方法但实现它的结构体可能不是 Sendable 的或者 SkillContext 里的 LLMProvider 被多个任务并发访问。我的经验是给协议加上 Sendable所有属性都用 letSkillContext 里的 arguments 统一转成值类型字典Provider 也声明为 Sendable。另外把技能的注册表隔离到一个专门的无主 actor 里读取注册表时用await registry.skill(for:)。这样既避免了数据竞争也让依赖关系更清晰。有些人会觉得 Swift 6 严格并发太麻烦干脆关掉语言模式。但对这种异步任务特别多的项目严格并发检查能在编译期揪出很多潜在的数据竞争。我建议就忍着改别豁免。4.3 工具调用的参数解析与错误处理工具调用参数解析是另一个高频雷区。模型返回的arguments字符串有各种奇形怪状多了前后空格、用了单引号、数字带引号、缺少必填字段。我一开始直接用JSONDecoder解码到 Codable 模型结果三天两头抛错。后来我改成先JSONSerialization转成[String: Any]再做字段类型归一化。比如 expression 字段如果带引号就尝试去掉多余引号无符号整数被解析成字符串就转成 Int。解析失败时不要直接抛错而是返回一个带错误说明的 SkillResult让模型知道是哪个字段出了问题。我还给每个 Skill 加了一个validate(arguments:)方法在 execute 前调用。这个 validate 可以复用 parameterSchema 做 JSON Schema 校验虽然 iOS 没有内置 JSON Schema 库但我写了个几十行的小验证器只查 required 和 type。这个小成本换来的稳定性非常值。4.4 调试技巧Agent 内部状态可视化Agent 是个黑盒光看聊天界面很难判断是模型选错了工具还是技能执行出了问题。我加了一个调试回调让每个关键步骤都能抛出来final class Agent { var onStep: ((AgentStep) - Void)? } enum AgentStep: CustomStringConvertible { case willSendToModel(messageCount: Int) case modelReturnedToolCall(String) case willExecuteSkill(String) case skillFinished(String, output: String) case modelReturnedText(String) var description: String { ... } }在 DEBUG 模式我把它输出到控制台同时驱动一个 DebugPanel 视图显示当前上下文 token 估算。这样当用户问“算一下 23”我能一眼看到 Agent 是不是先选了计算器又有没有正确传参。调试面板还有个隐藏功能显示每一步的耗时。如果模型请求耗时 3 秒而技能执行只有 0.1 秒那问题显然在网络或模型侧如果技能执行耗时 5 秒就要回头查技能内部逻辑。成本定位不那么准确但至少能分辨问题的取值范围。5. 后续扩展与个人体会5.1 可以继续做的方向这套 Skill 架构目前只做了文本类和数值类技能但协议本身具备扩展空间。比如我想加一个 ImageGenerationSkill让它接收一段 prompt返回一张图片附件。在 SwiftUI 里显示图片附件不是什么难事真正要解决的是附件上传和存储路径设计。另一个方向是技能热更新。现在技能是编译期写死 JSON 注册远程下发技能描述和实现还不够灵活。后续可以做一个沙箱机制让技能包以 zip 形式下载App 内校验后动态加载。这个方向比较复杂但几乎是插件化框架的必修课。还有低成本的改进把意图分类移到端上。现在每次对话前都要请求模型才能判断是否调用工具如果端上先用一个小模型把“数学计算”“备忘记录”这类高频意图分出来就能减少一半的模型调用。这个思路我还在试验但 Skill 协议化以后接入并不难。5.2 几个值得复盘的设计决策整个项目写下来有几个决策如果重新做我会更早坚定。第一个是 Skill 的 description 必须为“模型”写而不是为“人”写。最初我把 description 写成给开发者看的功能说明结果模型经常选错技能后来我老老实实地模仿 Prompt 工程那种风格写清楚触发条件和典型场景准确率立刻上来了。第二个是不要追求一次性支持 20 个技能。一次只给 Agent 暴露 5 个技能和暴露 20 个技能模型的选择准确率和响应速度完全是两个量级。token 预算不是唯一瓶颈候选太多会让模型犹豫。所以我做了一个开关面板让技能默认离线只有用户主动开启才进入候选池。第三个是协议最小化。我不在 Skill 协议里塞多余的方法比如生命周期回调、进度上报都是后来通过组合扩展加的。每加一个协议方法所有现有技能的维护成本都会上升。这也是我把 Skill 设计成纯 struct 而不是类的另一个原因struct 更能克制住“随便加状态”的冲动。5.3 一点真实的建议如果你也想搭一个类似的 SwiftUI Agent 项目我的建议特别简单先写一个能算数学的计算器技能把 Agent 循环完整跑通然后再去想架构和扩展。不要一上来就整很多协议、很多抽象层。抽象只有在第二个真实技能出现时才会变得有价值第一个技能时只会拖慢你。我一开始也是从网上找了不少参考资料但真正把代码跑起来之后才发现工具调用的细节和 UI 的流式刷新才是大头。多花点时间在捕获模型返回的非预期行为上比花时间纠结目录结构值钱得多。这个项目目前还是我的实验田接下来我打算把技能远程下发和端上意图分类两个方向做深一点等有结论了再回来继续分享。

相关新闻

openclaw优先读取机制:让爬虫先抓重点,稳定应对动态渲染

openclaw优先读取机制:让爬虫先抓重点,稳定应对动态渲染

上个月我调一个抓取任务时遇到了一件让人头疼的事:页面内容明明几秒钟就返回了,可最重要的正文直到最后才被解析出来,又赶上那个区块走了动态渲染,超时后整个任务直接失败,重试了三轮才把数据补齐。后来我仔细看了日志…

2026/10/14 8:30:01 阅读更多 →
大a投资特性解析:高流动性、杠杆效应与政策关联

大a投资特性解析:高流动性、杠杆效应与政策关联

1. 视频内容概述"神奇的大a(上)"这期视频主要探讨了当前市场环境下一个备受关注的投资标的——我们暂且称之为"大a"的特殊表现。作为系列内容的第八集,本期聚焦于大a的基本特性、历史表现和近期市场反应,为下…

2026/10/14 8:30:01 阅读更多 →
安全地毯高灵敏感应机制与耐磨材质优势及多场所防护安装维护指南

安全地毯高灵敏感应机制与耐磨材质优势及多场所防护安装维护指南

安全地毯高灵敏感应机制与快速触发原理安全地毯的核心工作逻辑并不复杂,操作人员踩上铺设区域后,内部感应元件立刻捕捉重量变化,触发预设信号并控制设备停机或降速。整个过程响应时间不超过10ms、几乎是踩下去瞬间就完成了信号传递。这种高灵…

2026/10/14 8:30:01 阅读更多 →

最新新闻

Python爬虫技术实战:从基础到分布式架构

Python爬虫技术实战:从基础到分布式架构

1. 爬虫技术全景解析:从入门到分布式架构 十年前我刚接触爬虫时,用正则表达式硬解析HTML的日子还历历在目。如今Python生态已经形成了完整的爬虫技术栈,从简单的Requests单页抓取到Scrapy分布式架构,技术方案的选择往往让初学者感…

2026/10/14 9:27:10 阅读更多 →
IEEE33节点配电网模型解析与工程实践

IEEE33节点配电网模型解析与工程实践

1. IEEE33节点配电网模型概述 作为一名电力系统仿真工程师,我最近在项目中深度使用了IEEE33节点配电网模型。这个经典模型就像电力系统研究领域的"Hello World",但它的实用价值远超入门级工具。经过实测,我发现它不仅适合教学演示&…

2026/10/14 9:27:10 阅读更多 →
TCP可靠传输机制、滑动窗口控制与字节流粘包处理介绍

TCP可靠传输机制、滑动窗口控制与字节流粘包处理介绍

文章目录 一、TCP的可靠传输 1.连接管理保证可靠传输 1.1三次握手地建立连接 1.2四次挥手地断开连接 1.2.1每端异位切入,独立顺序执行 2.确认应答、超时重传保证可靠传输 2.1已到报文的复发重传 2.1.1超时重传可能 2.1.2快速重传不可能 2.2请求响应消息的应用 2.2.1…

2026/10/14 9:27:10 阅读更多 →
生物分类系统:从界到种的科学解析与应用

生物分类系统:从界到种的科学解析与应用

1. 生物分类系统概述生物分类学(Taxonomy)作为生物学的基础学科,其核心任务是对地球上的生物进行系统性的识别、描述、命名和归类。现代生物分类体系采用"界门纲目科属种"七级分类阶元,这套体系自18世纪林奈创立以来&am…

2026/10/14 9:27:10 阅读更多 →
STEP 7:程序块保护

STEP 7:程序块保护

使用STEP 7编程工具,如果想对特定的程序块进行加密,可以采用程序块加密方式,此种加密方式分为两种: KNOW_HOW_PROTECT Block Privacy1 KNOW_HOW_PROTECT保护程序块 KNOW_HOW_PROTECT加密程序块方式与CPU固件版本无关&#xff0c…

2026/10/14 9:27:10 阅读更多 →
2026低代码平台测评:企业级能力与开发效率深度解析

2026低代码平台测评:企业级能力与开发效率深度解析

1. 低代码平台行业现状与测评背景 2026年的低代码开发领域已经进入成熟期,平台数量从2020年的不足百家激增至现在的300。这种爆发式增长带来两个显著变化:一方面,头部厂商通过持续并购形成生态壁垒;另一方面,垂直行业解…

2026/10/14 9:26:09 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/14 7:53:34 阅读更多 →