From c1396fd7cdf76ad156c31d5fc51763deac4fe230 Mon Sep 17 00:00:00 2001 From: CookSleep Date: Mon, 6 Apr 2026 00:49:05 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(core):=20=E6=AD=A3=E5=BC=8F=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20direct=20tool=20observation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/llm-core/agent/legacy-executor.ts | 8 ++++++++ .../core/src/llm-core/agent/openai/index.ts | 17 ++++++++++++----- packages/core/src/llm-core/agent/sub-agent.ts | 5 ++++- packages/core/src/llm-core/agent/types.ts | 10 +++++++++- .../llm-core/memory/message/database_history.ts | 5 ++++- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/core/src/llm-core/agent/legacy-executor.ts b/packages/core/src/llm-core/agent/legacy-executor.ts index e65042d9d..0a709d59e 100644 --- a/packages/core/src/llm-core/agent/legacy-executor.ts +++ b/packages/core/src/llm-core/agent/legacy-executor.ts @@ -479,6 +479,10 @@ function isAgentObservation(value: unknown): value is AgentObservation { return true } + if (isDirectToolOutput(value)) { + return true + } + if (!Array.isArray(value)) { return false } @@ -490,6 +494,10 @@ export function coerceToAgentObservation( observation: unknown, toolName?: string ): AgentObservation { + if (isDirectToolOutput(observation)) { + return observation + } + if (isAgentObservation(observation)) { if ( Array.isArray(observation) && diff --git a/packages/core/src/llm-core/agent/openai/index.ts b/packages/core/src/llm-core/agent/openai/index.ts index b7366f3cc..a4afff6ed 100644 --- a/packages/core/src/llm-core/agent/openai/index.ts +++ b/packages/core/src/llm-core/agent/openai/index.ts @@ -7,6 +7,7 @@ import { MessageContentComplex, ToolMessage } from '@langchain/core/messages' +import { isDirectToolOutput } from '@langchain/core/messages/tool' import { BaseOutputParser } from '@langchain/core/output_parsers' import { RunnableLambda, @@ -55,16 +56,22 @@ function _convertAgentStepToMessages( ) { if (isToolsAgentAction(action) && action.toolCallId !== undefined) { const log = action.messageLog as BaseMessage[] + const content = isDirectToolOutput(observation) ? '' : observation if ( - observation.length < 1 || - observation == null || - observation === 'null' + !isDirectToolOutput(observation) && + (content.length < 1 || content === 'null') ) { - observation = `The tool ${action.tool} returned no output. Try again or stop the tool call, tell the user failed to execute the tool.` + return log.concat( + new ToolMessage({ + content: `The tool ${action.tool} returned no output. Try again or stop the tool call, tell the user failed to execute the tool.`, + name: action.tool, + tool_call_id: action.toolCallId + }) + ) } return log.concat( new ToolMessage({ - content: observation, + content, name: action.tool, tool_call_id: action.toolCallId }) diff --git a/packages/core/src/llm-core/agent/sub-agent.ts b/packages/core/src/llm-core/agent/sub-agent.ts index 36c777d32..0885d92a6 100644 --- a/packages/core/src/llm-core/agent/sub-agent.ts +++ b/packages/core/src/llm-core/agent/sub-agent.ts @@ -5,6 +5,7 @@ import { SystemMessage, ToolMessage } from '@langchain/core/messages' +import { isDirectToolOutput } from '@langchain/core/messages/tool' import { StructuredTool } from '@langchain/core/tools' import { randomUUID } from 'crypto' import type { Awaitable, Session } from 'koishi' @@ -868,7 +869,9 @@ function createAgentToolMessages(steps: AgentStep[]): BaseMessage[] { ...steps.map( (step) => new ToolMessage({ - content: step.observation, + content: isDirectToolOutput(step.observation) + ? '' + : step.observation, tool_call_id: step.action.toolCallId, name: step.action.tool }) diff --git a/packages/core/src/llm-core/agent/types.ts b/packages/core/src/llm-core/agent/types.ts index 59119623a..d16e8a653 100644 --- a/packages/core/src/llm-core/agent/types.ts +++ b/packages/core/src/llm-core/agent/types.ts @@ -10,6 +10,7 @@ import type { MessageContentFileUrl, MessageContentVideo } from 'koishi-plugin-chatluna/utils/langchain' +import type { DirectToolOutput } from '@langchain/core/messages/tool' export interface ChatCompletionMessageToolCall { /** @@ -193,7 +194,14 @@ export type AgentObservationComplexContent = | MessageContentAudio | MessageContentVideo -export type AgentObservation = AgentObservationComplexContent[] | string +export type AgentDirectToolObservation = DirectToolOutput & { + replyEmitted?: boolean +} + +export type AgentObservation = + | AgentObservationComplexContent[] + | AgentDirectToolObservation + | string export interface ToolMask { mode: 'all' | 'allow' | 'deny' diff --git a/packages/core/src/llm-core/memory/message/database_history.ts b/packages/core/src/llm-core/memory/message/database_history.ts index 51a60af0c..a298f45f2 100644 --- a/packages/core/src/llm-core/memory/message/database_history.ts +++ b/packages/core/src/llm-core/memory/message/database_history.ts @@ -8,6 +8,7 @@ import { SystemMessage, ToolMessage } from '@langchain/core/messages' +import { isDirectToolOutput } from '@langchain/core/messages/tool' import { BaseChatMessageHistory } from '@langchain/core/chat_history' import { bufferToArrayBuffer, @@ -71,7 +72,9 @@ function createAgentToolMessages(steps: AgentStep[]): BaseMessage[] { ...steps.map( (step) => new ToolMessage({ - content: step.observation, + content: isDirectToolOutput(step.observation) + ? '' + : step.observation, tool_call_id: step.action.toolCallId, name: step.action.tool }) From 5a9de81aaa0622bd5dcb53de6f0f305cd1fff2c0 Mon Sep 17 00:00:00 2001 From: dingyi Date: Mon, 6 Apr 2026 09:25:19 +0800 Subject: [PATCH 2/3] fix(core,extension-agent): unify observation message content handling Reuse a shared observationToMessageContent helper so direct tool observations are converted consistently and ToolMessage content stays type-safe across agent flows. --- packages/core/src/llm-core/agent/executor.ts | 1 + packages/core/src/llm-core/agent/legacy-executor.ts | 4 ++++ packages/core/src/llm-core/agent/openai/index.ts | 6 +++--- packages/core/src/llm-core/agent/sub-agent.ts | 6 ++---- .../core/src/llm-core/memory/message/database_history.ts | 6 ++---- packages/extension-agent/src/sub-agent/session.ts | 7 +++++-- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/core/src/llm-core/agent/executor.ts b/packages/core/src/llm-core/agent/executor.ts index c0d38bcc4..0d4130c48 100644 --- a/packages/core/src/llm-core/agent/executor.ts +++ b/packages/core/src/llm-core/agent/executor.ts @@ -11,6 +11,7 @@ import type { AgentExecutorInput, AgentExecutorOutput } from './legacy-executor' export { coerceToAgentObservation, LegacyAgentExecutor, + observationToMessageContent, runAgent, toToolInputErrorObservation, type AgentExecutorInput, diff --git a/packages/core/src/llm-core/agent/legacy-executor.ts b/packages/core/src/llm-core/agent/legacy-executor.ts index 0a709d59e..fa90cf425 100644 --- a/packages/core/src/llm-core/agent/legacy-executor.ts +++ b/packages/core/src/llm-core/agent/legacy-executor.ts @@ -599,3 +599,7 @@ function toParsingErrorAction( log: text } } + +export function observationToMessageContent(observation: AgentObservation) { + return isDirectToolOutput(observation) ? '' : observation +} diff --git a/packages/core/src/llm-core/agent/openai/index.ts b/packages/core/src/llm-core/agent/openai/index.ts index a4afff6ed..8952b4d6e 100644 --- a/packages/core/src/llm-core/agent/openai/index.ts +++ b/packages/core/src/llm-core/agent/openai/index.ts @@ -7,7 +7,6 @@ import { MessageContentComplex, ToolMessage } from '@langchain/core/messages' -import { isDirectToolOutput } from '@langchain/core/messages/tool' import { BaseOutputParser } from '@langchain/core/output_parsers' import { RunnableLambda, @@ -31,6 +30,7 @@ import { } from './output_parser' import { BaseChatPromptTemplate } from '@langchain/core/prompts' import { getMessageContent } from 'koishi-plugin-chatluna/utils/string' +import { observationToMessageContent } from '../legacy-executor' /** * Checks if the given action is a FunctionsAgentAction. @@ -56,9 +56,9 @@ function _convertAgentStepToMessages( ) { if (isToolsAgentAction(action) && action.toolCallId !== undefined) { const log = action.messageLog as BaseMessage[] - const content = isDirectToolOutput(observation) ? '' : observation + const content = observationToMessageContent(observation) if ( - !isDirectToolOutput(observation) && + content === observation && (content.length < 1 || content === 'null') ) { return log.concat( diff --git a/packages/core/src/llm-core/agent/sub-agent.ts b/packages/core/src/llm-core/agent/sub-agent.ts index 0885d92a6..15d49e8a5 100644 --- a/packages/core/src/llm-core/agent/sub-agent.ts +++ b/packages/core/src/llm-core/agent/sub-agent.ts @@ -5,7 +5,6 @@ import { SystemMessage, ToolMessage } from '@langchain/core/messages' -import { isDirectToolOutput } from '@langchain/core/messages/tool' import { StructuredTool } from '@langchain/core/tools' import { randomUUID } from 'crypto' import type { Awaitable, Session } from 'koishi' @@ -13,6 +12,7 @@ import { z } from 'zod' import type { ChatLunaToolRunnable } from '../platform/types' import { getMessageContent } from 'koishi-plugin-chatluna/utils/string' import type { ChatLunaAgent } from './agent' +import { observationToMessageContent } from './legacy-executor' import { MessageQueue } from './types' import type { AgentEvent, AgentStep, SubagentContext, ToolMask } from './types' @@ -869,9 +869,7 @@ function createAgentToolMessages(steps: AgentStep[]): BaseMessage[] { ...steps.map( (step) => new ToolMessage({ - content: isDirectToolOutput(step.observation) - ? '' - : step.observation, + content: observationToMessageContent(step.observation), tool_call_id: step.action.toolCallId, name: step.action.tool }) diff --git a/packages/core/src/llm-core/memory/message/database_history.ts b/packages/core/src/llm-core/memory/message/database_history.ts index a298f45f2..6450cfb90 100644 --- a/packages/core/src/llm-core/memory/message/database_history.ts +++ b/packages/core/src/llm-core/memory/message/database_history.ts @@ -8,7 +8,6 @@ import { SystemMessage, ToolMessage } from '@langchain/core/messages' -import { isDirectToolOutput } from '@langchain/core/messages/tool' import { BaseChatMessageHistory } from '@langchain/core/chat_history' import { bufferToArrayBuffer, @@ -16,6 +15,7 @@ import { gzipEncode } from 'koishi-plugin-chatluna/utils/string' import { randomUUID } from 'crypto' +import { observationToMessageContent } from '../../agent/legacy-executor' import type { AgentStep } from '../../agent/types' import type { MessageRecord } from '../../../services/conversation_types' @@ -72,9 +72,7 @@ function createAgentToolMessages(steps: AgentStep[]): BaseMessage[] { ...steps.map( (step) => new ToolMessage({ - content: isDirectToolOutput(step.observation) - ? '' - : step.observation, + content: observationToMessageContent(step.observation), tool_call_id: step.action.toolCallId, name: step.action.tool }) diff --git a/packages/extension-agent/src/sub-agent/session.ts b/packages/extension-agent/src/sub-agent/session.ts index b0f242092..1a7fe06b1 100644 --- a/packages/extension-agent/src/sub-agent/session.ts +++ b/packages/extension-agent/src/sub-agent/session.ts @@ -6,7 +6,10 @@ import { HumanMessage, ToolMessage } from '@langchain/core/messages' -import type { AgentStep } from 'koishi-plugin-chatluna/llm-core/agent' +import { + observationToMessageContent, + type AgentStep +} from 'koishi-plugin-chatluna/llm-core/agent' import { getMessageContent } from 'koishi-plugin-chatluna/utils/string' import type { SubAgentInfo, SubAgentRunInfo } from '../types' @@ -219,7 +222,7 @@ function createAgentToolMessages(steps: AgentStep[]): BaseMessage[] { ...steps.map( (step) => new ToolMessage({ - content: step.observation, + content: observationToMessageContent(step.observation), tool_call_id: step.action.toolCallId, name: step.action.tool }) From f50f622e43a8861624139d6cfdbc52f58a01a3e3 Mon Sep 17 00:00:00 2001 From: dingyi <2187778735@qq.com> Date: Mon, 6 Apr 2026 10:04:48 +0800 Subject: [PATCH 3/3] Update packages/extension-agent/src/sub-agent/session.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- packages/extension-agent/src/sub-agent/session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension-agent/src/sub-agent/session.ts b/packages/extension-agent/src/sub-agent/session.ts index 1a7fe06b1..3d9c40fe3 100644 --- a/packages/extension-agent/src/sub-agent/session.ts +++ b/packages/extension-agent/src/sub-agent/session.ts @@ -7,8 +7,8 @@ import { ToolMessage } from '@langchain/core/messages' import { - observationToMessageContent, - type AgentStep + type AgentStep, + observationToMessageContent } from 'koishi-plugin-chatluna/llm-core/agent' import { getMessageContent } from 'koishi-plugin-chatluna/utils/string' import type { SubAgentInfo, SubAgentRunInfo } from '../types'