fleet-memory/hindsight-api/hindsight_api/engine/reflect/tools_schema.py
Nicolò Boschi 5b52a84fff
chore: internal renames (#204)
This commit renames the terminology across the entire codebase:
- "mental models" (fact_type='mental_model' in memory_units) → "observations"
- "reflections" table (stored reflect responses) → "mental_models"

Changes include:
- Database migration to rename tables, indexes, and constraints
- API endpoints: /reflections → /mental-models, /mental-models → /observations
- Config: ENABLE_MENTAL_MODELS → ENABLE_OBSERVATIONS
- Response models and Pydantic classes
- Reflect agent tools and prompts
- Control plane UI and routes
- Documentation and examples
- Regenerated OpenAPI spec and client SDKs (Python, TypeScript)
- Rust CLI: reflection commands → mental-model commands
- LiteLLM: updated fact_types documentation
2026-01-27 09:53:28 +01:00

234 lines
8.9 KiB
Python

"""
Tool schema definitions for the reflect agent.
These are OpenAI-format tool definitions used with native tool calling.
The reflect agent uses a hierarchical retrieval strategy:
1. search_mental_models - User-curated stored reflect responses (highest quality, if applicable)
2. search_observations - Consolidated knowledge with freshness awareness
3. recall - Raw facts (world/experience) as ground truth fallback
"""
# Tool definitions in OpenAI format
TOOL_SEARCH_MENTAL_MODELS = {
"type": "function",
"function": {
"name": "search_mental_models",
"description": (
"Search user-curated mental models (stored reflect responses). These are high-quality, manually created "
"summaries about specific topics. Use FIRST when the question might be covered by an "
"existing mental model. Returns mental models with their content and last refresh time."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query to find relevant mental models",
},
"max_results": {
"type": "integer",
"description": "Maximum number of mental models to return (default 5)",
},
},
"required": ["query"],
},
},
}
TOOL_SEARCH_OBSERVATIONS = {
"type": "function",
"function": {
"name": "search_observations",
"description": (
"Search consolidated observations (auto-generated knowledge). These are automatically "
"synthesized from memories. Returns observations with freshness info (updated_at, is_stale). "
"If an observation is STALE, you should ALSO use recall() to verify with current facts."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query to find relevant observations",
},
"max_tokens": {
"type": "integer",
"description": "Maximum tokens for results (default 5000). Use higher values for broader searches.",
},
},
"required": ["query"],
},
},
}
TOOL_RECALL = {
"type": "function",
"function": {
"name": "recall",
"description": (
"Search raw memories (facts and experiences). This is the ground truth data. "
"Use when: (1) no reflections/mental models exist, (2) mental models are stale, "
"(3) you need specific details not in synthesized knowledge. "
"Returns individual memory facts with their timestamps."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query string",
},
"max_tokens": {
"type": "integer",
"description": "Optional limit on result size (default 2048). Use higher values for broader searches.",
},
},
"required": ["query"],
},
},
}
TOOL_EXPAND = {
"type": "function",
"function": {
"name": "expand",
"description": "Get more context for one or more memories. Memory hierarchy: memory -> chunk -> document.",
"parameters": {
"type": "object",
"properties": {
"memory_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of memory IDs from recall results (batch multiple for efficiency)",
},
"depth": {
"type": "string",
"enum": ["chunk", "document"],
"description": "chunk: surrounding text chunk, document: full source document",
},
},
"required": ["memory_ids", "depth"],
},
},
}
TOOL_DONE_ANSWER = {
"type": "function",
"function": {
"name": "done",
"description": "Signal completion with your final answer. Use this when you have gathered enough information to answer the question.",
"parameters": {
"type": "object",
"properties": {
"answer": {
"type": "string",
"description": "Your response as plain text. Do NOT use markdown formatting. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.",
},
"memory_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of memory IDs that support your answer (put IDs here, NOT in answer text)",
},
"mental_model_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of mental model IDs that support your answer",
},
"observation_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of observation IDs that support your answer",
},
},
"required": ["answer"],
},
},
}
def _build_done_tool_with_directives(directive_rules: list[str]) -> dict:
"""
Build the done tool schema with directive compliance field.
When directives are present, adds a required field that forces the agent
to confirm compliance with each directive before submitting.
Args:
directive_rules: List of directive rule strings
"""
# Build rules list for description
rules_list = "\n".join(f" {i + 1}. {rule}" for i, rule in enumerate(directive_rules))
# Build the tool with directive compliance field
return {
"type": "function",
"function": {
"name": "done",
"description": (
"Signal completion with your final answer. IMPORTANT: You must confirm directive compliance before submitting. "
"Your answer will be REJECTED if it violates any directive."
),
"parameters": {
"type": "object",
"properties": {
"answer": {
"type": "string",
"description": "Your response as plain text. Do NOT use markdown formatting. NEVER include memory IDs, UUIDs, or 'Memory references' in this text - put IDs only in memory_ids array.",
},
"memory_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of memory IDs that support your answer (put IDs here, NOT in answer text)",
},
"mental_model_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of mental model IDs that support your answer",
},
"observation_ids": {
"type": "array",
"items": {"type": "string"},
"description": "Array of observation IDs that support your answer",
},
"directive_compliance": {
"type": "string",
"description": f"REQUIRED: Confirm your answer complies with ALL directives. List each directive and how your answer follows it:\n{rules_list}\n\nFormat: 'Directive 1: [how answer complies]. Directive 2: [how answer complies]...'",
},
},
"required": ["answer", "directive_compliance"],
},
},
}
def get_reflect_tools(directive_rules: list[str] | None = None) -> list[dict]:
"""
Get the list of tools for the reflect agent.
The tools support a hierarchical retrieval strategy:
1. search_mental_models - User-curated stored reflect responses (try first)
2. search_observations - Consolidated knowledge with freshness
3. recall - Raw facts as ground truth
Args:
directive_rules: Optional list of directive rule strings. If provided,
the done() tool will require directive compliance confirmation.
Returns:
List of tool definitions in OpenAI format
"""
tools = [
TOOL_SEARCH_MENTAL_MODELS,
TOOL_SEARCH_OBSERVATIONS,
TOOL_RECALL,
TOOL_EXPAND,
]
# Use directive-aware done tool if directives are present
if directive_rules:
tools.append(_build_done_tool_with_directives(directive_rules))
else:
tools.append(TOOL_DONE_ANSWER)
return tools