package workflow import ( "encoding/json" "fmt" "regexp" core "agent-system-core" ) // Vars 是一次执行里可以填进模板的值。 type Vars struct { Input string Slots map[string]string Steps map[string]string } var placeholder = regexp.MustCompile(`\{\{(input|slots\.[A-Za-z_][A-Za-z0-9_]*|steps\.[A-Za-z_][A-Za-z0-9_]*\.output)\}\}`) // Render 替换 {{input}}、{{slots.}}、{{steps..output}}。缺值替换成空串。 func Render(s string, v Vars) string { return placeholder.ReplaceAllStringFunc(s, func(token string) string { body := token[2 : len(token)-2] switch { case body == "input": return v.Input case len(body) > 6 && body[:6] == "slots.": return v.Slots[body[6:]] case len(body) > 6 && body[:6] == "steps.": id := body[6 : len(body)-len(".output")] return v.Steps[id] default: return token } }) } // RenderRaw 在 JSON 字符串内部替换占位符,结果仍是合法 JSON。 func RenderRaw(raw json.RawMessage, v Vars) (json.RawMessage, error) { if len(raw) == 0 { return nil, nil } var node any if err := json.Unmarshal(raw, &node); err != nil { return nil, fmt.Errorf("workflow: 参数不是合法 JSON") } out, err := json.Marshal(walk(node, v)) if err != nil { return nil, err } return out, nil } func walk(node any, v Vars) any { switch x := node.(type) { case string: return Render(x, v) case []any: for i := range x { x[i] = walk(x[i], v) } return x case map[string]any: for k, val := range x { x[k] = walk(val, v) } return x default: return node } } // RenderStep 渲染这一步会交给执行器的文本和参数。 func RenderStep(step core.WorkflowStep, v Vars) (core.WorkflowStep, error) { step.Prompt = Render(step.Prompt, v) step.Artifact = Render(step.Artifact, v) if step.Hitl != nil { step.Hitl.Question = Render(step.Hitl.Question, v) } if step.Card != nil { step.Card.Title = Render(step.Card.Title, v) } args, err := RenderRaw(step.Args, v) if err != nil { return step, err } step.Args = args return step, nil }