nofx MCP RequestBuilder 构建器实战指南:多轮对话、Function Calling 与精细参数控制
AI Agent金融科技后端前端【免费下载链接】nofxYour AI trading terminal assistant for US stocks, commodities, forex, and crypto.项目地址https://gitcode.com/gh_mirrors/nof/nofx点击查看免费下载导读nofx 的 mcp 包 是一个基于 Go 标准库、零外部依赖的多 Provider AI 客户端库其核心的RequestBuilder构建器 API 提供了一套比传统CallWithMessages更强大的请求构造方式支持多轮对话、Function Calling、采样参数精细控制与场景化预设。本文以mcp/intro/BUILDER_EXAMPLES.md为骨架结合mcp/request_builder.go、mcp/request.go、mcp/client.go等源码实现系统讲解构建器的完整用法、参数取值范围、预设场景参数以及底层调用链帮助你写出一份可复制、可运行的 Go 集成代码。一、基础用法用构建器发起第一次对话1.1 简单对话RequestBuilder采用链式调用先以NewRequestBuilder()创建构建器再通过WithSystemPrompt/WithUserPrompt追加消息最后Build()产出*Request对象交给客户端的CallWithRequest发送package main import ( fmt nofx/mcp ) func main() { // 创建客户端 client : mcp.NewDeepSeekClientWithOptions( mcp.WithAPIKey(sk-xxx), ) // 使用构建器创建请求 request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a helpful assistant). WithUserPrompt(What is Go programming language?). Build() // 调用 API result, err : client.CallWithRequest(request) if err ! nil { panic(err) } fmt.Println(result) }从源码看WithSystemPrompt/WithUserPrompt在空字符串时会直接跳过避免向消息列表写入空消息request_builder.go而Build()会做一次合法性校验——当消息列表为空时返回at least one message is required错误request_builder.go对应的测试用例见 request_builder_test.go。1.2 与传统方式对比旧 APICallWithMessages(system, user)仍然可用且完全兼容构建器 API 则把请求的“构造”与“发送”分离便于复用、组合与测试// 传统方式仍然可用 result, err : client.CallWithMessages( You are a helpful assistant, What is Go?, ) // 构建器方式新API功能更强大 request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a helpful assistant). WithUserPrompt(What is Go?). Build() result, err : client.CallWithRequest(request)两者的底层都走同一个Call模板流程client.go区别在于构建器生成的*Request可以携带 temperature、max_tokens、tools、tool_choice、stop 等高级字段最终由BuildRequestBodyFromRequest序列化为 OpenAI 兼容的 JSON 请求体client.go。二、多轮对话把上下文完整交给模型2.1 带上下文的对话多轮对话的本质是把system、user、assistant三种角色的消息按时间顺序全部放进messages数组。构建器提供的AddSystemMessage/AddUserMessage/AddAssistantMessage与With*Prompt完全等价均通过 request_builder.go 内部追加对应角色的Message// 构建包含历史的多轮对话 request : mcp.NewRequestBuilder(). AddSystemMessage(You are a trading advisor). AddUserMessage(Analyze BTC price). AddAssistantMessage(BTC is currently in an upward trend...). AddUserMessage(Whats the best entry point?). // 继续对话 WithTemperature(0.3). // 低温度更精确 Build() result, err : client.CallWithRequest(request)在 nofx 的量化交易场景中这种“先分析后追问入场点”的模式非常典型——把上一步的assistant回复原样回传模型才能基于已有结论继续推理。2.2 从历史记录构建如果对话历史是持久化保存的可以先用mcp.NewUserMessage/mcp.NewAssistantMessage等构造函数组装[]mcp.Message再用AddConversationHistory一次性注入// 假设你有保存的对话历史 history : []mcp.Message{ mcp.NewUserMessage(Hello), mcp.NewAssistantMessage(Hi! How can I help?), mcp.NewUserMessage(Whats the weather?), mcp.NewAssistantMessage(Its sunny today), } // 继续对话 request : mcp.NewRequestBuilder(). AddSystemMessage(You are helpful). AddConversationHistory(history). // 添加历史 AddUserMessage(What about tomorrow?). // 新问题 Build() result, err : client.CallWithRequest(request)消息构造函数NewSystemMessage/NewUserMessage/NewAssistantMessage与通用NewMessage(role, content)均定义在 request.go。Message结构体还额外支持ReasoningContent思考模型推理内容多轮对话中需原样回传、ToolCalls与ToolCallID工具调用专用这些字段在 client.go 的序列化逻辑中被分别处理为tool_calls/tool_call_id字段。上下文保护提示当Config.MaxContext大于 0 时客户端会在发送前自动估算 token 数并截断最旧的非 system 消息防止超出模型上下文窗口见 context_guard.go 与WithMaxContext选项。对于长历史对话建议通过mcp.WithMaxContext(131072)显式声明模型上下文容量。三、参数精细控制温度、top_p、惩罚与停止序列构建器对每个采样参数都做了取值范围校验超出范围会被静默修正或忽略request_builder.go参数方法合法范围越界行为温度WithTemperature(t)0 ~ 2自动钳制到边界最大输出 tokenWithMaxTokens(n) 0≤ 0 时忽略核采样 top_pWithTopP(p)0 ~ 1越界忽略频率惩罚WithFrequencyPenalty(p)-2 ~ 2越界忽略存在惩罚WithPresencePenalty(p)-2 ~ 2越界忽略停止序列WithStopSequences([]string)/AddStopSequence(s)任意非空字符串空串忽略Build()只把非 nil 的可选参数写入请求体未设置的参数不发送从而避免用 0 值覆盖服务端默认参数request_builder.go。3.1 代码生成低温度、精确request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a Go expert). WithUserPrompt(Generate a HTTP server). WithTemperature(0.2). // 低温度 更确定 WithTopP(0.1). // 低 top_p 更聚焦 WithMaxTokens(2000). AddStopSequence(). // 遇到代码块结束符停止 Build() code, err : client.CallWithRequest(request)3.2 创意写作高温度、随机request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a creative writer). WithUserPrompt(Write a sci-fi story about AI). WithTemperature(1.2). // 高温度 更创意 WithTopP(0.95). // 高 top_p 更多样 WithPresencePenalty(0.6). // 避免重复主题 WithFrequencyPenalty(0.5). // 避免重复词汇 WithMaxTokens(4000). Build() story, err : client.CallWithRequest(request)3.3 精确分析平衡参数request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a quantitative analyst). WithUserPrompt(Analyze BTC/USDT chart pattern). WithTemperature(0.5). // 中等温度 WithMaxTokens(1500). WithStopSequences([]string{---, END}). // 多个停止序列 Build() analysis, err : client.CallWithRequest(request)Provider 兼容细节BuildRequestBodyFromRequest会调用modelSupportsCustomTemperature判断目标模型是否接受自定义温度——OpenAI 的 gpt-5 系列与 o1/o3/o4 推理模型会拒绝非默认 temperature 并直接报错此时温度字段会被省略client.go。同时 OpenAI Provider 使用max_completion_tokens字段其余 Provider 使用max_tokensclient.go。四、Function Calling让 AI 学会调用你的工具4.1 定义工具并让 AI 自动决定工具参数使用 JSON Schema 描述。AddFunction(name, description, parameters)是AddTool的便捷封装内部构造Tool{Type: function, Function: FunctionDef{...}}request_builder.go// 定义工具参数 schemaJSON Schema 格式 weatherParams : map[string]any{ type: object, properties: map[string]any{ location: map[string]any{ type: string, description: City name, e.g., Beijing, Shanghai, }, unit: map[string]any{ type: string, enum: []string{celsius, fahrenheit}, }, }, required: []string{location}, } // 构建请求 request : mcp.NewRequestBuilder(). WithUserPrompt(北京今天天气怎么样). AddFunction( get_weather, // 函数名 Get current weather, // 函数描述 weatherParams, // 参数定义 ). WithToolChoice(auto). // 让 AI 自动决定是否调用 Build() response, err : client.CallWithRequest(request) // AI 可能返回 tool_calls你需要执行函数并返回结果 // 具体实现取决于 AI provider 的响应格式4.2 多个工具并行注册// 定义多个工具 request : mcp.NewRequestBuilder(). WithUserPrompt(帮我查询北京天气并计算100的平方根). AddFunction(get_weather, Get weather, weatherParams). AddFunction(calculate, Calculate math, calcParams). AddFunction(search_web, Search web, searchParams). WithToolChoice(auto). Build() response, err : client.CallWithRequest(request) // AI 会选择调用相应的工具4.3 强制使用特定工具当tool_choice传入具体的函数 JSON 时AI 必须调用指定函数request : mcp.NewRequestBuilder(). WithUserPrompt(北京). AddFunction(get_weather, Get weather, weatherParams). WithToolChoice({type: function, function: {name: get_weather}}). Build() // AI 必须调用 get_weather 函数4.4 底层实现工具调用如何流转tool_choice支持三种取值request_builder.goautoAI 自动决定、none禁止调用工具、或具体的函数选择 JSON。发送后AI 的响应可能包含结构化tool_calls。此时应使用CallWithRequestFull而非CallWithRequest获取完整响应——它返回*LLMResponse其中Content为纯文本回复、ToolCalls为结构化工具调用两者恰好一个非空interface.go、client.go。ToolCall结构体包含ID、Type、Function.Name和Function.ArgumentsJSON 字符串形式见 request.go。执行完函数后需要把结果以roletool的MessageToolCallIDContent追加进消息列表再发起第二轮请求模型才能基于工具结果给出最终答案——这一完整闭环可参考下面第六节的 Function Calling 完整示例。五、预设场景一行代码套用成熟参数针对高频场景mcp包提供了三个开箱即用的构造函数其参数在 request_builder.go 中硬编码并有一一对应的测试用例验证request_builder_test.go构造函数temperaturetopPmaxTokenspresencePenaltyfrequencyPenalty适用场景ForChat()0.7未设置2000未设置未设置通用聊天ForCodeGeneration()0.20.12000未设置未设置代码生成确定性优先ForCreativeWriting()1.20.9540000.60.5创意写作多样性优先5.1 ForChat - 聊天场景// 预设参数temperature0.7, maxTokens2000 request : mcp.ForChat(). WithSystemPrompt(You are a friendly chatbot). WithUserPrompt(Hello!). Build() // 等价于 request : mcp.NewRequestBuilder(). WithSystemPrompt(You are a friendly chatbot). WithUserPrompt(Hello!). WithTemperature(0.7). WithMaxTokens(2000). Build()5.2 ForCodeGeneration - 代码生成场景// 预设参数temperature0.2, topP0.1, maxTokens2000 request : mcp.ForCodeGeneration(). WithUserPrompt(Generate a REST API in Go). Build() // 自动使用低温度和低 top_p确保代码准确性5.3 ForCreativeWriting - 创意写作场景// 预设参数 // temperature1.2, topP0.95, maxTokens4000 // presencePenalty0.6, frequencyPenalty0.5 request : mcp.ForCreativeWriting(). WithSystemPrompt(You are a novelist). WithUserPrompt(Write a fantasy story). Build() // 自动使用高温度和惩罚参数增加创意和多样性预设构造函数返回的构建器同样支持链式追加WithModel、WithStream、AddStopSequence等一切方法且追加参数会覆盖预设值——例如代码评审时想放宽输出长度直接.WithMaxTokens(2000)即可。六、完整示例从量化顾问到聊天机器人6.1 量化交易 AI 顾问结合 nofx 的交易场景用构建器串联“市场分析 → 追问入场点”两步流程并显式配置重试与超时package main import ( fmt log nofx/mcp os time ) func main() { // 创建客户端 client : mcp.NewDeepSeekClientWithOptions( mcp.WithAPIKey(os.Getenv(DEEPSEEK_API_KEY)), mcp.WithMaxRetries(5), mcp.WithTimeout(60 * time.Second), ) // 场景1: 市场分析需要精确 analysisRequest : mcp.NewRequestBuilder(). WithSystemPrompt(You are a professional quantitative trader). WithUserPrompt(Analyze BTC/USDT 1H chart, current price $45,000). WithTemperature(0.3). // 低温度更精确 WithMaxTokens(1500). Build() analysis, err : client.CallWithRequest(analysisRequest) if err ! nil { log.Fatal(err) } fmt.Println( Market Analysis ) fmt.Println(analysis) // 场景2: 继续对话询问入场点 followUpRequest : mcp.NewRequestBuilder(). AddSystemMessage(You are a professional quantitative trader). AddUserMessage(Analyze BTC/USDT 1H chart, current price $45,000). AddAssistantMessage(analysis). // 添加之前的回复 AddUserMessage(Based on your analysis, whats the best entry point?). WithTemperature(0.3). Build() entryPoint, err : client.CallWithRequest(followUpRequest) if err ! nil { log.Fatal(err) } fmt.Println(\n Entry Point Suggestion ) fmt.Println(entryPoint) }6.2 代码评审助手利用ForCodeGeneration预设的低温度参数再叠加停止序列防止模型把评审意见写成代码块func reviewCode(client mcp.AIClient, code string) (string, error) { request : mcp.ForCodeGeneration(). // 使用代码场景预设 WithSystemPrompt(You are a senior Go developer reviewing code). WithUserPrompt(fmt.Sprintf(Review this code:\n\ngo\n%s\n, code)). WithMaxTokens(2000). AddStopSequence(---END---). Build() return client.CallWithRequest(request) } func main() { client : mcp.NewDeepSeekClientWithOptions( mcp.WithAPIKey(os.Getenv(DEEPSEEK_API_KEY)), ) code : func Add(a, b int) int { return a b } review, err : reviewCode(client, code) if err ! nil { log.Fatal(err) } fmt.Println(review) }6.3 AI 聊天机器人带历史记录把“历史管理”封装进结构体每次Chat调用都用AddMessages(bot.history...)把完整历史塞进请求——这是构建器相比CallWithMessages最大的优势之一因为历史消息可以自由组合、累积type ChatBot struct { client mcp.AIClient history []mcp.Message } func NewChatBot(client mcp.AIClient, systemPrompt string) *ChatBot { return ChatBot{ client: client, history: []mcp.Message{ mcp.NewSystemMessage(systemPrompt), }, } } func (bot *ChatBot) Chat(userMessage string) (string, error) { // 添加用户消息到历史 bot.history append(bot.history, mcp.NewUserMessage(userMessage)) // 构建请求包含完整历史 request : mcp.ForChat(). AddMessages(bot.history...). Build() // 调用 API response, err : bot.client.CallWithRequest(request) if err ! nil { return , err } // 添加 AI 回复到历史 bot.history append(bot.history, mcp.NewAssistantMessage(response)) return response, nil } func main() { client : mcp.NewDeepSeekClientWithOptions( mcp.WithAPIKey(os.Getenv(DEEPSEEK_API_KEY)), ) bot : NewChatBot(client, You are a friendly and helpful assistant) // 对话1 resp1, _ : bot.Chat(What is Go?) fmt.Println(User: What is Go?) fmt.Println(Bot:, resp1) // 对话2带上下文 resp2, _ : bot.Chat(What are its main features?) fmt.Println(\nUser: What are its main features?) fmt.Println(Bot:, resp2) // 对话3继续上下文 resp3, _ : bot.Chat(Show me an example) fmt.Println(\nUser: Show me an example) fmt.Println(Bot:, resp3) }6.4 Function Calling 完整示例一个完整的工具调用闭环包含三步定义工具 → 解析 AI 返回的 tool_call → 将工具结果以AddToolResult语义回传源码中通过构造roletool的 Message 实现见 client.gopackage main import ( encoding/json fmt nofx/mcp os ) // 天气查询函数模拟 func getWeather(location string) string { return fmt.Sprintf(Weather in %s: Sunny, 25°C, location) } func main() { client : mcp.NewDeepSeekClientWithOptions( mcp.WithAPIKey(os.Getenv(DEEPSEEK_API_KEY)), ) // 定义工具 weatherParams : map[string]any{ type: object, properties: map[string]any{ location: map[string]any{ type: string, description: City name, }, }, required: []string{location}, } // 第一步发送带工具的请求 request : mcp.NewRequestBuilder(). WithUserPrompt(北京天气怎么样). AddFunction(get_weather, Get current weather, weatherParams). WithToolChoice(auto). Build() response, err : client.CallWithRequest(request) if err ! nil { panic(err) } fmt.Println(AI Response:, response) // 第二步如果 AI 返回了 tool_call实际需要解析 JSON 响应 // 这里是示例实际需要根据 provider 的响应格式解析 // toolCall : parseToolCall(response) // weatherResult : getWeather(toolCall.Arguments.Location) // 第三步将工具结果返回给 AI // followUp : mcp.NewRequestBuilder(). // AddConversationHistory(previousMessages). // AddToolResult(toolCall.ID, weatherResult). // Build() // // finalResponse, _ : client.CallWithRequest(followUp) }进阶提示生产环境中建议使用CallWithRequestFull(req)拿到LLMResponse.ToolCalls其中ToolCall.Function.Arguments是 JSON 编码的参数字符串用encoding/json反序列化后即可安全调用本地函数随后把ToolCallID与结果构造成roletool的消息回传完成第二轮回合。七、最佳实践7.1 使用 MustBuild() vs Build()Build()返回(*Request, error)适合需要对校验错误做业务处理的场景MustBuild()在校验失败时直接panicrequest_builder.go适合“必定不会为空消息”的确定性场景例如ForChat()预设 至少一条用户消息// Build() - 返回 error需要处理 request, err : NewRequestBuilder(). WithUserPrompt(Hello). Build() if err ! nil { log.Fatal(err) } // MustBuild() - 如果失败会 panic适用于确定不会错的场景 request : NewRequestBuilder(). WithSystemPrompt(You are helpful). WithUserPrompt(Hello). MustBuild() // 构建失败会 panic7.2 重用构建器RequestBuilder是可重用的可变对象ClearMessages()可以清空消息列表但保留其他参数设置因此可以把它当作“模板”复用// 创建基础构建器 baseBuilder : mcp.NewRequestBuilder(). WithSystemPrompt(You are a trading advisor). WithTemperature(0.3) // 为不同问题添加用户消息 question1 : baseBuilder. AddUserMessage(Analyze BTC). Build() question2 : baseBuilder. ClearMessages(). // 清空之前的消息 AddSystemMessage(You are a trading advisor). AddUserMessage(Analyze ETH). Build()7.3 选择合适的预设// ✅ 代码生成 - 使用 ForCodeGeneration ForCodeGeneration().WithUserPrompt(Generate code) // ✅ 聊天 - 使用 ForChat ForChat().WithUserPrompt(Hello) // ✅ 创意写作 - 使用 ForCreativeWriting ForCreativeWriting().WithUserPrompt(Write a story) // ✅ 自定义 - 使用 NewRequestBuilder NewRequestBuilder().WithTemperature(0.6).WithUserPrompt(...)八、迁移指南从旧 API 平滑升级旧 API 完全保留现有代码无需任何修改即可继续运行interface.go 中的AIClient同时暴露新旧方法。需要更强控制力时按如下模式迁移// 旧 API仍然可用 result, err : client.CallWithMessages(system, user) // 迁移到新 API request : mcp.NewRequestBuilder(). WithSystemPrompt(system). WithUserPrompt(user). Build() result, err : client.CallWithRequest(request) // 如果需要更多控制 request : mcp.NewRequestBuilder(). WithSystemPrompt(system). WithUserPrompt(user). WithTemperature(0.8). // 新功能 WithMaxTokens(2000). // 新功能 Build() result, err : client.CallWithRequest(request)完整迁移策略参见 MIGRATION_GUIDE.md。九、请求如何到达 AI构建器背后的调用链了解底层调用链有助于排查问题与自定义扩展详见 client.goNewRequestBuilder()...Build() ↓ 产出 *Request client.CallWithRequest(req) ↓ 内置固定重试循环MaxRetries 次指数退避重试 callWithRequest(req) ↓ BuildRequestBodyFromRequest(req) // 消息 可选参数 → map 请求体 MarshalRequestBody(requestBody) // JSON 序列化 BuildUrl() // BaseURL /chat/completionsUseFullURL 时除外 BuildRequest → setAuthHeader // Authorization: Bearer APIKey HTTPClient.Do(req) // 默认使用 security.SafeHTTPClient含 SSRF 防护 ParseMCPResponse(body) // 解析 choices[0].message.content几个值得注意的默认行为config.go默认Timeout 120s、MaxRetries 3、RetryWaitBase 2s每次重试等待时间为基数 × 重试次数默认Temperature 0.5、MaxTokens 2000可通过环境变量AI_MAX_TOKENS覆盖默认 HTTP 客户端由security.SafeHTTPClient构造自带 SSRF 防护拦截私网 IP 与云元数据地址WithHTTPClient覆盖时会绕过这些保护仅建议在测试或提供等价安全措施的客户端中使用options.go可重试错误白名单覆盖网络错误、超时、429 限流、502/503/520/524 等client.go客户端还提供TokenUsageCallback回调与LastCallUsage字段用于在每次调用后统计 token 用量client.go。十、总结RequestBuilder把请求构造、参数控制与工具注册统一收敛为一条可复用的链式 APIForChat/ForCodeGeneration/ForCreativeWriting三个预设覆盖了大部分常见场景WithTemperature/WithTopP/ 惩罚参数与停止序列支持逐项精细微调AddFunctionWithToolChoice开启 Function Calling 能力而Build()/MustBuild()与ClearMessages()让构建器可以安全复用。结合 BUILDER_PATTERN_BENEFITS.md 了解模式设计动机或参考 mcp 包总览 查看客户端配置与 Provider 扩展方式即可在 nofx 中搭建稳定、可控的 AI 调用层。赞分享AI Agent金融科技后端前端【免费下载链接】nofxYour AI trading terminal assistant for US stocks, commodities, forex, and crypto.项目地址https://gitcode.com/gh_mirrors/nof/nofx点击查看免费下载相关推荐使用 langchaingo 与 Ollama 实现函数调用Function Calling从工具定义到多轮对话的完整实战使用 langchaingo 与 Ollama 实现函数调用Function Calling从工具定义到多轮对话的完整实战 导读 本篇文章围绕 langc人工智能大模型AI AgentRAG后端TensorZero 工具调用Tool Use实战用 Function Calling 配置天气聊天机器人并完成多轮对话TensorZero 工具调用Tool Use实战用 Function Calling 配置天气聊天机器人并完成多轮对话 TensorZero 是一个开源大模型LLMOpsLLM 网关后端模型评测可观测性模型优化多轮对话数据集构建Code-Feedback与ToolACE实战指南多轮对话数据集构建Code Feedback与ToolACE实战指南 在大语言模型训练中高质量的多轮对话数据集是提升模型交互能力的关键。本文将为您详细介绍如数据集大模型上一篇Temporal开发者终极指南10个高效调试技巧与IDE插件配置下一篇如何最大化B2 Command Line Tool的sync命令多线程、正则排除与dry-run参数全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考