mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-11-02 14:11:49 +08:00
fix(agent): parse employee drafts after reasoning blocks (#648)
This commit is contained in:
parent
894c945fab
commit
6bbd3ac4da
@ -295,6 +295,14 @@ public class AgentGenerationService {
|
|||||||
private JsonNode parseJson(String response) {
|
private JsonNode parseJson(String response) {
|
||||||
if (response == null || response.isBlank()) return null;
|
if (response == null || response.isBlank()) return null;
|
||||||
String cleaned = response.trim();
|
String cleaned = response.trim();
|
||||||
|
// Some reasoning models prepend thinking to content on non-streaming
|
||||||
|
// calls. Strip only leading blocks: tags inside JSON string values are
|
||||||
|
// legitimate persona text, and JSON examples in thinking are not drafts.
|
||||||
|
while (cleaned.startsWith("<think>")) {
|
||||||
|
int end = cleaned.indexOf("</think>", "<think>".length());
|
||||||
|
if (end < 0) return null; // Incomplete reasoning is not a final answer.
|
||||||
|
cleaned = cleaned.substring(end + "</think>".length()).trim();
|
||||||
|
}
|
||||||
if (cleaned.startsWith("```json")) cleaned = cleaned.substring(7);
|
if (cleaned.startsWith("```json")) cleaned = cleaned.substring(7);
|
||||||
else if (cleaned.startsWith("```")) cleaned = cleaned.substring(3);
|
else if (cleaned.startsWith("```")) cleaned = cleaned.substring(3);
|
||||||
if (cleaned.endsWith("```")) cleaned = cleaned.substring(0, cleaned.length() - 3);
|
if (cleaned.endsWith("```")) cleaned = cleaned.substring(0, cleaned.length() - 3);
|
||||||
|
|||||||
@ -0,0 +1,86 @@
|
|||||||
|
package vip.mate.agent.service;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||||
|
import org.springframework.ai.chat.model.ChatModel;
|
||||||
|
import org.springframework.ai.chat.model.ChatResponse;
|
||||||
|
import org.springframework.ai.chat.model.Generation;
|
||||||
|
import org.springframework.ai.chat.prompt.Prompt;
|
||||||
|
import vip.mate.agent.AgentGraphBuilder;
|
||||||
|
import vip.mate.exception.MateClawException;
|
||||||
|
import vip.mate.llm.model.ModelConfigEntity;
|
||||||
|
import vip.mate.llm.service.ModelConfigService;
|
||||||
|
import vip.mate.skill.service.SkillService;
|
||||||
|
import vip.mate.tool.service.AvailableToolService;
|
||||||
|
import vip.mate.wiki.service.WikiKnowledgeBaseService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class AgentGenerationServiceTest {
|
||||||
|
private ChatModel model;
|
||||||
|
private AgentGenerationService service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
var configs = mock(ModelConfigService.class);
|
||||||
|
var builder = mock(AgentGraphBuilder.class);
|
||||||
|
var tools = mock(AvailableToolService.class);
|
||||||
|
var skills = mock(SkillService.class);
|
||||||
|
var kbs = mock(WikiKnowledgeBaseService.class);
|
||||||
|
var config = new ModelConfigEntity();
|
||||||
|
model = mock(ChatModel.class);
|
||||||
|
when(configs.getDefaultModel()).thenReturn(config);
|
||||||
|
when(builder.buildRuntimeChatModel(config)).thenReturn(model);
|
||||||
|
when(tools.listAvailable()).thenReturn(List.of());
|
||||||
|
when(skills.listEnabledSkills(1L)).thenReturn(List.of());
|
||||||
|
when(kbs.listByWorkspace(1L)).thenReturn(List.of());
|
||||||
|
service = new AgentGenerationService(configs, builder, new ObjectMapper(), tools, skills, kbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void response(String text) {
|
||||||
|
when(model.call(any(Prompt.class))).thenReturn(new ChatResponse(
|
||||||
|
List.of(new Generation(new AssistantMessage(text)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
"{\"name\":\"周报助手\"}",
|
||||||
|
"```json\n{\"name\":\"周报助手\"}\n```",
|
||||||
|
"<think>先考虑配置</think>\n{\"name\":\"周报助手\"}",
|
||||||
|
"<think>{\"name\":\"思考中的示例\"}</think>\n```json\n{\"name\":\"周报助手\"}\n```",
|
||||||
|
" \n<think>第一步</think>\n<think>第二步</think>\n{\"name\":\"周报助手\"}"
|
||||||
|
})
|
||||||
|
void acceptsFinalJsonAfterOptionalThinking(String text) {
|
||||||
|
response(text);
|
||||||
|
assertEquals("周报助手", service.generateDraft("跨部门周报汇总", 1L).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
"{\"name\":\"助手\",\"systemPrompt\":\"保留 <think>示例</think> 原文\"}",
|
||||||
|
"<think>规划</think>\n{\"name\":\"助手\",\"systemPrompt\":\"保留 <think>示例</think> 原文\"}"
|
||||||
|
})
|
||||||
|
void preservesLiteralTagsInsideJsonStrings(String text) {
|
||||||
|
response(text);
|
||||||
|
assertEquals("保留 <think>示例</think> 原文", service.generateDraft("助手", 1L).getSystemPrompt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
"", "<think>{\"name\":\"未完成思考\"}",
|
||||||
|
"<think>{\"name\":\"只有思考\"}</think>",
|
||||||
|
"<think>规划</think>\n{\"name\":", "[]",
|
||||||
|
"说明文字 {\"name\":\"不应猜测提取\"}"
|
||||||
|
})
|
||||||
|
void rejectsMissingOrMalformedFinalObject(String text) {
|
||||||
|
response(text);
|
||||||
|
assertThrows(MateClawException.class, () -> service.generateDraft("助手", 1L));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user