mirror of
https://github.com/JackChen-me/open-multi-agent.git
synced 2026-10-22 02:53:26 -05:00
119 lines
5.1 KiB
Markdown
119 lines
5.1 KiB
Markdown
# Structured Agent Input
|
|
|
|
`Agent.run()`, `Agent.stream()`, and `OpenMultiAgent.runAgent()` accept either a
|
|
string or a complete `LLMMessage[]`. The string form is unchanged shorthand for
|
|
one user text message. Use the message form when the application owns prior
|
|
conversation turns or needs content blocks such as images:
|
|
|
|
```ts
|
|
import {
|
|
OpenMultiAgent,
|
|
type LLMMessage,
|
|
} from '@open-multi-agent/core'
|
|
|
|
const messages: LLMMessage[] = [
|
|
{ role: 'user', content: [{ type: 'text', text: 'Keep answers concise.' }] },
|
|
{ role: 'assistant', content: [{ type: 'text', text: 'Understood.' }] },
|
|
{
|
|
role: 'user',
|
|
content: [
|
|
{ type: 'text', text: 'What is shown here?' },
|
|
{
|
|
type: 'image',
|
|
source: {
|
|
type: 'base64',
|
|
media_type: 'image/png',
|
|
data: imageBytes.toString('base64'),
|
|
},
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
const result = await new OpenMultiAgent().runAgent(
|
|
{ name: 'vision', model: 'claude-sonnet-4-6' },
|
|
messages,
|
|
)
|
|
```
|
|
|
|
The selected model/provider must support every supplied content block. OMA does
|
|
not infer a vision-capability flag or silently remove unsupported blocks;
|
|
provider errors follow the normal agent failure path.
|
|
|
|
## API boundaries
|
|
|
|
| API | String form | Structured form | Conversation behavior |
|
|
|---|---|---|---|
|
|
| `Agent.run(input)` | One user text message | Complete `readonly LLMMessage[]` | Fresh; does not read or update persistent history |
|
|
| `Agent.stream(input)` | One user text message | Complete `readonly LLMMessage[]` | Fresh; same input semantics as `run()` |
|
|
| `OpenMultiAgent.runAgent(config, input)` | One user text message | Complete `readonly LLMMessage[]` | Fresh one-off Agent with orchestration, tracing, progress, budgets, and evaluation |
|
|
| `Agent.prompt(input)` | One user text message | One user turn as `readonly ContentBlock[]` | Appends that turn and the response to persistent history |
|
|
|
|
`AgentConfig.history` restores earlier persistent turns for `prompt()`. Passing a
|
|
content-block list to `prompt()` does not replace that history and cannot insert
|
|
assistant turns; use `run(messages)` for a caller-owned complete conversation.
|
|
`runTeam()` goals and `runTasks()` task descriptions remain text-only.
|
|
|
|
## Copying and validation
|
|
|
|
Structured inputs are runtime-validated with the same `LLMMessage` shape guard
|
|
used at adapter boundaries, then defensively deep-copied before hooks, runners,
|
|
progress callbacks, evaluation, or persistent history retain them. Mutating the
|
|
caller's arrays, content blocks, image source, or tool input after calling an API
|
|
does not change that run. `Agent.getHistory()` also returns a deep copy.
|
|
|
|
Invalid message/content shapes or data that cannot be cloned throw
|
|
`InvalidMessageError` before `beforeRun`, provider/backend execution, progress,
|
|
or online evaluation. Invalid `prompt()` input is not appended to history.
|
|
For `stream()`, this validation happens when `stream()` is called, before the
|
|
returned iterator starts.
|
|
|
|
## `beforeRun` semantics
|
|
|
|
`beforeRun` receives both views of the effective input:
|
|
|
|
```ts
|
|
beforeRun(ctx) {
|
|
return {
|
|
...ctx,
|
|
messages: ctx.messages, // complete defensive message copy
|
|
prompt: ctx.prompt, // text blocks from the latest user message
|
|
}
|
|
}
|
|
```
|
|
|
|
`ctx.prompt` remains the backwards-compatible concatenation of text blocks from
|
|
the latest user message. An image-only turn therefore has an empty `prompt` but
|
|
is fully available in `ctx.messages`.
|
|
|
|
Returning `messages` replaces the complete input. If the hook also changes
|
|
`prompt`, OMA applies the message replacement first, then replaces the latest
|
|
user message's text blocks with one text block. Non-text blocks keep their
|
|
relative order. Hook inputs and execution messages are copies, so a hook rewrite
|
|
does not mutate caller-owned data or the original user turn stored by
|
|
`Agent.prompt()`.
|
|
|
|
## Progress and online evaluation
|
|
|
|
String `runAgent()` calls retain the existing `agent_start` payload
|
|
`{ prompt: string }` and string evaluation input. Structured calls emit
|
|
`{ messages: LLMMessage[] }` and submit a separate message copy to online
|
|
evaluation. Progress callback mutations cannot affect execution or evaluation.
|
|
The evaluator's existing `storePayloads` policy still applies: `none` omits the
|
|
content, while `redacted` or `full` serializes a bounded payload according to the
|
|
documented privacy contract.
|
|
|
|
## External process and ACP backends
|
|
|
|
Process and ACP backends expose text prompt transports, not OMA's structured
|
|
message protocol. Supplying `LLMMessage[]` to `run()` / `stream()` / `runAgent()`,
|
|
or `ContentBlock[]` to `prompt()`, throws `InvalidMessageError` before a process
|
|
is spawned or an ACP session is opened. This fail-fast boundary prevents images
|
|
or caller-owned history from being silently discarded. Pass a string instead.
|
|
|
|
For the same reason, an external agent's `beforeRun` may rewrite `prompt` but may
|
|
not change `messages`. Existing string execution, including the process backend's
|
|
fresh-per-run behavior and ACP's protocol session behavior, is unchanged.
|
|
`AgentConfig.history` does not seed either external transport; it restores
|
|
messages only for LLM-backed `prompt()` conversations.
|