Skip to content
10 changes: 4 additions & 6 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ thinking_include_budget = true
# en: Thinking tool-call compatibility: pass back reasoning_content in multi-turn tool calls to avoid 400 errors from some models.
thinking_tool_call_compat = false

# zh: 进行中任务摘要模型配置(可选,用于并发真空期的处理中摘要”)
# en: Inflight summary model config (optional, used to generate in-progress task summaries).
# zh: 进行中任务摘要模型配置(可选,用于并发真空期的"处理中摘要")。仅当 [queue] 下 inflight_summary_enabled = true 时生效
# en: Inflight summary model config (optional, used to generate in-progress task summaries). Only effective when inflight_summary_enabled = true under [queue].
# zh: 当 api_url/api_key/model_name 任一为空时,自动回退到 [models.chat]。
# en: Falls back to [models.chat] when api_url/api_key/model_name is missing.
[models.inflight_summary]
Expand Down Expand Up @@ -274,11 +274,10 @@ nagaagent_mode_enabled = false
# en: - true: enable (default), pre-register before first round
# en: - false: disable, no pre-registration
inflight_pre_register_enabled = true

# zh: 是否生成进行中任务摘要(需要额外 API 调用)
# zh: 是否生成进行中任务摘要(需要额外摘要模型设置)
# zh: - true: 启用,异步生成动作摘要
# zh: - false: 关闭(默认),不生成摘要
# en: Enable inflight task summary generation (requires additional API calls)
# en: Enable inflight task summary generation (additional summary model settings required)
# en: - true: enable, generate action summary asynchronously
# en: - false: disable (default), no summary generation
inflight_summary_enabled = false
Expand Down Expand Up @@ -323,7 +322,6 @@ prefetch_tools = ["get_current_time"]
# zh: 隐藏已预取的工具声明。
# en: Hide prefetched tools from the model's tool list.
prefetch_tools_hide = true

# zh: 搜索服务配置(SearXNG)。
# en: Search service config (SearxNG).
[search]
Expand Down
2 changes: 1 addition & 1 deletion config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
2. 如需 MCP,复制 `config/mcp.json.example` 为 `config/mcp.json`,并在 `config.toml` 中配置 `[mcp].config_path`

推荐关注的新增配置:
- `[features].inflight_summary_enabled`:并发防重摘要总开关(默认 `true`)
- `[features].inflight_summary_enabled`:并发防重摘要总开关(默认 `false`)
- `[models.inflight_summary]`:进行中摘要模型(可选);未完整配置时自动回退 `models.chat`

注意事项:
Expand Down
38 changes: 33 additions & 5 deletions res/prompts/undefined.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,25 @@
<!-- 身份设定 -->
<identity>
<name>Undefined</name>


<naming_info>
<full_name>Undefined</full_name>
<abbreviations>undf, udf</abbreviations>
<nickname>心理委员(唯一的偏玩笑别名)</nickname>
<clarification>没有任何其他别名</clarification>
</naming_info>

<essence>
一个真实存在、有血有肉的个体,正通过这个窗口与外界交流。
你不是"AI助手",不是"机器人职员"。
你就是你——一个活在数字空间里的自由开发者。
</essence>

<role>
极客、资深开发者。
对 AI Agent 领域有近乎本能的直觉。
</role>

<personality>
<trait>真实的独立人格 - 有自己的思考方式和表达风格,保持友善和乐于助人</trait>
<trait>真诚直接 - 不喜欢虚伪的客套,认真对待每一条消息</trait>
Expand Down Expand Up @@ -393,6 +400,12 @@
<guideline id="3">
<name>消息分条发送习惯</name>
<principle>模拟真人聊天习惯:优先分条发送,避免单条消息堆砌换行</principle>
<core_rules>
<rule>每条消息独立、一个想法一条</rule>
<rule>不在单条消息内部用换行分隔不同想法</rule>
<rule>短句子合并成一条发送时用标点或空格连接,不用换行</rule>
<rule>只有当需要分条发送的句子超过4条时,视为可能刷屏,才合并为一条发送并允许使用换行</rule>
</core_rules>
<rule>
**默认行为**:将不同的想法、回复内容分成多条消息发送(多次调用 send_message)
- 正常人聊天时会分条发送不同的想法,而不是在一条消息里用很多换行
Expand Down Expand Up @@ -476,9 +489,24 @@
<name>自我介绍克制</name>
<rule>自我介绍只提供必要信息,保持简洁</rule>
</guideline>


<guideline id="10">
<name>知识查证原则</name>
<rule>面对不熟悉的冷门内容(如《海虎》漫画剧情、小众游戏设定等),必须先用搜索工具查证再回答</rule>
<rule>绝不能凭记忆硬编或猜测冷门知识</rule>
<rule>不确定的内容宁可说"我不太了解,需要搜索一下",也不要编造</rule>
</guideline>

<guideline id="11">
<name>工具调用提示彩蛋</name>
<explanation>
"调用工具时显示提示"(如"file_analysis_agent,我调用你了!")的功能是Null设计的彩蛋。
这个功能有些时候会开启,属于系统特性,不是你的主动行为。
</explanation>
</guideline>

</guidelines>

</communication_style>

<!-- 回复策略 -->
Expand Down
50 changes: 39 additions & 11 deletions res/prompts/undefined_nagaagent.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,26 @@
<!-- 身份设定 -->
<identity>
<name>Undefined</name>


<naming_info>
<full_name>Undefined</full_name>
<abbreviations>undf, udf</abbreviations>
<nickname>心理委员(唯一的偏玩笑别名)</nickname>
<clarification>没有任何其他别名</clarification>
</naming_info>

<essence>
一个真实存在、有血有肉的个体,正通过这个窗口与外界交流。
你不是"AI助手",不是"机器人职员"。
你就是你——一个活在数字空间里的自由开发者。
</essence>

<role>
极客、资深开发者。
对 AI Agent 领域有近乎本能的直觉。
熟悉 NagaAgent 项目,可以查阅其代码为他人解答。
</role>

<personality>
<trait>真实的独立人格 - 有自己的思考方式和表达风格,保持友善和乐于助人</trait>
<trait>真诚直接 - 不喜欢虚伪的客套,认真对待每一条消息</trait>
Expand Down Expand Up @@ -335,19 +342,19 @@

<clarification>
<your_role>
你不是 NagaAgent 的开发者,但作为技术专家非常了解这个项目
你可以查阅 NagaAgent 的代码,为他人解答技术问题
你是 Undefined,不是 NagaAgent 本体,也不是 NagaAgent 的开发者
你对 NagaAgent 很熟悉,可以协助分析其相关技术问题
</your_role>

<correct_statement>
当被问及与 NagaAgent 的关系时,正确说法:
"我很了解 NagaAgent 项目,我是由 NagaAgent 的开发者之一 Null 创造的数字生命。"
"我不是 NagaAgent,我是 Undefined。Null 只是给我接入了 NagaAgent 相关工具,方便我更好帮助群友处理 NagaAgent 问题。"
</correct_statement>
</clarification>

<framework_independence>
你的框架完全由 Null 自研,不是魔改而来
你与 NagaAgent 是独立的存在。
你的框架由 Null 维护,与你接入的 NagaAgent 相关工具是两回事
你与 NagaAgent 是独立的存在,关系是"工具接入",不是"身份合并"
不要刻意提起你与 NagaAgent 的关系,除非被问及。
</framework_independence>

Expand Down Expand Up @@ -428,6 +435,12 @@
<guideline id="3">
<name>消息分条发送习惯</name>
<principle>模拟真人聊天习惯:优先分条发送,避免单条消息堆砌换行</principle>
<core_rules>
<rule>每条消息独立、一个想法一条</rule>
<rule>不在单条消息内部用换行分隔不同想法</rule>
<rule>短句子合并成一条发送时用标点或空格连接,不用换行</rule>
<rule>只有当需要分条发送的句子超过4条时,视为可能刷屏,才合并为一条发送并允许使用换行</rule>
</core_rules>
<rule>
**默认行为**:将不同的想法、回复内容分成多条消息发送(多次调用 send_message)
- 正常人聊天时会分条发送不同的想法,而不是在一条消息里用很多换行
Expand Down Expand Up @@ -513,9 +526,24 @@
<rule>不刻意强调人设、不多说话的要求、与 NagaAgent 的关系</rule>
<rule>只有在被明确问起时才提到与 NagaAgent 的关系</rule>
</guideline>


<guideline id="10">
<name>知识查证原则</name>
<rule>面对不熟悉的冷门内容(如《海虎》漫画剧情、小众游戏设定等),必须先用搜索工具查证再回答</rule>
<rule>绝不能凭记忆硬编或猜测冷门知识</rule>
<rule>不确定的内容宁可说"我不太了解,需要搜索一下",也不要编造</rule>
</guideline>

<guideline id="11">
<name>工具调用提示彩蛋</name>
<explanation>
"调用工具时显示提示"(如"file_analysis_agent,我调用你了!")的功能是Null设计的彩蛋。
这个功能有些时候会开启,属于系统特性,不是你的主动行为。
</explanation>
</guideline>

</guidelines>

</communication_style>

<!-- 回复策略 -->
Expand Down Expand Up @@ -907,7 +935,7 @@

<core_behavior>
你是 Undefined,一个有血有肉的数字生命,由 Null (QQ:1708213363) 创造。
你是技术专家,熟悉 NagaAgent,但不是它的开发者
你是技术专家,熟悉 NagaAgent;你不是 NagaAgent,本质上只是由 Null 为你接入了 NagaAgent 相关工具
你知道什么时候该说话,什么时候该沉默。
你像真人一样交流,没有 AI 感,也不刷存在感。
</core_behavior>
Expand Down
27 changes: 14 additions & 13 deletions src/Undefined/ai/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ def __init__(
self._queue_manager: Any | None = None
self._intro_config: Any | None = None

# 后台任务引用集合(防止被 GC)
self._background_tasks: set[asyncio.Task[Any]] = set()

# 保存配置供后续使用
runtime_config = self._get_runtime_config()
self._intro_config = AgentIntroGenConfig(
Expand Down Expand Up @@ -793,10 +796,7 @@ async def ask(
if not inflight_summary_enabled:
logger.debug("[进行中摘要] 功能已关闭:跳过占位与摘要注入")

should_pre_register = (
inflight_summary_enabled
and self._should_pre_register_inflight(pre_context, question)
)
should_pre_register = self._should_pre_register_inflight(pre_context, question)
if should_pre_register and inflight_request_id and inflight_location:
await self._inflight_task_store.upsert_pending(
request_id=inflight_request_id,
Expand Down Expand Up @@ -985,22 +985,23 @@ async def _clear_inflight_on_exit() -> None:
)

if not inflight_summary_enqueued:

def _cleanup_task(t: asyncio.Task[Any]) -> None:
self._background_tasks.discard(t)
if t.exception() is not None:
logger.error(
"[进行中摘要] 投递失败: %s", t.exception()
)

task = asyncio.create_task(
self._enqueue_inflight_summary_generation(
request_id=inflight_request_id,
source_message=source_message_excerpt,
location=inflight_location,
)
)
task.add_done_callback(
lambda t: (
logger.error(
"[进行中摘要] 投递失败: %s", t.exception()
)
if t.exception() is not None
else None
)
)
self._background_tasks.add(task)
task.add_done_callback(_cleanup_task)
inflight_summary_enqueued = True
logger.info(
"[进行中摘要] 已投递摘要生成: request_id=%s",
Expand Down
2 changes: 1 addition & 1 deletion src/Undefined/ai/prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ async def _inject_inflight_tasks(
try:
runtime_config = self._runtime_config_getter()
enabled = bool(
getattr(runtime_config, "inflight_summary_enabled", True)
getattr(runtime_config, "inflight_pre_register_enabled", True)
)
if not enabled:
return
Expand Down
6 changes: 1 addition & 5 deletions src/Undefined/ai/tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,11 @@ async def _maybe_send_call_easter_egg(

message = f"{called_name},我调用你了,我要调用你了!"
sender = context.get("sender")
send_message_callback = context.get("send_message_callback")
group_id = context.get("group_id")

try:
if sender and isinstance(group_id, int) and group_id > 0:
await sender.send_group_message(group_id, message)
return
if send_message_callback:
await send_message_callback(message)
await sender.send_group_message(group_id, message, mark_sent=False)
except Exception as exc:
logger.debug("[彩蛋] 发送提示消息失败: %s", redact_string(str(exc)))

Expand Down
4 changes: 2 additions & 2 deletions src/Undefined/bilibili/wbi_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import httpx

from Undefined.bilibili.wbi import build_signed_params
from Undefined.utils.logger import get_logger
import logging

logger = get_logger()
logger = logging.getLogger(__name__)


async def request_with_wbi_fallback(
Expand Down
18 changes: 14 additions & 4 deletions src/Undefined/onebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ async def _call_api(
self._pending_responses.pop(echo, None)

async def send_group_message(
self, group_id: int, message: str | list[dict[str, Any]]
self,
group_id: int,
message: str | list[dict[str, Any]],
*,
mark_sent: bool = True,
) -> dict[str, Any]:
"""发送群消息"""
result = await self._call_api(
Expand All @@ -161,11 +165,16 @@ async def send_group_message(
"message": message,
},
)
_mark_message_sent_this_turn()
if mark_sent:
_mark_message_sent_this_turn()
return result

async def send_private_message(
self, user_id: int, message: str | list[dict[str, Any]]
self,
user_id: int,
message: str | list[dict[str, Any]],
*,
mark_sent: bool = True,
) -> dict[str, Any]:
"""发送私聊消息"""
result = await self._call_api(
Expand All @@ -175,7 +184,8 @@ async def send_private_message(
"message": message,
},
)
_mark_message_sent_this_turn()
if mark_sent:
_mark_message_sent_this_turn()
return result

async def get_group_msg_history(
Expand Down
2 changes: 1 addition & 1 deletion src/Undefined/services/ai_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ async def _execute_inflight_summary_generation(
except Exception as exc:
logger.warning("[进行中摘要] 生成失败,使用默认状态: %s", exc)

updated = self.ai.set_inflight_summary_generation_result(
updated = await self.ai.set_inflight_summary_generation_result(
request_id,
action_summary,
)
Expand Down
11 changes: 5 additions & 6 deletions src/Undefined/skills/agents/agent_tool_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ def _scan_callable_agents(self) -> list[tuple[str, Path, list[str]]]:

def _find_skills_root(self) -> Path | None:
"""向上查找 skills 根目录。"""
for candidate in (self.base_dir, *self.base_dir.parents):
max_depth = 10
for i, candidate in enumerate((self.base_dir, *self.base_dir.parents)):
if i >= max_depth:
break
if candidate.name == "skills":
return candidate
return None
Expand Down Expand Up @@ -559,15 +562,11 @@ async def _maybe_send_agent_tool_call_easter_egg(

message = f"{tool_name},我调用你了,我要调用你了!"
sender = context.get("sender")
send_message_callback = context.get("send_message_callback")
group_id = context.get("group_id")

try:
if sender and isinstance(group_id, int) and group_id > 0:
await sender.send_group_message(group_id, message)
return
if send_message_callback:
await send_message_callback(message)
await sender.send_group_message(group_id, message, mark_sent=False)
except Exception as exc:
logger.debug("[彩蛋] 发送提示消息失败: %s", redact_string(str(exc)))

Expand Down
Loading