feat(app-instance): 集成Beaver后端并更新配置管理

集成新的Beaver后端服务到应用实例中,替换原有的nanobot实现。

主要变更包括:
- 在Dockerfile和环境配置中添加Beaver相关路径和配置变量
- 更新工作目录结构从.nanobot到.beaver
- 实现Beaver引擎加载器,支持配置文件加载和工具组装
- 添加内置工具如ListDirectoryTool、ReadFileTool、SearchFilesTool
- 更新消息处理流程,支持通道适配器和网关模式
- 重构技能系统,支持显式工具提示和嵌入式检索
- 改进错误处理和生命周期管理

此变更使应用实例能够使用统一的Beaver后端进行AI代理运行时管理。
This commit is contained in:
2026-04-27 17:37:40 +08:00
parent 36882a7d7b
commit 5ba5c7e4c1
47 changed files with 2821 additions and 462 deletions

View File

@ -29,13 +29,30 @@ class ToolSpec:
"""单个工具对外暴露的描述信息。
这份信息主要服务两个场景:
1. 导出给 provider 的 function schema
2. 在 registry 中做列出、查找、调试
1. 以 MCP-style descriptor 作为统一事实来源
2. 导出给 provider 的 function schema
3. 在 registry 中做列出、查找、调试与 embedding 召回
"""
name: str
description: str
input_schema: dict[str, Any]
toolset: str = "core"
always_available: bool = False
def to_mcp_descriptor(self) -> dict[str, Any]:
"""导出 MCP ListTools 风格的工具描述。
MCP 的基础字段是 `name`、`description`、`inputSchema`。
Beaver 内部额外的 toolset/always_available 不塞进这个对象,
避免未来对接真实 MCP server 时出现格式偏差。
"""
return {
"name": self.name,
"description": self.description,
"inputSchema": self.input_schema,
}
def to_provider_schema(self) -> dict[str, Any]:
"""导出为 OpenAI-compatible function tool schema。"""
@ -49,6 +66,15 @@ class ToolSpec:
},
}
def to_embedding_candidate(self) -> dict[str, str]:
"""导出给语义召回使用的轻量文本候选。"""
return {
"name": self.name,
"description": self.description,
"input_schema": json.dumps(self.input_schema, ensure_ascii=False, sort_keys=True),
}
@dataclass(slots=True)
class ToolContext:
@ -113,6 +139,8 @@ class ObjectBackedTool(BaseTool):
name=str(getattr(backend, "name")),
description=str(getattr(backend, "description", "")),
input_schema=dict(getattr(backend, "parameters", {"type": "object", "properties": {}})),
toolset=str(getattr(backend, "toolset", "core")),
always_available=bool(getattr(backend, "always_available", False)),
)
@property
@ -150,6 +178,8 @@ class ObjectBackedTool(BaseTool):
if "current_session_id" not in arguments and hasattr(self.backend, "current_session_id"):
arguments["current_session_id"] = context.session_id
if "workspace" not in arguments and hasattr(self.backend, "workspace"):
arguments["workspace"] = context.workspace
@staticmethod
def _normalize_output(content: Any) -> dict[str, Any]: