From: aidotengineer

Building reliable AI systems, especially those interacting directly with end-users and relying on large language models (LLMs) in production, presents significant challenges due to uncertain conditions [00:00:07]. Effect, a TypeScript library, is designed to manage this complexity by enabling the creation of robust, type-safe, and composable systems [00:00:16].

Why Effect for AI and LLM Systems?

While the TypeScript language provides a strong foundation, it falls short when dealing with specific challenges inherent in AI system development [00:00:26]. These challenges include:

Effect provides the necessary tools to confidently handle these situations as AI platforms evolve [00:00:36].

Key Features of Effect for Building Reliable AI Systems

Effect offers several features that contribute to the reliability and maintainability of AI systems:

  • Strong type guarantees across the stack [00:00:44]
  • Powerful composition primitives [00:00:48]
  • Built-in concurrency, streaming, interruptions, and retry mechanisms [00:00:50]
  • Structured error modeling [00:00:54]
  • A clean dependency injection system that facilitates easier testing and modernization [00:00:57]
  • Easy observability via OpenTelemetry [00:01:02]

These benefits of using Effect in TypeScript for AI and LLM systems lead to more stable, testable, and maintainable code at scale [00:01:07]. Effect can also be gradually adopted into existing codebases, feeling like a natural extension of TypeScript [00:01:14].

Architecture and Implementation at 14.ai

At 14.ai, Effect is used across the entire stack for their AI-native customer support platform [00:01:23]:

  • Internal RPC Server: Handles application logic, built on Effect RPC and a modified TanStack Query [00:01:39].
  • Public API Server: Utilizes Effect HTTP with auto-generated OpenAPI documentation from annotated schemas [00:01:47].
  • Data Processing Engine: Syncs and processes data from CRM, documents, and databases for real-time analytics and reporting [00:01:53].
  • Agent Workflows: Written in a custom DSL built on Effect, allowing for a mix of deterministic and non-deterministic behavior [00:02:02].
  • Database: PostgreSQL is used for both data and vector storage, with Effect SQL handling queries [00:02:09].
  • Effect Schemas: Model everything, providing runtime validation, encoding/decoding, type-safe input/output handling, and auto-generated documentation [00:02:15].

Agent Design and Workflow with Effect

AI agents at 14.ai are essentially planners that take user input, devise a plan, choose appropriate actions, workflows, or sub-agents, execute them, and repeat until a task is complete [00:02:28].

  • Actions: Small, focused units of execution, such as fetching payment information or searching logs, comparable to tool calls [00:02:40].
  • Workflows: Deterministic multi-step processes, like cancelling a subscription, which might involve collecting reasons, offering retention options, checking eligibility, and then performing the cancellation [00:02:51].
  • Sub-agents: Group related actions and workflows into larger domain-specific modules, such as a billing agent or a log retrieval agent [00:03:03].

To model this complexity, a domain-specific language (DSL) for workflows was built using Effect’s functional programming principles (pipe-based system) as the foundation [00:03:13]. This allows for clear and composable expression of branching, sequencing, retries, state transitions, and memory [00:03:22].

Reliability Mechanisms

For mission-critical systems, reliability is paramount [00:03:32]:

  • LLM Provider Fallbacks: If one LLM provider fails, the system automatically falls back to another with similar performance characteristics (e.g., GPT-4 mini to GD Flash 2.0 for tool calling) [00:03:36].
  • Retry Policies: Modeled with Effect, these policies track state to avoid retrying failed providers [00:03:47].
  • Token Stream Duplication: For streamed answers to end-users, token streams are duplicated, sending one directly to the user and another for internal storage (e.g., analytics) [00:03:55]. Effect facilitates this process [00:04:05].

Testing and Developer Experience

Effect significantly enhances testing and developer experience:

  • Dependency Injection (DI): Heavily used for mocking LLM providers and simulating failure scenarios [00:04:09]. This approach allows swapping service providers with mock versions without affecting system internals [00:04:14].
  • Schema-Centric Design: Input, output, and error types are defined upfront with powerful encoding/decoding built-in [00:04:32]. This provides strong type safety and automatically documented schemas [00:04:40].
  • Modular and Composable Services: Services are provided at the entry point of systems, allowing for easy composition, overriding behavior, or swapping implementations [00:04:50]. Dependencies are guaranteed at compile time via the type level [00:04:59].
  • Strong Guard Rails: Effect helps prevent common mistakes, allowing engineers new to TypeScript to become productive quickly [00:05:14]. Once past the initial learning curve, it becomes harder to fall into bad patterns [00:05:23].

Lessons Learned

While powerful, using Effect effectively requires discipline [00:05:31].

  • Error Handling: It’s easy to accidentally catch errors upstream or out of sight, silently losing important failures if not careful [00:05:46].
  • Dependency Injection at Scale: While great in principle, tracing service provision, especially across multiple layers or subsystems, can become challenging to follow [00:05:57].
  • Learning Curve: Effect has a significant ecosystem of concepts and tools that can be overwhelming initially [00:06:10]. However, once the initial hurdle is overcome, the benefits compound [00:06:17].

Ultimately, Effect helps build scalable AI systems that are predictable and resilient, but it is not magic and still requires careful thought [00:06:27].

Conclusion

Effect allows for incremental adoption, starting with a single service or endpoint [00:06:37]. It is particularly useful for LLM and AI-based systems where reliability and coping with non-determinism are crucial [00:06:47]. Effect provides tools for predictable and observable systems [00:06:53].

It brings the rigor of functional programming into real-world TypeScript in a practical way for production use [00:07:01]. One does not need to be a functional programming purist to gain significant value; starting small allows benefits to build up over time [00:07:07].