ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

GitHub Copilot for Xcode 自定义工具实战:为 Xcode AI 助手构建并调试你的第一个专用工具

GitHub Copilot for Xcode 自定义工具实战:为 Xcode AI 助手构建并调试你的第一个专用工具 GitHub Copilot for Xcode 自定义工具实战为 Xcode AI 助手构建并调试你的第一个专用工具【免费下载链接】CopilotForXcodeAI coding assistant for Xcode项目地址: https://gitcode.com/GitHub_Trending/cop/CopilotForXcode本文带你走一遍 GitHub Copilot for Xcode 的工具扩展机制从ICopilotTool协议讲起用一个最小可运行的工具示例串起参数校验、注册、完成回调三个关键点最后覆盖权限配置与常见故障的排查路径。读完你可以判断自己的需求适合做成哪种工具并且知道工具没反应时去哪里找日志。从一次真实故障说起AI 为什么需要你的工具假设你让聊天面板执行一次构建模型侧的 Agent 决定调用run_in_terminal但工具面板里显示的是 No built-in tools available. Make sure background permissions are granted.。这不是模型问题而是客户端工具链没跑通。这类问题的根源在于Copilot for Xcode 的工具分两层。服务端工具如read_file、grep_search由语言服务器执行代码里定义在 Tool/Sources/ConversationServiceProvider/ToolNames.swift 的ServerToolName中而客户端工具由本地 Swift 进程执行模型只能发起 JSON-RPC 调用真正干活的是你机器上的代码。run_in_terminal、create_file、get_errors都属于后者。客户端工具的价值就在于它能把模型会说的能力落地成本地真实执行的能力在 Xcode 当前工程目录下跑命令、创建文件并回滚、读取编辑器里的编译错误、抓取网页内容。如果你的工作流里有固定动作——跑 lint、生成报告、调用内部 CLI——把它做成客户端工具比每次都手写提示词稳定得多。接入前的检查项动手写代码之前先确认三件事能省掉后面一半的调试时间。1. 工具调用入口在哪。所有客户端调用都经过ChatService收到InvokeClientToolRequest后到CopilotToolRegistry.shared.getTool(name:)里按名字查表。查不到直接返回错误。也就是说名字对不上注册表工具永远不会被执行模型只会收到一条失败响应。2. 你的实现放在哪个 target。内置工具全部位于 Core/Sources/ChatService/ToolCalls/ 下依赖ConversationServiceProvider、XcodeInspector、Terminal等模块。新增工具文件时确认它被加进了编译目标并且 import 的模块在 Package 依赖图里可达。3. 后台权限是否授齐。工具界面为空最常见的原因就是权限缺失。辅助功能权限决定能否读取 Xcode 编辑器内容GetErrorsTool依赖它文件夹访问权限决定能否写工程外的路径。权限请求界面长这样如果权限都齐了工具仍然不出现检查 Communication Bridge 是否连上最小实现路径把协议读懂客户端工具只有一个协议要实现。public protocol ICopilotTool { func invokeTool( _ request: InvokeClientToolRequest, completion: escaping (AnyJSONRPCResponse) - Void, contextProvider: ToolContextProvider? ) - Bool }定义见 Core/Sources/ChatService/ToolCalls/ICopilotTool.swift。三个入参各有用途request携带id、name、input参数表input里是模型给的键值对取值要靠input[key]?.value as? T做类型转换不能假定类型一定正确completion唯一的返回通道。工具无论成功失败都必须调用它一次否则这一轮对话会挂起contextProvider可选。实现类是ChatService本身提供chatTabInfo当前工作区路径、聊天页信息、updateFileEdits登记文件改动供撤销、updateChatHistory把本轮工具调用写进对话记录。只读类工具可以完全忽略它。返回值Bool表示这次调用是否已经处理完。同步能出结果的工具直接返回true需要等待终端输出的工具也返回true然后在Task里执行、在回调里补completion——参考RunInTerminalTool的做法它拿到workspacePath后通过XcodeInspector解析出工程根目录作为命令工作目录。参数校验缺失就立刻报错CreateFileTool的开头是个典型的 guard 链params、input、filePath、content任一为 nil立即用status: .error回包并return true。不要先执行再报错也不要静默吞掉——模型后续动作依赖这条错误文本来修正参数。guard let filePath input[filePath]?.value as? String, let content input[content]?.value as? String else { completeResponse(request, status: .error, response: Invalid parameters, completion: completion) return true }协议扩展里预置了completeResponse(_:status:response:completion:)status可取.success、.error、.cancelled内部会把字符串包进LanguageModelToolResult再编码成 JSON-RPC 响应。多段输出用completeResponses传数组。异步完成回调只调一次且要带上错误分支异步工具最容易踩的坑是某条代码路径漏调completion。对照CreateFileTool的完整分支参数非法、文件已存在、写入抛异常、写后校验失败——四条路径每条都调了completeResponse再return true。写自己的工具时把每个catch和每个guard else都当作必须回包的检查点。另外注意回包文案里带上具体错误比如写入失败时附上error信息模型和人都能直接定位。把工具挂进 Copilot注册位置与重名检查注册表是个单例注册写在私有初始化里key 是ToolName枚举的 rawValuepublic class CopilotToolRegistry { public static let shared CopilotToolRegistry() private var tools: [String: ICopilotTool] [:] private init() { tools[ToolName.createFile.rawValue] CreateFileTool() // 新工具在这里加一行 } }新工具先给 ToolNames.swift 的ToolName枚举加一个 caserawValue 用 snake_case如build_project再在init里加一行映射。两个检查点key 不能重复。字典后写覆盖前写重名时旧工具静默消失且没有任何日志提示——合并代码后先全局搜一遍 rawValuerawValue 要和服务端声明的工具名一致因为ChatService是按模型发来的params.name查表的两边拼写差一个字母就是查不到。让工具返回可读错误错误信息是模型和人共用的接口按这个标准写可定位带上文件路径、命令、终端会话 id 这类具体值。GetTerminalOutputTool在找不到会话时返回 Terminal id X not found而不是空字符串可行动说明下一步该做什么比如 File already exists at /path 比 create failed 更能引导模型改用编辑工具不泄漏敏感内容日志里记录原始error回包给模型的文本保持简洁。CreateFileTool还示范了一个细节写完文件后重新读取校验内容存在失败也回.error。文件系统操作以落盘校验通过为成功边界而不是以没抛异常为边界。调试与排障闭环工具开发最耗时的是验证。建议按这条闭环走先在工具面板确认可见。打开 Tools 设置里的 Built-In Tools 列表实现见 Core/Sources/HostApp/ToolsSettings/BuiltInToolsListView.swift界面打开时会调refreshClientTools()拉取工具清单。工具不在列表里问题在权限或注册与你的实现逻辑无关用日志看调用是否到达。所有内置工具都用Logger.client记日志在工具入口和每个错误分支各打一条带上request.params?.name与关键参数。调用没到入口 查表失败到了入口没回包 你的某条分支漏了completion在聊天里触发并观察对话记录。实现updateChatHistory后每次工具调用会以 AgentRound 形式写进对话工具面板会显示调用状态ToolCallStatusUpdater负责推进状态可以直接看到模型传了什么参数、收到什么响应验证副作用。文件类工具检查目标文件、终端类工具检查会话输出并确认撤销路径可用——CreateFileTool.undo(for:)展示了配套实现登记进FileEdit后工作集的回滚才能删掉新建文件。稳定性建议阻塞控制耗时操作放Task或会话回调里不要卡在invokeTool主路径上RunInTerminalTool对后台命令直接返回 running with ID 让模型稍后用get_terminal_output取结果这个模式值得复用幂等与冲突文件写入前先查fileExists避免覆盖目录创建用withIntermediateDirectories: true资源释放终端会话按toolCallId建立结束的任务会话不再持有引用上下文依赖要显式降级contextProvider是可选的GetErrorsTool在拿不到 Xcode 实例或聚焦编辑器时返回空结果而不是崩溃——依赖外部 UI 状态的工具都要有这条退路。下一步工具跑通之后再看两件事一是自动审批。Core/Sources/ChatService/ToolCalls/AutoApproval/ 下的ToolAutoApprovalManager支持按终端、敏感文件等维度配置免确认执行新工具想进免确认流程需要接入对应的 ApprovalStorage二是自定义 Agent 模式工具开关按模式分别生效BuiltInToolsListView里按selectedMode区分如果你的工具只适合特定工作流把启用范围收窄到对应模式。从ToolName加一个 case、ToolCalls目录加一个文件、注册表加一行开始就是完整的扩展路径。先做一个只读的小工具比如汇总当前工程某个目录的文件清单跑通调用—回包—记录整条链路再上写文件、跑命令这类有副作用的工具风险最小。【免费下载链接】CopilotForXcodeAI coding assistant for Xcode项目地址: https://gitcode.com/GitHub_Trending/cop/CopilotForXcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表