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]

Reply via email to