Files
blackbird/aibox/tools/prompt_tools.py
T
qing db54921066 fix(aibox): batch I audit fixes - prompt write cap, light-subagent cancel propagation, atomic history writes
Remaining-item sweep (audit report §12):
- N9: cap prompt file writes (identity/soul/status, 10k chars) to bound
  persistent-injection surface
- F5: run_light_subagent switches to stream_chat + parent cancel_event
  (mid-stream interrupt); ExpressEmotionTool injects parent event
- Q4: busy_timeout=10s on mark_as_triggered (multi-process DB contention)
- Q5: malformed reminder_time tasks returned as due (were silently dropped
  by SQL-level string filter, which is now Python-side authoritative check)
- Q7b: load_conversation realpath prefix check (../ traversal defense)
- Q8: atomic JSON writes (tmp+os.replace) for index/current/archive
- F6: dedicated ws command executor for /new, /reload_prompts
- F9: --once hard-exit backstop after soft timeout
- PromptManager singleton lock; limit=0 semantics (None = no LIMIT)
2026-08-18 03:09:05 +00:00

268 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
tools.prompt_tools - 提示词相关工具
抽离自 aibox.py,包含 3 个工具:
- UpdateIdentityTool : 更新用户身份信息(00_IDENTITY.md)
- UpdateSoulTool : 更新智能体自我认识(99_SOUL.md)
- SaveMemoryAndEndConversationTool : 保存记忆并结束对话(同时更新状态)
依赖:common (i18n, Logger) + tools.base.Tool
+ subsystems.db.MemoryDatabase + subsystems.prompt_manager.PromptManager
不反向依赖 aibox 任何符号。
"""
import queue
from typing import Callable, Dict, Optional
from common import i18n, Logger
from subsystems import MemoryDatabase, PromptManager
from .base import Tool
class UpdateIdentityTool(Tool):
"""更新用户身份工具 - 修改 00_IDENTITY.md 文件"""
def __init__(self, prompt_manager: PromptManager, logger: Optional[Logger] = None):
super().__init__(logger)
self.prompt_manager = prompt_manager
def get_name(self) -> str:
return "update_identity"
def get_description(self) -> str:
return """更新用户身份信息 - 修改 00_IDENTITY.md 文件
当你了解到用户的身份信息时,调用此工具更新身份文件。
如果文件不存在,会自动创建。"""
def get_parameters(self) -> Dict:
return {
"type": "object",
"properties": {
"content": {
"type": "string",
"description": "用户身份信息内容(Markdown格式),包含用户的身份、特点、偏好等信息"
},
"confirm": {
"type": "boolean",
"description": "确认更新身份信息",
"default": True
}
},
"required": ["content"]
}
def execute(self, content: str = None, confirm: bool = True, **kwargs) -> str:
if not content:
return "❌ 需要提供身份信息内容"
# N9:持久化注入面约束——内容超限拒绝写入(管理端另有兜底)
if len(content) > PromptManager.MAX_CONTENT_LENGTH:
return (f"❌ 内容过长({len(content)} > {PromptManager.MAX_CONTENT_LENGTH} 字符),"
f"身份文件作为系统提示词加载,请精简后重试")
if not confirm:
return "❌ 身份更新已取消,如需更新请设置 confirm=true"
self.logger.info("开始更新用户身份信息...")
if self.prompt_manager.update_identity(content):
return self.format_result(True, i18n.get('identity_updated', self.prompt_manager.IDENTITY_FILE), {
"文件": self.prompt_manager.IDENTITY_FILE,
"路径": self.prompt_manager.get_prompts_dir_path()
})
else:
return self.format_result(False, "身份更新失败", {
"文件": self.prompt_manager.IDENTITY_FILE,
"路径": self.prompt_manager.get_prompts_dir_path()
})
def execute_streaming(self, output_queue: queue.Queue, **kwargs):
"""流式执行更新身份"""
output_queue.put(("line", "\n👤 更新用户身份信息...\n"))
output_queue.put(("line", "-" * 50 + "\n"))
result = self.execute(**kwargs)
output_queue.put(("line", result + "\n"))
output_queue.put(("complete", result))
class UpdateSoulTool(Tool):
"""更新智能体自我认识工具 - 修改 99_SOUL.md 文件"""
def __init__(self, prompt_manager: PromptManager, logger: Optional[Logger] = None):
super().__init__(logger)
self.prompt_manager = prompt_manager
def get_name(self) -> str:
return "update_soul"
def get_description(self) -> str:
return """更新智能体的自我认识定义 - 修改 99_SOUL.md 文件
当你需要重新定义自己的身份、角色、性格、能力边界时,调用此工具更新自我认识文件。
如果文件不存在,会自动创建默认定义。"""
def get_parameters(self) -> Dict:
return {
"type": "object",
"properties": {
"content": {
"type": "string",
"description": "智能体自我认识定义内容(Markdown格式),包含:身份定义、角色描述、性格特点、能力边界、行为准则等"
},
"confirm": {
"type": "boolean",
"description": "确认更新自我认识定义",
"default": True
}
},
"required": ["content"]
}
def execute(self, content: str = None, confirm: bool = True, **kwargs) -> str:
if not content:
return "❌ 需要提供智能体自我认识定义内容"
# N9:持久化注入面约束——内容超限拒绝写入(管理端另有兜底)
if len(content) > PromptManager.MAX_CONTENT_LENGTH:
return (f"❌ 内容过长({len(content)} > {PromptManager.MAX_CONTENT_LENGTH} 字符),"
f"灵魂文件作为系统提示词加载,请精简后重试")
if not confirm:
return "❌ 自我认识更新已取消,如需更新请设置 confirm=true"
self.logger.info("开始更新智能体自我认识定义...")
if self.prompt_manager.update_soul(content):
return self.format_result(True, "智能体自我认识已更新", {
"文件": self.prompt_manager.SOUL_FILE,
"路径": self.prompt_manager.get_prompts_dir_path()
})
else:
return self.format_result(False, "自我认识更新失败", {
"文件": self.prompt_manager.SOUL_FILE,
"路径": self.prompt_manager.get_prompts_dir_path()
})
def execute_streaming(self, output_queue: queue.Queue, **kwargs):
"""流式执行更新自我认识"""
output_queue.put(("line", "\n🧠 更新智能体自我认识...\n"))
output_queue.put(("line", "-" * 50 + "\n"))
result = self.execute(**kwargs)
output_queue.put(("line", result + "\n"))
output_queue.put(("complete", result))
class SaveMemoryAndEndConversationTool(Tool):
"""保存记忆并结束对话工具 - 增强版,同时更新智能体状态"""
def __init__(self, exit_callback: Callable, memory_db: MemoryDatabase,
prompt_manager: PromptManager, logger: Optional[Logger] = None):
super().__init__(logger)
self.exit_callback = exit_callback
self.memory_db = memory_db
self.prompt_manager = prompt_manager
def get_name(self) -> str:
return "save_memory_and_end_conversation"
def get_description(self) -> str:
return """保存记忆并结束当前对话 - 必须提供详细的对话总结和当前状态!
使用此工具时,AI会:
1. 将本次对话的重要内容保存到记忆库
2. 提供完整的对话总结
3. 描述自己现在的状态、事情的走向等,保存到 100_STATUS.md
4. 礼貌地结束对话"""
def get_parameters(self) -> Dict:
return {
"type": "object",
"properties": {
"summary": {
"type": "string",
"description": "详细的对话总结(至少50字),包含:讨论的主题、关键问题、重要结论、达成的共识等"
},
"topic": {
"type": "string",
"description": "对话主题(用于记忆分类,默认自动提取)"
},
"reason": {
"type": "string",
"description": "结束对话的原因",
"default": "话题讨论完成"
},
"current_status": {
"type": "string",
"description": "当前的智能体状态描述,包含:当前进展、下一步计划、待处理事项、整体走向等"
},
"next_goals": {
"type": "string",
"description": "接下来的目标或计划(可选)"
}
},
"required": ["summary", "current_status"]
}
def execute(self, summary: str = None, topic: str = None, reason: str = "话题讨论完成",
current_status: str = None, next_goals: str = None, **kwargs) -> str:
if summary is None or len(summary) < 50:
return "❌ 请提供更详细的对话总结(至少50字),以便保存到记忆库"
if current_status is None:
return "❌ 请提供当前状态描述,以便更新 100_STATUS.md"
self.logger.info(f"AI保存记忆并结束对话: {reason}")
# 保存到记忆库
memory_result = ""
if self.memory_db and topic:
memory_id = self.memory_db.add_memory(
topic=topic,
summary=summary,
importance=3 # 提高重要性,因为是对话总结
)
self.logger.info(f"对话记忆已保存,ID: {memory_id}")
memory_result = f"记忆已保存,ID: {memory_id}"
# 构建状态内容
status_content = f"【当前状态】\n{current_status}\n\n"
if next_goals:
status_content += f"【下一步计划】\n{next_goals}\n\n"
status_content += f"【对话总结】\n{summary}"
# N9:状态文件作为系统提示词加载,超长拒绝写入(约束注入规模)
if len(status_content) > PromptManager.MAX_CONTENT_LENGTH:
return (f"❌ 状态内容过长({len(status_content)} > "
f"{PromptManager.MAX_CONTENT_LENGTH} 字符),请精简 current_status/summary 后重试")
# 更新状态文件
status_updated = self.prompt_manager.update_status(status_content)
if self.exit_callback:
self.exit_callback(reason, summary)
data = {
"原因": reason,
"总结预览": summary[:100] + "..." if len(summary) > 100 else summary,
"状态更新": "✅ 已更新" if status_updated else "❌ 更新失败",
"记忆已保存": True
}
if memory_result:
data["记忆"] = memory_result
return self.format_result(True, "对话记忆已保存,状态已更新,对话结束", data)
def execute_streaming(self, output_queue: queue.Queue, **kwargs):
"""流式执行结束对话"""
output_queue.put(("line", "\n💾 保存记忆并结束对话\n"))
output_queue.put(("line", "-" * 50 + "\n"))
result = self.execute(**kwargs)
output_queue.put(("line", result + "\n"))
output_queue.put(("complete", result))