一、AI Agent初步认识以及手写React Agent

20 阅读6分钟

AI Agent概述:AI Agent简单的概述就是能够感知环境、进行决策并执行相应的动作以达成特定目标的智能系统。传统的AI 大模型相当于是一本百科全书或者顾问,为你提供对应问题的解决方案;但是AI Agent则相当于你的一个雇员,你只需要给予一个任务描述,它就可以自动的去分解任务、指定合理的步骤并执行,最终完成你交代的任务。

Agent Type:

  • Simple reflex Agent(基于简单反射的Agent)
  • Model-Based Reflex Agent(基于模型反射的Agent)
  • Goal-Based Agent(基于目标的Agent)
  • Utility-Based Agent(基于通用的Agent)
  • Learning Agent(自学习Agent)

1.1 Agent 核心组件

  1. 规划与推理:针对一个任务进行分解拆分成多个子任务,并确定最优路径
  2. 记忆:分为长期记忆以及短期记忆,主要记录操作步骤、返回结果以及用户习惯等信息,避免重复操作以及在多步任务中保持连贯性
  3. 工具使用:Agent的手脚用来执行各种大模型产生的决策
  4. 行动:基于规划和工具使用来进行实际的操作,比如写入文本、获取API数据等操作

1.2 AI Agent 主要分类

Workflow Agent(工作流智能体)

该种智能体主要是有工程师将对应任务分解整理成一条高效的工作流然后Agent在工作流的对应节点上执行相关的任务,相当于流水线上的工人。这样做的优点是不会流程稳定不会产生出乎意料的结果,但同样也会存在抗风险能力差以及耗费人力的问题。

autonomous Agent(自主智能体)

该种智能体就相当于是一个管家,处理问题的能力更加自主需要人工参与的步骤更少,减轻了工程师的压力。但是对应也会存在更大的结果不确定性,或者出现更多不当操作;所以需要更加准确合理的设计人工介入的阶段。

基本步骤:思考——行动——观察——再思考

常见类别:

  • 单智能体循环:只含有一个智能体按照上述步骤进行循环直至获得正确解或者其他某种条件
  • 多智能体循环:包含多个智能体扮演不同的角色针对任务进行协商、思考以及分工协作共同完成对应的任务

总结:

  • 目前在商业应用落地中工作流智能体更加成熟,因为其更加稳定可控;但是自主智能体更先进,是技术前景需要更深的技术探索
  • 关于AI Agent 智能体的上述两种类型并不是对立的,而是优势互补共同存在的,在一个复杂的商业系统中需要需要将两者结合使用

1.3 ReactAgent 相关信息以及代码实例

ReactAgent相关信息

说明:ReactAgent是基于Graph工作时构建的智能体,具体细节是将智能体相关工具抽象成各个Node(每一个节点代表了对应智能体功能),实际的任务处理中根据任务的状态以及上个节点的返回结果选择下一个节点。

对于节点类型主要包含以下:

  • Model Node:主要用于调用LLM进行推理和决策
  • Tool Node:是模型的手脚,包含了智能体完成任务需要的所有工具
  • Hook Node:俗称钩子节点,这个节点通常不参与Agent的决策与行动,而是用来限制智能体的推理以及不计成本的执行,在自动化推理流程中引入人工干预防止处理流程偏离正常轨迹

甚至可以武断的说明一个智能体系统能否上线生产,它的模型能力一般是放在第二位的,首要的是看该系统的Hook Node设计是否合理、严谨!

手写React Agent 代码实例

说明:为了更好的体会AI Agent的功能我们需要在代码实操中体会,下面是一个简单的ReactAgent实现,主要功能是提出一个需求让AI Agent 自己去调用工具执行操作以实现这个需求,我们这里的需求是:请帮我用HTML、CSS、JS创建一个简单的贪吃蛇游戏,分成三个文件,分别是snake.html、snake.css、snake.js

实现语言:Java(JDK21)

构建工具:Gradle

相关工具包:

implementation 'com.openai:openai-java:4.0.0'(注:之所以使用openAI的包是因为目前市面上所有的模型API都是支持openAI相关方法调用的,使用这个包可是让你调用不同模型AP时只需要修改API_KEY,URL,ModelName,而不需要改其他相关代码)
implementation 'com.alibaba.fastjson2:fastjson2:2.0.56'

模型连接相关参数配置类:

public class ModelConfig {
      // 通义千问
//    public static final String API_KEY = "自己官网申请";
//    public static final String BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
//    public static final String LLM_NAME = "qwen-plus";

    // openAI(需要科学上网)
    public static final String API_KEY="自己官网申请";
    public static final String BASE_URL = "https://api.openai.com/v1";
    public static final String LLM_NAME = "gpt-4o-mini";
}

1、定义工具注解以及工具代码实现

说明:定义工具及参数注解主要是为了让智能体可以将工具信息整合提交给大模型让模型选择使用那个方法

// 工具(方法功能注解)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Tool {
    String description();
}
// 工具参数(方法如惨注解)
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ToolParam {
    String description();
}
// 相关工具类,包含各种工具方法
public class AgentTools {
    // 存放接收到的大模型回答响应
    private final ObjectMapper objectMapper=new ObjectMapper();

    /**
     * @description: 将相关内容写入指定文件中
     * @author: 
     * @date: 2026-01-18
     */
    @Tool(description = "将指定内容写入本地文件中")
    public String writeFile(@ToolParam(description = "包含'file_path'和'content'的json串") String jsonInput){
        try{
            JsonNode jsonNode = objectMapper.readTree(jsonInput);
            String filePath = jsonNode.get("file_path").asText();
            String content = jsonNode.get("content").asText();

            try(FileWriter fileWriter = new FileWriter(filePath)){
                fileWriter.write(content);
                return String.format("写入成功");
            }catch (Exception e){
                return String.format("写入文件 %s 时发生错误 %s", filePath, e.getMessage());
            }
        }catch (Exception e){
            return String.format("解析Action input或者执行writeFile工具时报错:%s",e.getMessage());
        }
    }

    /**
     * @description: 计算两个正整数之和
     * @author: 
     * @date: 2026-01-18
     */
    @Tool(description = "求两数之和")
    public String sumNumber(@ToolParam(description = "包含a,b两个正整数的json串") String jsonInput) throws JsonProcessingException {
       try{
           JsonNode jsonNode = objectMapper.readTree(jsonInput);
           int a = jsonNode.get("a").asInt();
           int b = jsonNode.get("b").asInt();

           return "最终计算结果:"+String.valueOf((a+b));

       } catch (Exception e){
           return String.format("解析Action input或者执行writeFile工具时报错:%s",e.getMessage());
       }
    }
}

2、模型推理并执行对应方法完成任务实现

public class ToolUtils {
    /**
     * @description: 获取对应的Agent工具的方法已经相关解释,格式化成对应字符串
     * @author: 
     * @date: 2026-01-18
     */
    public static String getToolDescription(Class<?> classes){
        List<String> toolNameList = new ArrayList<>();
        List<String> formattedToolList = new ArrayList<>();

        for(Method declaredMethod : classes.getDeclaredMethods()){
            if(declaredMethod.isAnnotationPresent(Tool.class)){
                Tool declaredMethodAnnotation = declaredMethod.getAnnotation(Tool.class);
                String toolName = declaredMethod.getName();
                String toolDescription = declaredMethodAnnotation.description();
                String toolParamDescription = declaredMethod.getParameters()[0].getAnnotation(ToolParam.class).description();
                String formatTool = String.format("- toolName: %s, toolDescription: %s, toolParamDescription: %s", toolName, toolDescription, toolParamDescription);

                toolNameList.add(toolName);
                formattedToolList.add(formatTool);
            }
        }

        String toolNames = String.join("\n", toolNameList);
        String formattedTools = String.join("\n", formattedToolList);

        return formattedTools;
    }
}
public class OpenAITest {
    
    private static final String REACT_PROMPT_TEMLATE = """
            ## 角色定义
            你是一个强大的 AI 助手,通过思考和使用工具来解决用户的问题。
            
            ## 任务
            你的任务是尽你所能回答以下问题。你可以使用以下工具:
            {tools}
            
            ## 规则
            - Action 中只返回工具的名字
            - 每次只做Reason/Action/AcionInput 或者FinalAnswer的输出过程,不要一次性做
            
            ## 输出过程
            Reason: 你思考过程
            Action: 你的下一步动作,你想要执行什么工具
            ActionInput: 你调用工具输入的参数是什么
            
            或者
            
            FinalAnswer: 表示最终答案
            
            ## 用户需求
            Question: {input}
            
            ## 历史聊天记录
            {history}
            """;

    private OpenAIClient apiClient;
    public OpenAITest(OpenAIClient apiClient){
        this.apiClient = apiClient;
    }

    /**
     * @description: 请求大模型进行相关问题的处理
     * @author: 
     * @date: 2026-01-18
     */
    public String run(String input) throws NoSuchMethodException {
        //将工具添加到列表中
        HashMap<String, Method> tools = new HashMap<>();
        tools.put("writeFile", AgentTools.class.getMethod("writeFile", String.class));
        tools.put("sumNumber", AgentTools.class.getMethod("sumNumber",String.class));

        // 存储历史聊天记录
        StringBuilder history = new StringBuilder();

        int i = 0;
        while (i<10){
            try {
                // 构建提示词
                String prompt = buildPrompt(input, history.toString());
                ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
                        .addAssistantMessage(prompt)
                        .model(ModelConfig.LLM_NAME)
                        .build();
                ChatCompletion chatCompletion = apiClient.chat().completions().create(params);
                String rawLlmOutput = chatCompletion.choices().get(0).message().content().get();

                // 对大模型输出做格式化处理
                ParsedOutput parsedOutput = parsedLlmOutput(rawLlmOutput);

                //判断是否为最终答案
                if(parsedOutput.type.equals("final_answer")){
                    return parsedOutput.answer;
                }

                //执行模型选择的工具
                String observation = excuteTool(parsedOutput, tools);
                System.out.println("最终执行结果:"+ observation);

                //添加为历史聊天记录
                history.append("Reason:").append(parsedOutput.reason).append("\n")
                        .append("action:").append(parsedOutput.action).append("\n")
                        .append("actionInput:").append(parsedOutput.actionInput).append("\n")
                        .append("observation:").append(observation).append("\n");
            }catch (Exception e){
                e.printStackTrace();
                i++;
            }

        }

        return "达到最大循环次数!";
    }

    /**
     * @description: 将相关的工具信息以及对应历史聊天记录添加到问题提示词中
     * @author: 
     * @date: 2026-01-18
     */
    public String buildPrompt(String input, String history){
        String prompt = REACT_PROMPT_TEMLATE.replace("{tools}", ToolUtils.getToolDescription(AgentTools.class));
        prompt = prompt.replace("{history}", history);
        prompt = prompt.replace("{input}", input);
        return prompt;
    }

    //自定义一个返回类型
    private record ParsedOutput(String type, String answer, String reason, String action, String actionInput, String message){}

    /**
     * @description: 对大模型的响应回答做格式化
     * @author: 
     * @date: 2026-01-18
     */
    private ParsedOutput parsedLlmOutput(String llmOutput){
        if(llmOutput.contains("FinalAnswer:")){
            return new ParsedOutput("final_answer",llmOutput.split("FinalAnswer:")[1].strip(),null, null, null, null);
        }

        Pattern actionPattern = Pattern.compile("Reason:(.*?)Action:(.*?)ActionInput:(.*)", Pattern.DOTALL);
        Matcher matcher = actionPattern.matcher(llmOutput);
        if(matcher.find()){
            String reason = matcher.group(1).trim();
            String action = matcher.group(2).trim();
            String actionInput = matcher.group(3).trim();

            return new ParsedOutput("action", null, reason, action, actionInput, null);
        }

        return new ParsedOutput("error", null, null, null, null, String.format("解析LLM输出失败:%s", llmOutput));
    }

    /**
     * @description: 根据方法名称以及参数执行对应的工具方法
     * @author: 
     * @date: 2026-01-18
     */
    private static String excuteTool(ParsedOutput parsedOutput, HashMap<String, Method> tools) throws InvocationTargetException, IllegalAccessException {
        String toolName = parsedOutput.action;
        String toolParam = parsedOutput.actionInput;
        Method toolMethod = tools.get(toolName);
        Object observation = toolMethod.invoke(new AgentTools(), toolParam);
        return observation.toString();
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //获取连接信息创建连接对象
        OpenAIClient openAIClient = OpenAIOkHttpClient.builder()
                .apiKey(ModelConfig.API_KEY)
                .baseUrl(ModelConfig.BASE_URL)
                .build();

        OpenAITest openAITest = new OpenAITest(openAIClient);

        //String prompting = "将1到10的整数写入文件中";

        String prompting = "请帮我用HTML、CSS、JS创建一个简单的贪吃蛇游戏,分成三个文件,分别是snake.html、snake.css、snake.js";

        String result = openAITest.run(prompting);

        System.out.println(result);
    }
}

最终实现效果: 在这里插入图片描述 在这里插入图片描述

以上是AI Agent初步的认识,有兴趣的朋友可以持续关注,并在评论区提出自己的看法或者问题!