Water: an agent harness for Python that wraps your LLM agents instead of replacing them
The production-ready agent harness framework for Python
At a glance
- What is it?
- Water is a Python framework that supplies the orchestration, retries, approval gates and deployment scaffolding around agents you already have. It is a beta-stage project with a small API surface and a thin public track record.
- Who is it for?
- Adopt Water if you already have agent code you like and want a typed, async orchestration layer around it, and you can read the source when the README stops answering questions. Do not adopt it if you need a framework that owns prompting, memory and model abstraction end to end, or if you require a stable API.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 178 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 18, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What Water actually is, and the problem it picks up
Most teams writing agents spend their first weeks on the same non-agent work: sequencing steps, retrying a provider that returned a 429, trimming a conversation that outgrew its context window, and putting a human in front of anything that spends money or deletes data. Water positions itself as that layer and nothing more. The README states it plainly: it provides the infrastructure around your AI agents, not the agents themselves, and it works with LangChain, CrewAI, Agno, OpenAI, Anthropic or custom agents.
That framing is narrower than it sounds, and it is the reason the project is worth a look. If you have already written a working agent, the useful question is not which framework to rewrite it in. It is whether the surrounding plumbing has a shape you can reuse. Water's answer is a Flow object that composes tasks, plus a separate water.agents module for LLM-backed tasks, provider failover, tool execution and batch runs. The two halves are independent: you can use Flow with plain Python functions and never touch a model provider.
The intended user is a Python developer who is comfortable with asyncio and pydantic models and who wants typed inputs and outputs at each step. The pyproject.toml classifiers list Python 3.8 through 3.12 and mark the development status as Beta. That matches what the release history shows: v0.1.2 added the ReAct-style agentic loop, v0.1.3 added sub-agents, layered memory and semantic tool search, and v0.1.4 landed on 2026-03-24.
How a Water flow moves data between tasks
A task is a unit of work with a declared input schema, an output schema and an execute function. The execute function receives two arguments, params and context, and returns a dictionary. The README's quick start reads the incoming value as params["input_data"]["value"], which tells you the framework nests the flow's payload under an input_data key rather than passing it directly. That is a detail worth noticing early, because every task you write has to agree with it.
Flows are built with a fluent API. Sequential chaining uses then. Parallel execution uses parallel and merges the results. Branching takes a list of predicate-and-task pairs and routes on the first match. Loop repeats while a condition holds, with a max_iterations ceiling. Map runs one task per item in a list. Dag takes explicit dependencies as a mapping from task id to a list of prerequisite ids. There is also try_catch with try_tasks, catch_task and finally_task, and per-step modifiers: when for conditional execution and fallback for a substitute task.
Composition is handled by SubFlow, which wraps a flow as a task and remaps field names through input_mapping and output_mapping, and by compose_flows, which chains several flows into one pipeline with an id. The mapping dictionaries are the part that will bite you first: they rename keys, so a mismatch between an output_mapping value and the next task's expected field produces a runtime error rather than a type error.
The agents module layers on top. create_agent_task builds a prompt from a template and a provider instance. create_streaming_agent_task adds an on_chunk callback that receives deltas. create_agent_team coordinates AgentRole objects under a strategy of sequential, round_robin or dynamic. ToolExecutor runs a tool-calling loop with max_rounds. FallbackChain tries providers in order under first_success, round_robin or lowest_latency. ContextManager trims message lists with TruncationStrategy.SLIDING_WINDOW and a reserve_tokens buffer.
Installing water-ai and running a first flow
The package name on PyPI is water-ai, not water. The README gives a single install command and does not mention extras in the installation section, though pyproject.toml defines optional dependency groups named openai, anthropic, all and dev. Installing the base package pulls pydantic 2, fastapi and uvicorn, which is worth knowing if you only wanted the flow primitives.
pip install water-aiThe quick start defines a pydantic input model, a pydantic output model, and a plain function that returns a dictionary. The function signature is (params, context), and the value arrives under params["input_data"]. The task is created with create_task, passing id, description, input_schema, output_schema and execute.
from water import Flow, create_task
from pydantic import BaseModel
class NumberInput(BaseModel):
value: int
class NumberOutput(BaseModel):
result: int
def add_five(params, context):
return {"result": params["input_data"]["value"] + 5}
task = create_task(
id="add",
description="Add five",
input_schema=NumberInput,
output_schema=NumberOutput,
execute=add_five,
)The flow is assembled with then and finalized with register. The README calls register in the same expression that builds the flow, which suggests registration is required before the flow can run. Execution is asynchronous, so the run call sits inside an async function and is driven by asyncio.run.
import asyncio
flow = Flow(id="math", description="Math flow").then(task).register()
async def main():
result = await flow.run({"value": 10})
print(result)
asyncio.run(main())The README shows the expected output as {"result": 15}. If you get a KeyError on input_data instead, your execute function is reading the payload one level too high. If you get a validation error, the input_schema does not match the dictionary you passed to run.
Where the harness stops being enough
The sharpest limitation is one the project states about itself. Water is not an agent framework in the sense that LangChain or CrewAI are. It does not give you memory abstractions beyond what the agents module exposes, it does not give you a retriever or a vector store, and it does not give you a chain DSL for prompt composition. If you have not yet written an agent, Water gives you nothing to wrap. The README's own list of supported frameworks reads as a compatibility claim, not as a set of integrations it ships.
There are concrete gaps in the documentation as well. The README's section on approval gates is cut off mid-sentence in the published text, so the API for the feature that the topics list highlights is not described on the page. Sandboxing, observability and deployment tooling are named in the overview and in the pyproject keywords, but the README does not show a configuration for any of them. The README does not document rollback, versioning of registered flows, or what happens to in-flight tasks when a provider fails mid-loop.
Version status is the other constraint. The package is at 0.1.4 and classified as Beta. The API you write against today can change between minor releases, and the release notes show that it did: semantic tool search and layered memory arrived in 0.1.3, one release before the current one. Treat every import path as provisional.
The last push to the repository was on 2026-03-24, roughly six months before this writing. That is not an archived project, and the Apache-2.0 licence means you can fork it, but the cadence is not something to assume continues.
Water against LangGraph and CrewAI
The closest comparison is LangGraph, because both model work as a graph of steps with explicit edges. LangGraph centres on a state object that nodes read from and write to, and its checkpointer persists that state so a run can resume after a failure. Water centres on typed task inputs and outputs, and its composition vocabulary is broader in one direction: parallel, branch, loop, map, dag, try_catch and fallback are all first-class flow methods rather than patterns you assemble from nodes. The trade-off is state. Water's README does not describe a checkpointing or resume mechanism, so a long-running flow that dies halfway is a flow you rerun.
CrewAI is a different shape entirely. It organises work around roles, goals and tasks assigned to agents, and the framework decides much of the interaction. Water's create_agent_team exposes a similar idea through AgentRole and a strategy of sequential, round_robin or dynamic, but the team is one construct inside a larger orchestration library rather than the organising principle. If your problem is genuinely multi-agent collaboration with emergent delegation, CrewAI's model fits more directly. If your problem is a pipeline with a handful of LLM calls, retries and a human approval step, Water's flow methods describe it more literally.
A plain asyncio script is the honest third option, and for a two-step pipeline it is the right one. Water earns its dependency footprint when you need branch, loop, fallback and try_catch semantics that you would otherwise hand-roll, and when you want the same shape across several pipelines.
Licence, upgrade cost and what a version bump costs you
Water is Apache-2.0, and pyproject.toml declares the matching classifier. That permits commercial use, modification and redistribution, and it includes a patent grant. It does not require you to publish your own source. This is a description of the licence text, not legal advice; if you are redistributing the package inside a product, have counsel read the notice and attribution requirements.
The practical upgrade cost comes from two places. First, the dependency floor: pydantic>=2.0.0, fastapi>=0.104.0 and uvicorn[standard]>=0.24.0 are installed even if you only want Flow. FastAPI and uvicorn are the deployment side of the harness, so a team that already runs a different ASGI stack will be carrying a second one. Second, the version range: the classifiers claim Python 3.8 support while the dependency list requires pydantic 2, and pydantic 2 dropped Python 3.7 but supports 3.8, so the claim is not obviously wrong, yet it is a combination worth testing on your own interpreter before you pin it.
Upgrades between 0.1.x releases should be treated as potentially breaking. The gap between v0.1.2 and v0.1.3 added sub-agents, layered memory and semantic tool search, which is a large surface for a single minor version. Pin an exact version in your lockfile and read the release notes before moving.
Editorial conclusion
Adopt Water if you already have agent code you like and want a typed, async orchestration layer around it, and you can read the source when the README stops answering questions. Do not adopt it if you need a framework that owns prompting, memory and model abstraction end to end, or if you require a stable API. Before committing, verify the approval gate and sandboxing APIs in the water/ package, check that the pinned pydantic, fastapi and uvicorn versions resolve against your environment, and confirm which Python versions you actually need to support, since the package declares 3.8 while depending on pydantic 2.
Frequently asked questions
What is Water, the agent harness framework for Python?
Water is a Python package published as water-ai that provides orchestration, retries, provider failover, tool execution and deployment scaffolding around AI agents. The README describes it as the infrastructure around your agents, not the agents themselves, and states it works with LangChain, CrewAI, Agno, OpenAI, Anthropic or custom agents.
How do I install Water?
The README gives a single command, pip install water-ai. The base install pulls pydantic 2, fastapi and uvicorn; pyproject.toml also defines optional dependency groups named openai, anthropic, all and dev.
Does Water work with LangChain, CrewAI or Agno?
The README states that Water works with any agent framework, naming LangChain, CrewAI, Agno, OpenAI, Anthropic and custom agents. Because Water supplies the surrounding orchestration rather than the agent itself, the integration point is the task you wrap, not an adapter the project ships.
Community notes