Skip to content

Commit 100bd92

Browse files
bmquinnmbklein
authored andcommitted
Merge pull request #4851 from nulib/5668-agent-plan-data-model
Add Meadow.Data.Schemas.AgentPlan schema and Meadow.Data.Planner context
2 parents cabbbe6 + cca3689 commit 100bd92

File tree

11 files changed

+3063
-0
lines changed

11 files changed

+3063
-0
lines changed

README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,135 @@ And force a re-index:
123123
Meadow.Data.Indexer.reindex_all()
124124
```
125125

126+
### AI Agent Plans
127+
128+
Meadow supports AI agent-generated plans for batch modifications to works. The system uses a two-table structure that allows agents to propose work-specific changes based on high-level prompts.
129+
130+
#### Data Model
131+
132+
**Plans** - High-level task definitions
133+
- `prompt`: Natural language instruction (e.g., "Add a date_created EDTF string for the work based on the work's existing description, creator, and temporal subjects")
134+
- `query`: OpenSearch query string identifying target works
135+
- Collection query: `"collection.id:abc-123"`
136+
- Specific works: `"id:(work-id-1 OR work-id-2 OR work-id-3)"`
137+
- `status`: `:pending`, `:approved`, `:rejected`, `:executed`, or `:error`
138+
139+
**PlanChanges** - Work-specific modifications
140+
- `plan_id`: Foreign key to parent plan
141+
- `work_id`: Specific work being modified
142+
- `add`: Map of values to append to existing work data
143+
- `delete`: Map of values to remove from existing work data
144+
- `replace`: Map of values to fully replace in work data
145+
- `status`: Individual approval/rejection tracking
146+
147+
Each PlanChange must specify at least one operation (`add`, `delete`, or `replace`).
148+
149+
#### PlanChange payloads
150+
151+
- `add` merges values into existing metadata. For lists (like subjects or notes) the values are appended when they are not already present. Scalar fields (e.g., `title`) are merged according to the context (`:append` for `add`, `:replace` for `replace`).
152+
- `delete` removes the provided values verbatim. For controlled vocabularies this means the JSON structure must match what is stored in the database (role/term maps). The planner normalizes structs and string-keyed maps automatically when executing changes.
153+
- `replace` overwrites existing values for the provided keys. Use this when the existing content should be replaced entirely instead of appended or removed.
154+
155+
Controlled metadata entries (subjects, creators, contributors, etc.) follow the shape below. For subjects you must supply both the `role` (with at least `id`/`scheme`) and the `term.id`; extra fields such as `label` or `variants` are ignored during execution but can be included when working with structs in IEx:
156+
157+
```elixir
158+
%{
159+
descriptive_metadata: %{
160+
subject: [
161+
%{
162+
role: %{id: "TOPICAL", scheme: "subject_role"},
163+
term: %{
164+
id: "http://id.loc.gov/authorities/subjects/sh85141086",
165+
label: "Universities and colleges",
166+
variants: ["Colleges", "Higher education institutions"]
167+
}
168+
}
169+
]
170+
}
171+
}
172+
```
173+
174+
When constructing PlanChanges you can mix-and-match operations as needed. For example, to remove an outdated subject and add a new one in a single change:
175+
176+
```elixir
177+
delete: %{
178+
descriptive_metadata: %{
179+
subject: [
180+
%{role: %{id: "TOPICAL", scheme: "subject_role"}, term: %{id: "mock1:result2"}}
181+
]
182+
}
183+
},
184+
add: %{
185+
descriptive_metadata: %{
186+
subject: [
187+
%{role: %{id: "TOPICAL", scheme: "subject_role"}, term: %{id: "mock1:result5"}}
188+
]
189+
}
190+
}
191+
```
192+
193+
#### Example Workflows
194+
195+
**Adding new metadata:**
196+
```elixir
197+
# 1. Create a plan with a query - PlanChanges are auto-generated for matching works
198+
{:ok, plan} = Meadow.Data.Planner.create_plan(%{
199+
prompt: "Add a date_created EDTF string for the work based on the work's existing description, creator, and temporal subjects",
200+
query: "collection.id:abc-123"
201+
})
202+
203+
# 2. Agent updates each auto-generated PlanChange with work-specific values
204+
changes = Meadow.Data.Planner.list_plan_changes(plan.id)
205+
206+
change_a = Enum.at(changes, 0)
207+
{:ok, updated_change_a} = Meadow.Data.Planner.update_plan_change(change_a, %{
208+
add: %{descriptive_metadata: %{date_created: ["1896-11-10"]}}
209+
})
210+
211+
change_b = Enum.at(changes, 1)
212+
{:ok, updated_change_b} = Meadow.Data.Planner.update_plan_change(change_b, %{
213+
add: %{descriptive_metadata: %{date_created: ["1923-05"]}}
214+
})
215+
```
216+
217+
**Removing unwanted values:**
218+
```elixir
219+
# Remove extraneous subject headings
220+
{:ok, change} = Meadow.Data.Planner.create_plan_change(%{
221+
plan_id: plan.id,
222+
work_id: "work-id",
223+
delete: %{
224+
descriptive_metadata: %{
225+
subject: [
226+
%{role: %{id: "TOPICAL", scheme: "subject_role"}, term: %{id: "http://example.org/photograph"}},
227+
%{role: %{id: "TOPICAL", scheme: "subject_role"}, term: %{id: "http://example.org/image"}}
228+
]
229+
}
230+
}
231+
})
232+
```
233+
234+
**Replacing existing values:**
235+
```elixir
236+
# Replace the title
237+
{:ok, change} = Meadow.Data.Planner.create_plan_change(%{
238+
plan_id: plan.id,
239+
work_id: "work-id",
240+
replace: %{descriptive_metadata: %{title: "New Title"}}
241+
})
242+
```
243+
244+
**Reviewing and executing:**
245+
```elixir
246+
# 3. User reviews and approves
247+
{:ok, _} = Meadow.Data.Planner.approve_plan(plan, "[email protected]")
248+
{:ok, _} = Meadow.Data.Planner.approve_plan_change(change_a, "[email protected]")
249+
{:ok, _} = Meadow.Data.Planner.approve_plan_change(change_b, "[email protected]")
250+
251+
# 4. Execute approved changes
252+
{:ok, executed_plan} = Meadow.Data.Planner.execute_plan(plan)
253+
```
254+
126255
### Doing development on the Meadow Pipeline lambdas
127256

128257
In the AWS developer environment, the lambdas associated with the pipeline are shared amongst developers. In order to do development and see whether it's working you can override the configuration to use your local files instead of the deployed lambdas. Example below (you don't have to override them all. Just the ones you need).

0 commit comments

Comments
 (0)