大模型 能力集成与智能工作流自动化实践:第一版该做到什么程度
设计第一个 LLM 自动化工作流时,应先限定范围,避免过早引入复杂编排。
一开始规划的方案总是非常宏大:搞多 Agent 协同(Multi-Agent Swarm)、搞复杂的图路由(LangGraph 式的循环图结构)、让 AI 自动反思自我修正(Self-Correction)、甚至计划让 LLM 动态自发生成 Python 脚本去执行业务逻辑。
链路节点过多会增加延迟和格式失败点。是否保留某个节点,应根据端到端时延、失败率和维护成本的实测结果判断。
项目卡在 80% 无法上线。大家静下心来反思:第一版 LLM 自动化工作流,到底该做到什么程度?
答案只有两个字:克制。用确定性的编排把复杂度锁住,只把最核心的一两个非结构化转化任务交给 LLM,才是 1.0 版本上线的正确姿势。
1.0 极简工作流与确定性编排
第一版工作流的核心原则是:用确定性的程序逻辑包裹 LLM 调用,绝不让 LLM 决定工作流的执行走向。
在 1.0 架构中,工作流只有四步:
- 确定性输入预处理与模板填充:提取输入参数,填充至严格约束的 Prompt 模板中。
- 带 Schema 约束的单次模型推理:要求 LLM 必须以 JSON 格式输出结果。
- 强类型校验与自动重试修补:对输出的 JSON 进行 Schema 断言,如果不符合要求,执行一次带 Error Message 的修正提示。
- 确定性业务逻辑执行:解析后的数据由标准的 Go/Node.js 代码去操作数据库或 API,绝不让大模型直接调用具有副作用的工具。
1.0 版本的工作流应让模型只负责解析和建议,状态转换、工具调用及数据库或 API 操作全部由确定性代码执行。
放弃了复杂的自主路由和多 Agent 协同后,工作流执行时间从 25 秒缩短到了 1.2 秒,整体成功率直接拉升到了 99.5%。
生产级 Go 语言 LLM 结构化输出与容错管道
下面展示使用 Go 语言实现的轻量级 LLM 结构化输出解析、 Schema 校验与带错误提示的单次自动修补管道。
package main import ( "context" "encoding/json" "errors" "fmt" "strings" "time" ) // TargetSchema 期望从 LLM 提取的结构化字段 type TargetSchema struct { Summary string `json:"summary"` Priority int `json:"priority"` // 1: 低, 2: 中, 3: 高 ActionItems []string `json:"action_items"` } // PipelineLLM 轻量级工作流管道 type PipelineLLM struct { maxRetries int } func NewPipelineLLM() *PipelineLLM { return &PipelineLLM{maxRetries: 1} } // ExecuteWorkflow 核心 1.0 工作流 func (p *PipelineLLM) ExecuteWorkflow(ctx context.Context, inputText string) (*TargetSchema, error) { if strings.TrimSpace(inputText) == "" { return nil, errors.New("empty_input: 输入文本不能为空") } prompt := p.buildPrompt(inputText, "") var lastErr error for attempt := 0; attempt <= p.maxRetries; attempt++ { // 1. 调用模型获取原生字符串输出 rawOutput, err := p.callLlmApi(ctx, prompt) if err != nil { lastErr = fmt.Errorf("llm_api_error: %w", err) continue } // 2. 解析 JSON parsed, err := p.parseAndValidate(rawOutput) if err == nil { // 校验通过,直接返回确定性结果 return parsed, nil } // 3. 解析失败,重新构建修补 Prompt(带上具体解析报错信息) lastErr = err fmt.Printf("[Pipeline] 第 %d 次解析 JSON 失败: %v, 发起修补提示...\n", attempt+1, err) prompt = p.buildPrompt(inputText, fmt.Sprintf("你上次输出的 JSON 存在格式错误: %v, 请严格按照 JSON 格式重新输出。", err)) } // 达到最大重试后降级兜底,不让全流程挂掉 fmt.Printf("[Pipeline] 结构化提取失败,触发 1.0 静态规则兜底: %v\n", lastErr) return p.fallbackDefault(inputText), nil } func (p *PipelineLLM) buildPrompt(input string, errorHint string) string { basePrompt := fmt.Sprintf(`请分析以下内容并输出标准的 JSON 对象,格式如下: { "summary": "一句话摘要", "priority": 1或2或3, "action_items": ["待办事项1", "待办事项2"] } 输入内容: %s`, input) if errorHint != "" { basePrompt += "\n\n⚠️ 注意: " + errorHint } return basePrompt } // 解析与 Schema 校验 func (p *PipelineLLM) parseAndValidate(raw string) (*TargetSchema, error) { // 清理 Markdown 代码块包裹 ```json ... ``` cleaned := strings.TrimSpace(raw) if strings.HasPrefix(cleaned, "```json") { cleaned = strings.TrimPrefix(cleaned, "```json") cleaned = strings.TrimSuffix(cleaned, "```") } else if strings.HasPrefix(cleaned, "```") { cleaned = strings.TrimPrefix(cleaned, "```") cleaned = strings.TrimSuffix(cleaned, "```") } cleaned = strings.TrimSpace(cleaned) var res TargetSchema err := json.Unmarshal([]byte(cleaned), &res) if err != nil { return nil, fmt.Errorf("json_unmarshal_failed: %w", err) } // 业务逻辑校验 if res.Summary == "" { return nil, errors.New("schema_error: summary 字段不能为空") } if res.Priority < 1 || res.Priority > 3 { return nil, errors.New("schema_error: priority 必须在 1~3 之间") } return &res, nil } // 静态兜底逻辑 func (p *PipelineLLM) fallbackDefault(input string) *TargetSchema { return &TargetSchema{ Summary: "【自动降级】处理异常,已保存原始输入", Priority: 1, ActionItems: []string{"人工复核输入内容"}, } } func (p *PipelineLLM) callLlmApi(ctx context.Context, prompt string) (string, error) { // 模拟返回格式良好的 JSON return `{ "summary": "系统在周三下午发生网络延迟告警", "priority": 3, "action_items": ["排查 LLM API 超时", "检查熔断策略"] }`, nil } func main() { pipeline := NewPipelineLLM() ctx, cancel := context.WithTimeout(context.Background(), 3000*time.Millisecond) defer cancel() result, err := pipeline.ExecuteWorkflow(ctx, "系统在周三下午发生网络延迟告警,请紧急处理") if err != nil { fmt.Printf("工作流完全失败: %v\n", err) return } fmt.Printf("1.0 工作流成功执行!摘要: %s, 优先级: %d, 待办数: %d\n", result.Summary, result.Priority, len(result.ActionItems)) }代码设计中有两个核心原则:
第一,自动修补尝试限制在 1 次。如果第 1 次修补提示发送后,模型依然输出非法 JSON,继续多轮对话只会拉长延时并浪费 Token。此时直接进入静态兜底代码最安全。
第二,极简的 JSON Clean 过滤。由于各种 LLM 偶尔会自带```jsonMarkdown 格式,前端/后端必须包含通用的前缀剥离逻辑,防止标准json.Unmarshal报错。
1.0 版本迭代三勿原则
在落地第一个 LLM 自动化工作流时,请时刻牢记这三条“勿做原则”:
- 切勿在第一版引入复杂的 Agent 循环:除非业务场景非要不可,否则只用顺序结构或单分支判断。
- 切勿让 LLM 负责控制流路由:路由判断尽量用经典的
if-else或正则表达式,把 LLM 仅作为“文本抽取与转化器”使用。 - 切勿缺少静态 Fallback 兜底:任何模型调用都有失败的概率。当所有的重试与修补都失效时,系统必须能返回一个保底数据,确保上游业务不中断。
先用最克制的 1.0 版本跑通闭环、把成功率和延迟做到极致,再根据真实反馈逐步引入复杂的 AI 特性,才是工程落地的稳妥之路。