Skip to content

Commit c60cc1f

Browse files
committed
Added Tool.inject example to READEME.md
1 parent cf7eff1 commit c60cc1f

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,43 @@ let searchTool =
184184
|> Tool.describe "Searches the knowledge base for relevant documents"
185185
```
186186

187+
### Tools: Dependency Injection (`Tool.inject`)
188+
189+
Most real-world tool functions need a dependency — a database, an HTTP client, a domain service. But the agent shouldn't see (or be able to fill in) that dependency: it's a host concern, not a model concern.
190+
191+
`Tool.inject` partially applies the **leftmost parameter** of a tool's underlying function with a value you supply, and returns a new `ToolDef` whose metadata and method signature are exactly one parameter shorter. The captured dependency is forwarded to the original function at invoke time.
192+
193+
```fsharp
194+
/// <summary>Looks up a user by id</summary>
195+
/// <param name="db">The database connection</param>
196+
/// <param name="userId">The user's id</param>
197+
let lookupUser (db: IDb) (userId: int) : string =
198+
db.GetUserName userId
199+
200+
let lookupUserTool =
201+
Tool.createWithDocs <@ lookupUser @>
202+
|> Tool.inject realDb
203+
// The agent now sees a 1-parameter tool: { Name = "lookupUser"; Parameters = [userId: int] }
204+
// At invoke time, `realDb` is passed automatically; the model only supplies `userId`.
205+
```
206+
207+
**Why `Tool.inject`?**
208+
- **Hide infrastructure from the model** — the LLM only sees parameters it can meaningfully reason about
209+
- **Per-request dependencies** — capture a tenant-scoped service, a request-scoped logger, etc. by injecting a fresh value each time you build the agent
210+
- **No wrapper boilerplate** — you don't need to hand-write a closure-shaped tool function just to thread a dependency through
211+
- **XML metadata still works** — descriptions on the remaining parameters survive the injection
212+
213+
`Tool.inject` is composable in pipelines and works whether the dependency is the only parameter or one of many. You can also inject into functions whose remaining input is `unit`:
214+
215+
```fsharp
216+
let nowFromClock (clock: IClock) () : string = clock.Now()
217+
218+
let nowTool =
219+
Tool.create <@ nowFromClock @>
220+
|> Tool.inject systemClock
221+
|> Tool.describe "Returns the current time"
222+
```
223+
187224
### ChatAgent: Pipeline-Style Configuration
188225

189226
Build agents using a clean pipeline:

0 commit comments

Comments
 (0)