
LangChain Go 中基于 Ollama 构建 Agent 的实战指南MRKL 输出解析容错与提示工程最佳实践【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo本文以 langchaingo 仓库中的 Ollama Agent 使用指南 为核心系统讲解在 Go 项目中使用 Ollama 模型驱动 MRKL Agent 的原理、配置与排障方法。文章将结合仓库源码重点剖析针对 Ollama 模型没有原生 Function Calling 能力而引入的parseOutput容错解析逻辑并给出可直接运行的完整代码示例与测试验证方案。读完本文你将掌握如何在 langchaingo 中为 Ollama 模型配置稳定的 Agent 工作流并具备独立排查 unable to parse output 等常见问题的能力。背景为什么 Ollama 模型需要特殊处理OpenAI 等模型原生支持 function/tool calling模型输出会按照约定的 JSON 结构返回工具调用参数程序可以直接消费。而 Ollama 本地模型通常不具备这种原生能力Agent 必须依赖 MRKLModular Reasoning, Knowledge and Language式的纯文本协议进行推理模型需要输出Thought:思考、Action:动作、Action Input:动作输入来表达我要调用哪个工具当推理完成时模型需要输出Final Answer:来表达最终答案。一旦模型输出的格式略有偏差例如大小写不一致、冒号后多了空格、用了 The answer is: 这类自然语言表达Agent 的解析器就可能直接报错。这正是仓库中 Issue #1045 描述的核心问题Ollama 模型与 Agent 搭配时模型可能不按预期格式生成响应导致解析错误。MRKL Agent 与解析器的容错改进解析器改进概览针对上述问题langchaingo 对 MRKL Agent 的parseOutput函数做了增强使其能够更灵活地识别多种输出格式。改进点集中在 mrkl.go 的parseOutput方法上Final Answer 的大小写变体识别不仅支持标准的Final Answer:还支持final answer:、final answer :冒号前多空格等变体自然语言变体识别支持the final answer is:、the answer is:等表达大小写不敏感的 Action 模式匹配Action:与Action Input:均可忽略大小写。源码级解析流程从源码看parseOutput的判定顺序是agents/mrkl.go第一步标准格式向后兼容检查。先直接查找大写Final Answer:常量_finalAnswerAction命中即取最后一个分段作为最终答案并返回AgentFinish同时把输出记录到Log字段。第二步大小写不敏感变体扫描。将模型输出转为小写后依次匹配以下变体变体模式示例final answer:final answer: 42final answer :final answer : 42the final answer is:the final answer is: 42the answer is:the answer is: 42这里有一个重要的保护逻辑提取出的答案如果后面还跟着\naction:说明模型并没有真正结束它可能还在规划下一步工具调用此时不会误判为最终答案而是继续走 Action 解析。第三步Action/Action Input 模式匹配。使用正则(?i)Action:\s*(.?)\s*Action\sInput:\s*(?s)(.)进行大小写不敏感、跨行(?s)使.匹配换行的匹配把模型输出解析为AgentAction{Tool, ToolInput}。第四步向后兼容回退。若上面的正则没有命中再尝试仓库原有的正则Action:\s*(.)\s*Action Input:\s(?s)*(.)仍失败则返回ErrUnableToParseOutput包装的错误错误类型定义见 errors.go。测试用例佐证仓库中的 markl_test.go 通过TestMRKLOutputParser覆盖了这些解析路径Action: foo Action Input: bar→ 解析出工具foo、输入bar多行输入Action: foo\nAction Input:\nbar\nbaz→ 工具输入为bar\nbaz带Observation:尾缀的输入Action: calculator\nAction Input: 5 3\nObservation:→ 工具calculator、输入5 3\nObservation:该尾缀由 Executor 在调用工具时通过strings.TrimSuffix清理见 executor.go。这些测试说明解析器改进的目标是让格式上不够规整的模型输出也能被正确理解从而显著降低 Ollama 模型的 Agent 使用门槛。最佳实践让 Ollama Agent 稳定工作1. 使用清晰的系统提示词Ollama 模型遵循提示词的指令程度较高因此在创建 Agent 时应在系统提示词中明确写出期望的输出格式把Thought/Action/Action Input/Observation/Final Answer的结构逐行交代清楚systemPrompt : You are a helpful assistant that uses tools to answer questions. IMPORTANT: You must follow this exact format: For using a tool: Thought: [your reasoning] Action: [tool name] Action Input: [tool input] For final answer: Thought: I now know the final answer Final Answer: [your answer] Always use Final Answer: to indicate your final response. agent : agents.NewOneShotAgent( ollamaLLM, tools, agents.WithSystemMessage(systemPrompt), )需要说明的是在当前仓库源码中WithSystemMessage是通过agents.NewOpenAIOption().WithSystemMessage(...)暴露给 OpenAI Functions Agent 的见 options.go对于 MRKL AgentNewOneShotAgent提示词模板由前缀、格式指令、后缀三部分组成见 mrkl_prompt.go你可以用 WithPromptPrefix、WithPromptSuffix、WithPromptFormatInstructions以及WithPrompt选项来定制系统级指令。无论采用哪种方式核心原则一致把期望格式写进提示词模型输出就越规整。2. 选择合适的模型不同 Ollama 模型对格式指令的遵从度差异明显官方指南 给出了经验性建议推荐使用llama3、mistral、mixtral、gemma2 —— 这些模型对 MRKL 格式理解较好可能需要调优llama2、codellama —— 需要更强的提示词约束需要充分测试phi 等小参数模型 —— 输出格式稳定性较弱务必用测试用例验证后再上生产。3. 调整温度等采样参数降低温度Temperature通常能显著提升输出格式的一致性。langchaingo 的 Ollama 实现支持在创建 LLM 时直接传入底层采样参数llm, err : ollama.New( ollama.WithModel(llama3), ollama.WithOptions(ollama.Options{ Temperature: 0.2, // Lower temperature for more consistent formatting }), )从源码看这些参数最终会被映射到 Ollama 的生成选项上NumPredict对应MaxTokens、Temperature直接透传、Stop对应停止词、TopK/TopP、Seed、重复惩罚等也一一对应见 ollamallm.go。除了Temperature仓库还提供了大量 Runner 级与采样级选项例如 WithRunnerNumCtx上下文窗口默认 2048、WithPredictRepeatLastN、WithPredictMirostat等均可按需组合。4. 处理格式变体得益于改进后的解析器以下格式现在都能被正确识别为最终答案Final Answer: X标准格式final answer: X全小写The answer is: X自然语言表达Answer: X简化表达同理Action Input:与Action input:等各种大小写组合都能被识别。这四种变体与 Action 的大小写容错构成了 Ollama Agent 稳定性的第一道防线。5. 完整示例实现将以上最佳实践组合起来即可得到一个完整的可运行程序package main import ( context fmt log github.com/tmc/langchaingo/agents github.com/tmc/langchaingo/llms/ollama github.com/tmc/langchaingo/tools ) func main() { // Create Ollama LLM with appropriate settings llm, err : ollama.New( ollama.WithModel(llama3), ollama.WithOptions(ollama.Options{ Temperature: 0.2, NumPredict: 512, }), ) if err ! nil { log.Fatal(err) } // Create tools calculator : tools.Calculator{} // Create agent with clear instructions systemPrompt : You are a helpful math assistant. Use the calculator tool for computations. Format your responses as: - For calculations: Action: calculator then Action Input: [expression] - For final answers: Final Answer: [result] agent : agents.NewOneShotAgent( llm, []tools.Tool{calculator}, agents.WithSystemMessage(systemPrompt), agents.WithMaxIterations(5), ) // Create executor executor : agents.NewExecutor( agent, agents.WithMaxIterations(5), ) // Run the agent result, err : executor.Call( context.Background(), map[string]any{ input: What is 25 * 4?, }, ) if err ! nil { log.Printf(Error: %v, err) } else { fmt.Printf(Result: %v\n, result[output]) } }示例中的 Calculator 工具使用 starlark 求值器执行数学表达式其Name()为calculator、Description()描述了输入格式MRKL 提示词会通过toolNames/toolDescriptions自动将工具清单注入模板见 mrkl_prompt.go。理解底层执行流程要让上面的示例真正跑通还需要理解 Agent 的迭代执行机制。NewExecutor返回的 Executor 是负责运行 Agent 的链式组件其Call方法按MaxIterations次循环执行executor.go调用Agent.Plan生成下一步计划动作列表或结束信号若返回finish最终答案立即返回结果若返回动作列表则逐个执行doAction会按大写形式在工具映射表中查找工具getNameToTool将工具名统一转为大写找不到时把 X is not a valid tool, try another one 作为 Observation 反馈给模型让它重新选择executor.go每次工具执行的 Observation 会通过constructMrklScratchPad拼接进agent_scratchpad形成下一轮推理的上下文mrkl.go若迭代耗尽仍未产出Final Answer返回ErrNotFinishedagent not finished before max iterations。另外MRKL Agent 在调用 LLM 时还注入了停止词\nObservation:避免模型抢答工具结果mrkl.go。默认最大迭代次数为 5initialize.go可通过 WithMaxIterations 调整。故障排查报错 unable to parse output原因模型输出与期望格式不匹配parseOutput的所有分支都未命中。解决方案按优先级降低温度减少随机性让模型更保守地遵循格式换用能力更强的模型如 llama3、mixtral在系统提示词中补充示例展示思考→动作→观察→最终答案的完整链路考虑 few-shot prompting给出 12 组正例。进阶Executor 支持通过WithParserErrorHandler挂载解析错误处理器options.go。解析失败时错误信息会被格式化为 Observation 追加到上下文让模型在下一轮自行修正executor.go这比直接终止更有利于自愈。报错 agent not finished before max iterations原因模型始终没有生成 Final Answer迭代次数耗尽。解决方案在系统提示词中明确写出Final Answer:并说明何时该输出它临时调大MaxIterations如 810用于调试观察模型是否只是步子迈得慢检查模型是否在输出本文解析器已支持的变体如The answer is:确认是否有其他格式问题。模型不断重复动作原因模型不理解拿到工具结果后就应该停止并给出最终答案。解决方案在提示词中加入明确的停止条件例如一旦得到计算结果立即用 Final Answer 回复在系统提示词中给出完整示例展示动作→观察→最终答案的完整闭环考虑编写自定义输出解析器对模型行为做更严格的约束。用测试验证你的 Agent 配置将提示词与参数调整好后建议用自动化测试固化验证防止后续改动回归。以下是 官方指南 提供的测试骨架// Test function to verify Ollama agent works correctly func TestOllamaAgent(t *testing.T) { ctx : context.Background() llm, err : ollama.New( ollama.WithModel(llama3), ) require.NoError(t, err) calculator : tools.Calculator{} agent : agents.NewOneShotAgent( llm, []tools.Tool{calculator}, agents.WithMaxIterations(3), ) executor : agents.NewExecutor(agent) testCases : []struct { input string expected string }{ {What is 22?, 4}, {Calculate 10*5, 50}, {What is 100 divided by 4?, 25}, } for _, tc : range testCases { result, err : executor.Call(ctx, map[string]any{ input: tc.input, }) if err ! nil { t.Logf(Warning: %s failed: %v, tc.input, err) continue } output : fmt.Sprintf(%v, result[output]) if !strings.Contains(output, tc.expected) { t.Errorf(Expected %s in output, got: %s, tc.expected, output) } } }测试中require来自github.com/stretchr/testify这是 langchaingo 仓库测试中广泛使用的断言库可参考 markl_test.go 与各包的*_test.go。运行前请确保本地已启动 Ollama 服务并拉取对应模型如ollama pull llama3若网络环境无法访问模型仓库也可以借助仓库内部使用的 httprr 录制回放机制将真实请求录制下来做离线回归测试。总结为了让 Ollama 模型在 Agent 场景下可靠工作langchaingo 从两个层面做了工程化处理解析层容错改进parseOutput支持 Final Answer 大小写变体、The answer is:等自然语言表达以及大小写不敏感的 Action 匹配并配套TestMRKLOutputParser测试用例mrkl.go、markl_test.go使用层最佳实践清晰的系统提示词、合适的模型选型、降低温度、配合MaxIterations与解析错误处理器进行容错。诚然这些改进让 Ollama 模型在使用 Agent 时更可靠但相比原生支持 function calling 的模型它仍然依赖谨慎的提示词工程。建议在实际项目中先用小规模测试用例验证模型对格式的遵从度再逐步放开业务场景遇到格式问题优先从提示词是否明确与采样参数是否激进两个方向排查。本文涉及的源码、测试与完整指南均可直接在 agents 目录 与 llms/ollama 目录 中继续深入阅读。【免费下载链接】langchaingoLangChain for Go, the easiest way to write LLM-based programs in Go项目地址: https://gitcode.com/GitHub_Trending/la/langchaingo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考