From b313523aba7d4db316eebc892c1b824878be676a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=AD=90=E9=BB=98?= <925456043@qq.com> Date: Tue, 11 Aug 2026 21:41:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E4=B8=AD=E6=96=87=E5=8F=98=E9=87=8F=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 开启 Enjoy 中文表达式支持 - 补充中文、英文及上游引用回归测试 --- .../flow/core/node/TemplateNode.java | 2 + .../flow/core/node/TemplateNodeTest.java | 125 ++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 easy-agents-flow/src/test/java/com/easyagents/flow/core/node/TemplateNodeTest.java diff --git a/easy-agents-flow/src/main/java/com/easyagents/flow/core/node/TemplateNode.java b/easy-agents-flow/src/main/java/com/easyagents/flow/core/node/TemplateNode.java index d6006cb..2aa57e3 100644 --- a/easy-agents-flow/src/main/java/com/easyagents/flow/core/node/TemplateNode.java +++ b/easy-agents-flow/src/main/java/com/easyagents/flow/core/node/TemplateNode.java @@ -34,6 +34,8 @@ public class TemplateNode extends BaseNode { private String template; static { + // Enjoy 默认仅识别 ASCII 变量名,工作流参数需要支持中文名称。 + Engine.setChineseExpression(true); engine = Engine.create("template", e -> { e.addSharedStaticMethod(StringUtil.class); }); diff --git a/easy-agents-flow/src/test/java/com/easyagents/flow/core/node/TemplateNodeTest.java b/easy-agents-flow/src/test/java/com/easyagents/flow/core/node/TemplateNodeTest.java new file mode 100644 index 0000000..c584d57 --- /dev/null +++ b/easy-agents-flow/src/test/java/com/easyagents/flow/core/node/TemplateNodeTest.java @@ -0,0 +1,125 @@ +package com.easyagents.flow.core.node; + +import com.easyagents.flow.core.chain.Chain; +import com.easyagents.flow.core.chain.ChainDefinition; +import com.easyagents.flow.core.chain.ChainState; +import com.easyagents.flow.core.chain.Parameter; +import com.easyagents.flow.core.chain.RefType; +import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository; +import com.easyagents.flow.core.chain.repository.InMemoryNodeStateRepository; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + +/** + * 内容模板节点变量渲染回归测试。 + */ +public class TemplateNodeTest { + + /** + * 验证模板可以使用中文参数名称。 + */ + @Test + public void shouldRenderChineseParameterNames() { + TemplateNode node = templateNode( + "#(申请人)\n\n#(被申请人)", + "申请人", + "被申请人"); + Chain chain = chain(Map.of( + "申请人", "申请内容", + "被申请人", "答辩内容")); + + Map result = node.execute(chain); + + Assert.assertEquals( + "申请内容\n\n答辩内容", + result.get("finalContent")); + } + + /** + * 验证开启中文表达式后继续兼容英文参数名称。 + */ + @Test + public void shouldKeepRenderingEnglishParameterNames() { + TemplateNode node = templateNode( + "#(applicant)\n\n#(respondent)", + "applicant", + "respondent"); + Chain chain = chain(Map.of( + "applicant", "申请内容", + "respondent", "答辩内容")); + + Map result = node.execute(chain); + + Assert.assertEquals( + "申请内容\n\n答辩内容", + result.get("finalContent")); + } + + /** + * 验证自动模板变量可以通过引用读取上游节点输出。 + */ + @Test + public void shouldRenderManagedUpstreamReference() { + String parameterName = "ref_node__llm_2e_output"; + TemplateNode node = templateNode( + "模型输出:#(" + parameterName + ")"); + Parameter parameter = new Parameter(parameterName); + parameter.setRefType(RefType.REF); + parameter.setRef("node_llm.output"); + node.setParameters(Collections.singletonList(parameter)); + Chain chain = chain(Map.of( + "node_llm.output", "回答内容")); + + Map result = node.execute(chain); + + Assert.assertEquals( + "模型输出:回答内容", + result.get("finalContent")); + } + + /** + * 创建指定输入参数的内容模板节点。 + * + * @param template 模板内容 + * @param parameterNames 输入参数名称 + * @return 内容模板节点 + */ + private TemplateNode templateNode( + String template, + String... parameterNames) { + TemplateNode node = new TemplateNode(); + node.setId("template-node"); + node.setName("内容模板"); + node.setTemplate(template); + node.setParameters(Arrays.stream(parameterNames) + .map(Parameter::new) + .toList()); + node.setOutputDefs(Collections.singletonList( + new Parameter("finalContent"))); + return node; + } + + /** + * 创建带初始化状态和输入变量的工作流。 + * + * @param inputs 工作流输入 + * @return 工作流 + */ + private Chain chain(Map inputs) { + Chain chain = new Chain( + new ChainDefinition(), + "template-node-" + UUID.randomUUID()); + chain.setChainStateRepository( + new InMemoryChainStateRepository()); + chain.setNodeStateRepository( + new InMemoryNodeStateRepository()); + ChainState state = chain.initializeState(); + state.getMemory().putAll(inputs); + return chain; + } +}