知识目录 / Agent 开发

Agent4J 最佳实践:构建高质量的 Agent 应用

总结 Agent4J 开发中的最佳实践,帮助你构建高质量的 Agent 应用。

Agent4J 最佳实践:构建高质量的 Agent 应用

前言

在前面的文章中,我们学习了 Agent4J 的各种功能。现在让我们总结一些最佳实践,帮助你构建高质量的 Agent 应用。

这些实践来自我自己的经验和社区反馈,希望能帮你少走弯路。

1. Agent 设计

1.1 描述要精准

Agent 的描述会直接影响 LLM 的行为。描述越精准,Agent 越能正确理解自己的角色。

java
// 不好的描述
agent.setDescription("一个助手");

// 好的描述
agent.setDescription("""
    一个资深的 Java 开发工程师,擅长:
    1. 代码审查和重构
    2. 单元测试编写
    3. 性能优化
    4. Bug 修复
    
    工作风格:
    - 严谨、细致
    - 注重代码质量
    - 善于解释技术问题
    """);

1.2 角色要明确

给 Agent 一个明确的角色,而不是一个通用的助手:

java
// 不好的角色
agent.setName("Assistant");

// 好好的角色
agent.setName("CodeReviewer");
agent.setName("DatabaseExpert");
agent.setName("DevOpsEngineer");

1.3 工具要精简

只给 Agent 需要的工具,不要一股脑地添加所有工具:

java
// 不好的做法
agent.getSkills().addAll(BuiltInSkills.all());

// 好的做法
if (needsFileSystem) {
    agent.getSkills().add(BuiltInSkills.fileSystem());
}
if (needsCommandExecution) {
    agent.getSkills().add(BuiltInSkills.commandExecution());
}

工具越多,LLM 越难选择正确的工具。

2. 任务设计

2.1 任务要具体

给 Agent 的任务越具体,它越能准确执行:

java
// 不好的任务
session.command("帮我整理代码");

// 好的任务
session.command("""
    请帮我整理 src/main/java 目录下的代码:
    1. 把 entity 类移到 model 包
    2. 把 dao 类移到 repository 包
    3. 把 service 类移到 service 包
    4. 更新所有 import 语句
    """);

2.2 提供上下文

如果任务需要特定的上下文,要在描述中提供:

java
session.command("""
    项目背景:
    - 这是一个电商系统,使用 Spring Boot + MyBatis Plus
    - 数据库是 MySQL 8.0
    - 使用 Java 17
    
    任务:
    请根据 order 表的结构,生成对应的实体类和 Mapper
    
    要求:
    - 使用 Lombok 注解
    - 实现软删除
    - 添加创建时间和更新时间字段
    """);

2.3 设置约束

明确告诉 Agent 什么可以做,什么不可以做:

java
session.command("""
    请重构 UserService 类。
    
    约束:
    - 不要修改公共 API
    - 不要删除现有方法
    - 保持向后兼容
    - 添加必要的注释
    """);

3. 错误处理

3.1 实现重试机制

网络请求可能会失败,实现重试机制:

java
public class RetryHandler implements AgentResultHandler {
    
    private static final int MAX_RETRIES = 3;
    private int retryCount = 0;
    private String task;
    private AgentClientSession session;
    
    public RetryHandler(String task, AgentClientSession session) {
        this.task = task;
        this.session = session;
    }
    
    @Override
    public void onMessage(String msg) {
        System.out.print(msg);
    }
    
    @Override
    public void onError(Exception e) {
        if (retryCount < MAX_RETRIES) {
            retryCount++;
            System.out.println("重试第 " + retryCount + " 次...");
            session.command(task)
                .then(this)
                .error(this);
        } else {
            System.out.println("重试次数用尽,任务失败");
            e.printStackTrace();
        }
    }
}

3.2 处理超时

对于长时间运行的任务,设置超时:

java
public class TimeoutHandler implements AgentResultHandler {
    
    private final long timeout;
    private final ScheduledExecutorService scheduler;
    private volatile boolean completed = false;
    
    public TimeoutHandler(long timeoutSeconds) {
        this.timeout = timeoutSeconds;
        this.scheduler = Executors.newSingleThreadScheduledExecutor();
    }
    
    @Override
    public void onMessage(String msg) {
        if (!completed) {
            System.out.print(msg);
        }
    }
    
    @Override
    public void onComplete() {
        completed = true;
        scheduler.shutdown();
    }
    
    public void startTimeout(Runnable onTimeout) {
        scheduler.schedule(() -> {
            if (!completed) {
                completed = true;
                onTimeout.run();
            }
        }, timeout, TimeUnit.SECONDS);
    }
}

3.3 记录日志

记录 Agent 的执行过程,便于调试:

java
public class LoggingHandler implements AgentResultHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingHandler.class);
    
    @Override
    public void onMessage(String msg) {
        logger.info("Agent 消息: {}", msg);
        System.out.print(msg);
    }
    
    @Override
    public void onTool(ToolDescriptor tool, ToolStatus status) {
        logger.info("工具调用: {} - {}", tool.getName(), status);
    }
    
    @Override
    public void onPlanStepComplete(Plan plan, int current, int total, String step, String result) {
        logger.info("计划步骤完成: {}/{} - {}", current, total, step);
    }
    
    @Override
    public void onError(Exception e) {
        logger.error("Agent 错误", e);
    }
}

4. 性能优化

4.1 合理使用 Plan

Plan 的每个步骤都是一个独立的 LLM 调用,会消耗 Token。合理使用 Plan:

java
// 不好的做法:把简单任务拆分成太多步骤
session.command("""
    请执行以下步骤:
    1. 读取文件
    2. 分析内容
    3. 生成报告
    4. 保存报告
    """);

// 好的做法:简单任务直接执行
session.command("请读取 report.txt 文件,分析内容,生成报告并保存");

4.2 并行处理

对于独立的子任务,使用 Sub-Agent 并行处理:

java
// 不好的做法:顺序处理
session.command("""
    请分析这三个文件:
    1. 分析 file1.txt
    2. 分析 file2.txt
    3. 分析 file3.txt
    """);

// 好的做法:并行处理
session.command("""
    请同时分析这三个文件:
    1. file1.txt
    2. file2.txt
    3. file3.txt
    """);

4.3 缓存结果

对于重复的查询,缓存结果:

java
public class CachingAgent {
    
    private Map<String, String> cache = new ConcurrentHashMap<>();
    private AgentClient agent;
    
    public String query(String key, String question) {
        return cache.computeIfAbsent(key, k -> {
            StringBuilder result = new StringBuilder();
            agent.createSession()
                .command(question)
                .then(msg -> result.append(msg))
                .error(e -> result.append("错误:").append(e.getMessage()));
            return result.toString();
        });
    }
}

5. 安全性

5.1 限制工具权限

对于危险的工具,限制权限:

java
@ToolInfo(name = "execute_command", description = "执行 Shell 命令")
public class SafeCommandTool implements Tool<CommandParam> {
    
    private static final List<String> FORBIDDEN_COMMANDS = List.of(
        "rm -rf",
        "sudo",
        "chmod 777",
        "dd if="
    );
    
    @Override
    public String execute(CommandParam param) {
        String command = param.getCommand();
        
        // 检查是否是禁止的命令
        for (String forbidden : FORBIDDEN_COMMANDS) {
            if (command.contains(forbidden)) {
                return "错误:不允许执行此命令";
            }
        }
        
        // 执行命令
        return executeCommand(command);
    }
}

5.2 验证输入

验证 Agent 的输入,防止注入攻击:

java
public class InputValidator {
    
    public static boolean isValidInput(String input) {
        // 检查长度
        if (input.length() > 10000) {
            return false;
        }
        
        // 检查特殊字符
        if (input.contains("<script>") || input.contains("javascript:")) {
            return false;
        }
        
        return true;
    }
}

5.3 审计日志

记录所有敏感操作:

java
public class AuditLogger {
    
    private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT");
    
    public static void logToolCall(String agentName, String toolName, String params) {
        auditLogger.info("Agent: {}, Tool: {}, Params: {}", agentName, toolName, params);
    }
    
    public static void logFileOperation(String agentName, String operation, String path) {
        auditLogger.info("Agent: {}, Operation: {}, Path: {}", agentName, operation, path);
    }
}

6. 测试

6.1 单元测试

测试自定义工具:

java
@Test
public void testWeatherTool() {
    WeatherTool tool = new WeatherTool();
    WeatherParam param = new WeatherParam();
    param.setCity("北京");
    
    String result = tool.execute(param);
    
    assertNotNull(result);
    assertTrue(result.contains("北京"));
}

测试 Agent 的完整流程:

java
@Test
public void testAgentWorkflow() {
    LLMModel llm = LLMModel.create(ModelType.OpenAI, "https://token-plan-cn.xiaomimimo.com", "mimo-v-2.5-pro", "your-api-key");
    
    AgentClient agent = new AgentClient();
    agent.setName("TestAgent");
    agent.setModel(llm);
    agent.getTools().add(new MockTool());
    
    AtomicBoolean completed = new AtomicBoolean(false);
    StringBuilder result = new StringBuilder();
    
    agent.createSession()
        .command("测试任务")
        .then(new AgentResultHandler() {
            public void onMessage(String msg) {
                result.append(msg);
            }
            public void onComplete() {
                completed.set(true);
            }
        })
        .error(e -> fail("不应该出错"));
    
    // 等待完成
    await().atMost(30, TimeUnit.SECONDS).untilTrue(completed);
    
    assertNotNull(result.toString());
}

6.3 Mock 测试

使用 Mock 工具测试特定场景:

java
@ToolInfo(name = "mock_tool", description = "模拟工具")
public class MockTool implements Tool<MockParam> {
    
    private String mockResult;
    
    public MockTool(String mockResult) {
        this.mockResult = mockResult;
    }
    
    @Override
    public String execute(MockParam param) {
        return mockResult;
    }
}

7. 监控

7.1 性能监控

监控 Agent 的性能指标:

java
public class PerformanceMonitor {
    
    private static final Map<String, Long> toolExecutionTimes = new ConcurrentHashMap<>();
    private static final Map<String, Integer> toolCallCounts = new ConcurrentHashMap<>();
    
    public static void recordToolCall(String toolName, long executionTime) {
        toolExecutionTimes.merge(toolName, executionTime, Long::sum);
        toolCallCounts.merge(toolName, 1, Integer::sum);
    }
    
    public static void printReport() {
        System.out.println("工具执行统计:");
        toolCallCounts.forEach((tool, count) -> {
            long totalTime = toolExecutionTimes.getOrDefault(tool, 0L);
            double avgTime = (double) totalTime / count;
            System.out.printf("  %s: 调用 %d 次, 平均耗时 %.2f ms%n", tool, count, avgTime);
        });
    }
}

7.2 Token 使用监控

监控 Token 使用情况:

java
public class TokenMonitor {
    
    private static final Map<String, Long> tokenUsage = new ConcurrentHashMap<>();
    
    public static void recordUsage(String model, long tokens) {
        tokenUsage.merge(model, tokens, Long::sum);
    }
    
    public static long getTotalUsage(String model) {
        return tokenUsage.getOrDefault(model, 0L);
    }
}

7.3 错误监控

监控和统计错误:

java
public class ErrorMonitor {
    
    private static final Map<String, Integer> errorCounts = new ConcurrentHashMap<>();
    
    public static void recordError(String errorType) {
        errorCounts.merge(errorType, 1, Integer::sum);
    }
    
    public static void printReport() {
        System.out.println("错误统计:");
        errorCounts.forEach((error, count) -> {
            System.out.printf("  %s: %d 次%n", error, count);
        });
    }
}

8. 部署

8.1 环境变量

使用环境变量管理配置:

java
public class Config {
    
    public static String getApiKey() {
        return System.getenv("API_KEY");
    }
    
    public static String getBaseUrl() {
        return System.getenv().getOrDefault("BASE_URL", "https://token-plan-cn.xiaomimimo.com");
    }
    public static String getBaseUrl() {
        return System.getenv().getOrDefault("BASE_URL", "https://token-plan-cn.xiaomimimo.com");
    }
    
    public static String getModel() {
        return System.getenv().getOrDefault("MODEL", "mimo-v-2.5-pro");
    }
}

实现健康检查端点:

java
@RestController
public class HealthController {
    
    @GetMapping("/health")
    public ResponseEntity<Map<String, Object>> health() {
        Map<String, Object> status = new HashMap<>();
        status.put("status", "UP");
        status.put("timestamp", Instant.now());
        status.put("version", "1.0.0");
        
        return ResponseEntity.ok(status);
    }
}

8.3 优雅关闭

实现优雅关闭:

java
@Component
public class GracefulShutdown implements DisposableBean {
    
    private static final Logger logger = LoggerFactory.getLogger(GracefulShutdown.class);
    
    @Override
    public void destroy() {
        logger.info("开始优雅关闭...");
        
        // 等待正在执行的任务完成
        // 保存会话状态
        // 释放资源
        
        logger.info("优雅关闭完成");
    }
}

总结

这些最佳实践来自实际开发经验,希望能帮你构建高质量的 Agent 应用。记住:

  1. Agent 设计:描述要精准,角色要明确,工具要精简
  2. 任务设计:任务要具体,提供上下文,设置约束
  3. 错误处理:实现重试机制,处理超时,记录日志
  4. 性能优化:合理使用 Plan,并行处理,缓存结果
  5. 安全性:限制工具权限,验证输入,审计日志
  6. 测试:单元测试,集成测试,Mock 测试
  7. 监控:性能监控,Token 使用监控,错误监控
  8. 部署:环境变量,健康检查,优雅关闭

Agent4J 是一个强大的框架,但只有正确使用,才能发挥它的最大价值。

最后

感谢你阅读这个系列的文章。希望这些内容能帮助你更好地使用 Agent4J。

如果你有任何问题或建议,欢迎在 GitHub 上提出 Issue。

祝你在 Agent 开发的道路上一切顺利!


项目地址https://github.com/onlyGuo/agent4j

系列文章目录

  1. Agent4J 是什么?我为什么写这个框架
  2. 快速开始:5 分钟跑通第一个 Agent
  3. 核心概念详解:理解 Agent、Session、Model、Tool、Skill
  4. 内置工具与技能:Agent 开箱即用的能力
  5. 自定义工具开发:扩展 Agent 的能力边界
  6. 实战案例:用 Agent4J 构建全栈项目
  7. 高级特性:Plan 和 Sub-Agent 的妙用
  8. 会话管理与持久化:保存和恢复对话
  9. Agent4J 最佳实践:构建高质量的 Agent 应用(本文)