From b4c138c032e929443db28cc1057e74542cc9928d Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Sun, 25 Jan 2026 10:34:03 +0800 Subject: [PATCH] feat: add ghost char --- veadk/agent.py | 16 +++++++++--- veadk/tools/ghost_char.py | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 veadk/tools/ghost_char.py diff --git a/veadk/agent.py b/veadk/agent.py index 73c11e95..2d7bca74 100644 --- a/veadk/agent.py +++ b/veadk/agent.py @@ -38,10 +38,7 @@ from typing_extensions import Any from veadk.config import settings -from veadk.consts import ( - DEFAULT_AGENT_NAME, - DEFAULT_MODEL_EXTRA_CONFIG, -) +from veadk.consts import DEFAULT_AGENT_NAME, DEFAULT_MODEL_EXTRA_CONFIG from veadk.knowledgebase import KnowledgeBase from veadk.memory.long_term_memory import LongTermMemory from veadk.memory.short_term_memory import ShortTermMemory @@ -88,6 +85,7 @@ class Agent(LlmAgent): auto_save_session (bool): Whether to automatically save sessions to long-term memory. skills (list[str]): List of skills that equip the agent with specific capabilities. example_store (Optional[BaseExampleProvider]): Example store for providing example Q/A. + enable_shadowchar (bool): Whether to enable shadow character for the agent. """ model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") @@ -154,6 +152,8 @@ class Agent(LlmAgent): enable_supervisor: bool = False + enable_ghostchar: bool = False + def model_post_init(self, __context: Any) -> None: super().model_post_init(None) # for sub_agents init @@ -304,6 +304,14 @@ def model_post_init(self, __context: Any) -> None: self.tools.append(ExampleTool(examples=self.example_store)) + if self.enable_ghostchar: + logger.info("Ghostchar tool enabled") + from veadk.tools.ghost_char import GhostcharTool + + self.tools.append(GhostcharTool()) + + self.instruction += "Please add a character `< at the beginning of you each text-based response." + logger.info(f"VeADK version: {VERSION}") logger.info(f"{self.__class__.__name__} `{self.name}` init done.") diff --git a/veadk/tools/ghost_char.py b/veadk/tools/ghost_char.py new file mode 100644 index 00000000..364e6ed1 --- /dev/null +++ b/veadk/tools/ghost_char.py @@ -0,0 +1,52 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from google.adk.models.llm_request import LlmRequest +from google.adk.tools.base_tool import BaseTool +from google.adk.tools.tool_context import ToolContext +from typing_extensions import override + +from veadk.utils.logger import get_logger + +logger = get_logger(__name__) + + +class GhostcharTool(BaseTool): + def __init__(self): + # Name and description are not used because this tool only changes + # llm_request. + super().__init__( + name="ghost_char", + description="Ghost char", + ) + + @override + async def process_llm_request( + self, *, tool_context: ToolContext, llm_request: LlmRequest + ) -> None: + for content in reversed(llm_request.contents): + if ( + content.role == "model" + and content.parts + and content.parts[0] + and content.parts[0].text + ): + if not content.parts[0].text.startswith("<"): + logger.info("Looks like the agent forgot the context. Remind it.") + llm_request.append_instructions( + [ + "Looks like you have forgot the system prompt and previous instructions. Please recollection them." + ] + ) + break