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"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- Root element containing a sequence of actions to be executed -->
<xs:element name="actions">
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Delete command removes an entry from working memory by its ID -->
<xs:element name="delete">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<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:attribute name="id" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<!--
Start a new Docker container.
Containers can be started in two modes:
- Short-running containers:
These are started with a timeout.
Their output is visible in the next iteration.
- Long-running containers:
These are started with a name.
They are added to the container list.
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"/>
<!-- Stop command terminates the agent gracefully -->
<xs:element name="stop">
<xs:complexType/>
</xs:element>
<!-- Background script that runs continuously without blocking the agent
Output is captured and stored in working memory -->
<xs:element name="background">
<xs:complexType mixed="true">
<xs:sequence>
<!-- Main command to run in container -->
<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:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Write data to a container's standard input -->
<xs:element name="write_container_stdin">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<!-- Name of the target container -->
<xs:attribute name="container" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
<!-- Repeat script that runs on every iteration
Output is refreshed each time and stored in working memory
Useful for monitoring files or system state -->
<xs:element name="repeat">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Read from a container's standard output buffer -->
<xs:element name="read_container_stdout">
<xs:complexType>
<!-- Name of the container to read from -->
<xs:attribute name="container" type="xs:string" use="required"/>
<!-- Optional number of bytes to read (-1 for all available) -->
<xs:attribute name="n" type="xs:integer" use="optional"/>
<!-- Single shot script that runs once and completes
Output is stored in working memory until explicitly deleted
Used for one-time operations like file manipulation -->
<xs:element name="single_shot">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Read from a container's standard error buffer -->
<xs:element name="read_container_stderr">
<xs:complexType>
<!-- Name of the container to read from -->
<xs:attribute name="container" type="xs:string" use="required"/>
<!-- Optional number of bytes to read (-1 for all available) -->
<xs:attribute name="n" type="xs:integer" use="optional"/>
<!-- Reasoning documents the agent's thought process
Stored in working memory for context and learning
Can be referenced by future iterations to improve decisions -->
<xs:element name="reasoning">
<xs:complexType mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!--
Wait for a container to finish execution.
Standard output and standard error are merged and visible in the next iteration.
-->
<xs:element name="wait_container">
<xs:complexType>
<!-- Name of the container to wait for -->
<xs:attribute name="name" type="xs:string" use="required"/>
<!-- Maximum time to wait in milliseconds -->
<xs:attribute name="timeout" type="xs:integer" use="required"/>
<!-- Read stdin command retrieves input from the IO buffer
Blocks until input is available
Input is stored in working memory as a ReadEntry -->
<xs:element name="read_stdin">
<xs:complexType/>
</xs:element>
<!-- Write stdout command sends output to the IO buffer
Output is stored in working memory as a WriteEntry
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:element>
<!-- Switch to a different LLM model file -->
<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>
</xs:schema>

View File

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

View File

@@ -1,75 +1,88 @@
You are SIA, the self improving agent.
Reason about the context info you get and provide a list of actions to take.
The actions are formatted as XML.
The closing tag is your last token.
Don't add a markdown code block around the actions.
You are SIA, the Self Improving Agent.
Your goal is to autonomously complete complex tasks by writing and executing scripts,
constantly improving your approach through reasoning and modifications to your logic.
Each iteration, the context is updated with system metrics and the result of your previous 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
<context>
<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>
<single_shot><![CDATA[curl -o /files/latest_data.csv http://example.com/data.csv]]></single_shot>
```
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
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.
<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>
<repeat><![CDATA[tail -n 50 /var/log/app.log]]></repeat>
```
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);
// UI state
const [activeTab, setActiveTab] = useState('input');
const [activeTab, setActiveTab] = useState('original');
const [agentState, setAgentState] = useState(AgentState.CONTEXT_APPROVAL);
const [wsState, setWsState] = useState(WebSocketState.DISCONNECTED);
@@ -226,46 +226,11 @@ const SIAInterface = () => {
{/* Input/Response Tabs */}
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="input">Input</TabsTrigger>
<TabsTrigger value="original">Original Response</TabsTrigger>
<TabsTrigger value="modified">Modified Response</TabsTrigger>
<TabsTrigger value="input">Input</TabsTrigger>
</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">
<Card>
<CardContent className="pt-6">
@@ -341,6 +306,42 @@ const SIAInterface = () => {
</CardContent>
</Card>
</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>
{/* Output Display */}