首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >golang源码分析:langchaingo(10)

golang源码分析:langchaingo(10)

作者头像
golangLeetcode
发布2026-03-18 18:07:11
发布2026-03-18 18:07:11
1010
举报

除了RAG,我们也可以定义agentTool交给大模型调用,下面我们看一个调用的例子

代码语言:javascript
复制
    agentTools := []tools.Tool{
        randomNumberTool{},
    }
    agent := agents.NewOneShotAgent(llm, agentTools)
    executor := agents.NewExecutor(
        agent,
        agents.WithCallbacksHandler(callbacks.LogHandler{}),
    )
    result, err := chains.Run(ctx, executor, "告诉我一个随机数")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result)

其中tool的定义如下,包括Name,Description和Call三个方法

代码语言:javascript
复制
type randomNumberTool struct{}
func (r randomNumberTool) Name() string {
    return "随机数计算工具"
}
func (r randomNumberTool) Description() string {
    return "用于获取随机数"
}
func (r randomNumberTool) Call(ctx context.Context, input string) (string, error) {
    return "1024", nil
}

它实现了Tool的所有接口

代码语言:javascript
复制
type Tool interface {
    Name() string
    Description() string
    Call(ctx context.Context, input string) (string, error)
}

然后用Tool定义了一个Agent

代码语言:javascript
复制
func NewOneShotAgent(llm llms.Model, tools []tools.Tool, opts ...Option) *OneShotZeroAgent {
    options := mrklDefaultOptions()
    for _, opt := range opts {
        opt(&options)
    }
    return &OneShotZeroAgent{
        Chain: chains.NewLLMChain(
            llm,
            options.getMrklPrompt(tools),
            chains.WithCallback(options.callbacksHandler),
        ),
        Tools:            tools,
        OutputKey:        options.outputKey,
        CallbacksHandler: options.callbacksHandler,
    }
}

看到chains.NewLLMChain我们知道,它其实也是langchain中的一个节点

代码语言:javascript
复制
type OneShotZeroAgent struct {
    // Chain is the chain used to call with the values. The chain should have an
    // input called "agent_scratchpad" for the agent to put its thoughts in.
    Chain chains.Chain
    // Tools is a list of the tools the agent can use.
    Tools []tools.Tool
    // Output key is the key where the final output is placed.
    OutputKey string
    // CallbacksHandler is the handler for callbacks.
    CallbacksHandler callbacks.Handler
}

他的提示词定义如下,它把tool的名字和描述写入了提示词

代码语言:javascript
复制
func createMRKLPrompt(tools []tools.Tool, prefix, instructions, suffix string) prompts.PromptTemplate {
    template := strings.Join([]string{prefix, instructions, suffix}, "\n\n")
    return prompts.PromptTemplate{
        Template:       template,
        TemplateFormat: prompts.TemplateFormatGoTemplate,
        InputVariables: []string{"input", "agent_scratchpad", "today"},
        PartialVariables: map[string]any{
            "tool_names":        toolNames(tools),
            "tool_descriptions": toolDescriptions(tools),
        },
    }
}

然后定义了一个执行器,包括最大执行次数,重试间隔等参数

代码语言:javascript
复制
func NewExecutor(agent Agent, opts ...Option) *Executor {
    options := executorDefaultOptions()
    for _, opt := range opts {
        opt(&options)
    }
    return &Executor{
        Agent:                   agent,
        Memory:                  options.memory,
        MaxIterations:           options.maxIterations,
        ReturnIntermediateSteps: options.returnIntermediateSteps,
        CallbacksHandler:        options.callbacksHandler,
        ErrorHandler:            options.errorHandler,
    }
}
代码语言:javascript
复制
type Executor struct {
    Agent            Agent
    Memory           schema.Memory
    CallbacksHandler callbacks.Handler
    ErrorHandler     *ParserErrorHandler
    MaxIterations           int
    ReturnIntermediateSteps bool
}

最后还是调用了langchain的Run方法来执行

代码语言:javascript
复制
func Run(ctx context.Context, c Chain, input any, options ...ChainCallOption) (string, error) {
    outputValues, err := Call(ctx, c, inputValues, options...)
代码语言:javascript
复制
func Call(ctx context.Context, c Chain, inputValues map[string]any, options ...ChainCallOption) (map[string]any, error) { // nolint: lll
  outputValues, err := callChain(ctx, c, fullValues, options...)
代码语言:javascript
复制
func callChain(
    ctx context.Context,
    c Chain,
    fullValues map[string]any,
    options ...ChainCallOption,
) (map[string]any, error) {
    outputValues, err := c.Call(ctx, fullValues, options...)

只不过Run调用的Call方法是执行器的

代码语言:javascript
复制
func (e *Executor) Call(ctx context.Context, inputValues map[string]any, _ ...chains.ChainCallOption) (map[string]any, error) { //nolint:lll
    inputs, err := inputsToString(inputValues)
    if err != nil {
        return nil, err
    }
    nameToTool := getNameToTool(e.Agent.GetTools())
    steps := make([]schema.AgentStep, 0)
    for i := 0; i < e.MaxIterations; i++ {
        var finish map[string]any
        steps, finish, err = e.doIteration(ctx, steps, nameToTool, inputs)
        if finish != nil || err != nil {
            return finish, err
        }
    }
    if e.CallbacksHandler != nil {
        e.CallbacksHandler.HandleAgentFinish(ctx, schema.AgentFinish{
            ReturnValues: map[string]any{"output": ErrNotFinished.Error()},
        })
    }
    return e.getReturn(
        &schema.AgentFinish{ReturnValues: make(map[string]any)},
        steps,
    ), ErrNotFinished
}

它把agent的tools做成名字到tool的映射,然后执行

代码语言:javascript
复制
func (e *Executor) doIteration( // nolint
    ctx context.Context,
    steps []schema.AgentStep,
    nameToTool map[string]tools.Tool,
    inputs map[string]string,
) ([]schema.AgentStep, map[string]any, error) {
    actions, finish, err := e.Agent.Plan(ctx, steps, inputs)
    if errors.Is(err, ErrUnableToParseOutput) && e.ErrorHandler != nil {
        formattedObservation := err.Error()
        if e.ErrorHandler.Formatter != nil {
            formattedObservation = e.ErrorHandler.Formatter(formattedObservation)
        }
        steps = append(steps, schema.AgentStep{
            Observation: formattedObservation,
        })
        return steps, nil, nil
    }
    if err != nil {
        return steps, nil, err
    }
    if len(actions) == 0 && finish == nil {
        return steps, nil, ErrAgentNoReturn
    }
    if finish != nil {
        if e.CallbacksHandler != nil {
            e.CallbacksHandler.HandleAgentFinish(ctx, *finish)
        }
        return steps, e.getReturn(finish, steps), nil
    }
    for _, action := range actions {
        steps, err = e.doAction(ctx, steps, nameToTool, action)
        if err != nil {
            return steps, nil, err
        }
    }
    return steps, nil, nil
}

先通过plain拆分成action列表,然后依次执行每个action

代码语言:javascript
复制
func (a *OneShotZeroAgent) Plan(
    ctx context.Context,
    intermediateSteps []schema.AgentStep,
    inputs map[string]string,
) ([]schema.AgentAction, *schema.AgentFinish, error) {
    fullInputs := make(map[string]any, len(inputs))
    for key, value := range inputs {
        fullInputs[key] = value
    }
    fullInputs["agent_scratchpad"] = constructMrklScratchPad(intermediateSteps)
    fullInputs["today"] = time.Now().Format("January 02, 2006")
    var stream func(ctx context.Context, chunk []byte) error
    if a.CallbacksHandler != nil {
        stream = func(ctx context.Context, chunk []byte) error {
            a.CallbacksHandler.HandleStreamingFunc(ctx, chunk)
            return nil
        }
    }
    output, err := chains.Predict(
        ctx,
        a.Chain,
        fullInputs,
        chains.WithStopWords([]string{"\nObservation:", "\n\tObservation:"}),
        chains.WithStreamingFunc(stream),
    )
    if err != nil {
        return nil, nil, err
    }
    return a.parseOutput(output)
}

可以看到它也是调用langchain的Call方法,就是交给大模型来拆分步骤:

代码语言:javascript
复制
// Predict can be used to execute a chain if the chain only expects one string output.
func Predict(ctx context.Context, c Chain, inputValues map[string]any, options ...ChainCallOption) (string, error) {
    outputValues, err := Call(ctx, c, inputValues, options...)
    if err != nil {
        return "", err
    }
    outputKeys := c.GetOutputKeys()
    if len(outputKeys) != 1 {
        return "", ErrMultipleOutputsInPredict
    }
    outputValue, ok := outputValues[outputKeys[0]].(string)
    if !ok {
        return "", ErrOutputNotStringInPredict
    }
    return outputValue, nil
}

最终调用了tool的Call方法完成了放调用

代码语言:javascript
复制
func (e *Executor) doAction(
    ctx context.Context,
    steps []schema.AgentStep,
    nameToTool map[string]tools.Tool,
    action schema.AgentAction,
) ([]schema.AgentStep, error) {
    if e.CallbacksHandler != nil {
        e.CallbacksHandler.HandleAgentAction(ctx, action)
    }
    tool, ok := nameToTool[strings.ToUpper(action.Tool)]
    if !ok {
        return append(steps, schema.AgentStep{
            Action:      action,
            Observation: fmt.Sprintf("%s is not a valid tool, try another one", action.Tool),
        }), nil
    }
    observation, err := tool.Call(ctx, action.ToolInput)
    if err != nil {
        return nil, err
    }
    return append(steps, schema.AgentStep{
        Action:      action,
        Observation: observation,
    }), nil
}

可以看到agent已经有MCP的影子了,只不过,它在langchaingo中规定了接口,限制比较严格,没有MCP那么灵活,本质上都是交给大模型决策,然后有调用端发起请求,获得结果。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档