diff options
Diffstat (limited to 'personal_agent/agent.py')
| -rw-r--r-- | personal_agent/agent.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/personal_agent/agent.py b/personal_agent/agent.py new file mode 100644 index 0000000..73cc422 --- /dev/null +++ b/personal_agent/agent.py @@ -0,0 +1,128 @@ +import json + +from personal_agent.context import ContextBuilder +from personal_agent.identity import AgentIdentity, UserProfile +from personal_agent.llm import LLM +from personal_agent.memory.models import WorkingMemory +from personal_agent.tools import ( + MemoryAddFactTool, + MemoryAddNoteTool, + MemoryRemoveFactTool, + MemoryRemoveNoteTool, + TaskStartTool, + ToolError, + ToolRegistry, +) + + +class Agent: + def __init__( + self, + llm: LLM, + agent_identity: AgentIdentity, + user_profile: UserProfile, + tools: ToolRegistry, + ) -> None: + self.llm = llm + self.tools = tools + + self.context = ContextBuilder( + agent_identity, + user_profile, + ) + + self.working_memory = WorkingMemory() + + self.tools.register(MemoryAddFactTool(self.working_memory)) + self.tools.register(MemoryRemoveFactTool(self.working_memory)) + self.tools.register(MemoryRemoveNoteTool(self.working_memory)) + self.tools.register(MemoryAddNoteTool(self.working_memory)) + self.tools.register(TaskStartTool(self.working_memory)) + + self.messages: list[dict[str, object]] = [] + + def chat(self, user_input: str) -> str: + print(f"\n[memory: {self.working_memory}]") + self.messages.append( + { + "role": "user", + "content": user_input, + } + ) + + for _ in range(5): + response = self.llm.chat( + self.context.build_messages( + self.messages, + self.working_memory, + ), + tools=self.tools.schemas(), + ) + + if response.tool_calls: + print(f"\n[tool call: {response.tool_calls}]\n") + + if not response.tool_calls: + answer = response.content or "" + + self.messages.append( + { + "role": "assistant", + "content": answer, + } + ) + + return answer + + self.messages.append( + { + "role": "assistant", + "content": response.content, + "tool_calls": [ + { + "id": call.id, + "type": "function", + "function": { + "name": call.name, + "arguments": json.dumps(call.arguments), + }, + } + for call in response.tool_calls + ], + } + ) + + for call in response.tool_calls: + tool = self.tools.get(call.name) + + if tool is None: + result = f"Unknown tool: {call.name}" + else: + try: + result = tool.execute(call.arguments).content + except ToolError as error: + result = f"Tool error: {error}" + + self.messages.append( + { + "role": "tool", + "tool_call_id": call.id, + "content": result, + } + ) + raise RuntimeError("Tool call limit exceeded") + + def end_task(self) -> None: + self.working_memory.end_task() + + def start_task(self, objective: str) -> None: + self.working_memory.start_task(objective) + + def pause_task(self) -> None: + self.working_memory.pause_task() + + def resume_task(self) -> None: + self.working_memory.resume_task() + + def complete_task(self) -> None: + self.working_memory.complete_task() |
