yunfengzhou-hub commented on code in PR #1268:
URL: https://github.com/apache/flink-agents/pull/1268#discussion_r4309929964
##########
docs/content/docs/development/multi-agent/subagents.md:
##########
@@ -24,35 +24,136 @@ specific language governing permissions and limitations
under the License.
-->
-A sub-agent handles a task delegated by a parent agent. An internal sub-agent
runs
-an agent workflow in the same job. An external sub-agent calls an agent service
-through a `SubagentSetup` implementation.
+## Overview
-To declare parent and child workflows in YAML, see
-[Declaring sub-agents]({{< ref
"docs/development/create-agent/yaml#declaring-sub-agents" >}}).
-YAML supports references to internal child Agents in the same file and
descriptors
-for external sub-agents.
+A sub-agent is an agent that another agent delegates a task to. The parent
hands it
+input, the sub-agent does the work, and the parent receives the outcome as a
+`SubagentResult`. Use one when the delegated work is multi-step or
long-running and
+should run isolated from the parent.
-## Declare an internal sub-agent
+The work runs either inside the job or outside it. An *internal* sub-agent is a
+child `Agent`, including a `ReActAgent`, compiled into the parent's plan. The
+child's own actions and events run in the same job. An *external* sub-agent
has no
+plan of its own. The work happens in your own code or in a remote agent
service,
+outside the parent's event loop. It takes one of two forms, *deferred* or
*async
+(pub/sub)*.
-Use `InternalSubagentResourceDescriptorBuilder` to attach a description and
input
-schema to a child agent. The builder returns a normal resource descriptor for
`addResource`
-or `add_resource`.
+Either way, the call is initiated by the model or by you. In *model-driven
+delegation* you list the sub-agent on a chat model setup and the model chooses
when
+to delegate. In *caller-driven delegation* one of your actions submits the task
+itself.
+Every sub-agent call runs through
+[durable execution]({{< ref
"docs/development/create-agent/actions#durable-execution" >}}).
+
+## Declare a sub-agent
+
+A sub-agent enters the parent's plan as a resource registered under
+`ResourceType.AGENT`. The name you register it under is the name that
invocations and
+metrics use. What you register depends on the form. An internal sub-agent
registers
+a child `Agent`. An external sub-agent registers a descriptor that names your
setup
+class. A YAML file declares either form, as
+[Declaring sub-agents]({{< ref
"docs/development/create-agent/yaml#declaring-sub-agents" >}})
+describes.
+
+### Internal: compile a child agent {#declare-an-internal-sub-agent}
+
+Nothing in a child's own code marks it as a sub-agent. The child is an ordinary
+agent with its own resources, actions, and event loop, and it runs standalone
+exactly as written. A parent registers it as an `AGENT` resource, and that
+registration is the whole of the difference. It gives the child a name in the
+parent's plan. It can also give the child an input schema, so a chat model can
call
+it. And it supplies the caller that submits input and awaits the child's
outputs.
+
+Any `Agent` can serve as the child. The two common kinds are a workflow agent
and a
+`ReActAgent`. Either way, the child's plan is compiled into the parent's when
the
+parent's plan is built.
+
+#### A workflow agent as the child
+
+Any workflow agent can serve as the child. The one used through this page is
built
+the way
+[Request and Response Example]({{< ref
"docs/development/calling-llms/chat#usage-example" >}})
+builds an agent that owns a chat model, and this page calls it `ReviewAgent`.
It has
+a connection resource, a model setup, an action that turns an `InputEvent`
into a
+`ChatRequestEvent`, and an action that turns the `ChatResponseEvent` into
+`OutputEvent`s.
+
+The one part a sub-agent changes is how that first action reads its input. A
+caller-driven call passes the object the caller submitted. A model-driven call
+passes the arguments map built from the input schema.
+
+{{< tabs "Read the Input in a Child Action" >}}
+
+{{< tab "Python" >}}
+```python
+# Inside ReviewAgent.
+
+
+@action(EventType.InputEvent)
+@staticmethod
+def ask(event: Event, ctx: RunnerContext) -> None:
+ task = InputEvent.from_event(event).input
+ # A model-driven call passes the arguments map; a caller-driven call
+ # passes the submitted object.
+ diff = task["code"] if isinstance(task, dict) else str(task)
+ ctx.send_event(
+ ChatRequestEvent(
+ model="review_model",
+ messages=[ChatMessage.user(f"Review this diff:\n{diff}")],
+ )
+ )
+```
+{{< /tab >}}
+
+{{< tab "Java" >}}
```java
-import org.apache.flink.agents.api.resource.ResourceType;
-import
org.apache.flink.agents.api.subagent.InternalSubagentResourceDescriptorBuilder;
+// Inside ReviewAgent.
-parent.addResource(
- "reviewer",
- ResourceType.AGENT,
- new InternalSubagentResourceDescriptorBuilder()
- .agent(new ReviewAgent())
- .description("Review code for correctness and potential defects")
- .inputType(ReviewRequest.class)
- .build());
+@Action(EventType.InputEvent)
+public static void ask(Event event, RunnerContext ctx) throws Exception {
+ Object task = InputEvent.fromEvent(event).getInput();
+ // A model-driven call passes the arguments map; a caller-driven call
+ // passes the submitted object.
+ String diff =
+ task instanceof Map
+ ? String.valueOf(((Map<?, ?>) task).get("code"))
+ : String.valueOf(task);
+ ctx.sendEvent(
+ new ChatRequestEvent(
+ "reviewModel",
+ List.of(new ChatMessage(MessageRole.USER, "Review this
diff:\n" + diff))));
+}
```
+{{< /tab >}}
+
+{{< /tabs >}}
+While a call runs, the child behaves like a scoped agent of its own:
+
+- Its `OutputEvent`s are accumulated, in order, into the call's result instead
of
+ landing on the parent's context. See
+ [the result](#the-result) for the shape.
+- It resolves resources against its own plan. Register the resources a child
uses,
+ such as its chat model connection, on the child itself. In Python a resource
+ registered solely on the parent is not visible to the child.
+- Its sensory and short-term memory start empty for each call, including calls
+ continuing a session, and are shared by the child's actions within that call.
+ Long-term memory, when configured, is shared with the caller.
+- It may register and await sub-agents of its own, so nesting is unbounded in
+ depth.
+
+Registering the child takes one of two forms. You register a descriptor, or you
+register the `Agent` itself. Which one you need follows from who calls it. A
chat
+model can delegate only to a sub-agent it can describe and build arguments
for. So a
+child exposed to a model needs the descriptor form, and that descriptor
carries a
Review Comment:
Done, with one deviation: the two calling modes are named in a single
sentence rather than redefined, because Overview already defines them and a
second definition would give two places to keep in step. The table follows,
then the descriptor form, the direct form, and the YAML pointer. The
description row also caught a contradiction, since the section required a
description while the delegate section said a missing one falls back to a
generic one. build() requires only agent, so the table is right and that
sentence is gone.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]