Agent Environment Protocol - 基于文件系统的 Agent 管理协议
Project description
AEP - Agent Environment Protocol
A file-system-first terminal environment for LLM agents.
Manage capabilities with Profile; run session-bound tools with AEP.
Why AEP?
AEP is designed around one assumption: an agent already knows how to work in a terminal. Instead of stuffing every capability into prompt text or forcing everything through remote wrappers, AEP mounts capabilities into a workspace and gives the host a small runtime API.
It keeps three capability categories:
tools/: a shared Python tool environment, invoked throughtools runskills/: isolated skill packages, invoked throughskills runlibrary/: tree-structured reference documents with generatedindex.md
The public Python surface is intentionally small:
Profile: add tools, skills, library docs, MCP config, then generate indexesAEP: mount one profile into one workspace, build agent context, expose tool schemas for one logical session, route tool calls for that sessionToolSchemaBinding: lightweightsession_id + schemaspair returned byAEP.tool_schemas(session_id)
Architecture
Current structure is split into two public surfaces:
Profile: resource management and indexingAEP: runtime mounting, agent context generation, and session-bound tool execution
Internal implementation is organized under:
src/aep/runtime/: runtime facade, context rendering, session runtime, tool-call dispatchersrc/aep/capability/: tools, skills, library, indexingsrc/aep/profile.py: public config facade
Profile directory layout
config_dir/
├── tools/
│ ├── .venv/
│ ├── requirements.txt
│ ├── index.md
│ └── *.py
├── skills/
│ ├── index.md
│ └── <skill-name>/
│ ├── .venv/
│ ├── SKILL.md
│ ├── requirements.txt
│ └── scripts/...
├── library/
│ ├── index.md
│ └── ...
└── _mcp/
└── <server>/config.json
API Summary
from aep import AEP, Profile, ToolSchemaBinding
Profile(config_dir, ...)Profile.add_tool(...)Profile.add_skill(...)Profile.add_library(...)Profile.add_mcp_server(...)Profile.index()AEP(config_or_profile, workspace=..., agent_dir=".agents")AEP.build_context()AEP.tool_schemas(session_id) -> ToolSchemaBindingAEP.call_tool(session_id, name, arguments)AEP.detach()
tool_schemas(session_id) returns:
ToolSchemaBinding(
session_id="agent-1",
schemas=[...],
)
schemas is what you send to the model. session_id stays on the host side and is used to route later tool calls back into the correct logical session.
Quick Start
Installation
Once published, install via pip:
pip install agent-env-protocol
For local development:
git clone https://github.com/Slipstream-Max/Agent-Environment-Protocol
cd Agent-Environment-Protocol
uv sync --extra dev
CLI
# 1. Create or update a profile
aep index --profile ./agent_config
# 2. Add capabilities
aep tool add ./examples/calc.py --name calc --profile ./agent_config
aep skill add ./examples/greeter --name greeter --profile ./agent_config
aep library add ./docs/guide.md --target-dir intro --profile ./agent_config
aep index --profile ./agent_config
# 3. Start a shell inside one workspace
aep shell --profile ./agent_config --workspace ./workspace
# Inside the shell:
# > tools list
# > tools run "tools.calc.add(1, 2)"
# > tools run PY<<
# > import pandas as pd
# > print(tools.calc.add(1, 2))
# > PY
# > skills run greeter/main.py
Minimal Python API
import asyncio
from aep import AEP, Profile
async def main() -> None:
profile = Profile("./agent_capabilities")
profile.index()
aep = AEP(profile, workspace="./my_project")
context = aep.build_context()
binding = aep.tool_schemas("agent-main")
print(context)
print(binding.session_id)
print(binding.schemas)
result = await aep.call_tool(
binding.session_id,
"aep_exec",
{"command": "tools list"},
)
print(result)
aep.detach()
asyncio.run(main())
Agent Context
AEP.build_context() returns one prompt block for the model. It contains:
- an introduction explaining that this is an agent terminal environment
- the exposed tool schemas and what each one does
- the special command conventions:
tools run "...",tools run PY<< ... PY, andskills run ... - the generated indexes for the current profile
This keeps prompt construction centralized in one place instead of scattering it between profile and adapters.
OpenAI SDK Integration
import asyncio
import json
from dataclasses import dataclass
from openai import AsyncOpenAI
from aep import AEP, Profile
@dataclass
class AgentState:
session_id: str
tools: list[dict]
messages: list[dict]
def parse_arguments(raw: str | None) -> dict:
if not raw:
return {}
value = json.loads(raw)
return value if isinstance(value, dict) else {}
async def run_turn(
*,
client: AsyncOpenAI,
model: str,
aep: AEP,
agent: AgentState,
user_text: str,
) -> None:
agent.messages.append({"role": "user", "content": user_text})
while True:
response = await client.chat.completions.create(
model=model,
messages=agent.messages,
tools=agent.tools,
tool_choice="auto",
)
message = response.choices[0].message
agent.messages.append(
{
"role": "assistant",
"content": message.content or "",
"tool_calls": [
{
"id": call.id,
"type": call.type,
"function": {
"name": call.function.name,
"arguments": call.function.arguments,
},
}
for call in (message.tool_calls or [])
],
}
)
if not message.tool_calls:
return
for call in message.tool_calls:
result = await aep.call_tool(
agent.session_id,
call.function.name,
parse_arguments(call.function.arguments),
)
agent.messages.append(
{
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
}
)
async def main() -> None:
profile = Profile("./config")
profile.index()
aep = AEP(profile, workspace="./workspace")
context = aep.build_context()
binding1 = aep.tool_schemas("agent-1-session")
binding2 = aep.tool_schemas("agent-2-session")
system_prompt = (
"You are working inside an AEP terminal environment.\n\n"
f"{context}"
)
agent1 = AgentState(
session_id=binding1.session_id,
tools=binding1.schemas,
messages=[{"role": "system", "content": system_prompt}],
)
agent2 = AgentState(
session_id=binding2.session_id,
tools=binding2.schemas,
messages=[{"role": "system", "content": system_prompt}],
)
client = AsyncOpenAI()
await run_turn(
client=client,
model="gpt-4.1-mini",
aep=aep,
agent=agent1,
user_text="Run `pwd` and then export A=1.",
)
await run_turn(
client=client,
model="gpt-4.1-mini",
aep=aep,
agent=agent2,
user_text="Run `pwd` and then export B=2.",
)
env1 = await aep.call_tool(agent1.session_id, "aep_env", {})
env2 = await aep.call_tool(agent2.session_id, "aep_env", {})
print(env1)
print(env2)
aep.detach()
if __name__ == "__main__":
asyncio.run(main())
Each agent gets the same tool names, but a different host-side session_id. Session isolation is explicit on the host and invisible to the model.
Tool Schemas
The default tool surface is:
aep_exec: execute one command in the current bound sessionaep_output: fetch incremental output for one executionaep_kill: stop a queued or running executionaep_history: inspect recent command historyaep_env: inspect custom session environment variables
References
- Detailed API docs:
docs/api.md - Architecture notes:
docs/arch.md
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agent_env_protocol-0.2.0.tar.gz.
File metadata
- Download URL: agent_env_protocol-0.2.0.tar.gz
- Upload date:
- Size: 113.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67334ca23d41d344f6c5bad996c7d99c19a6a63ffcc9f5458f7efd6470031515
|
|
| MD5 |
6e9a59f7083dfd268f1dc1cb7122d62b
|
|
| BLAKE2b-256 |
cef2b529327ca2c759fb06b2a8764308fe0dc990c68de01decb5e94a6de549c6
|
Provenance
The following attestation bundles were made for agent_env_protocol-0.2.0.tar.gz:
Publisher:
publish.yml on Slipstream-Max/Agent-Environment-Protocol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_env_protocol-0.2.0.tar.gz -
Subject digest:
67334ca23d41d344f6c5bad996c7d99c19a6a63ffcc9f5458f7efd6470031515 - Sigstore transparency entry: 1059630396
- Sigstore integration time:
-
Permalink:
Slipstream-Max/Agent-Environment-Protocol@61024537c6746b82bf8718ced414318708f7db19 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Slipstream-Max
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@61024537c6746b82bf8718ced414318708f7db19 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agent_env_protocol-0.2.0-py3-none-any.whl.
File metadata
- Download URL: agent_env_protocol-0.2.0-py3-none-any.whl
- Upload date:
- Size: 52.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7aca6fade2a75311f2c0bb52add5d8824b43a92d8500fa39a7ee4d5a36a10b25
|
|
| MD5 |
f8160019838fa8017c44dbd9f896be90
|
|
| BLAKE2b-256 |
9c56ac3e8a1e14a425ec3cc94a4e29e0004ff0b53b2de3f26f63fa3808dae526
|
Provenance
The following attestation bundles were made for agent_env_protocol-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on Slipstream-Max/Agent-Environment-Protocol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agent_env_protocol-0.2.0-py3-none-any.whl -
Subject digest:
7aca6fade2a75311f2c0bb52add5d8824b43a92d8500fa39a7ee4d5a36a10b25 - Sigstore transparency entry: 1059630405
- Sigstore integration time:
-
Permalink:
Slipstream-Max/Agent-Environment-Protocol@61024537c6746b82bf8718ced414318708f7db19 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Slipstream-Max
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@61024537c6746b82bf8718ced414318708f7db19 -
Trigger Event:
push
-
Statement type: