https://www.boundaryml.com logo
🟡 suggestion
# questions
v
In the BAML function, we iterate through this typed history list and use the _.role() dynamic directive to inject content into the specific context of the LLM. Code snippet
Copy code
function AgentLoop(history: ChatItem) -> ToolCall | AssistantMessage {
  client GPT4
  
  prompt #"
    {# System Instructions #}
    {{ _.role("system") }}
    You are an intelligent assistant with filesystem access.
    If you need to perform an action, output a ToolCall structure.
    If the task is complete or you need to ask a question, output an AssistantMessage.

    {# Dynamic History Rendering #}
    {% for item in history %}
      
      {# 1. User Message #}
      {% if item is UserMessage %}
        {{ _.role("user") }}
        {{ item.content }}
      
      {# 2. Assistant Message (Normal Reply) #}
      {% elif item is AssistantMessage %}
        {{ _.role("assistant") }}
        {{ item.content }}

      {# 3. Assistant-initiated Tool Call (Assistant Role) #}
      {% elif item is ToolCall %}
        {{ _.role("assistant") }}
        Thinking: {{ item.thought }}
        Calling Tool: {{ item.tool_name }}
        Args: {{ item.arguments }}

      {# 4. Tool Result (Tool Role - Key Optimization Point) #}
      {# Most LLMs stop generating and enter analysis mode when they see the 'tool' role #}
      {% elif item is ToolResult %}
        {{ _.role("tool") }}
        Tool Output (ID: {{ item.tool_call_id }}):
        {{ item.output }}
        {% if item.is_error %}
          (Execution Failed)
        {% endif %}

      {% endif %}
    {% endfor %}

    {# Force output format to match BAML schema #}
    {{ ctx.output_format }}
  "#
}
Why is this design effective? Clear Context Boundaries: By using _.role("tool"), the LLM can architecturally distinguish between "what I just said" and "real feedback from the external world." This prevents the model from confusing contexts and stops it from answering its own questions within the same response (preventing "hallucinated calls").
I know that BAML treats functions as tool calls. I have a thought on how to better align with the training data of LLM vendors, who still wrap tool calls and tool results with special tokens. My suggestion is that BAML allows us to parse certain field types as tool calls and tool results. However, it would still use ctx.output, so there's no need to actually execute the tool call, but it can better leverage the LLM vendors' support for tool calls.