首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >LangChainGo 自定义工作流:用 Chain 串联多步大模型任务

LangChainGo 自定义工作流:用 Chain 串联多步大模型任务

作者头像
技术圈
发布2026-07-14 13:45:21
发布2026-07-14 13:45:21
220
举报

单次 Prompt 调用很难解决复杂的业务流程。当需要提取关键词、翻译、并生成摘要时,如果全塞进一个 Prompt,大模型的输出格式极易失控,且调试困难。

合理的方案是将大任务拆分为多个子任务,再通过链式结构串联。每个步骤关注单一职责,便于调试和控制。本文详细介绍如何使用 LangChainGo 中的 Chain 接口实现多步大模型任务的自定义编排。

复杂业务编排的痛点

技术文章的自动化处理流程通常包含三个核心阶段:

  • 提取技术关键词:为分类与检索做准备。
  • 翻译关键词:支持多语言检索。
  • 生成中文摘要:提供概览信息。

如果将所有任务放在单个 Prompt 中处理,面临如下工程问题:

  • 输出格式不稳定:模型极易遗漏部分指令或偏离格式要求。
  • 无法局部重试:某一步失败需要重新运行整个工作流,浪费 Token 资源。
  • 调试极其繁琐:调整一个任务的 Prompt 可能会负面影响其它步骤的输出。

使用模块化的 Chain(链)可以有效解决这些问题。每个步骤处理一个具体任务,将前一步的输出作为下一步的输入。这样可以实现单步调试、独立重试以及精细化 prompt 优化。

LangChainGo 中的 Chain 接口

LangChainGo 在 github.com/tmc/langchaingo/chains 包中定义了统一的 Chain 接口。自定义工作流必须实现该接口。

以下是 Chain 接口的核心定义:

代码语言:javascript
复制
   type Chain interface {
    Call(ctx context.Context, inputs map[string]any, options ...ChainCallOption) (map[string]any, error)
    GetMemory() schema.Memory
    GetInputKeys() []string
    GetOutputKeys() []string
}

接口方法解析:

  • Call:工作流执行的入口。接收输入参数 Map,返回包含最终结果的 Map。
  • GetMemory:获取关联的记忆组件。无状态的工作流返回一个不执行任何操作的简单记忆体即可。
  • GetInputKeys:声明该链需要的输入参数 Key。
  • GetOutputKeys:声明该链输出的参数 Key。

内置链的局限性

LangChainGo 提供了 LLMChainSimpleSequentialChain 等内置实现。

LLMChain 是最基础的单步链,它绑定一个 PromptTemplate 与 Model。接收参数渲染 Prompt,再提交给大模型获取结果。

SimpleSequentialChain 是简单的顺序链,它依次执行多个链,前一步的输出直接传给下一步。这种设计的局限性很明显:

  • 仅支持单变量传递:数据流必须是严格的线性单向流。
  • 变量无法共享:第三步(生成摘要)无法同时获取第一步的原始输入和第二步的翻译输出。
  • 难以灵活控制:不适合直接表达多变量共享、复杂控制流;外部查询或校验需额外封装成单输入单输出 Chain。

要突破这些局限,最直接的工程方案就是实现自定义的 Chain 接口。

自定义 Chain 实战

下面定义一个 TranslationSummaryChain,串联提取、翻译与摘要三步任务。

结构体持有大模型实例和三个步骤的 PromptTemplate:

代码语言:javascript
复制
   type TranslationSummaryChain struct {
    llm               llms.Model
    keywordPrompt     prompts.PromptTemplate
    translationPrompt prompts.PromptTemplate
    summaryPrompt     prompts.PromptTemplate
}

构造函数初始化结构体。注意,LangChainGo 默认采用 Go 的 text/template 语法渲染提示词,因此应使用 {{.variable}} 格式:

代码语言:javascript
复制
   func NewTranslationSummaryChain(llm llms.Model) *TranslationSummaryChain {
    return &TranslationSummaryChain{
        llm:               llm,
        keywordPrompt:     prompts.NewPromptTemplate("提取关键词:{{.text}}", []string{"text"}),
        translationPrompt: prompts.NewPromptTemplate("翻译关键词:{{.keywords}}", []string{"keywords"}),
        summaryPrompt:     prompts.NewPromptTemplate("根据{{.translated_keywords}}摘要:{{.text}}", []string{"text", "translated_keywords"}),
    }
}

核心的 Call 方法分三步执行。首先是第一步,验证参数,获取 LLM 选项,并调用大模型提取关键词:

代码语言:javascript
复制
   func (c *TranslationSummaryChain)Call(ctx context.Context, inputs map[string]any, options ...chains.ChainCallOption)(map[string]any,error){
    textVal, ok := inputs["text"].(string)
    if!ok {returnnil, fmt.Errorf("missing input key: text")}
    opts := chains.GetLLMCallOptions(options...)
    kwInput, err := c.keywordPrompt.Format(map[string]any{"text": textVal})
    if err !=nil{returnnil, err }
    kwOutput, err := llms.GenerateFromSinglePrompt(ctx, c.llm, kwInput, opts...)
    if err !=nil{returnnil, err }
    // 函数体未完,后续逻辑接下文...

第二步,将上一步生成的中文关键词翻译为英文(同样传递 options 配置):

代码语言:javascript
复制
       // 接上文 Call 方法内部逻辑:
    transInput, err := c.translationPrompt.Format(map[string]any{"keywords": kwOutput})
    if err !=nil{
        returnnil, err
    }
    transOutput, err := llms.GenerateFromSinglePrompt(ctx, c.llm, transInput, opts...)
    if err !=nil{
        returnnil, err
    }
    // 函数体未完,后续逻辑接下文...

第三步,结合原始文本 textVal 和翻译关键词 transOutput 渲染最终 Prompt 并生成摘要:

代码语言:javascript
复制
       // 接上文 Call 方法内部逻辑:
    sumInput, err := c.summaryPrompt.Format(map[string]any{
        "text": textVal,"translated_keywords": transOutput,
    })
    if err !=nil{returnnil, err }
    sumOutput, err := llms.GenerateFromSinglePrompt(ctx, c.llm, sumInput, opts...)
    if err !=nil{returnnil, err }
    returnmap[string]any{"keywords": kwOutput,"translated_keywords": transOutput,"summary": sumOutput},nil
}

最后,实现 Chain 接口约定的辅助方法:

代码语言:javascript
复制
   func (c *TranslationSummaryChain)GetMemory() schema.Memory {return memory.NewSimple()}
func(c *TranslationSummaryChain)GetInputKeys()[]string   {return[]string{"text"}}
func(c *TranslationSummaryChain)GetOutputKeys()[]string{
    return[]string{"keywords","translated_keywords","summary"}
}

使用 memory.NewSimple() 满足无状态链的调用要求。

执行自定义工作流

在业务逻辑中,创建自定义链实例,并通过 chains.Call 函数来启动整个流程:

代码语言:javascript
复制
   func ExecuteWorkflow(ctx context.Context, llm llms.Model){
    workflow :=NewTranslationSummaryChain(llm)
    result, err := chains.Call(ctx, workflow,map[string]any{
        "text":"Go 语言在 1.24 版本中引入了 os.OpenRoot 功能...",
    })
    if err !=nil{
        log.Fatalf("执行工作流失败: %v", err)
    }
    fmt.Printf("生成的摘要: %s\n", result["summary"])
}

使用全局 chains.Call 可以复用输入/输出校验、Memory 加载与保存;若链实现了 callback handler,也能正常触发链级回调。

写在最后

通过直接实现 Chain 接口,开发者可以轻松掌控复杂多步任务中的变量流转、条件控制与异常重试。在需要多步大模型调用串联的场景下,这提供了极佳的灵活性和系统稳定性。

后续可以结合大模型的 Function Calling 功能或接入持久化记忆组件,构建功能更复杂的智能体应用。在处理批量任务或无依赖分支时,还可以结合并发控制提升整体吞吐。

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

本文分享自 技术圈子 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 复杂业务编排的痛点
  • LangChainGo 中的 Chain 接口
  • 内置链的局限性
  • 自定义 Chain 实战
  • 执行自定义工作流
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档