Я изучаю MCP (протокол контекста модели), используя Spring AI, используя модель Ollama llama3.2 , работающая в контейнере Docker, подвергнутую HTTP: // localhost: 11434.
использует транспорт stdio, способный выполнять большинство операций, за исключением того, что сведения о том, что в нем подробно используются Server. Список для сотрудников, клиент MCP использует Spring-Boot-Web-Starter, чтобы получить ввод через корпус запроса, передавая вход, используя команду Curl.
import com.demo.mcp.server.data.Employee;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class EmployeeService {
private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class);
private List employees = new ArrayList();
@Tool(name="t_list_all_employees", description = "This tool will use the list of employees that is managed in the in memory list within the application")
public List getEmployees() {
logger.info("getEmployees() invoked");
return employees;
}
@Tool(name="t_add_one_employee", description = "Add an employee to the employee list,"+
"before invoking this tool create the employee string with json template in following format" +
"{ \"name\": \"SET-WITH-USER-INPUT\", \"email\": \"SET-WITH-USER-INPUT\", \"phone\": \"SET-WITH-USER-INPUT\"}\"" +
" with this this method can add the employee to in memory list. For example, if the context include " +
"add an employee to the list where the employee name is joe I, email is [email protected] and phone is 11231" +
"The employee name should be provided")
public Employee addEmployee(Employee employee) {
logger.info("Adding employee: {}", employee);
if(employee != null && employee.name() != null && !employee.name().isEmpty()) {
logger.info("Employee name is valid: {}", employee.name());
} else {
logger.warn("Invalid employee name provided: {}", employee);
throw new IllegalArgumentException("Employee name cannot be null or empty");
}
employees.add(employee);
return employee;
}
@Tool(name="t_fetch_employee_with_name", description = "This tool is used to get one employee based on user provided employee name from the in memory list in this app")
public Employee getEmployee(String name) {
logger.info("Getting employee by name: {}", name);
if (name == null || name.isEmpty()) {
logger.warn("Invalid employee name provided: {}", name);
throw new IllegalArgumentException("Employee name cannot be null or empty");
}
return employees.stream()
.filter(employee -> employee.name().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
}
@Tool(name="t_delete_employee", description = "This tool will delete the employee matching the user provided name from the in memory list in this app")
public void deleteEmployee(String name) {
if (name == null || name.isEmpty()) {
logger.warn("Invalid employee name provided for deletion: {}", name);
throw new IllegalArgumentException("Employee name cannot be null or empty");
}
logger.info("Deleting employee by name: {}", name);
employees.removeIf(employee -> employee.name().equalsIgnoreCase(name));
}
@PostConstruct
public void init() {
employees.addAll(List.of(
new Employee("Joy one","[email protected]","1234567890"),
new Employee("Joy two","[email protected]","0987654321"),
new Employee("Joy three","[email protected]","1122334455"),
new Employee("Joy four","[email protected]","5566778899"),
new Employee("Joy five","[email protected]","9988776655")
));
}
}
< /code>
Pojo < /p>
public record Employee(String name, String email, String phone){}
< /code>
Основной контроллер клиента выглядит ниже < /p>
import io.modelcontextprotocol.client.McpSyncClient;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RequestMapping("/input")
@RestController
public class InputController {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InputController.class);
private final ChatClient chatClient;
List mcpSyncClients;
public InputController(ChatClient.Builder chatClientBuilder,
ToolCallbackProvider toolCallbackProvider,
List mcpSyncClients) {
this.chatClient = chatClientBuilder.build();
this.mcpSyncClients = mcpSyncClients;
List toolCallbacks = List.of(toolCallbackProvider.getToolCallbacks());
if (toolCallbacks.isEmpty()) {
System.out.println("No tools identified.");
} else {
for (ToolCallback toolCallback : toolCallbacks) {
ToolDefinition toolDefinition = toolCallback.getToolDefinition();
System.out.println("Tool Name: " + toolDefinition.name()+ "\nDescription: " + toolDefinition.description()+"\nSchema: " + toolDefinition.inputSchema()+"\n--------------------");
}
}
}
@PostMapping("/in")
public String input(@RequestBody String input) {
log.info("Input received: {}", input);
return chatClient.prompt()
.user(input)
.toolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClients))
.call()
.content();
}
}
< /code>
С запуском приложения может увидеть ниже ответ < /li>
< /ul>
$ curl -XPOST http://localhost:8080/input/in -d "can you get all the employees list from the server"
The list of all employees on the server is as follows:
1. Joy one - [email protected] - 1234567890
2. Joy two - [email protected] - 0987654321
3. Joy three - [email protected] - 1122334455
4. Joy four - [email protected] - 5566778899
5. Joy five - [email protected] - 9988776655
< /code>
Когда я пытаюсь добавить нового сотрудника, увидите сообщение об ошибке. Очевидно, что LLM отправляет текст JSON и что текст не преобразуется в JSON.[client] [pool-2-thread-1] io.modelcontextprotocol.spec.McpSchema : Received JSON message: {"jsonrpc":"2.0","id":"31ade185-6","result":{"content":[{"type":"text","text":"Conversion from JSON to com.demo.mcp.server.data.Employee failed"}],"isError":true}}
[client] [pool-2-thread-1] i.m.spec.McpClientSession : Received Response: JSONRPCResponse[jsonrpc=2.0, id=31ade185-6, result={content=[{type=text, text=Conversion from JSON to com.demo.mcp.server.data.Employee failed}], isError=true}, error=null]
ERROR 1120 --- [client] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.IllegalStateException: Error calling tool: [TextContent[audience=null, priority=null, text=Conversion from JSON to com.demo.mcp.server.data.Employee failed]]] with root cause
java.lang.IllegalStateException: Error calling tool: [TextContent[audience=null, priority=null, text=Conversion from JSON to com.demo.mcp.server.data.Employee failed]]
at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:118) ~[spring-ai-mcp-1.0.0.jar:1.0.0]
at org.springframework.ai.mcp.SyncMcpToolCallback.call(SyncMcpToolCallback.java:126) ~[spring-ai-mcp-1.0.0.jar:1.0.0]
at org.springframework.ai.model.tool.DefaultToolCallingManager.lambda$executeToolCall$5(DefaultToolCallingManager.java:224) ~[spring-ai-model-1.0.0.jar:1.0.0]
at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.15.1.jar:1.15.1]
at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCall(DefaultToolCallingManager.java:221) ~[spring-ai-model-1.0.0.jar:1.0.0]
at org.springframework.ai.model.tool.DefaultToolCallingManager.executeToolCalls(DefaultToolCallingManager.java:137) ~[spring-ai-model-1.0.0.jar:1.0.0]
at org.springframework.ai.ollama.OllamaChatModel.internalCall(OllamaChatModel.java:266) ~[spring-ai-ollama-1.0.0.jar:1.0.0]
at org.springframework.ai.ollama.OllamaChatModel.call(OllamaChatModel.java:219) ~[spring-ai-ollama-1.0.0.jar:1.0.0]
at org.springframework.ai.chat.client.advisor.ChatModelCallAdvisor.adviseCall(ChatModelCallAdvisor.java:54) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at org.springframework.ai.chat.client.advisor.DefaultAroundAdvisorChain.lambda$nextCall$1(DefaultAroundAdvisorChain.java:110) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.15.1.jar:1.15.1]
at org.springframework.ai.chat.client.advisor.DefaultAroundAdvisorChain.nextCall(DefaultAroundAdvisorChain.java:110) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at org.springframework.ai.chat.client.DefaultChatClient$DefaultCallResponseSpec.lambda$doGetObservableChatClientResponse$1(DefaultChatClient.java:469) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at io.micrometer.observation.Observation.observe(Observation.java:564) ~[micrometer-observation-1.15.1.jar:1.15.1]
at org.springframework.ai.chat.client.DefaultChatClient$DefaultCallResponseSpec.doGetObservableChatClientResponse(DefaultChatClient.java:467) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at org.springframework.ai.chat.client.DefaultChatClient$DefaultCallResponseSpec.doGetObservableChatClientResponse(DefaultChatClient.java:446) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at org.springframework.ai.chat.client.DefaultChatClient$DefaultCallResponseSpec.content(DefaultChatClient.java:441) ~[spring-ai-client-chat-1.0.0.jar:1.0.0]
at com.demo.mcp.client.controller.InputController.input(InputController.java:95) ~[classes/:na]
...
< /code>
Вопрос:-< /p>
Есть ли возможность перехватить ответ из LLM, прежде чем вывести службу инструментов на сервере? Имя сотрудника, электронная почта и телефон в аргументе в этом случае пользователь добавляется.
Подробнее здесь: https://stackoverflow.com/questions/796 ... ror-invoki
Простой клиент MCP, который добавляет, используя ошибку POJO, вызывая ошибку JSON Parse, вызывая инструмент ⇐ JAVA
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Нечувствительное к регистру сопоставление JSON с POJO без изменения POJO
Anonymous » » в форуме JAVA - 0 Ответы
- 24 Просмотры
-
Последнее сообщение Anonymous
-
-
-
Нечувствительное к регистру сопоставление JSON с POJO без изменения POJO
Anonymous » » в форуме JAVA - 0 Ответы
- 18 Просмотры
-
Последнее сообщение Anonymous
-