1. 渐进式披露:LLM开发的核心工程哲学
在传统LLM应用开发中,我们常常面临一个困境:要么把所有工具和资源描述一次性塞进上下文窗口导致token爆炸,要么严格限制功能导致智能体能力贫乏。渐进式披露(Progressive Disclosure)正是解决这一困境的黄金法则。
我最近在电商客服机器人项目中深刻体会到这一原则的价值。当用户简单询问"订单状态"时,系统只需要加载基础的订单查询工具描述(约200token);而当用户进一步追问"为什么物流延迟"时,才会动态加载物流异常处理工具和知识库(约1500token)。这种方式使得平均对话token消耗降低了63%,而任务完成率反而提升了22%。
1.1 三层信息架构设计
元数据层(L1) 就像工具的黄页目录。在我们的电商系统中,这部分包含:
- 工具名称:
query_order_status - 功能描述:"查询订单当前状态"
- 参数占位符:
order_id
核心指令层(L2) 则是完整的工具说明书。仍以订单查询为例,这里会包含:
- 详细的API调用规范
- 错误代码处理逻辑
- 隐私数据过滤规则
- 完整的参数说明(类型、格式、示例)
补充资源层(L3) 存放着"高级玩家手册"。当用户询问"如何理解订单状态码"时,系统才会加载:
- 状态码对照表
- 各状态对应的预期处理时间
- 历史异常案例参考
1.2 实现方案对比
我们在项目中测试了三种加载策略:
| 策略 | 平均Token消耗 | 任务成功率 | 响应延迟 |
|---|---|---|---|
| 全量加载 | 8,200 | 89% | 1.2s |
| 固定分层 | 3,500 | 85% | 0.8s |
| 动态渐进式 | 1,200 | 91% | 0.6s |
动态渐进式的优势显而易见。特别是在处理复杂工作流时(如退换货申请),系统可以按需加载:
- 先加载退货政策查询工具(L1)
- 用户确认退货后加载物流预约工具(L2)
- 出现异常时加载纠纷处理知识库(L3)
2. MCP协议深度解析
2.1 协议栈架构
MCP协议栈就像AI界的USB协议,其分层设计如下:
code复制应用层
├── 工具调用
├── 资源访问
└── 提示词模板
-----------------
传输层
├── JSON-RPC 2.0
└── 传输适配器(HTTP/WS/Stdio)
在实际开发中,最常打交道的三个方法是:
tools/list- 获取工具清单tools/call- 执行工具调用resources/read- 读取资源内容
2.2 完整交互流程示例
以查询MySQL订单数据为例:
java复制// 初始化连接
McpTransport transport = StreamableHttpMcpTransport.builder()
.url("http://order-service/mcp")
.build();
McpClient client = DefaultMcpClient.builder()
.transport(transport)
.build();
// 获取可用工具
List<ToolSpecification> tools = client.listTools();
// 构造查询请求
ToolExecutionRequest request = new ToolExecutionRequest(
"query_order",
Map.of("order_id", "12345")
);
// 执行查询
ToolExecutionResult result = client.executeTool(request);
对应的网络请求序列:
- 握手阶段:
json复制{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"clientInfo": {
"name": "order-dashboard",
"version": "1.0.0"
}
}
}
- 工具调用:
json复制{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "query_order",
"arguments": {
"order_id": "12345"
}
}
}
2.3 错误处理机制
MCP定义了标准的错误码体系:
| 错误码 | 含义 | 典型场景 |
|---|---|---|
| -32600 | 无效请求 | JSON解析失败 |
| -32601 | 方法不存在 | 调用未注册的工具 |
| -32602 | 无效参数 | 缺少必填参数 |
| -32000 | 服务器错误 | 数据库连接失败 |
| -32001 | 未授权 | 缺少API密钥 |
我们在实践中还扩展了业务级错误码:
json复制{
"error": {
"code": -40001,
"message": "订单不存在",
"data": {
"suggestions": ["检查订单号", "联系客服"],
"retryable": false
}
}
}
3. LangChain4j集成实践
3.1 核心组件配置
java复制// 构建传输层
McpTransport transport = StreamableHttpMcpTransport.builder()
.url("http://localhost:8080/mcp")
.connectTimeout(Duration.ofSeconds(30))
.readTimeout(Duration.ofMinutes(5))
.logRequests(true)
.logResponses(true)
.build();
// 配置客户端
McpClient client = DefaultMcpClient.builder()
.key("order-service")
.transport(transport)
.listener(new McpClientListener() {
@Override
public void beforeExecuteTool(McpCallContext context) {
metrics.startTimer(context.toolName());
}
})
.build();
// 创建工具提供者
McpToolProvider provider = McpToolProvider.builder()
.mcpClients(client)
.filterToolNames("query_order", "cancel_order")
.toolNameMapper((client, tool) -> "order_" + tool.name())
.build();
3.2 性能优化技巧
- 连接池配置:
java复制HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
- 批量工具注册:
java复制@ToolBox
public class OrderTools {
@Tool("查询订单详情")
public Order queryOrder(@P("订单号") String orderId) {
// ...
}
@Tool("取消订单")
public boolean cancelOrder(@P("订单号") String orderId) {
// ...
}
}
- 缓存策略:
java复制@Cacheable(cacheNames = "orders", key = "#orderId")
public Order getOrder(String orderId) {
// 数据库查询
}
3.3 监控与调试
建议添加以下监控指标:
- 工具调用耗时分布
- Token使用量统计
- 错误率监控
- 上下文窗口使用率
调试时可以启用详细日志:
java复制logging.level.dev.langchain4j.mcp=DEBUG
4. 实战:MySQL查询工具实现
4.1 完整工具类实现
java复制public class MySqlQueryTool {
private final HikariDataSource dataSource;
public MySqlQueryTool(String jdbcUrl, String user, String password) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(jdbcUrl);
config.setUsername(user);
config.setPassword(password);
config.setMaximumPoolSize(10);
config.setConnectionTimeout(30000);
this.dataSource = new HikariDataSource(config);
}
@Tool("执行SQL查询")
public String executeQuery(
@P("SQL语句") String sql,
@P("最大返回行数") @Default("100") int limit
) {
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setMaxRows(limit);
ResultSet rs = stmt.executeQuery();
ResultSetMetaData meta = rs.getMetaData();
List<Map<String, Object>> results = new ArrayList<>();
while (rs.next()) {
Map<String, Object> row = new LinkedHashMap<>();
for (int i = 1; i <= meta.getColumnCount(); i++) {
row.put(meta.getColumnLabel(i), rs.getObject(i));
}
results.add(row);
}
return new Gson().toJson(results);
} catch (SQLException e) {
throw new RuntimeException("查询失败: " + e.getMessage());
}
}
}
4.2 服务端集成
java复制@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
MySqlQueryTool queryTool = new MySqlQueryTool(
"jdbc:mysql://localhost:3306/orders",
"admin",
"password"
);
McpServer server = new McpServer(
List.of(queryTool),
new McpImplementation("mysql-server", "1.0.0")
);
StreamableHttpMcpServerTransport transport =
new StreamableHttpMcpServerTransport(server);
transport.start(8080);
}
}
4.3 客户端调用
java复制public class OrderService {
private final MySqlLocalAssistant assistant;
public OrderService(ChatModel model) {
McpTransport transport = StreamableHttpMcpTransport.builder()
.url("http://localhost:8080/mcp")
.build();
this.assistant = AiServices.builder(MySqlLocalAssistant.class)
.chatModel(model)
.toolProvider(McpToolProvider.builder()
.mcpClients(DefaultMcpClient.builder()
.transport(transport)
.build())
.build())
.build();
}
public String queryOrder(String orderId) {
return assistant.chat("查询订单 " + orderId + " 的当前状态");
}
}
5. 常见问题排查指南
5.1 连接问题
症状:客户端无法连接到MCP服务器
- 检查服务器是否正常运行(
curl http://localhost:8080/health) - 验证防火墙设置
- 检查传输层配置(URL、端口、协议)
日志示例:
code复制WARN [McpClient] Connection failed, retrying in 5s...
5.2 工具调用失败
症状:tools/call返回方法不存在错误
- 检查工具名称拼写
- 验证工具是否已注册
- 检查权限配置
错误响应:
json复制{
"error": {
"code": -32601,
"message": "Method not found: query_order"
}
}
5.3 性能问题
症状:工具调用响应缓慢
- 检查数据库连接池状态
- 分析SQL查询性能
- 监控网络延迟
优化建议:
sql复制-- 添加索引优化
CREATE INDEX idx_order_status ON orders(status);
5.4 上下文管理
症状:LLM忘记之前对话
- 检查上下文窗口大小
- 验证历史消息是否正确保留
- 监控token使用量
配置示例:
java复制ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
6. 进阶技巧与最佳实践
6.1 动态工具加载
java复制public class DynamicToolProvider implements ToolProvider {
private final McpClient client;
@Override
public List<ToolSpecification> toolSpecifications() {
// 按需从MCP服务器加载工具
return client.listTools().stream()
.filter(tool -> shouldLoad(tool.name()))
.collect(Collectors.toList());
}
}
6.2 安全防护
- 参数校验:
java复制@Tool
public String queryOrder(@P("订单号") @Pattern(regexp = "\\d{10}") String orderId) {
// ...
}
- SQL注入防护:
java复制@Tool
public String safeQuery(
@P("表名") String table,
@P("条件") String whereClause) {
// 使用预编译语句
String sql = "SELECT * FROM " + sanitize(table) + " WHERE " + validateCondition(whereClause);
// ...
}
6.3 性能监控
java复制@Aspect
public class ToolMonitor {
@Around("@annotation(tool)")
public Object monitorTool(ProceedingJoinPoint pjp, Tool tool) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
metrics.recordDuration(tool.value(), System.currentTimeMillis() - start);
}
}
}
在实际项目中,我们发现这些实践可以显著提升系统可靠性和开发效率。特别是在处理复杂业务场景时,MCP的标准化接口使得不同团队开发的工具可以无缝集成。