feat(config,compose): sdk agents with vault prompt granules and skill plugins, ro vault with rw sub-mounts, beaver-runner
This commit is contained in:
@@ -3,8 +3,9 @@ from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
from beaver_gateway.agents.base import ExposedMcp
|
||||
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeCodeOptions
|
||||
from beaver_gateway.agents.claude import ClaudeAgent, ClaudeOptions
|
||||
from beaver_gateway.agents.raycast import RaycastAgent, RemoteTool, UserPreferences
|
||||
from beaver_gateway.core.prompt import assemble
|
||||
from beaver_gateway.core.registry import Gateway
|
||||
from beaver_gateway.core.turn_record import TurnRecord, slugify
|
||||
from beaver_gateway.frontends.admin import AdminFrontend
|
||||
@@ -16,6 +17,30 @@ from beaver_gateway.mcp.types import HttpMcp, McpServer
|
||||
|
||||
VAULT = Path("/vault")
|
||||
CHATS_DIR = VAULT / "💬 чаты"
|
||||
BEAVER = VAULT / "мета" / "бобер"
|
||||
PROMPTS = BEAVER / "промпты"
|
||||
GRANULES = PROMPTS / "гранулы"
|
||||
SKILLS = BEAVER / "скиллы"
|
||||
|
||||
VOICE = GRANULES / "голос.md"
|
||||
PROFILE = PROMPTS / "профиль.md"
|
||||
CORRECTIONS = PROMPTS / "поправки.md"
|
||||
VAULT_MAP = GRANULES / "карта-vault.md"
|
||||
|
||||
# §4.2: сборки промптов. Окружение kind'а - константная гранула, экземплярное
|
||||
# (файл, топик) едет первым сообщением. Диспетчер/дистиллятор/триаж - M1b+.
|
||||
DEEP_PROMPT = (
|
||||
VOICE,
|
||||
PROFILE,
|
||||
CORRECTIONS,
|
||||
VAULT_MAP,
|
||||
GRANULES / "окружения" / "глубокий.md",
|
||||
GRANULES / "глубокий.md",
|
||||
)
|
||||
QUICK_PROMPT = (VOICE, PROFILE, GRANULES / "быстрый.md")
|
||||
|
||||
# §4.3: наборы скиллов = папки = плагины. Глубокие грузят общие + vault.
|
||||
DEEP_SKILLS = (SKILLS / "общие", SKILLS / "vault")
|
||||
|
||||
|
||||
def chat_log_path(record: TurnRecord, vault: Path) -> Path:
|
||||
@@ -46,6 +71,8 @@ def _calendar_mcps() -> list[HttpMcp]:
|
||||
calendar_mcps = _calendar_mcps()
|
||||
calendar_exposed = tuple(ExposedMcp(name=m.name) for m in calendar_mcps)
|
||||
|
||||
# Секреты MCP - только через env подпроцесса (mcp stdio даёт ему белый список
|
||||
# + это), никогда argv: процесс модели видит `ps` всего контейнера.
|
||||
mcps = [
|
||||
McpServer.stdio(
|
||||
name="obsidian-fs",
|
||||
@@ -59,25 +86,24 @@ mcps = [
|
||||
),
|
||||
McpServer.stdio(
|
||||
name="firefly",
|
||||
command=[
|
||||
"bunx",
|
||||
"-y",
|
||||
"@firefly-iii-mcp/local",
|
||||
"--pat",
|
||||
os.environ["FIREFLY_PAT"],
|
||||
"--baseUrl",
|
||||
os.environ["FIREFLY_BASE_URL"],
|
||||
"--preset",
|
||||
"default",
|
||||
],
|
||||
command=["bunx", "-y", "@firefly-iii-mcp/local", "--preset", "default"],
|
||||
env={
|
||||
"FIREFLY_III_PAT": os.environ["FIREFLY_PAT"],
|
||||
"FIREFLY_III_BASE_URL": os.environ["FIREFLY_BASE_URL"],
|
||||
},
|
||||
lenient=True,
|
||||
),
|
||||
McpServer.http(name="telegram", url=os.environ["BEAVERGRAM_MCP"]),
|
||||
*calendar_mcps,
|
||||
]
|
||||
|
||||
|
||||
CBO_PROMPT = (Path(__file__).parent / "prompt.md").read_text(encoding="utf-8")
|
||||
# Руки глубокого (§4.4): firefly без delete_*, obsidian-fs не даётся - свои
|
||||
# файловые тулзы есть, vault смонтирован в /vault.
|
||||
CLAUDE_MCPS = (
|
||||
ExposedMcp(name="firefly", deny=("delete_*",)),
|
||||
ExposedMcp(name="telegram"),
|
||||
*calendar_exposed,
|
||||
)
|
||||
|
||||
|
||||
UserPrefsRu = lambda: UserPreferences( # noqa: E731
|
||||
@@ -87,34 +113,25 @@ UserPrefsRu = lambda: UserPreferences( # noqa: E731
|
||||
)
|
||||
|
||||
|
||||
def claude(name: str, model: str, effort: str | None = None) -> ClaudeAgent:
|
||||
def deep(name: str, model: str, effort: str | None = None) -> ClaudeAgent:
|
||||
return ClaudeAgent(
|
||||
name=name,
|
||||
model=model,
|
||||
system_prompt=CBO_PROMPT,
|
||||
cwd=VAULT,
|
||||
options=ClaudeCodeOptions(
|
||||
prompt_sources=DEEP_PROMPT,
|
||||
skill_sets=DEEP_SKILLS,
|
||||
options=ClaudeOptions(
|
||||
effort=effort,
|
||||
# Headless `claude -p` instead of driving the TUI over a
|
||||
# pseudo-tty. The pi is exactly the machine the PTY path hurt
|
||||
# most: it spent seconds per spawn waiting for an Ink render
|
||||
# loop to settle (hence startup_delay creeping to 60s) and
|
||||
# occasionally lost a prompt to a swallowed paste. Piped
|
||||
# stdin has neither problem, and gives us token streaming.
|
||||
#
|
||||
# Dropped with it: `--remote-control`, which only exists for
|
||||
# interactive sessions, and the admin terminal's ability to
|
||||
# type into a stuck session by hand — the failure mode that
|
||||
# was for is what this transport removes.
|
||||
transport="stream_json",
|
||||
include_partial_messages=True,
|
||||
disallowed_tools=("AskUserQuestion", "ExitPlanMode", "EnterPlanMode"),
|
||||
),
|
||||
expose_mcps=(
|
||||
ExposedMcp(name="firefly"),
|
||||
ExposedMcp(name="telegram"),
|
||||
*calendar_exposed,
|
||||
# §3.7: у глубоких вопросы текстом, планов и сабагентов нет.
|
||||
disallowed_tools=(
|
||||
"AskUserQuestion",
|
||||
"ExitPlanMode",
|
||||
"EnterPlanMode",
|
||||
"NotebookEdit",
|
||||
"Task",
|
||||
),
|
||||
),
|
||||
expose_mcps=CLAUDE_MCPS,
|
||||
)
|
||||
|
||||
|
||||
@@ -122,13 +139,13 @@ def raycast(name: str, model: str, reasoning_effort: str | None = None) -> Rayca
|
||||
return RaycastAgent(
|
||||
name=name,
|
||||
model=model,
|
||||
system_prompt=CBO_PROMPT,
|
||||
system_prompt=assemble(QUICK_PROMPT),
|
||||
reasoning_effort=reasoning_effort,
|
||||
available_native_tools=(RemoteTool.WEB_SEARCH, RemoteTool.READ_PAGE),
|
||||
user_preferences=UserPrefsRu,
|
||||
expose_mcps=(
|
||||
ExposedMcp(name="obsidian-fs"),
|
||||
ExposedMcp(name="firefly"),
|
||||
ExposedMcp(name="firefly", deny=("delete_*",)),
|
||||
ExposedMcp(name="telegram"),
|
||||
*calendar_exposed,
|
||||
),
|
||||
@@ -136,12 +153,12 @@ def raycast(name: str, model: str, reasoning_effort: str | None = None) -> Rayca
|
||||
|
||||
|
||||
agents = [
|
||||
claude("beaver-opus-high", "claude-opus-5", effort="high"),
|
||||
deep("beaver-opus-high", "claude-opus-5", effort="high"),
|
||||
raycast("beaver-gemini-pro-high", "google-gemini-3.1-pro", reasoning_effort="high"),
|
||||
claude("beaver-fable-high", "claude-fable-5", effort="high"),
|
||||
claude("beaver-opus-medium", "claude-opus-5", effort="medium"),
|
||||
claude("beaver-fable-medium", "claude-fable-5", effort="medium"),
|
||||
claude("beaver-opus-xhigh", "claude-opus-5", effort="xhigh"),
|
||||
deep("beaver-fable-high", "claude-fable-5", effort="high"),
|
||||
deep("beaver-opus-medium", "claude-opus-5", effort="medium"),
|
||||
deep("beaver-fable-medium", "claude-fable-5", effort="medium"),
|
||||
deep("beaver-opus-xhigh", "claude-opus-5", effort="xhigh"),
|
||||
raycast("beaver-gemini-pro-low", "google-gemini-3.1-pro", reasoning_effort="low"),
|
||||
raycast(
|
||||
"beaver-gemini-flash-high", "google-gemini-3.5-flash", reasoning_effort="high"
|
||||
@@ -168,7 +185,7 @@ frontends = [
|
||||
host="0.0.0.0",
|
||||
port=62993,
|
||||
vault_path=CHATS_DIR,
|
||||
default_agent="research",
|
||||
default_agent="beaver-opus-high",
|
||||
log_all_chats=True,
|
||||
log_path=chat_log_path,
|
||||
public_base_url=_public("/md"),
|
||||
|
||||
Reference in New Issue
Block a user