Solon AI 开发学习19 - 结合 Solon Flow 实现 ReAct 效果

Solon Flow 是一个通用流程编排引擎,采用 yaml 或 json 配置。下面演示 solon-ai 和 solon-flow 演示一个人机交互的 RcAct 效果。

1、演示步骤预期:

让 LLM 根据主题编写文章,然后由人工审核,如果没有通过重复上面的动作。步骤如下:

  • Agent 编写: 流程进入 agent 节点,LLM 编写初稿。
  • 人机回路 1: 流程进入 review 节点。
    • 用户输入 reject,并输入反馈:"文章太短,请增加挑战部分的具体细节。"
  • 动态循环: 评审 link 到 "agent",流程回到 agent 节点。
  • Agent 修改: LLM 读取反馈,修改内容。
  • 人机回路 2: 流程再次进入 review 节点。
    • 用户输入 approve
  • 流程结束: 评审 link 到 "approved",流程结束。

2、流程编排

flow/demo1.yml

yaml 复制代码
id: demo1
layout:
  - {type: 'start'}
  - {task: '@agent', id: 'agent'}
  - {task: '@review', id: 'review', type: exclusive, link: [
      {nextId: 'final_approval', title: 'approved', when: '"APPROVED".equals(review_status)'},
      {nextId: 'final_failure', title: 'failed', when: '"REJECTED".equals(review_status) && revision_count.get() >= MAX_REVISIONS'},
      {nextId: 'agent', title: 'revise', when: '"REJECTED".equals(review_status) && revision_count.get() < MAX_REVISIONS'},
      {nextId: 'review', title: 'review'}
    ]}
  - {task: '@final_approval', id: 'final_approval', link: 'end'}
  - {task: '@final_failure', id: 'final_failure', link: 'end'}
  - {type: 'end', id: 'end'}

3、编写组件

  • agent 根据状态编写内容或修改内容。
java 复制代码
@Component("agent")
public class AiNodeAgent implements TaskComponent {
    private static final Logger log = LoggerFactory.getLogger(AiNodeAgent.class);

    private final ChatModel chatModel;
    public AiNodeAgent(ChatModel chatModel) {
        this.chatModel = chatModel;

    }

    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        AtomicInteger revision_count = context.getAs("revision_count");
        String draft_content = context.getAs("draft_content");
        String feedback = context.getAs("feedback");
        List<ChatMessage> messages = context.getAs("messages");

        Prompt prompt = new Prompt();
        //构建 LLM 提示词
        if (revision_count.get() == 0) {
            //第一次:编写内容
            String topic = messages.get(0).getContent();
            prompt.addMessage(ChatMessage.ofSystem("你是一个专业的内容创作者,请根据主题草拟一篇简短的文章。"));
            prompt.addMessage(ChatMessage.ofUser("请草拟关于主题 '" + topic + "' 的文章。"));
        } else {
            //循环:根据反馈修改内容
            prompt.addMessage(ChatMessage.ofSystem("你是一个专业的内容创作者。你收到了人工审核员的反馈,请根据反馈修改你的草稿。"));
            prompt.addMessage(ChatMessage.ofUser("这是你的旧草稿:\\n---\\n" + draft_content + "\\n---\\n这是人工审核员的反馈:\\n---\\n" + feedback + "\\n---\\n请提供修改后的新草稿。"));
        }

        ChatMessage new_draft = chatModel.prompt(prompt).call().getMessage();

        revision_count.incrementAndGet();
        log.info("--- LLM 完成第 {} 次草稿/修改 ---", revision_count.get());

        context.put("draft_content", new_draft.getContent());
        context.put("review_status", "PENDING");
        messages.add(ChatMessage.ofAssistant("提交第 " + revision_count.get() + " 次草稿进行审核。"));
    }
}
  • review 模拟人工审核流程,流程在此暂停,等待人工输入。
java 复制代码
@Component("review")
public class AiNodeReview implements TaskComponent {
    private static final Logger log = LoggerFactory.getLogger(AiNodeReview.class);

    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        AtomicInteger revision_count = context.getAs("revision_count");
        String draft_content = context.getAs("draft_content");
        List<ChatMessage> messages = context.getAs("messages");

        String feedback;
        String status = null;

        log.info("**人机回路节点激活** - 当前草稿 ({} 次修改):{}",
                revision_count.get(),
                (draft_content.length() > 200 ? draft_content.substring(0, 200) + "..." : draft_content));

        //为了演示,我们用 "控制台" 模拟人工输入:

        while (true) {
            System.out.println("请输入审核结果 (approve or reject):");
            String action = getInput();

            if ("approve".equals(action)) {
                feedback = "Approved.";
                status = "APPROVED";
                break;
            } else if ("reject".equals(action)) {
                System.out.println("请输入拒绝反馈意见: ");
                feedback = getInput();
                status = "REJECTED";
                break;
            } else {
                System.out.println("输入无效,请重新输入。");
            }
        }

        context.put("review_status", status);
        context.put("feedback", feedback);

        messages.add(ChatMessage.ofUserTmpl("审核结果: #{status}. 反馈: #{feedback}")
                .paramAdd("status", status)
                .paramAdd("feedback", feedback)
                .generate());
    }

    private String getInput() throws Throwable {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        return reader.readLine();
    }
}
  • final_approval 运行通过。
java 复制代码
@Component("final_approval")
public class AiNodeFinalApproval implements TaskComponent {
    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        List<ChatMessage> messages = context.getAs("messages");
        messages.add(ChatMessage.ofAssistant("最终内容已发布!"));
    }
}
  • final_failure 运行失败。
java 复制代码
@Component("final_failure")
public class AiNodeFinalFailure implements TaskComponent {
    @Override
    public void run(FlowContext context, Node node) throws Throwable {
        List<ChatMessage> messages = context.getAs("messages");
        messages.add(ChatMessage.ofAssistant("流程失败:内容修改次数过多,已退出。"));
    }
}

4、组合运行与测试

java 复制代码
@Configuration
public class DemoAgent {
    private static final Logger log = LoggerFactory.getLogger(DemoAi.class);

    @Inject
    FlowEngine flowEngine;

    @Bean
    public ChatModel chatModel(){
        return ChatModel.of("http://127.0.0.1:11434/api/chat")
                .provider("ollama")
                .model("qwen2.5:1.5b")
                .build();
    }

    @Init
    public void test() {
        FlowContext context = FlowContext.of()
                .put("MAX_REVISIONS", 3)
                .put("draft_content", "")
                .put("review_status", "NONE")
                .put("feedback", "")
                .put("revision_count", new AtomicInteger(0))
                .put("messages", Utils.asList(ChatMessage.ofUser("智能家居的未来趋势和潜在挑战。")));


        log.info("--- 启动内容审核 Agent ---");

        //执行
        flowEngine.eval("demo1", context);

        //执行后打印
        System.out.println(context.get("draft_content").toString());
        
        List<ChatMessage> messageList = context.getAs("messages");
        for (ChatMessage message : messageList) {
            System.out.println(message);
        }
    }
}

5、运行效果截图

相关推荐
imbackneverdie13 小时前
AI生图可以自由修改了!
人工智能·ai·信息可视化·科研绘图·ai工具·科研工具·ai生图
空中海13 小时前
第二章:Maven进阶篇 — 依赖管理与构建生命周期
java·maven
xun-ming13 小时前
AI时代Java程序员自救手册
java·开发语言·人工智能
DavidSoCool13 小时前
GB28181 PTZCmd 完整指令对照表(8 位 16 进制)+ 详细注释 + 使用说明
java·sip·gb28181
Huang26010813 小时前
Producer 上传参考音频 API 集成指南
ai
张健115640964813 小时前
C++访问控制与友元
java·开发语言·c++
Sam_Deep_Thinking13 小时前
中小团队需要一个资源微服务
java·微服务·架构
Thanks_ks13 小时前
透过 Copy-On-Write 机制:理解并发编程中的性能与一致性权衡
java·多线程·并发编程·底层原理·写时复制·copyonwrite·性能优
jonyleek14 小时前
私有化部署大模型时,如何平衡“数据安全”与“推理性能”的矛盾?
人工智能·ai·大模型·jvs·ai套件·jvs-ai套件
程序员差不多先生14 小时前
Openvela+ 瑞芯微+DeepSeek 桌面机器人实战评测
机器人·瑞芯微·deepseek·openvela·桌面机器人