feat: integrate Jinja2 templating for agent responses and memory consolidation
- Added Jinja2 template support for various agent responses, including identity, skills, and memory consolidation. - Introduced new templates for evaluating notifications, handling subagent announcements, and managing platform policies. - Updated the agent context and memory modules to utilize the new templating system for improved readability and maintainability. - Added a new dependency on Jinja2 in pyproject.toml.
This commit is contained in:
@@ -10,6 +10,8 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from nanobot.utils.prompt_templates import render_template
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nanobot.providers.base import LLMProvider
|
||||
|
||||
@@ -37,19 +39,6 @@ _EVALUATE_TOOL = [
|
||||
}
|
||||
]
|
||||
|
||||
_SYSTEM_PROMPT = (
|
||||
"You are a notification gate for a background agent. "
|
||||
"You will be given the original task and the agent's response. "
|
||||
"Call the evaluate_notification tool to decide whether the user "
|
||||
"should be notified.\n\n"
|
||||
"Notify when the response contains actionable information, errors, "
|
||||
"completed deliverables, or anything the user explicitly asked to "
|
||||
"be reminded about.\n\n"
|
||||
"Suppress when the response is a routine status check with nothing "
|
||||
"new, a confirmation that everything is normal, or essentially empty."
|
||||
)
|
||||
|
||||
|
||||
async def evaluate_response(
|
||||
response: str,
|
||||
task_context: str,
|
||||
@@ -65,10 +54,12 @@ async def evaluate_response(
|
||||
try:
|
||||
llm_response = await provider.chat_with_retry(
|
||||
messages=[
|
||||
{"role": "system", "content": _SYSTEM_PROMPT},
|
||||
{"role": "user", "content": (
|
||||
f"## Original task\n{task_context}\n\n"
|
||||
f"## Agent response\n{response}"
|
||||
{"role": "system", "content": render_template("agent/evaluator.md", part="system")},
|
||||
{"role": "user", "content": render_template(
|
||||
"agent/evaluator.md",
|
||||
part="user",
|
||||
task_context=task_context,
|
||||
response=response,
|
||||
)},
|
||||
],
|
||||
tools=_EVALUATE_TOOL,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Load and render agent system prompt templates (Jinja2) under nanobot/templates/.
|
||||
|
||||
Agent prompts live in ``templates/agent/`` (pass names like ``agent/identity.md``).
|
||||
Shared copy lives under ``agent/_snippets/`` and is included via
|
||||
``{% include 'agent/_snippets/....md' %}``.
|
||||
"""
|
||||
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
_TEMPLATES_ROOT = Path(__file__).resolve().parent.parent / "templates"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def _environment() -> Environment:
|
||||
# Plain-text prompts: do not HTML-escape variable values.
|
||||
return Environment(
|
||||
loader=FileSystemLoader(str(_TEMPLATES_ROOT)),
|
||||
autoescape=False,
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
)
|
||||
|
||||
|
||||
def render_template(name: str, *, strip: bool = False, **kwargs: Any) -> str:
|
||||
"""Render ``name`` (e.g. ``agent/identity.md``, ``agent/platform_policy.md``) under ``templates/``.
|
||||
|
||||
Use ``strip=True`` for single-line user-facing strings when the file ends
|
||||
with a trailing newline you do not want preserved.
|
||||
"""
|
||||
text = _environment().get_template(name).render(**kwargs)
|
||||
return text.rstrip() if strip else text
|
||||
Reference in New Issue
Block a user