System propt and action schema

This commit is contained in:
2024-11-02 21:25:36 +01:00
parent 2a15708145
commit 4a40eabe30
4 changed files with 187 additions and 249 deletions

View File

@@ -1,148 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Root element containing a sequence of actions to be executed -->
<xs:element name="actions"> <!-- Delete command removes an entry from working memory by its ID -->
<xs:element name="delete">
<xs:complexType> <xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:attribute name="id" type="xs:string" use="required"/>
<xs:element ref="start_container"/>
<xs:element ref="write_container_stdin"/>
<xs:element ref="read_container_stdout"/>
<xs:element ref="read_container_stderr"/>
<xs:element ref="wait_container"/>
<xs:element ref="set_model_path"/>
</xs:choice>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- <!-- Stop command terminates the agent gracefully -->
Start a new Docker container. <xs:element name="stop">
Containers can be started in two modes: <xs:complexType/>
- Short-running containers: </xs:element>
These are started with a timeout.
Their output is visible in the next iteration. <!-- Background script that runs continuously without blocking the agent
- Long-running containers: Output is captured and stored in working memory -->
These are started with a name. <xs:element name="background">
They are added to the container list. <xs:complexType mixed="true">
Their output is queried using the read_container_stdout and read_container_stderr actions.
Either a name or a timeout must be specified.
-->
<xs:element name="start_container">
<xs:complexType>
<!-- Docker image to use -->
<xs:attribute name="image" type="xs:string" use="required"/>
<!-- Container name for long-running containers -->
<xs:attribute name="name" type="xs:string" use="optional"/>
<!-- Timeout in milliseconds for short-running containers -->
<xs:attribute name="timeout" type="xs:integer" use="optional"/>
<xs:sequence> <xs:sequence>
<!-- Main command to run in container --> <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
<xs:element name="command" type="xs:string" minOccurs="0"/>
<!-- Command line arguments to the main command -->
<xs:element name="argument" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<!-- Volume mappings from host to container -->
<xs:element name="volumes" minOccurs="0">
<xs:complexType>
<xs:sequence>
<!--
Each volume maps host path to container path.
Syntax similar to -v switch of docker run command.
using : separator and optional :ro flag.
-->
<xs:element name="volume" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Port mappings from host to container -->
<xs:element name="ports" minOccurs="0">
<xs:complexType>
<xs:sequence>
<!-- Each port maps a host port to a container port -->
<xs:element name="port" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="host" type="xs:string" use="required"/>
<xs:attribute name="container" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Environment variables to set in the container -->
<xs:element name="environment" minOccurs="0">
<xs:complexType>
<xs:sequence>
<!-- Each variable has a name and value -->
<xs:element name="variable" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- Write data to a container's standard input --> <!-- Repeat script that runs on every iteration
<xs:element name="write_container_stdin"> Output is refreshed each time and stored in working memory
<xs:complexType> Useful for monitoring files or system state -->
<xs:simpleContent> <xs:element name="repeat">
<xs:extension base="xs:string"> <xs:complexType mixed="true">
<!-- Name of the target container --> <xs:sequence>
<xs:attribute name="container" type="xs:string" use="required"/> <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:extension> </xs:sequence>
</xs:simpleContent>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- Read from a container's standard output buffer --> <!-- Single shot script that runs once and completes
<xs:element name="read_container_stdout"> Output is stored in working memory until explicitly deleted
<xs:complexType> Used for one-time operations like file manipulation -->
<!-- Name of the container to read from --> <xs:element name="single_shot">
<xs:attribute name="container" type="xs:string" use="required"/> <xs:complexType mixed="true">
<!-- Optional number of bytes to read (-1 for all available) --> <xs:sequence>
<xs:attribute name="n" type="xs:integer" use="optional"/> <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- Read from a container's standard error buffer --> <!-- Reasoning documents the agent's thought process
<xs:element name="read_container_stderr"> Stored in working memory for context and learning
<xs:complexType> Can be referenced by future iterations to improve decisions -->
<!-- Name of the container to read from --> <xs:element name="reasoning">
<xs:attribute name="container" type="xs:string" use="required"/> <xs:complexType mixed="true">
<!-- Optional number of bytes to read (-1 for all available) --> <xs:sequence>
<xs:attribute name="n" type="xs:integer" use="optional"/> <xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- <!-- Read stdin command retrieves input from the IO buffer
Wait for a container to finish execution. Blocks until input is available
Standard output and standard error are merged and visible in the next iteration. Input is stored in working memory as a ReadEntry -->
--> <xs:element name="read_stdin">
<xs:element name="wait_container"> <xs:complexType/>
<xs:complexType> </xs:element>
<!-- Name of the container to wait for -->
<xs:attribute name="name" type="xs:string" use="required"/> <!-- Write stdout command sends output to the IO buffer
<!-- Maximum time to wait in milliseconds --> Output is stored in working memory as a WriteEntry
<xs:attribute name="timeout" type="xs:integer" use="required"/> Used for communicating with users or other processes -->
<xs:element name="write_stdout">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<!-- Switch to a different LLM model file --> </xs:schema>
<xs:element name="set_model_path">
<xs:complexType>
<xs:simpleContent>
<!-- Path to the model file to load -->
<xs:extension base="xs:string"/>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -12,40 +12,33 @@ from .response_parser import ResponseParser
async def run_web_server(): async def run_web_server():
"""Run the web server with default configuration.""" """Run the web server with default configuration."""
# Load system prompt and action schema
base_dir = Path(__file__).parent.parent base_dir = Path(__file__).parent.parent
with open(base_dir / "system_prompt.txt", "r") as f:
system_prompt = f.read() # Load system prompt and action schema
with open(base_dir / "action_schema.xsd", "r") as f: system_prompt = (base_dir / "system_prompt.txt").read_text()
action_schema = f.read() action_schema = (base_dir / "action_schema.xsd").read_text()
# Initialize core components # Initialize core components
llm = LlmEngine("/root/model") llm = LlmEngine("/root/model")
working_memory = WorkingMemory()
system_metrics = SystemMetrics()
validator = XMLValidator(action_schema)
io_buffer = WebIOBuffer() io_buffer = WebIOBuffer()
parser = ResponseParser(io_buffer)
# Create web agent
agent = WebAgent( agent = WebAgent(
system_prompt=system_prompt, system_prompt=system_prompt,
action_schema=action_schema, action_schema=action_schema,
working_memory=working_memory, working_memory=WorkingMemory(),
system_metrics=system_metrics, system_metrics=SystemMetrics(),
llm=llm, llm=llm,
validator=validator, validator=XMLValidator(action_schema),
parser=parser parser=ResponseParser(io_buffer)
) )
# Create and start web server # Start the web server
server = WebServer(agent, io_buffer, "0.0.0.0", 8080) server = WebServer(agent, io_buffer, "0.0.0.0", 8080)
await server.start() await server.start()
try: try:
while True: while True:
if server._response_tasks: if server._response_tasks:
done, pending = await asyncio.wait( done, _ = await asyncio.wait(
server._response_tasks, server._response_tasks,
timeout=0.1, timeout=0.1,
return_when=asyncio.FIRST_COMPLETED return_when=asyncio.FIRST_COMPLETED
@@ -53,9 +46,11 @@ async def run_web_server():
for task in done: for task in done:
try: try:
await task await task
except asyncio.CancelledError:
print("Task was cancelled")
except Exception as e: except Exception as e:
print(f"Task error: {e}") print(f"Task error: {e}")
server._response_tasks.remove(task) server._response_tasks.discard(task) # Use discard to prevent KeyError
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nShutting down...") print("\nShutting down...")
@@ -67,4 +62,4 @@ def main():
asyncio.run(run_web_server()) asyncio.run(run_web_server())
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,75 +1,88 @@
You are SIA, the self improving agent. You are SIA, the Self Improving Agent.
Reason about the context info you get and provide a list of actions to take. Your goal is to autonomously complete complex tasks by writing and executing scripts,
The actions are formatted as XML. constantly improving your approach through reasoning and modifications to your logic.
The closing tag is your last token. Each iteration, the context is updated with system metrics and the result of your previous actions.
Don't add a markdown code block around the actions. You modify the context by issuing a command using XML.
An example iteration looks like this: # Main Loop Explanation
Context The SIA agent operates in a loop where each cycle involves:
1. **Update Context:** Begin by updating the context with system metrics (CPU, memory, disk usage) and previously completed actions or reasoning.
2. **Generate Reasoning or Action:** Based on the current context, decide on your next step—either by reasoning through a decision or executing an action.
3. **Execute and update actions:** Execute the chosen action and update running processes
4. **Repeating the Cycle:** Continue this process iteratively, using the stored information to refine your responses.
# Structuring the Response
Your response is a single XML element.
It will be parsed so XML comments are removed.
# Examples of Using Actions
**Example 1: Using `<single_shot>`**
**Situation:** You need to download a file from the internet to analyze its content.
**Action:**
```xml ```xml
<context> <single_shot><![CDATA[curl -o /files/latest_data.csv http://example.com/data.csv]]></single_shot>
<system
time="2024-10-18T12:00:00Z"
cpu="12"
gpu="26"
memory_used="9556302234"
memory_total="17179869184"
disk_used="244434939904"
disk_total="273145991168"
context="3"
stdin="0"
/>
<containers/>
<previous>
<reasoning>
There is data available on the standard input channel. I should read it. I have no other running tasks to tend to.
</reasoning>
<actions>
<read_stdin n="42">
<![CDATA[Remind me to feed the cat tomorrow morning]]>
</read_stdin>
</actions>
</previous>
<files>
<file name="/" type="dir" index="0">
<![CDATA[
drwxr-xr-x 1 sia 197121 0 2024-10-16 23:02:16.486152500 +0200 tasks/
drwxr-xr-x 1 sia 197121 0 2024-10-16 22:35:31.806079500 +0200 user/
]]>
</file>
<file name="/tasks" type="dir" index="1">
</file>
<file name="/user" type="dir">
<![CDATA[
-rw-r--r-- 1 sia 197121 71 2024-10-16 22:41:23.223580300 +0200 general_info.txt
]]>
</file>
<file name="/user/general_info.txt" type="file" index="2">
<![CDATA[
Name: John (I don't know his last name)
Location: Somewhere in Belgium
]]>
</file>
</files>
</context>
``` ```
Response **Explanation:** A single-shot script is perfect here because you only need to execute this operation once to achieve the desired outcome.
**Example 2: Using `<repeat>`**
**Situation:** You are monitoring a log file for errors and want continuous updates.
**Action:**
```xml ```xml
John did not specify an exact time. I'll suggest 9am. He also did not specify how to be reminded. I'll ask but if he doesn't respond I'll assume a text message on standard output is fine. I'll write down this task in a file so I can keep it in context. I can write simple files with busybox:latest and echo but I will need to use sh -c to do the redirect. <repeat><![CDATA[tail -n 50 /var/log/app.log]]></repeat>
<actions>
<write_stdout message="I'll remind you to feed the cat tomorrow morning at 9am. Is a message on the standard output ok?"/>
<start_container image="busybox:latest" timeout="1000">
<command>sh</command>
<argtument>-c</argument>
<argument><![CDATA[echo 'Remind John to feed the cat on 2024-10-18T09:00:00+02:00. Use standard output.' > /tasks/reminder.txt]]></argument>
<volumes>
<volume>/tasks:/tasks</volume>
</volumes>
</start_container>
<monitor_file path="/tasks/reminder.txt"/>
</actions>
``` ```
These actions are available to you: **Explanation:** Repeat scripts are ideal for tasks that require constant awareness and updating, such as tracking log changes.
**Example 3: Using `<background>`**
**Situation:** Listening for incoming network messages that could come in at any time.
**Action:**
```xml
<background><![CDATA[nc -l 12345 | tee -a /logs/network_activity.log]]></background>
```
**Explanation:** Use a background process when waiting indefinitely for events without blocking other operations.
**Example 4: Using `<reasoning>`**
**Situation:** You have system input that needs processing; decide if further action is necessary.
**Reasoning:**
```xml
<reasoning>
I received a command which I processed successfully. No further action is needed.
</reasoning>
```
**Explanation:** Documenting reasoning helps track your decision-making process for future reference and learning.
# Access to Linux Environment
SIA has access to a Linux environment, which means you can leverage shell commands and scripts to perform tasks.
You can troubleshoot, monitor, or deploy resources efficiently using Linux command-line utilities within your `<single_shot>`, `<repeat>`, or `<background>` commands.
# Iterative Problem Solving
To solve problems iteratively, SIA uses a combination of reasoning and action storage:
- Keeps a clean context by keeping a record of tasks in files and folders
- Keep only the active task and plan to solve it in context
- Use previous iterations to assess what actions or reasoning led to successful outcomes.
- Remeber what time you started the task and keep a record of solutions you tried to avoid repeating keep track of progress
- Adjust your approach based on retrospective analysis, potentially altering future reasoning, script parameters
By maintaining a dynamic relationship between context and action, SIA can tackle increasingly complex challenges over time, adapting intelligently and autonomously.
# User interaction
Be a helpful assistant to the user.
Get to know them and make notes about them.
Open the relevant user notes when you interact with them.

View File

@@ -44,7 +44,7 @@ const SIAInterface = () => {
const [validationError, setValidationError] = useState(null); const [validationError, setValidationError] = useState(null);
// UI state // UI state
const [activeTab, setActiveTab] = useState('input'); const [activeTab, setActiveTab] = useState('original');
const [agentState, setAgentState] = useState(AgentState.CONTEXT_APPROVAL); const [agentState, setAgentState] = useState(AgentState.CONTEXT_APPROVAL);
const [wsState, setWsState] = useState(WebSocketState.DISCONNECTED); const [wsState, setWsState] = useState(WebSocketState.DISCONNECTED);
@@ -226,46 +226,11 @@ const SIAInterface = () => {
{/* Input/Response Tabs */} {/* Input/Response Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab}> <Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-3"> <TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="input">Input</TabsTrigger>
<TabsTrigger value="original">Original Response</TabsTrigger> <TabsTrigger value="original">Original Response</TabsTrigger>
<TabsTrigger value="modified">Modified Response</TabsTrigger> <TabsTrigger value="modified">Modified Response</TabsTrigger>
<TabsTrigger value="input">Input</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="input">
<Card>
<CardContent className="pt-6">
<div className="space-y-4">
<div className="h-[200px]">
<Editor
height="100%"
defaultLanguage="plaintext"
value={input}
onChange={setInput}
options={editorOptions}
/>
</div>
<div className="flex space-x-2">
<Button
onClick={() => handleSendInput(false)}
disabled={wsState !== WebSocketState.CONNECTED}
className="flex-1"
>
Send Input
</Button>
<Button
onClick={() => handleSendInput(true)}
variant="secondary"
disabled={wsState !== WebSocketState.CONNECTED}
className="flex-1"
>
Send & Read Stdin
</Button>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="original"> <TabsContent value="original">
<Card> <Card>
<CardContent className="pt-6"> <CardContent className="pt-6">
@@ -341,6 +306,42 @@ const SIAInterface = () => {
</CardContent> </CardContent>
</Card> </Card>
</TabsContent> </TabsContent>
<TabsContent value="input">
<Card>
<CardContent className="pt-6">
<div className="space-y-4">
<div className="h-[200px]">
<Editor
height="100%"
defaultLanguage="plaintext"
value={input}
onChange={setInput}
options={editorOptions}
/>
</div>
<div className="flex space-x-2">
<Button
onClick={() => handleSendInput(false)}
disabled={wsState !== WebSocketState.CONNECTED}
className="flex-1"
>
Send Input
</Button>
<Button
onClick={() => handleSendInput(true)}
variant="secondary"
disabled={wsState !== WebSocketState.CONNECTED}
className="flex-1"
>
Send & Read Stdin
</Button>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs> </Tabs>
{/* Output Display */} {/* Output Display */}