Skip to content

Commit 3ebaf6a

Browse files
Support interpolation into prompt-based function
* functions of type prompts can use the parameters in their prompts
1 parent 39e0e35 commit 3ebaf6a

File tree

5 files changed

+129
-59
lines changed

5 files changed

+129
-59
lines changed

graphs/prompts/pages/Authoring Prompts.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ A prompt file can also contain metadata about how to generate and run the prompt
44
{{embed ((66d7f3ff-8769-40b3-b6b5-fc4fceea879e)) }}
55
The example above defines a lot of metadata. A prompt file might also be very simple.
66
{{embed ((66d8a396-9268-4917-882f-da4c52b7b5dd)) }}
7-
Use the [prompt engine]([[Running the Prompt Engine]]) to run this prompt file against an LLM.
8-
-
7+
Use the [prompt engine]([[Running the Prompt Engine]]) to run this prompt file against an LLM.

graphs/prompts/pages/Tools.md

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,78 @@
11
# Using a Tool
22
A [[tool]] augments an LLM assistant by providing it what capabilities in the real world.
3-
By adding tools to prompts during the [authoring process]([[Authoring Prompts]]), our assistant can start to incorporate these new capabilities into a [[conversation loop]]. Here's a prompt that incorporates a tool into the conversation ((66da266d-79cb-489e-afa3-d205619b6f3e)).
3+
By adding tools to prompts during the [authoring process]([[Authoring Prompts]]), our assistant can start to incorporate these new capabilities into a [[conversation loop]]. Here's a prompt that incorporates a tool into the conversation ((66da266d-79cb-489e-afa3-d205619b6f3e))
44
- # Developing Tools
5-
- ## Containerized Tools can share data
5+
- ## Containerized Tools
6+
- All tool definition have `name`, `description` and `parameters` fields. This is the data that is passed to the LLM so that it can request access to the underlying tool.
7+
A _container_ tool also has a `container` field which defines how the [prompt engine]([[Running the Prompt Engine]]) should run the tool. Here is a sample container tool definition.
8+
```
9+
---
10+
tools:
11+
- name: sqlite
12+
description: run the sqlite command
13+
parameters:
14+
type: object
15+
properties:
16+
database:
17+
type: string
18+
description: the path to the database
19+
sql:
20+
type: string
21+
description: the sql statement to run
22+
container:
23+
image: vonwig/sqlite:latest
24+
command:
25+
- "{{database}}"
26+
- "{{sql|safe}}"
27+
---
28+
```
29+
- only the `image` field is mandatory but the `command` `entrypoint` and `env` fields are also supported.
30+
- all of the fields support interpolation using `"{{parameter}}"` syntax. This allows us to define tools that will be invoked using parameters bound by the LLM.
31+
- the `"{{sql|safe}}"` syntax above is a django-inspired reference which means that the `sql` string is passed through a `safe` filter. (TODO: describe the concept of parameter safety)
32+
- ### Containerized Tools can share data
633
- Every _tool_ container will automatically run with a volume mounted into the container at `/thread`. This means that within the scope of a [conversation loop](conversation-loop), different tools can share data using this mount point.
7-
The data is ephemeral so by default, the volume is deleted after each execution of the prompt engine. If you pass the argument `--thread-id {volume_name}` to the prompt engine then the volume will be preserved for inspection.
34+
The data is ephemeral so by default, the volume is deleted after each execution of the prompt engine. If you pass the argument `--thread-id {volume_name}` to the prompt engine then the volume will be preserved for inspection.
35+
- ### Containerized Tools can access parts of the host file system
36+
- Every _tool_ container will have one host directory mounted into the container at `/project`. By default, this mount is `rw` but a tool definition can specify `project: ro` or `project: none` in the container definition. This is encouraged when a tool will not need access to a project's file system. The [prompt engine]([[Running the Prompt Engine]]) is always started with a `--host-dir` parameter which defines what part of the file system the assistant can access during the run.
37+
- ## Prompt-based Tools
38+
- Besides tools that run in containers, we can also build a tool interface from another prompt. The definition of prompt-based tool continues to have the `name`, `description` and `parameters` fields but it also must a `type: prompt` and a mandatory `ref` field that points at the prompt file checked in to GitHub.
39+
```
40+
---
41+
tools:
42+
- name: fix-pylint-violation
43+
description: fix a pylint violation
44+
parameters:
45+
type: object
46+
properties:
47+
code:
48+
type: string
49+
description: the code block
50+
violation:
51+
type: string
52+
description: the description of the violation
53+
type: "prompt"
54+
ref: "github:docker/labs-ai-tools-for-devs?path=prompts/pylint/fix-violation.md"
55+
---
56+
```
57+
58+
When defining a `prompt` type tool, the parameters will be available to be interpolated into the prompt definition before being sent to the model. For example, the above function definition has parameters `code` and `violation` so the prompt file itself can reference those using moustache template syntax. For example the prompt file reference above could look like the file below (note the reference `{{code}}` and `{{violation}}` )
59+
60+
```
61+
# prompt user
62+
63+
## Original Code
64+
{{code}}
65+
66+
## Pylint Output
67+
{{violation}}
68+
69+
70+
**STRICTLY FOLLOW THE RULES BELOW:**
71+
generate new code which will resolve the violation
72+
Return the response in the following format:
73+
```python
74+
<corrected code>
75+
```
76+
77+
Instead of a `ref` in the above definition, users can also define a `prompt-file` which must be a relative path (relative the prompt file that was originally passed to the engine). This `prompt-file` field is useful for developing prompts, because we can use local definitions that have not yet been pushed to git.
78+
```

prompts/pylint/7-1-fix.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ tools:
1111
violation:
1212
type: string
1313
description: the description of the violation
14-
prompt:
15-
ref: github:docker/labs-ai-tools-for-devs?path=prompts/pylint/fix-violation.md
16-
- name: sqlite
14+
type: "prompt"
15+
ref: "github:docker/labs-ai-tools-for-devs?path=prompts/pylint/fix-violation.md"
1716
---
1817

1918
# prompt user

prompts/pylint/fix-violation.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# prompt user
32

43
## Original Code

src/prompts.clj

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@
172172
(defn get-prompts
173173
"run extractors and then render prompt templates
174174
returns ordered collection of chat messages"
175-
[{:keys [prompts user platform] :as opts}]
175+
[{:keys [parameters prompts user platform] :as opts}]
176176
(let [;; TODO the docker default no longer makes sense here
177-
m (run-extractors opts)
177+
m (merge (run-extractors opts) parameters)
178178
renderer (partial selma-render prompts (facts m user platform))
179179
prompts (if (fs/directory? prompts)
180180
(->> (fs/list-dir prompts)
@@ -207,54 +207,56 @@
207207
(->> (filter #(= function-name (-> % :function :name)) functions)
208208
first)
209209
:function)]
210-
(try
211-
(cond
212-
(:container definition) ;; synchronous call to container function
213-
(let [arg-context (merge
214-
;; TODO raw is a bad name when merging
215-
{:raw (if json-arg-string
216-
json-arg-string
217-
"{}")}
218-
(when json-arg-string (json/parse-string json-arg-string true)))
219-
function-call (merge
220-
(:container definition)
221-
(dissoc opts :functions)
222-
{:command (into []
223-
(concat
224-
[]
225-
(->>
226-
(-> definition :container :command)
227-
(map (partial interpolate arg-context))
228-
(into []))))}
229-
(when user {:user user})
230-
(when pat {:pat pat})
231-
(when timeout {:timeout timeout}))
232-
{:keys [pty-output exit-code done] :as result} (docker/run-function function-call)]
233-
(cond
234-
(and (= :exited done) (= 0 exit-code))
235-
(resolve pty-output)
236-
(and (= :exited done) (not= 0 exit-code))
237-
(fail (format "call exited with non-zero code (%d): %s" exit-code pty-output))
238-
(= :timeout done)
239-
(fail (format "call timed out: %s" (:timeout result)))
240-
:else
241-
(fail (format "call failed"))))
242-
(= "prompt" (:type definition)) ;; asynchronous call to another agent - new conversation-loop
243-
;; TODO set a custom map for prompts in the next conversation loop
244-
(do
245-
(jsonrpc/notify :message {:content (format "## (%s) sub-prompt" (:ref definition))})
246-
(let [{:keys [messages _finish-reason]}
247-
(async/<!! (conversation-loop
248-
(assoc opts :prompts (git/prompt-file (:ref definition)))))]
249-
(jsonrpc/notify :message {:content (format "## (%s) end sub-prompt" (:ref definition))})
250-
(resolve (->> messages
251-
(filter #(= "assistant" (:role %)))
252-
(last)
253-
:content))))
254-
:else
255-
(fail (format "bad container definition %s" definition)))
256-
(catch Throwable t
257-
(fail (format "system failure %s" t))))
210+
(let [arg-context (merge
211+
;; TODO raw is a bad name when merging
212+
{:raw (if json-arg-string
213+
json-arg-string
214+
"{}")}
215+
(when json-arg-string (json/parse-string json-arg-string true)))]
216+
(try
217+
(cond
218+
(:container definition) ;; synchronous call to container function
219+
(let [function-call (merge
220+
(:container definition)
221+
(dissoc opts :functions)
222+
{:command (into []
223+
(concat
224+
[]
225+
(->>
226+
(-> definition :container :command)
227+
(map (partial interpolate arg-context))
228+
(into []))))}
229+
(when user {:user user})
230+
(when pat {:pat pat})
231+
(when timeout {:timeout timeout}))
232+
{:keys [pty-output exit-code done] :as result} (docker/run-function function-call)]
233+
(cond
234+
(and (= :exited done) (= 0 exit-code))
235+
(resolve pty-output)
236+
(and (= :exited done) (not= 0 exit-code))
237+
(fail (format "call exited with non-zero code (%d): %s" exit-code pty-output))
238+
(= :timeout done)
239+
(fail (format "call timed out: %s" (:timeout result)))
240+
:else
241+
(fail (format "call failed"))))
242+
243+
(= "prompt" (:type definition)) ;; asynchronous call to another agent - new conversation-loop
244+
(do
245+
(jsonrpc/notify :message {:content (format "## (%s) sub-prompt" (:ref definition))})
246+
(let [{:keys [messages _finish-reason]}
247+
(async/<!! (conversation-loop
248+
(assoc opts
249+
:prompts (git/prompt-file (:ref definition))
250+
:parameters arg-context)))]
251+
(jsonrpc/notify :message {:content (format "## (%s) end sub-prompt" (:ref definition))})
252+
(resolve (->> messages
253+
(filter #(= "assistant" (:role %)))
254+
(last)
255+
:content))))
256+
:else
257+
(fail (format "bad container definition %s" definition)))
258+
(catch Throwable t
259+
(fail (format "system failure %s" t)))))
258260
(fail "no function found")))
259261

260262
(defn- run-prompts

0 commit comments

Comments
 (0)