-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to send and receive messages interactively #142
Comments
Can Space provide a method : |
This was a fundamental change and I'm glad you're bringing it up for discussion. I probably should've explained more of the reasoning in the release notes. To put it briefly, instantiating the agent in the main process prevents multiprocessing or other concurrent processing approaches that do not share a memory space with the main process. References to local objects are not possible across processes. So the API needed to change to where an Agent is instantiated within its own process and communicates entirely via incoming and outgoing message queues. If you look at the Space implementations, you'll see that they now provide a queue type that its agents use for communication. The good thing is that by abstracting communication to rely solely on queues, we allow more possibilities for distributed processing. For example, one could create a new space type to distribute agents using a background job processing framework like Celery. In a situation like that it's even more clear that memory cannot be shared directly between the agent instances and the main application that launches them. This will also be the case when Javascript based agents are introduced. So I do think that this is a better abstraction moving forward, and as you mentioned it's consistent with other Actor model implementations. That said, I agree that it makes certain things harder to do, like inspecting or altering an agent's state from another process. This is a common enough need that I'd like to explore how we might make this easier. I'm not sure yet what the best solution is. I love your idea of creating a "proxy" agent that you can use to interact with an agent instance. I think something like that is a real possibility and it would help greatly with testing and debugging. The I have a rough idea right now that might work reasonably well. I have it in mind to explore the idea of a common Sending this space._route({
"to": "my_agent",
"action": {
"name": "eval",
"args": {
"code": "print(self.my_property)"
}
}
}) Notice that we call The new The limitations would be that arguments and return values need to be serializable. You also wouldn't be able to return a reference to an internal variable that you can modify directly. I'm curious to hear your thoughts on all this. Does the reasoning for the API change make sense? And would something like the above meet your needs? |
Thank you for your detailed explanation. I basically agree with all your views and decisions here.
Architecturally, this is a very clear and thorough design. It's very much in line with the actor's philosophy. I read the implementation details and I like the design of multiprocess_space.py as you said
Should AMQPSpace inherit MultiprocessSpace? It seems better if _AgentAMQPProcess only exposes inbound_queue and outbound_queue, just like _AgentProcess does. Conceptually, it seems that we should move
Completely agree 😄
I think there might be two different requirement here:
Sounds great! "Code execution (interpreter) support" seems likely to satisfy both of the above needs. Looking forward to seeing this prototype soon! |
I agree. I plan to clean up the implementations as soon as possible. There's some obvious duplication.
That could be a way to do it, yes. I'll keep this in mind incase we find more reason to make that change.
Thanks! I can't say it'll be "soon" but definitely as soon as possible. The React app rewrite and JS client will take some time. But it should be easy enough to add an |
Regarding Jupyter's approach seems worth learning (Messaging in Jupyter), which uses msg_id for both request and send: each message has a uuid as msg_id by default. When the sender of the message receives the same msg_id, it knows that this is the return value (response), which is synchronous mode, if it does not care whether there is a return value, it is asynchronous mode. My SyncAgent basically adopts this idea. This brings the following benefits:
These places seem to be simpler and more consistent if we adopt a similar approach to jupyter. |
@wwj718 thanks for this feedback. I agree with your points.
[edit] I paused working on a PR. See below. |
Okay now I remember why I separated the This problem stems from wanting to return the return value from an action. It comes down to the fact that in python, a method will have a return value of You can see where I deal with this issue on this line. There are different ways we can handle this. A couple possibilities:
Regardless, I agree that we should probably get rid of the I'm not sure I agree with re-using the same Looking at the jupyter documentation, it looks like the way they deal with this is to include the entire "parent header" which itself contains the original message id. So while we're not returning the entire header, the What do you think? I can work on a PR after we settle on what we'd like to do. |
It seems meaningful to return return_value by default. When return_value is None, None still carries information: it means that the action is completed. On the sender side, it decides whether to utilize this information: when it has request requirements, it can process this information, and when it does not care, it ignores it. From an encapsulation perspective, this knowledge is only retained inside the sender object.
Alan Kay said optimization is sweet trouble. Smalltalk is completely built on the idea that "everything is an object; objects communicate through messages; objects interpret messages that they understand." Within the language, hundreds of thousands of objects may be born and die every second, and millions of messages flow between objects. .Performance won't be an issue, and if we encounter one, the potential for optimization is huge.
It may be better to separate the semantics of the request from the semantics of the message id (separation of concerns). As you said "We might want to be more explicit about the expectation here". Such as we can add a request parameter in action.args
It's a bit like the early days of agency. |
By switching rabbitmq to a message delivery system like Syndicate , performance can be greatly improved, and its performance is even enough to support the infrastructure of the operating system. |
woah. This is seriously great feedback! I think you nailed it.
Great point. It keeps it really simple and yeah.. it does feel like I'm prematurely optimizing. Regarding Syndicate. I'm extremely interested. I wasn't aware of it. I was wondering that I can't be the first to want to build a language spanning actor model system, and there it is... And it already supports python and TypeScript! I don't think I'm going to switch gears just yet, but I want to look into using Syndicate or other transport technologies pretty soon. I think after the React/JS updates, I'll be taking some time to reconsider architecture and tech. We're still in an experimental phase, but after some front-end tech is in place, then the broader shape of the framework is largely there, and I think that'll be a good time to reconsider technologies. I'm going to put together a PR to clean up the request semantics to always return the response. I'll ping you to see if you want to take a look when it's ready. In the future, I'm thinking that whenever bigger changes are coming, I'll make sure to put up a PR and leave it open for feedback for at least a day or two before merging. I think that'll be a better practice so that we can iron out issues like this before a release. @wwj718 I really appreciate the feedback you've given here. You have a really great perspective and I'm so thankful you're thinking about this critically. I need that! :) |
I'm so happy that we finally reached a consensus on this topic.
I very much agree with your considerations. At this stage, we pay more attention to the clarity of structure and concepts. It is easy to replace partial components.
I'm always willing to participate in discussions 😄 |
I'm in the middle of working on the request/response changes and I just wanted to share an idea I had regarding the other topic in this thread, the idea of a proxy agent. While I think the So with this idea you could create a sort of "foreground" agent, and this would allow you to create applications that require interaction in the foreground, like a terminal process or something like that. It should work with the python repl as well. I realize that running all agents in the background has made certain things difficult or even impossible. This approach would have made the Gradio app changes easier and I wouldn't have had to disable functionality, so I think it has real use cases. I'll spend some time exploring this after the request changes. |
PR #144 is up to improve the request behavior. I'm giving it a day or so to wait for feedback if anyone's inclined. |
In previous versions, an agent was instantiated and then added to the space. The agent could be referenced from outside the space. We could "hold" a agent in an environment like Jupyter and interact with other agents through it. In the latest version, space is responsible for instantiating the agent. We can't seem to get a reference to an agent. How to interactively communicate with other agents in the space?
Another benefit of referencing the agent is that it can be programmed dynamically, which seems to be helpful for early exploration.
Isolating the agent in a space is very consistent with actor-style programming. Actors can only communicate with each other through messages and cannot call functions from each other.
In erlang/elixer, I remember that users can still send messages to specific actors in the repl environment.
The text was updated successfully, but these errors were encountered: