GitHub user alnzng added a comment to the discussion: [Feature] Sub-agent
Resource for Flink Agents - Framework part
Thanks for this proposal @pltbkd . I think treating agents as a first-class
resource is a good direction.
One thing I would like to understand better: for the external agent case
specifically, what is the unique value of this design, compared with doing the
same thing using the existing Tool or Action APIs? Today I can wrap a remote
agent as a Tool. It registers, the LLM can call it, it runs under durable
execution, and it takes a structured input. Or I can write an Action and call
the remote directly. I just want to learn more here, not to question why we
need this.
For extenal agent proposal, let's use a concrete example to discuss. Say I call
LangGraph agents owned by another team. I give one a metric dashboard link, it
pulls logs, runs its own tools, and returns a diagnosis. Their protocol is not
one single HTTP request, it is three steps (check below). You POST a message to
a thread, it starts a background run and returns right away. Then you open an
SSE stream to watch that thread, and the run keeps going even if your client
disconnects. And you can GET the thread status any time, which returns running,
completed, failed, or not_started. The thread id is given by the client, and it
gives multi-turn continuity.
```
1. Send a message (triggers background execution):
POST /api/agents/{agent_id}/threads/{thread_id}/messages
curl -X POST
'http://localhost:5110/api/agents/demo_agent_async/threads/my_thread_123/messages'
\
-H 'Content-Type: application/json' \
-d '{"message": {"content": "Hello, how are you?"}}'
# → {"thread_id": "my_thread_123", "status": "started"}
2. Stream the response (SSE):
GET /api/agents/{agent_id}/threads/{thread_id}/stream
curl -N
'http://localhost:5110/api/agents/demo_agent_async/threads/my_thread_123/stream'
\
-H 'Accept: text/event-stream'
3. Check status:
GET /api/agents/{agent_id}/threads/{thread_id}
# → status: running | completed | failed | not_started
```
So this remote is not a stateless function, it is a durable async job. It
already exposes, on the server side, a similar lifecycle that @ofekron
described. The thread id is a stable invocation id. The status endpoint gives
real states. Completion is explicit, it is status completed, not the SSE socket
closing. This matters for recovery. If Flink crashes after the call started, I
do not need to run the remote agent again. I can use the same thread id to ask
the server what happened. If it is still running, I reconnect to the stream. If
it already finished, I read the saved result. So after a crash, recovery can be
a query, not a re-run.
This is where I am not sure the current design fits. The external agent
endpoint splits submit and stream on purpose. The current design merges them
back into one callable. So, on failover there is only one move, run the whole
callable again. The server actually handles the easy case well. If I re-post
while the run is still active, it short-circuits and just returns the status,
and re-opening the stream re-attaches. The problem is the narrow window where
the run already completed but Flink crashed before saving the result. On
recovery the thread is no longer active, so a replay post is accepted and
starts a new run, which the checkpointer appends as a new turn. That is
duplicate work, and it corrupts a multi-turn thread. Deterministic SessionId
does not help me here, because it maps to the thread id, and re-posting to that
thread is not idempotent in this window.
On recovery, the right behavior could be like this. Do not send the message
again. First ask the server for the thread status. If it is still running,
re-attach to the stream. If it already finished, just read the result. If it
failed, report the error. This needs the call to be split into two parts,
submit and probe. But the current interface is a single callable, so there is
no place to express this split. To make it work, I would have to bypass the
abstraction and handle the low level durable call myself. At that point I am
basically writing it by hand with a Tool plus an Action, which is what I can
already do today. And that brings me back to my first question.
GitHub link:
https://github.com/apache/flink-agents/discussions/909#discussioncomment-17846970
----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: [email protected]