metal-kompanion/docs/tool-calling-without-native...

2.7 KiB

Tool Calling Without Native Support (Happy-Code Compatibility)

When models lack built-in tool/function calling, we can still get reliable tool use via a protocol-in-prompt approach plus a thin runtime.

1) JSON Tool Protocol (deterministic)

Model must respond with a single JSON object, no prose.

{
  "thought": "<very short planning note>",
  "action": {
    "tool": "<tool_name>",
    "args": { }
  }
}
  • If more steps are needed, the runtime feeds the tool's JSON result back as the next user message with role=tool.
  • The model must then either emit another action or finish with:
{
  "final": { "content": { } }
}

Guardrails

  • Reject any output that isn't valid JSON or that contains extra text.
  • Cap thought to ~200 chars.
  • Disallow calling tools not in the registry.

2) Command DSL (fallback)

If JSON parsing is brittle, accept a single line command and parse it:

@tool {json-args}

Example: @tool kom.memory.v1.search_memory {"namespace":"project:metal", "query":{"text":"embedding model"}}

3) Prompt Template (drop-in)

Use this system message for happy-code/claude-code sessions:

You are a coding assistant that can call tools via a JSON protocol. Available tools (names & schema will be provided). Always reply with a single JSON object. No markdown. No commentary. Use this schema: { "thought": string (short), "action": { "tool": string, "args": object } } OR { "final": { "content": any } }. If a tool result is needed, emit action. If done, emit final. Never invent tool names or fields.

4) Minimal Runtime (pseudocode)

while True: msg = llm(messages) data = json.loads(msg) if 'action' in data: tool = registry[data['action']['tool']] result = tool(**data['action']['args']) messages.append({"role":"tool","name":tool.name,"content":json.dumps(result)}) continue elif 'final' in data: return data['final']['content'] else: error("Invalid protocol")

5) MCP Integration

  • Map registry to MCP tools (e.g., kom.memory.v1.*).
  • Provide each tool's JSON Schema to the model in the system message (strict).

6) Testing Checklist

  • Invalid JSON → reject → ask to resend.
  • Unknown tool → reject.
  • Args mismatch → show schema snippet, ask to correct.
  • Multi-step flows → verify tool result is consumed in next turn.

7) Example Session

System: (template above + list of tools & schemas) User: Save this note: "Embedding model comparison takeaways" into project:metal Assistant: {"thought":"need to upsert note","action":{"tool":"kom.memory.v1.upsert_memory","args":{"namespace":"project:metal","items":[{"text":"Embedding model comparison takeaways"}]}}} Tool (kom.memory.v1.upsert_memory): { "upserted": 1 } Assistant: {"final":{"content":{"status":"ok","upserted":1}}}