Go语言原生AI Agent框架:构建高性能、类型安全的智能应用

news2026/5/7 15:46:30
1. 项目概述为什么Go需要一个原生的AI Agent框架在当前的AI开发浪潮中Python生态几乎占据了绝对主导地位从PyTorch、TensorFlow到LangChain、LlamaIndex开发者们被海量的Python库所包围。作为一名长期在后台系统、高并发服务和云原生领域使用Go的工程师我常常感到一种割裂感当业务逻辑需要集成AI能力时我不得不在Go的主工程和Python的AI服务之间搭建复杂的RPC或HTTP桥梁这不仅引入了额外的运维复杂度也牺牲了Go在性能、部署简易性和类型安全上的天然优势。这就是我最初关注到neurocult/agency这个项目的原因。它不是一个简单的OpenAI API客户端封装而是一个野心勃勃的尝试旨在用纯Go、符合Go语言哲学的方式重新定义我们构建AI驱动应用特别是自主智能体Autonomous Agents的模式。它的核心主张是你不需要为了使用大语言模型而离开Go的舒适区。你可以用编写一个普通HTTP服务或并发任务调度器同样的清晰思维来编排LLM的调用、工具使用Function Calling和多步推理流程。简单来说Agency想成为Go生态中的“LangChain”但摒弃了后者因翻译自Python而产生的设计上的“水土不服”。它从零开始用interface、struct和channel这些Go开发者熟悉的原语构建了一套用于组合AI操作Operations和流程Processes的抽象层。对于任何苦于在Go中集成AI功能又不想引入沉重外部依赖的团队来说这无疑是一个值得深入探索的解决方案。2. 核心设计哲学简洁、组合与可观测性Agency的设计深深植根于Go语言的几个核心哲学简洁、显式优于隐式、组合优于继承。这使其与Python生态中常见的AI框架产生了鲜明对比。2.1 纯Go实现与类型安全首先Agency是一个纯Go模块。这意味着你只需要一个go get命令无需管理Python环境、虚拟环境或复杂的C依赖。这对于构建需要最终编译成单一静态二进制文件的微服务或CLI工具来说是巨大的优势。所有的操作、消息和配置都是强类型的编译器能在你运行前就捕获大量的错误比如错误地传递了消息类型或缺少必要的参数。// 错误示例编译器会报错因为Execute期望的是agency.Message而不是字符串。 // answer, err : assistant.Execute(ctx, “Hello”) // 编译错误 // 正确示例使用强类型的Message构造器 input : agency.NewTextMessage(agency.UserRole, “Hello”) answer, err : assistant.Execute(ctx, input)这种类型安全在构建复杂的工作流时至关重要。当你在组合多个操作例如先调用LLM生成计划再调用一个搜索工具最后让LLM总结时你能清晰地定义每个步骤的输入输出格式避免运行时才发现数据格式不匹配的尴尬。2.2 清晰的架构操作、流程与拦截器Agency将AI能力抽象为三个核心概念它们共同构成了库的骨架操作Operation这是最基本的执行单元。一个操作就是一个实现了agency.Operation接口的类型它接收一个上下文Context和一条输入消息Message并返回一条输出消息或错误。OpenAI的文本补全、图像生成甚至你自定义的一个数据库查询都可以被封装成一个操作。type Operation interface { Execute(ctx context.Context, input Message) (Message, error) }流程Process这是Agency真正强大的地方。一个流程可以将多个操作按照特定逻辑组合起来形成一个完整的工作流。库内置了SequentialProcess顺序执行、ParallelProcess并行执行等你也可以轻松创建自己的流程逻辑。这相当于为你提供了构建复杂AI智能体的乐高积木。拦截器Interceptor这是实现可观测性和中间件功能的关键。你可以在操作或流程执行前后注入拦截器用于日志记录、性能监控、修改输入输出、实现重试逻辑等。这遵循了Go中常见的中间件模式让横切关注点Cross-Cutting Concerns的管理变得非常优雅。这种设计迫使开发者进行清晰的关注点分离。你的业务逻辑定义在“流程”中而具体的模型调用如调用GPT-4被封装在“操作”里。如果你想从OpenAI切换到Anthropic的Claude理论上你只需要换掉底层的操作实现上层的业务流程几乎不需要改动。实操心得刚开始接触时不要试图一次性理解所有概念。先从定义一个最简单的“文本对话”操作开始感受如何创建、配置并执行它。然后尝试将两个操作比如“生成SQL”和“执行SQL”组合成一个顺序流程。最后再为这个流程添加一个打印日志的拦截器。通过这个“三步走”的实践你能最直观地体会到Agency设计上的层次感。3. 从零开始快速上手与核心API详解让我们暂时忘掉那些复杂的概念直接从一个最经典的“聊天助手”例子开始亲手敲一遍代码这是理解任何框架最快的方式。3.1 环境准备与安装首先确保你的Go版本在1.20以上。然后创建一个新模块并引入Agencymkdir my-ai-agent cd my-ai-agent go mod init my-ai-agent go get github.com/neurocult/agency由于Agency的核心设计是与具体AI提供商解耦的我们还需要安装一个“提供商适配器”。这里我们使用官方维护的OpenAI适配器它也兼容其他提供OpenAI兼容API的服务如LocalAI、Ollama。go get github.com/neurocult/agency/providers/openai接下来你需要一个OpenAI的API密钥。建议将其存储在环境变量中。我们可以使用godotenv库来从.env文件加载这在开发时非常方便。go get github.com/joho/godotenv在项目根目录创建.env文件OPENAI_API_KEYsk-your-actual-openai-api-key-here3.2 构建你的第一个AI助手现在创建一个main.go文件让我们实现一个简单的命令行聊天程序。package main import ( bufio context fmt os strings // 自动加载 .env 文件中的环境变量 _ github.com/joho/godotenv/autoload github.com/neurocult/agency github.com/neurocult/agency/providers/openai ) func main() { // 1. 创建OpenAI提供者实例 provider : openai.New(openai.Params{ Key: os.Getenv(OPENAI_API_KEY), // 从环境变量读取密钥 // 你可以在这里配置BaseURL以指向其他兼容API如本地部署的模型 // BaseURL: “http://localhost:8080/v1”, }) // 2. 创建一个“文本到文本”的操作这本质上是一个配置好的LLM调用器 assistant : provider. TextToText(openai.TextToTextParams{ Model: “gpt-4o-mini”, // 指定模型也支持 gpt-4-turbo, gpt-3.5-turbo 等 Temperature: 0.7, // 控制创造性默认值通常为0.7 MaxTokens: 1000, // 限制回复的最大长度 }). SetPrompt(“You are a concise and helpful programming assistant. Answer in a clear and technical manner.”) // 设置系统提示词 // 3. 初始化消息历史记录。Agency使用Message数组来维护对话上下文。 messages : []agency.Message{} reader : bufio.NewReader(os.Stdin) ctx : context.Background() fmt.Println(“Chat with AI Assistant (type ‘quit’ to exit)”) fmt.Println(“”) for { fmt.Print(“\nYou: “) userInput, _ : reader.ReadString(‘\n’) userInput strings.TrimSpace(userInput) if userInput “quit” { break } // 4. 将用户输入包装成Agency的Message对象。UserRole表明这是用户消息。 inputMsg : agency.NewTextMessage(agency.UserRole, userInput) // 5. 执行操作 // SetMessages(messages) 将历史对话上下文注入本次执行。 // Execute 方法返回一个Message其中包含AI的回复。 answer, err : assistant.SetMessages(messages).Execute(ctx, inputMsg) if err ! nil { fmt.Printf(“Error: %v\n”, err) continue } // 6. 处理回复 reply : string(answer.Content()) fmt.Printf(“Assistant: %s\n”, reply) // 7. 更新消息历史为下一轮对话做准备。 // 通常需要将本轮的用户消息和AI回复都追加进去。 messages append(messages, inputMsg, answer) // 注意在实际长时间对话中需要管理上下文窗口长度 // 避免超出模型的Token限制。可以编写一个拦截器来智能截断历史。 } }运行这个程序go run main.go你应该能和一个GPT-4o-mini模型进行对话了。虽然这看起来只是一个简单的API调用封装但请留意我们构建“助手”的链式调用方式provider.TextToText(...).SetPrompt(...)这已经体现了Agency的流畅API设计思想。注意事项SetMessages方法在每次执行时都会设置完整的上下文。这意味着如果你在循环外创建了assistant对象并在循环内不断调用SetMessages那么每次调用都是独立的。这种设计给了你极大的灵活性你可以为不同的对话会话轻松管理不同的历史记录而不是被一个全局的会话状态所束缚。4. 进阶实践构建复杂流程与自定义操作单一的问-答模式远非AI能力的全部。真正的价值在于将多个步骤串联起来形成自动化的工作流。这就是Agency中“流程Process”和“自定义操作”大显身手的地方。4.1 组合操作实现一个简单的RAG流程检索增强生成RAG是当前让LLM获取最新、特定领域知识的主流方案。其核心流程是用户提问 - 从知识库检索相关文档 - 将文档作为上下文连同问题一起提交给LLM - 得到基于知识的回答。假设我们有一个极其简单的“内存知识库”让我们用Agency的SequentialProcess来模拟这个流程。package main import ( “context” “fmt” “strings” “github.com/neurocult/agency” “github.com/neurocult/agency/processors” “github.com/neurocult/agency/providers/openai” ) // 1. 定义一个自定义的“检索”操作 type simpleRetriever struct { docs []string } func (r *simpleRetriever) Execute(ctx context.Context, input agency.Message) (agency.Message, error) { question : string(input.Content()) // 模拟检索逻辑这里只是简单查找包含关键词的文档 var retrievedDocs []string for _, doc : range r.docs { if strings.Contains(doc, question) { retrievedDocs append(retrievedDocs, doc) } } // 将检索到的文档合并成一个上下文字符串 context : strings.Join(retrievedDocs, “\n\n”) // 返回一个新的Message角色可以是自定义的这里用SystemRole表示系统提供的上下文 return agency.NewTextMessage(agency.SystemRole, context), nil } func main() { provider : openai.New(openai.Params{Key: os.Getenv(“OPENAI_API_KEY”)}) llm : provider.TextToText(openai.TextToTextParams{Model: “gpt-4o-mini”}) // 2. 初始化我们的“知识库” retriever : simpleRetriever{ docs: []string{ “Agency is a Go library for building AI agents.”, “It emphasizes clean architecture and composability.”, “The core concepts are Operation, Process, and Interceptor.”, “You can chain operations together to form complex workflows.”, }, } // 3. 定义一个“提示词组装”操作使用Processors // Processors是Agency提供的一些常用操作工具集。 promptAssembler : processors.PromptTemplate( “Answer the question based on the following context. If the context doesn’t contain the answer, say ‘I don’t know’.\n\nContext:\n{{.context}}\n\nQuestion: {{.question}}\n\nAnswer:”, ) // 4. 使用SequentialProcess组合流程 // 流程的输入是用户问题输出是最终答案。 ragPipeline : processors.SequentialProcess( // 第一步执行检索输出是上下文。 retriever, // 第二步将上一步的上下文和原始问题组装成给LLM的最终提示词。 // 这里使用了Processors中的Map将多个输入合并传递给下一个操作。 processors.Map(func(ctx context.Context, inputs …agency.Message) ([]agency.Message, error) { // inputs[0] 是原始用户问题 // inputs[1] 是检索器返回的上下文 question : string(inputs[0].Content()) context : string(inputs[1].Content()) // 将两个变量注入到promptAssembler assembledInput : agency.NewUserMessage(map[string]any{ “question”: question, “context”: context, }) return []agency.Message{assembledInput}, nil }), // 第三步将组装好的提示词发给LLM。 promptAssembler, // 第四步LLM生成最终答案。 llm, ) // 5. 执行流程 ctx : context.Background() question : agency.NewTextMessage(agency.UserRole, “What are the core concepts of Agency?”) answer, err : ragPipeline.Execute(ctx, question) if err ! nil { panic(err) } fmt.Printf(“Q: %s\n”, string(question.Content())) fmt.Printf(“A: %s\n”, string(answer.Content())) // 预期输出会基于我们提供的“知识库”文档进行回答。 }这个例子虽然简单但它清晰地展示了如何将不同的“操作”像管道一样连接起来。SequentialProcess确保它们按顺序执行并将上一个操作的输出传递给下一个作为输入。在实际项目中你的“检索器”可以替换为连接Pinecone、Weaviate等向量数据库的复杂操作。4.2 利用拦截器实现重试与日志在生产环境中网络波动或API限流可能导致单次调用失败。为操作添加重试机制是提升鲁棒性的常见做法。同时记录每个步骤的输入输出对于调试和监控至关重要。Agency的拦截器让这一切变得简单。package main import ( “context” “fmt” “log” “time” “github.com/neurocult/agency” “github.com/neurocult/agency/providers/openai” ) // 重试拦截器 func withRetry(maxAttempts int, delay time.Duration) agency.Interceptor { return func(next agency.Operation) agency.Operation { return agency.OperationFunc(func(ctx context.Context, msg agency.Message) (agency.Message, error) { var lastErr error for i : 0; i maxAttempts; i { if i 0 { log.Printf(“Retry attempt %d for operation”, i) time.Sleep(delay) } result, err : next.Execute(ctx, msg) if err nil { return result, nil } lastErr err // 可以根据错误类型决定是否重试这里简化处理所有错误都重试 log.Printf(“Operation failed (attempt %d): %v”, i1, err) } return nil, fmt.Errorf(“operation failed after %d attempts: %w”, maxAttempts, lastErr) }) } } // 日志拦截器 func withLogging(name string) agency.Interceptor { return func(next agency.Operation) agency.Operation { return agency.OperationFunc(func(ctx context.Context, msg agency.Message) (agency.Message, error) { start : time.Now() log.Printf(“[%s] Input: %s”, name, string(msg.Content())[:min(50, len(string(msg.Content())))]) // 日志截断前50字符 defer func() { log.Printf(“[%s] Finished in %v”, name, time.Since(start)) }() result, err : next.Execute(ctx, msg) if err ! nil { log.Printf(“[%s] Error: %v”, name, err) } else { log.Printf(“[%s] Output: %s”, name, string(result.Content())[:min(50, len(string(result.Content())))]) } return result, err }) } } func min(a, b int) int { if a b { return a }; return b } func main() { provider : openai.New(openai.Params{Key: os.Getenv(“OPENAI_API_KEY”)}) // 创建一个基础的LLM操作 baseLLM : provider.TextToText(openai.TextToTextParams{Model: “gpt-4o-mini”}) // 使用拦截器包装操作形成一个新的、增强后的操作 robustLLM : baseLLM. WithInterceptors( withLogging(“LLM_Call”), withRetry(3, time.Second*2), ) // 现在使用robustLLM它就自带日志和重试功能了 ctx : context.Background() input : agency.NewTextMessage(agency.UserRole, “Explain quantum computing in one sentence.”) output, err : robustLLM.Execute(ctx, input) if err ! nil { log.Fatal(err) } fmt.Println(string(output.Content())) }拦截器的强大之处在于它的可组合性和非侵入性。你可以在开发阶段添加详细的日志拦截器在生产环境移除或替换为更轻量的监控拦截器。重试、超时、缓存、审计等横切逻辑都可以通过拦截器来实现而你的核心业务代码即操作和流程的定义完全不受影响。实操心得设计自定义操作时尽量让每个操作只做一件事并保持明确的输入输出。这样的操作就像纯函数一样更容易测试、复用和组合。拦截器是处理副作用如日志、重试的最佳位置。这种模式会让你构建的AI工作流代码非常清晰易于维护和扩展。5. 深入探索外部函数调用与智能体构建Agency的愿景是构建自主智能体而智能体区别于简单聊天机器人的关键能力之一就是使用工具Tool Use即OpenAI API中的“Function Calling”。Agency对此提供了优雅的支持。5.1 定义和使用工具假设我们想让AI助手能查询天气。我们需要做三件事1) 定义一个能被调用的天气查询函数2) 将这个函数描述告诉LLM3) 处理LLM的响应并实际调用函数。package main import ( “context” “encoding/json” “fmt” “log” “github.com/neurocult/agency” “github.com/neurocult/agency/providers/openai” openaiLib “github.com/sashabaranov/go-openai” // 使用底层的OpenAI Go SDK类型 ) // 1. 定义工具函数的结构。这需要匹配OpenAI的Function定义。 var getWeatherTool openai.ToolDefinition{ Type: “function”, Function: openaiLib.FunctionDefinition{ Name: “get_current_weather”, Description: “Get the current weather in a given location”, Parameters: map[string]any{ “type”: “object”, “properties”: map[string]any{ “location”: map[string]any{ “type”: “string”, “description”: “The city and state, e.g. San Francisco, CA”, }, “unit”: map[string]any{ “type”: “string”, “enum”: []string{“celsius”, “fahrenheit”}, }, }, “required”: []string{“location”}, }, }, } // 2. 实现工具函数 func getCurrentWeather(location, unit string) string { // 这里应该是调用真实天气API我们模拟一下 log.Printf(“[Tool Called] get_current_weather with location%s, unit%s”, location, unit) weatherMap : map[string]string{ “Beijing”: “Sunny, 22 degrees Celsius”, “London”: “Cloudy, 15 degrees Celsius”, “San Francisco”: “Foggy, 18 degrees Celsius”, } if report, ok : weatherMap[location]; ok { return report } return fmt.Sprintf(“Weather information for %s is currently unavailable.”, location) } func main() { provider : openai.New(openai.Params{Key: os.Getenv(“OPENAI_API_KEY”)}) // 3. 创建支持工具调用的LLM操作 assistant : provider. TextToText(openai.TextToTextParams{ Model: “gpt-4o”, }). WithTools([]openai.ToolDefinition{getWeatherTool}) // 注入工具定义 ctx : context.Background() messages : []agency.Message{ agency.NewTextMessage(agency.UserRole, “What’s the weather like in Beijing and London?”), } // 4. 执行对话 response, err : assistant.SetMessages(messages).Execute(ctx) if err ! nil { log.Fatal(err) } // 5. 检查响应类型 respMsg : response.(*openai.TextMessage) // 类型断言因为返回的是特定provider的消息 log.Printf(“Raw Response Role: %s”, respMsg.Role) log.Printf(“Raw Response Content: %s”, string(respMsg.Content)) // 重点处理工具调用 if respMsg.ToolCalls ! nil len(respMsg.ToolCalls) 0 { log.Println(“AI requested to call a tool.”) // 6. 解析工具调用请求 for _, toolCall : range respMsg.ToolCalls { if toolCall.Function.Name “get_current_weather” { var args struct { Location string json:“location” Unit string json:“unit,omitempty” } if err : json.Unmarshal([]byte(toolCall.Function.Arguments), args); err ! nil { log.Printf(“Failed to parse tool arguments: %v”, err) continue } if args.Unit “” { args.Unit “celsius” } // 7. 执行真实的工具函数 weatherResult : getCurrentWeather(args.Location, args.Unit) // 8. 将工具执行结果作为新的消息追加到对话历史中并再次调用LLM toolResultMsg : openai.NewToolResultMessage(toolCall.ID, weatherResult) messages append(messages, response, toolResultMsg) // 追加AI的请求和工具结果 // 第二次执行让LLM基于工具结果生成最终回复 finalResponse, err : assistant.SetMessages(messages).Execute(ctx) if err ! nil { log.Fatal(err) } fmt.Printf(“Assistant: %s\n”, string(finalResponse.Content())) return } } } else { // 没有工具调用直接输出回复 fmt.Printf(“Assistant: %s\n”, string(response.Content())) } }这个过程虽然看起来步骤不少但Agency已经将最复杂的部分——与OpenAI API中tool_calls字段的交互——封装好了。你只需要关注三件事定义工具、实现函数、在收到工具调用请求时执行函数并将结果返回给对话流。这种模式是构建能够执行代码、查询数据库、调用API的真正“智能体”的基础。5.2 构建自主智能体的雏形将工具调用、流程组合和状态管理结合起来我们就可以设计一个更自主的智能体。例如一个能根据目标自动规划并执行步骤的研究助手。// 概念性代码展示思路 type ResearchAgent struct { planner agency.Operation // 负责拆解目标的LLM searcher agency.Operation // 负责网络搜索的自定义操作 summarizer agency.Operation // 负责总结的LLM memory []agency.Message // 存储整个任务的历史 } func (a *ResearchAgent) Execute(ctx context.Context, goal string) (string, error) { // 1. 规划步骤 plan, err : a.planner.Execute(ctx, agency.NewUserMessage(“Plan steps to research: ” goal)) // ... 解析计划 ... // 2. 对每个步骤可能调用搜索工具 for _, step : range steps { searchResult, err : a.searcher.Execute(ctx, agency.NewUserMessage(step)) // ... 处理结果存入memory ... } // 3. 基于所有搜索结果进行总结 finalReport, err : a.summarizer.SetMessages(a.memory).Execute(ctx, agency.NewUserMessage(“Write a final report”)) return string(finalReport.Content()), err }Agency库目前正在向这个方向积极开发目标是提供一套更强大的API来简化这类“自主循环”智能体的构建。其核心思想仍然是组合将规划、执行、观察、总结等不同能力的“操作”组合成一个能自主运行的“流程”。6. 常见问题、排查技巧与生态展望在实际使用和探索Agency的过程中你可能会遇到一些典型问题。以下是我在项目实践和源码阅读中总结的一些经验。6.1 常见问题速查表问题现象可能原因解决方案panic: runtime error: invalid memory address or nil pointer dereference1.OPENAI_API_KEY环境变量未设置或为空。2. Provider初始化失败但后续仍尝试调用其方法。1. 检查.env文件或环境变量确保密钥正确加载。使用fmt.Println(os.Getenv(“OPENAI_API_KEY”))调试。2. 确保provider : openai.New(...)成功执行例如参数格式正确。错误Post “https://api.openai.com/v1/chat/completions“: context deadline exceeded网络连接超时或OpenAI API响应缓慢。1. 为操作添加上下文超时ctx, cancel : context.WithTimeout(context.Background(), 30*time.Second)。2. 使用拦截器实现重试逻辑见上文示例。工具调用Function Calling不生效1. 使用的模型不支持工具调用如gpt-3.5-turbo-instruct。2. 工具定义ToolDefinition的JSON Schema格式不正确。3. 没有正确处理ToolCalls响应。1. 确保使用支持工具调用的模型如gpt-4o,gpt-4-turbo,gpt-3.5-turbo-1106及更新版本。2. 仔细对照OpenAI官方文档检查参数定义。3. 参考上文示例正确解析respMsg.ToolCalls字段并执行函数。对话历史上下文管理混乱错误地管理了messages切片导致上下文丢失或重复。牢记SetMessages()设置的是本次执行的完整上下文。推荐维护一个独立的[]agency.Message切片每次执行后将用户的输入和AI的输出都追加进去。对于长对话需要实现一个截断策略如只保留最近N条消息或优先截断最早的非系统消息。如何切换AI提供商目前官方主要维护OpenAI适配器。Agency的设计是Provider无关的。你可以参考providers/openai的源码为Anthropic、Cohere、本地模型通过Ollama等实现自己的Provider和Operation。社区正在贡献更多适配器。6.2 性能与调试技巧利用拦截器进行性能剖析可以轻松创建一个拦截器记录每个操作的执行耗时帮助你定位工作流中的性能瓶颈。func withMetrics(name string) agency.Interceptor { return func(next agency.Operation) agency.Operation { return agency.OperationFunc(func(ctx context.Context, msg agency.Message) (agency.Message, error) { start : time.Now() defer func() { metrics.RecordDuration(name, time.Since(start)) }() return next.Execute(ctx, msg) }) } }流式输出Streaming对于需要长时间生成文本的场景流式输出能极大提升用户体验。Agency的OpenAI Provider支持流式响应。你需要处理Stream通道并实时处理收到的文本块。stream, err : assistant.SetMessages(messages).Stream(ctx, input) if err ! nil { … } for chunk : range stream { if chunk.Error ! nil { … } fmt.Print(string(chunk.Content)) }结构化输出JSON Mode与输出解析这是让LLM返回稳定结构数据的重要特性。Agency通过SetResponseFormat等方法支持。结合Go的json.Unmarshal可以可靠地将LLM输出转换为你的业务结构体。这在构建数据提取、分类等应用时必不可少。6.3 生态现状与未来展望Agency是一个相对年轻但设计理念清晰的项目。它的优势在于“Go原生”为Go开发者提供了符合语言习惯的AI应用构建体验。然而其生态相比Python的LangChain还处于早期阶段。现有能力核心抽象Operation, Process, Interceptor非常稳固OpenAI适配器功能完整覆盖了聊天补全、视觉、语音、函数调用等主流API。组合能力是其最大亮点。待完善部分如官方路线图所示更多的Provider适配器如Anthropic、Gemini、更强大的智能体原语如ReAct模式、内置的输出解析器Output Parsers等都在开发中。社区参与是加速其成熟的关键。适用场景当前特别适合需要在Go服务中深度集成AI能力且希望保持代码简洁、类型安全、部署简单的团队。对于需要大量现成“工具链”如与上百种数据库、应用集成的工具的复杂场景可能还需要等待社区贡献或自行开发。我个人非常看好这种“小而美”的框架。它不追求大而全而是专注于提供一套坚实、可扩展的底层抽象。这给了开发者最大的灵活性可以根据自己的业务需求去构建上层建筑而不是被框架的设计所绑架。对于Go社区来说Agency的出现填补了一个重要的空白让Go在AI应用开发领域有了一席之地。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2584419.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…