NDK-SDK
Natural-language tool calling for LLM agents — a Python implementation of the NLT (Natural Language Tools) approach.
Benchmark results
Tested on 5 free-tier models, 29 prompts, 7 tools. Baseline = standard JSON-schema tool calling. NLT = natural-language descriptions + ACTION: name(args) free-text output.
Model BL Tool% NLT Tool% BL Full% NLT Full% BL Malf% NLT Malf%
------------------------------------------------------------------------------------------
deepseek-v4-flash-free 83% 97% 66% 76% 17% 0% ↑14%
lfm-2.5-1.2b-instruct:free 79% 93% 69% 72% 21% 7% ↑14%
llama-3.2-3b-instruct:free 97% 100% 79% 76% 3% 0% ↑3%
llama-3.3-70b-instruct:free 93% 100% 72% 76% 3% 0% ↑7%
gpt-oss-20b:free 86% 100% 72% 76% 14% 0% ↑14%
Aggregate: Tool selection 88% → 98% (+10.3%) | Full success 72% → 75% (+3.4%) | Malformed 12% → 1% (-10.3%)
Smallest/weakest models benefit most — exactly as the paper predicts.
How it works
| Step | JSON-Schema (baseline) | NLT (this SDK) |
|---|---|---|
| Tool definition | JSON Schema in tools param |
Prose description in system prompt |
| Model output | Structured tool_calls object |
Free-text ending with ACTION: name(args) |
| Parsing | json.loads() on arguments |
Regex extraction from plain text |
Quick start
pip install NDK-SDK
from ndk_sdk import NLTConverter, FreeTextParser
# Define your tools (standard OpenAI format)
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}]
# Convert to NL descriptions
converter = NLTConverter(tools)
system_msg = converter.to_openai_system_message()
# Use with any OpenAI-compatible API
response = client.chat.completions.create(
model="your-model",
messages=[system_msg, {"role": "user", "content": "Weather in Paris?"}]
)
# Parse the free-text response
parser = FreeTextParser()
tool_call = parser.parse(response.choices[0].message.content)
# -> ToolCall(name="get_weather", arguments={"city": "Paris"})
Two-phase selection (decoupled approach)
from ndk_sdk import ToolSelector, NLTConverter
converter = NLTConverter(tools)
selector = ToolSelector(converter)
# Phase 1: YES/NO — should we use a tool?
msgs = selector.build_selection_prompt("What's the weather in Paris?")
selection = selector.parse_selection(model_response)
# -> SelectionResult(use_tool=True, reasoning="...")
# Phase 2: Only if YES, pick the tool and args
if selection.use_tool:
msgs = selector.build_tool_call_prompt("What's the weather in Paris?", selection.reasoning)
tool_call = parser.parse(model_response)
Limitations
- Full-success gains are modest (+3.4%). NLT dramatically improves tool selection (88→98%), but argument correctness is the harder problem and only improves slightly. The model picks the right tool more often, but still sometimes gets the args wrong.
- The malformed-output improvement (12%→1%) is partly from the retry fallback. When the parser fails to extract an
ACTION:line, NLT retries once with a clarifying prompt before counting it as a failure. Without the fallback, the malformed rate would be higher. - Tested on 5 models, 29 prompts. Larger-scale benchmarks needed to confirm generalization.
Roadmap
- Improve argument extraction accuracy (the weakest dimension)
- Larger test set (50+ prompts, 10+ models)
- Structured argument parsing (e.g. guided generation for complex types)
- LangChain / MCP integration adapters
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ndk_sdk-0.1.0.tar.gz.
File metadata
- Download URL: ndk_sdk-0.1.0.tar.gz
- Upload date:
- Size: 6.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4e7ea6f56448be59d0b526ceca836073aeef548eaac244bb57ae71fb00dcfe2
|
|
| MD5 |
6b2d1dc3167d9f2eb4feff22cca6e9e8
|
|
| BLAKE2b-256 |
a7ddf9af141b934f7414877beb247f9125dcaf84531fc7d96bc18863a2eb11f0
|
Provenance
The following attestation bundles were made for ndk_sdk-0.1.0.tar.gz:
Publisher:
publish.yml on abduznik/NDK-SDK-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ndk_sdk-0.1.0.tar.gz -
Subject digest:
a4e7ea6f56448be59d0b526ceca836073aeef548eaac244bb57ae71fb00dcfe2 - Sigstore transparency entry: 2226393998
- Sigstore integration time:
-
Permalink:
abduznik/NDK-SDK-py@c6019a5ec258de69f110f2e37d185e7281a3db7a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/abduznik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c6019a5ec258de69f110f2e37d185e7281a3db7a -
Trigger Event:
release
-
Statement type:
File details
Details for the file ndk_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ndk_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
459d4fecf2517ba90733410c21ef48a30013ad54a0e0c50656d986fe1342f3f2
|
|
| MD5 |
b961c6b763c6ec09c0c7e7a5a83eb1cc
|
|
| BLAKE2b-256 |
9c61b79178cd49ee764143f6d1627f617da41a46e030aa871d324feadafafe9f
|
Provenance
The following attestation bundles were made for ndk_sdk-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on abduznik/NDK-SDK-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ndk_sdk-0.1.0-py3-none-any.whl -
Subject digest:
459d4fecf2517ba90733410c21ef48a30013ad54a0e0c50656d986fe1342f3f2 - Sigstore transparency entry: 2226394348
- Sigstore integration time:
-
Permalink:
abduznik/NDK-SDK-py@c6019a5ec258de69f110f2e37d185e7281a3db7a -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/abduznik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c6019a5ec258de69f110f2e37d185e7281a3db7a -
Trigger Event:
release
-
Statement type: