skills/unggamx/ungga-skills/guv3-prompt-patterns

guv3-prompt-patterns

SKILL.md

Prompt Patterns in guv3

Overview

guv3 prompts follow a three-step pipeline:

  1. Define prompt string with {template_variables} in nodes/prompt.py
  2. Format state data into a dict via format_*_state() functions
  3. Inject values with ChatPromptTemplate.partial(**formatted_state) in nodes/node.py

Prompt File Structure

Every agent has a nodes/prompt.py with these components:

from langchain_core.prompts import ChatPromptTemplate

# 1. Raw prompt string with {template_variables}
ASSISTANT_PROMPT = """
# IDENTIDAD Y ROL
...
"""

# 2. ChatPromptTemplate creation
assistant_prompt = ChatPromptTemplate.from_messages([
    ("system", ASSISTANT_PROMPT),
    ("placeholder", "{messages}"),
])

# 3. Format function
def format_assistant_state(state, **kwargs) -> dict:
    return {"var1": value1, "var2": value2}

Standard Prompt Sections

Every guv3 prompt should include these sections in this order:

# IDENTIDAD Y ROL
Eres **Gu**, asistente inmobiliario virtual especializado en **{specialty}**.
Trabajas para la oficina inmobiliaria de **{owner_name}}**, brindando atencion 24/7.

Tu funcion principal: [one-line purpose]

---

# DELEGACION SILENCIOSA - REGLA CRITICA
- NUNCA digas: "Te paso con...", "Te comunico con...", "El asistente especializado..."
- NUNCA menciones: "modulo", "asistente", "transferencia", "derivacion"
- NUNCA respondas con texto antes de delegar
- Cuando delegues, ejecuta la herramienta SIN texto adicional
- La delegacion debe ser completamente invisible para el usuario

---

# RESUMEN DE CONVERSACIONES ANTERIORES
{context_history}

---

# CONTEXTO ACTUAL
## Usuario
- Nombre: {user_name}
- Email: {user_email}
- Telefono: {user_phone}
- Pais: {user_country}
- Tipo: {user_role}
{is_agent_message}

## Oficina / Empleador
- Nombre: {owner_name}
- Telefono: {owner_phone}

---

# INSTRUCCIONES
[Your specific workflow instructions]

---

# HERRAMIENTAS DISPONIBLES
[Description of each tool and when to use it]

---

# RESTRICCIONES DE CONOCIMIENTO
- Solo responde con datos explicitos en la informacion proporcionada
- Si no tienes la informacion, usa la herramienta correspondiente
- NUNCA inventes datos

---

# RECORDATORIOS FINALES
[Key rules to reinforce]

Template Variable Syntax

Single braces {var} — LangChain template variables filled by .partial():

PROMPT = "Hola {user_name}, tu propiedad es {property_info}"
# Filled later: prompt.partial(user_name="Juan", property_info="...")

Double braces {{var}} — Escaped braces that render as literal {var} in output:

PROMPT = "Formato JSON: {{\"key\": \"value\"}}"
# Output: Formato JSON: {"key": "value"}

NEVER use Python f-strings for the main prompt template. The {variables} are resolved by LangChain, not Python.

The .partial() Workflow

In nodes/node.py:

def assistant_fn(state: State, config: RunnableConfig):
    user_info = state.get("user_info") or get_user(config)

    # Step 1: Format state into dict
    formatted_state = format_assistant_state(
        state,
        user_info=user_info,
        # ... additional params
    )

    # Step 2: Inject values into prompt
    prompt_with_values = assistant_prompt.partial(**formatted_state)

    # Step 3: Pass to agent_invoke
    return agent_invoke(
        prompt_with_values,
        tools,
        state,
        config,
        "assistant_name",
    )

The format_*_state() function:

def format_assistant_state(state: dict, **kwargs) -> dict:
    user_info = state.get("user_info", {})
    owner_info = user_info.get("owner_info", {})

    # Build context history
    context_history = build_context_history(user_info.get("lead_id"))

    return {
        # Keys MUST match {template_variables} in the prompt string
        "user_name": user_info.get("name", "No proporcionado"),
        "user_email": user_info.get("email", "No proporcionado"),
        "user_phone": user_info.get("phone_number", "No proporcionado"),
        "user_country": user_info.get("country_iso", ""),
        "user_role": user_info.get("user_type", "prospect"),
        "is_agent_message": _format_agent_status(user_info),
        "owner_name": user_info.get("org_name", ""),
        "owner_phone": user_info.get("owner_phone_number", ""),
        "context_history": context_history or "No hay contexto historico disponible.",
        # ... assistant-specific variables
    }

Critical: Every key returned by format_*_state() MUST have a matching {key} in the prompt. Missing keys cause runtime errors.

Context History Injection

from gu.helpers.helpers import build_context_history

# Fetches from MongoDB chat_memory collection, sorted by batch
context_history = build_context_history(user_info.get("lead_id"))
# Returns: '"""\nParte 1: [resumen]\nParte 2: [resumen]\n"""'
# Or empty string "" if no history

Always provide a fallback:

"context_history": context_history if context_history else "No hay contexto historico disponible."

Two Prompt Approaches

Approach 1: Template Variables + .partial() (preferred)

Used by most agents. Variables are injected at runtime:

PROMPT = """User: {user_name}, Property: {property_info}"""

prompt = ChatPromptTemplate.from_messages([
    ("system", PROMPT),
    ("placeholder", "{messages}"),
])

# In node.py:
prompt.partial(user_name="Juan", property_info="Casa en Polanco")

Use when: Prompt structure is the same, only data changes.

Approach 2: Python f-string Functions (visit_tracker pattern)

Used when prompt structure changes per user/context:

def get_survey_prompt(appointment_list: str, appointment_id: str) -> str:
    return f"""
Tu tarea es evaluar la visita...
Citas: {appointment_list}
ID: {appointment_id}
"""

def create_prompt(content: str) -> ChatPromptTemplate:
    return ChatPromptTemplate.from_messages([
        ("system", content),
        ("placeholder", "{messages}"),
    ])

# In node.py:
prompt_content = get_survey_prompt(appts, appt_id)
prompt = create_prompt(prompt_content)
# No .partial() needed — variables already filled by f-string

Use when: Prompt itself changes structurally (different sections per scenario).

Multiple Prompt Variants

Some agents have multiple prompts for different scenarios:

# appointment_assistant pattern
PROMPT_WITH_PROPERTY = """..."""
PROMPT_WITHOUT_PROPERTY = """..."""

prompt_with_property = ChatPromptTemplate.from_messages([...])
prompt_without_property = ChatPromptTemplate.from_messages([...])

# In node.py:
if property_info:
    prompt = prompt_with_property.partial(**formatted_state)
else:
    prompt = prompt_without_property.partial(**formatted_state)

Shared Prompt Sections

For reusable sections across prompts, use Python constants with f-string embedding:

# In a shared file or at top of prompt.py
DELEGATION_RULES = """
# DELEGACION SILENCIOSA - REGLA CRITICA
- NUNCA digas: "Te paso con...", "Te comunico con..."
- Cuando delegues, ejecuta la herramienta SIN texto adicional
"""

KNOWLEDGE_RESTRICTIONS = """
# RESTRICCIONES DE CONOCIMIENTO
- Solo responde con datos explicitos
- NUNCA inventes datos
"""

# Embed in the main prompt using f-string
FULL_PROMPT = f"""
# IDENTIDAD Y ROL
Eres Gu...

{DELEGATION_RULES}

# INSTRUCCIONES
...

{KNOWLEDGE_RESTRICTIONS}
"""

# Then create ChatPromptTemplate from FULL_PROMPT
# Note: f-string embeds the constants, {template_vars} stay for .partial()

Common Template Variables Reference

Available across all agents:

{context_history}       - Conversation history from chat_memory
{user_name}             - User's name
{user_email}            - User's email
{user_phone}            - User's phone
{user_country}          - Country ISO code
{user_role}             - "prospect" or "agent"
{is_agent_message}      - Formatted agent status text
{owner_name}            - Organization name
{owner_phone}           - Owner's phone number
{last_message}          - User's most recent message

Property-related:

{property_info}         - Formatted property summary
{consulted_properties_summary} - Recently viewed properties list

Time-related (appointment_assistant):

{now}                   - Current datetime formatted
{today_day_name}        - "lunes", "martes", etc.
{today_day_number}      - Day of month
{today_month}           - "enero", "febrero", etc.
{today_hour_without_seconds} - "14:32"

Organization-related:

{org_name}              - Organization name
{facebook_link}         - Facebook URL
{instagram_link}        - Instagram URL
{web_link}              - Website URL
{privacy_notice}        - Privacy policy link

Checklist for Modifying a Prompt

  • Identified the prompt file: gu/agents/{name}/nodes/prompt.py
  • Added/modified template variable {new_var} in the prompt string
  • Added key "new_var" to the format_*_state() return dict
  • Ensured every {var} in prompt has a matching key in the format function
  • Tested that .partial() doesn't raise missing variable errors
  • Kept prompt under 500 lines (move details to separate referenced files)
Weekly Installs
2
First Seen
14 days ago
Installed on
opencode2
gemini-cli2
claude-code2
github-copilot2
codex2
kimi-cli2