当前位置: 首页 > news >正文

如何实现一个MCP server呢?

以github.com/mark3labs/mcp-go为例

开始以一个简单的查看文件列表工具作为范例,来展示该如何开发mcp server

func main() {// 创建 MCP 服务器mcpServer := server.NewMCPServer("file-server","1.0.0",server.WithResourceCapabilities(true, true),server.WithPromptCapabilities(true),server.WithToolCapabilities(true),)// 添加查看文件列表的工具mcpServer.AddTool(mcp.NewTool(ToolListFiles,mcp.WithDescription("List files in ~/Documents"),), handleListFiles)// 创建 SSE 服务器sseServer := server.NewSSEServer(mcpServer,server.WithBaseURL("http://localhost:8080"),server.WithSSEEndpoint("/sse"),server.WithMessageEndpoint("/message"),)// 启动服务器go func() {if err := sseServer.Start(":8080"); err != nil && err != http.ErrServerClosed {fmt.Printf("Failed to start server: %v\n", err)}}()fmt.Println("Server started on :8080")// 保持程序运行select {}
}// 处理查看文件列表的工具请求
func handleListFiles(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {documentsPath, err := os.UserHomeDir()if err != nil {return nil, err}documentsPath = filepath.Join(documentsPath, "Documents")files, err := ioutil.ReadDir(documentsPath)if err != nil {return nil, err}var fileNames []stringfor _, file := range files {fileNames = append(fileNames, file.Name())}return &mcp.CallToolResult{Content: []mcp.Content{mcp.TextContent{Type: "text",Text: fmt.Sprintf("%v", fileNames),},},}, nil
}

server启动

服务器初始化

接收服务器的名称、版本和一系列可选参数,用于配置服务器的功能和能力。通过传入不同的 ServerOption,可以启用或禁用特定的功能,如资源管理、工具调用、提示处理等

// NewMCPServer creates a new MCP server instance with the given name, version and options
func NewMCPServer(name, version string,opts ...ServerOption,
) *MCPServer {s := &MCPServer{resources:            make(map[string]resourceEntry),resourceTemplates:    make(map[string]resourceTemplateEntry),prompts:              make(map[string]mcp.Prompt),promptHandlers:       make(map[string]PromptHandlerFunc),tools:                make(map[string]ServerTool),name:                 name,version:              version,notificationHandlers: make(map[string]NotificationHandlerFunc),capabilities: serverCapabilities{tools:     nil,resources: nil,prompts:   nil,logging:   false,},}for _, opt := range opts {opt(s)}return s
}

资源、功能注册

MCP 服务器可以暴露各种资源和功能,包括资源(Resources)、工具(Tools)和提示(Prompts)。这些资源和功能需要在服务器启动前进行注册。

资源是MCP中的上下文对象,用于跨步骤携带信息、文件、图片、结构化数据等

{"resources": [{"id": "logs-result-123","type": "text","name": "分析结果","data": "这里是日志分析结果..."}]
}

将资源和对应的处理函数关联起来,当客户端请求读取该资源时,服务器会调用相应的处理函数来返回资源内容。

// AddResource registers a new resource and its handler
func (s *MCPServer) AddResource(resource mcp.Resource,handler ResourceHandlerFunc,
) {s.capabilitiesMu.Lock()if s.capabilities.resources == nil {s.capabilities.resources = &resourceCapabilities{}}s.capabilitiesMu.Unlock()s.resourcesMu.Lock()defer s.resourcesMu.Unlock()s.resources[resource.URI] = resourceEntry{resource: resource,handler:  handler,}
}

工具注册

工具是 MCP 中的一种功能,允许client调用mcp server上的特定操作。

// AddTool registers a new tool and its handler
func (s *MCPServer) AddTool(tool mcp.Tool, handler ToolHandlerFunc) {s.AddTools(ServerTool{Tool: tool, Handler: handler})
}// AddTools registers multiple tools at once
func (s *MCPServer) AddTools(tools ...ServerTool) {s.capabilitiesMu.Lock()if s.capabilities.tools == nil {s.capabilities.tools = &toolCapabilities{}}s.capabilitiesMu.Unlock()s.toolsMu.Lock()for _, entry := range tools {s.tools[entry.Tool.Name] = entry}s.toolsMu.Unlock()// Send notification to all initialized sessionss.sendNotificationToAllClients("notifications/tools/list_changed", nil)
}

server消息处理

HandleMessage 方法是 MCP 服务器处理传入 JSON - RPC 消息的核心方法,它会根据消息的类型和方法进行不同的处理,并返回相应的响应。比如初始化请求、心跳请求、列出资源请求、列出工具请求、调用工具等

// HandleMessage processes an incoming JSON-RPC message and returns an appropriate response
func (s *MCPServer) HandleMessage(ctx context.Context,message json.RawMessage,
) mcp.JSONRPCMessage {// Add server to contextctx = context.WithValue(ctx, serverKey{}, s)var err *requestErrorvar baseMessage struct {JSONRPC string        `json:"jsonrpc"`Method  mcp.MCPMethod `json:"method"`ID      any           `json:"id,omitempty"`}if err := json.Unmarshal(message, &baseMessage); err != nil {return createErrorResponse(nil,mcp.PARSE_ERROR,"Failed to parse message",)}// Check for valid JSONRPC versionif baseMessage.JSONRPC != mcp.JSONRPC_VERSION {return createErrorResponse(baseMessage.ID,mcp.INVALID_REQUEST,"Invalid JSON-RPC version",)}// 如果消息 ID 为空,说明这是一个通知消息if baseMessage.ID == nil {var notification mcp.JSONRPCNotification// 尝试将消息解析为通知消息结构体if err := json.Unmarshal(message, &notification); err != nil {// 如果解析通知消息失败,返回一个解析错误响应return createErrorResponse(nil,mcp.PARSE_ERROR,"Failed to parse notification",)}// 处理通知消息s.handleNotification(ctx, notification)// 通知消息不需要响应,返回 nilreturn nil }switch baseMessage.Method {   // 根据不同的方法名进行不同的处理switch baseMessage.Method {case mcp.MethodInitialize:// 初始化请求var request mcp.InitializeRequestvar result *mcp.InitializeResult// 尝试将消息解析为初始化请求结构体if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理初始化请求前执行一些操作s.hooks.beforeInitialize(ctx, baseMessage.ID, &request)// 处理初始化请求result, err = s.handleInitialize(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理初始化请求后执行一些操作s.hooks.afterInitialize(ctx, baseMessage.ID, &request, result)// 返回初始化请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodPing:// 心跳请求var request mcp.PingRequestvar result *mcp.EmptyResult// 尝试将消息解析为心跳请求结构体if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理心跳请求前执行一些操作s.hooks.beforePing(ctx, baseMessage.ID, &request)// 处理心跳请求result, err = s.handlePing(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理心跳请求后执行一些操作s.hooks.afterPing(ctx, baseMessage.ID, &request, result)// 返回心跳请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodResourcesList:// 列出资源请求var request mcp.ListResourcesRequestvar result *mcp.ListResourcesResult// 检查服务器是否支持资源功能if s.capabilities.resources == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("resources %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理列出资源请求前执行一些操作s.hooks.beforeListResources(ctx, baseMessage.ID, &request)// 处理列出资源请求result, err = s.handleListResources(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理列出资源请求后执行一些操作s.hooks.afterListResources(ctx, baseMessage.ID, &request, result)// 返回列出资源请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodResourcesTemplatesList:// 列出资源模板请求var request mcp.ListResourceTemplatesRequestvar result *mcp.ListResourceTemplatesResult// 检查服务器是否支持资源功能if s.capabilities.resources == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("resources %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理列出资源模板请求前执行一些操作s.hooks.beforeListResourceTemplates(ctx, baseMessage.ID, &request)// 处理列出资源模板请求result, err = s.handleListResourceTemplates(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理列出资源模板请求后执行一些操作s.hooks.afterListResourceTemplates(ctx, baseMessage.ID, &request, result)// 返回列出资源模板请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodResourcesRead:// 读取资源请求var request mcp.ReadResourceRequestvar result *mcp.ReadResourceResult// 检查服务器是否支持资源功能if s.capabilities.resources == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("resources %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理读取资源请求前执行一些操作s.hooks.beforeReadResource(ctx, baseMessage.ID, &request)// 处理读取资源请求result, err = s.handleReadResource(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理读取资源请求后执行一些操作s.hooks.afterReadResource(ctx, baseMessage.ID, &request, result)// 返回读取资源请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodPromptsList:// 列出提示请求var request mcp.ListPromptsRequestvar result *mcp.ListPromptsResult// 检查服务器是否支持提示功能if s.capabilities.prompts == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("prompts %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理列出提示请求前执行一些操作s.hooks.beforeListPrompts(ctx, baseMessage.ID, &request)// 处理列出提示请求result, err = s.handleListPrompts(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理列出提示请求后执行一些操作s.hooks.afterListPrompts(ctx, baseMessage.ID, &request, result)// 返回列出提示请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodPromptsGet:// 获取提示请求var request mcp.GetPromptRequestvar result *mcp.GetPromptResult// 检查服务器是否支持提示功能if s.capabilities.prompts == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("prompts %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理获取提示请求前执行一些操作s.hooks.beforeGetPrompt(ctx, baseMessage.ID, &request)// 处理获取提示请求result, err = s.handleGetPrompt(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理获取提示请求后执行一些操作s.hooks.afterGetPrompt(ctx, baseMessage.ID, &request, result)// 返回获取提示请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodToolsList:// 列出工具请求var request mcp.ListToolsRequestvar result *mcp.ListToolsResult// 检查服务器是否支持工具功能if s.capabilities.tools == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("tools %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理列出工具请求前执行一些操作s.hooks.beforeListTools(ctx, baseMessage.ID, &request)// 处理列出工具请求result, err = s.handleListTools(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理列出工具请求后执行一些操作s.hooks.afterListTools(ctx, baseMessage.ID, &request, result)// 返回列出工具请求的响应return createResponse(baseMessage.ID, *result)case mcp.MethodToolsCall:// 调用工具请求var request mcp.CallToolRequestvar result *mcp.CallToolResult// 检查服务器是否支持工具功能if s.capabilities.tools == nil {// 如果不支持,记录方法未找到错误信息err = &requestError{id:   baseMessage.ID,code: mcp.METHOD_NOT_FOUND,err:  fmt.Errorf("tools %w", ErrUnsupported),}} else if unmarshalErr := json.Unmarshal(message, &request); unmarshalErr != nil {// 如果解析失败,记录错误信息err = &requestError{id:   baseMessage.ID,code: mcp.INVALID_REQUEST,err:  &UnparseableMessageError{message: message, err: unmarshalErr, method: baseMessage.Method},}} else {// 调用钩子函数,在处理调用工具请求前执行一些操作s.hooks.beforeCallTool(ctx, baseMessage.ID, &request)// 处理调用工具请求result, err = s.handleToolCall(ctx, baseMessage.ID, request)}if err != nil {// 如果处理过程中出现错误,调用钩子函数记录错误信息,并返回错误响应s.hooks.onError(ctx, baseMessage.ID, baseMessage.Method, &request, err)return err.ToJSONRPCError()}// 调用钩子函数,在处理调用工具请求后执行一些操作s.hooks.afterCallTool(ctx, baseMessage.ID, &request, result)// 返回调用工具请求的响应return createResponse(baseMessage.ID, *result)default:// 如果方法名不匹配任何已知方法,返回方法未找到错误响应return createErrorResponse(baseMessage.ID,mcp.METHOD_NOT_FOUND,fmt.Sprintf("Method %s not found", baseMessage.Method),)}
}

初始化请求处理

handleInitialize 方法用于处理初始化请求,根据服务器配置设置相应的功能能力(资源、提示、工具、日志),构建并返回初始化结果,同时若上下文中存在客户端会话则对其进行初始化。

func (s *MCPServer) handleInitialize(ctx context.Context,id interface{},request mcp.InitializeRequest,
) (*mcp.InitializeResult, *requestError) {capabilities := mcp.ServerCapabilities{}// Only add resource capabilities if they're configuredif s.capabilities.resources != nil {capabilities.Resources = &struct {Subscribe   bool `json:"subscribe,omitempty"`ListChanged bool `json:"listChanged,omitempty"`}{Subscribe:   s.capabilities.resources.subscribe,ListChanged: s.capabilities.resources.listChanged,}}// Only add prompt capabilities if they're configuredif s.capabilities.prompts != nil {capabilities.Prompts = &struct {ListChanged bool `json:"listChanged,omitempty"`}{ListChanged: s.capabilities.prompts.listChanged,}}// Only add tool capabilities if they're configuredif s.capabilities.tools != nil {capabilities.Tools = &struct {ListChanged bool `json:"listChanged,omitempty"`}{ListChanged: s.capabilities.tools.listChanged,}}if s.capabilities.logging {capabilities.Logging = &struct{}{}}result := mcp.InitializeResult{ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,ServerInfo: mcp.Implementation{Name:    s.name,Version: s.version,},Capabilities: capabilities,Instructions: s.instructions,}if session := ClientSessionFromContext(ctx); session != nil {session.Initialize()}return &result, nil
}

资源列表请求处理

handleListResources 处理列出资源的请求。它从服务器中检索所有资源,对资源进行排序,并根据请求中的分页参数返回相应的资源列表

func (s *MCPServer) handleListResources(ctx context.Context,id interface{},request mcp.ListResourcesRequest,
) (*mcp.ListResourcesResult, *requestError) {// 对资源进行读锁定,防止在读取资源时其他 goroutine 修改资源s.resourcesMu.RLock()// 初始化一个切片,用于存储所有资源,切片的初始容量为服务器中资源的数量resources := make([]mcp.Resource, 0, len(s.resources))// 遍历服务器中存储的所有资源条目for _, entry := range s.resources {// 将每个资源条目中的资源添加到 resources 切片中resources = append(resources, entry.resource)}// 释放资源的读锁定s.resourcesMu.RUnlock()// 对资源切片进行排序,按照资源的名称进行升序排序sort.Slice(resources, func(i, j int) bool {return resources[i].Name < resources[j].Name})// 根据请求中的游标参数进行分页,返回当前页的资源列表、下一页的游标和可能的错误resourcesToReturn, nextCursor, err := listByPagination[mcp.Resource](ctx, s, request.Params.Cursor, resources)if err != nil {// 如果分页过程中出现错误,返回一个包含错误信息的请求错误对象return nil, &requestError{id:   id,code: mcp.INVALID_PARAMS,err:  err,}}// 创建一个 ListResourcesResult 对象,包含当前页的资源列表和下一页的游标result := mcp.ListResourcesResult{Resources: resourcesToReturn,PaginatedResult: mcp.PaginatedResult{NextCursor: nextCursor,},}// 返回包含资源列表和分页信息的结果对象,以及 nil 错误return &result, nil
}

工具调用请求处理

handleToolCall 处理调用工具的请求。它接收一个上下文、请求 ID 和调用工具的请求作为参数,然后查找对应的工具处理程序,应用中间件,并执行处理程序以获取工具调用的结果。

func (s *MCPServer) handleToolCall(ctx context.Context,id interface{},request mcp.CallToolRequest,
) (*mcp.CallToolResult, *requestError) {// 对工具进行读锁定,防止在查找工具时其他 goroutine 修改工具列表s.toolsMu.RLock()// 从服务器存储的工具中查找请求的工具tool, ok := s.tools[request.Params.Name]// 释放工具的读锁定s.toolsMu.RUnlock()// 如果未找到请求的工具,返回一个包含错误信息的请求错误对象if !ok {return nil, &requestError{id:   id,code: mcp.INVALID_PARAMS,err:  fmt.Errorf("tool '%s' not found: %w", request.Params.Name, ErrToolNotFound),}}// 获取找到的工具的处理程序finalHandler := tool.Handler// 对中间件进行读锁定,防止在获取中间件时其他 goroutine 修改中间件列表s.middlewareMu.RLock()// 获取服务器存储的所有工具处理程序中间件mw := s.toolHandlerMiddlewares// 释放中间件的读锁定s.middlewareMu.RUnlock()// 反向遍历中间件列表,将中间件依次应用到最终处理程序上// 这样可以确保中间件按照添加的顺序依次执行for i := len(mw) - 1; i >= 0; i-- {finalHandler = mw[i](finalHandler)}// 使用最终处理程序处理工具调用请求,获取工具调用的结果和可能的错误result, err := finalHandler(ctx, request)// 如果处理过程中出现错误,返回一个包含错误信息的请求错误对象if err != nil {return nil, &requestError{id:   id,code: mcp.INTERNAL_ERROR,err:  err,}}// 返回工具调用的结果和 nil 错误return result, nil
}
http://www.xdnf.cn/news/34543.html

相关文章:

  • 基于蚁群算法的柔性车间调度最优化设计
  • mysql的函数(第二期)
  • Linux下 文件的查找、复制、移动和解压缩
  • spring-batch批处理框架(1)
  • Qt项目——Tcp网络调试助手服务端与客户端
  • 时态--06--现在完成時
  • GPT-SoVITS 使用指南
  • 【概率论】条件期望
  • 【网络原理】UDP协议
  • 下采样(Downsampling)
  • stm32(gpio的四种输出)
  • c++:线程(std::thread)
  • java怎么找bug?Arthas原理与实战指南
  • opencv图像旋转(单点旋转的原理)
  • 中国AIOps行业分析
  • [dp19_01背包] 目标和 | 最后一块石头的重量 II
  • AUTOSAR图解==>AUTOSAR_SWS_IntrusionDetectionSystemManager
  • 652SJBH动漫网站Cosplay
  • 嵌入式芯片中的 低功耗模式 内容细讲
  • 【NLP 66、实践 ⑰ 基于Agent + Prompt Engineering文章阅读】
  • linux socket编程之udp(实现客户端和服务端消息的发送和接收)
  • Springboot+vue3开发项目——热点事件
  • [特殊字符] 高质量 Java 综合题 × 10(附应用场景 + 多知识点考核)
  • Spring Boot常用注解全解析:从入门到实战
  • 洛谷P1120 小木棍
  • 《AI大模型应知应会100篇》第26篇:Chain-of-Thought:引导大模型进行步骤推理
  • 94. 二叉树的中序遍历
  • Simulink中建立交流单项永磁同步电机模型教程
  • python——列表和元组
  • 深入剖析 HashMap:内部结构与性能优化