langgraph-codeact

repository·main·Indexed 20 days ago

https://github.com/langchain-ai/langgraph-codeact

A LangGraph implementation of the CodeAct architecture that enables LLMs to solve complex tasks by generating and executing Python code instead of relying on JSON function calls. It features persistent state for message history and variables, support for custom, LangChain, and MCP tools, and a customizable code execution sandbox API. The library provides utilities like create_codeact for graph initialization and create_default_prompt for system prompt generation.

Tokens
2.2K
Snippets
8
Records
9
Agent score
23%

What's inside langgraph-codeact

  1. What is CodeAct architecture?

    main

    CodeAct is an architecture that implements an alternative to standard JSON function-calling. Instead of relying solely on structured tool calls, it allows the model to use a Turing-complete programming language (like Python) to combine and transform tool outputs. This enables solving complex tasks in fewer steps by leveraging the full power of programming logic.

    Key features include:

    • Persistent State: Message history and Python variables are saved between turns, allowing for advanced follow-up questions.
    • Flexible Tooling: Supports custom tools, LangChain tools, and MCP tools.
    • Model Agnostic: Works with any model supported by LangChain (tested with Claude 3.7).
    • Customizable Sandbox: Allows you to provide your own code execution environment via a simple functional API.
  2. Implement a custom code sandbox

    main

    To use your own code execution environment, you must provide a function that accepts two arguments:

    1. code: The string of code to execute.
    2. _locals: A dictionary of locals to run the code in (this includes the provided tools and any variables set in previous turns).

    The function must return a tuple containing:

    • result: A string representing the output (e.g., stdout or an error message).
    • new_vars: A dictionary of new variables created during the execution of the code.

    Warning: For production environments, use a secure sandboxed environment (like langchain-sandbox) rather than eval() or exec().

    def eval(code: str, _locals: dict[str, Any]) -> tuple[str, dict[str, Any]]:
        original_keys = set(_locals.keys())
        try:
            with contextlib.redirect_stdout(io.StringIO()) as f:
                exec(code, builtins.__dict__, _locals)
            result = f.getvalue()
            if not result:
                result = "<code ran, no output printed to stdout>"
        except Exception as e:
            result = f"Error during execution: {repr(e)}"
    
        new_keys = set(_locals.keys()) - original_keys
        new_vars = {key: _locals[key] for key in new_keys}
        return result, new_vars
  3. Define tools for CodeAct

    main

    You can provide a list of tools to the CodeAct graph. These can be standard Python functions, LangChain tools, or MCP tools. In the example below, we define several math functions to be used by the agent.

    import math
    from langchain_core.tools import tool
    
    def add(a: float, b: float) -> float:
        """Add two numbers together."""
        return a + b
    
    def multiply(a: float, b: float) -> float:
        """Multiply two numbers together."""
        return a * b
    
    # ... other functions ...
    
    tools = [
        add,
        multiply,
        # ...
    ]
  4. Create and compile a CodeAct graph

    main

    Use create_codeact to initialize the graph with your model, tools, and sandbox function. You can then compile the graph using a checkpointer (like MemorySaver) to enable state persistence across turns.

    You can also customize the system prompt using the prompt argument in create_codeact.

    from langchain.chat_models import init_chat_model
    from langgraph_codeact import create_codeact
    from langgraph.checkpoint.memory import MemorySaver
    
    model = init_chat_model("claude-3-7-sonnet-latest", model_provider="anthropic")
    
    # Create the graph with model, tools, and the sandbox function
    code_act = create_codeact(model, tools, eval)
    
    # Compile with a checkpointer for memory
    agent = code_act.compile(checkpointer=MemorySaver())
  5. Run the CodeAct agent

    main

    You can interact with the compiled agent using .invoke() for a single final result or .stream() for token-by-token output. When streaming, you can specify stream_mode (e.g., ["values", "messages"]) to receive different types of updates.

    To maintain state across multiple interactions, provide a thread_id in the config object.

    messages = [{
        "role": "user",
        "content": "Your prompt here"
    }]
    
    for typ, chunk in agent.stream(
        {"messages": messages},
        stream_mode=["values", "messages"],
        config={"configurable": {"thread_id": 1}},
    ):
        if typ == "messages":
            print(chunk[0].content, end="")
        elif typ == "values":
            print("\n\n---answer---\n\n", chunk)
    }
  6. Create a CodeAct agent with `create_codeact`

    main

    The create_codeact function is the primary entry point for building a CodeAct agent. It returns a StateGraph that implements the CodeAct architecture: a loop between a model node (generating code) and a sandbox node (executing code).

    Arguments:

    • model (BaseChatModel): The language model used to generate code.
    • tools (Sequence[Union[StructuredTool, Callable]]): A list of tools available to the agent. You can pass raw Python functions or StructuredTool objects.
    • eval_fn (Union[EvalFunction, EvalCoroutine]): A function or coroutine that acts as your code sandbox. It must accept a code string and a locals dictionary, and return a tuple of (stdout_output, new_variables_dict).
    • prompt (Optional[str]): An optional custom system prompt. If omitted, create_default_prompt is used.
    • state_schema (StateSchemaType): The state schema to use (defaults to CodeActState).
    from langgraph_codeact import create_codeact
    
    def my_sandbox(code: str, context: dict) -> tuple[str, dict]:
        # Implementation of your sandbox
        # Returns (output_string, updated_context_dict)
        pass
    
    agent = create_codeact(
        model=chat_model,
        tools=[my_tool_func],
        eval_fn=my_sandbox
    )
  7. Define the CodeActState schema

    main

    The CodeActState class defines the state structure used by a CodeAct agent. It extends MessagesState and includes two additional fields:

    • script (Optional[str]): The Python code snippet currently being executed.
    • context (dict[str, Any]): A dictionary containing the execution context, including available tools and variables defined in previous steps.
    class CodeActState(MessagesState):
        script: Optional[str]
        context: dict[str, Any]
  8. Generate a default prompt with `create_default_prompt`

    main

    The create_default_prompt function generates a system prompt that instructs the LLM to output either Python code snippets (in fenced code blocks) or direct text responses. It automatically inspects the provided tools and includes their signatures and descriptions in the prompt so the model knows how to call them.

    Arguments:

    • tools: A list of tools (either as Python functions or StructuredTool instances).
    • base_prompt (Optional[str]): An optional string to prepend to the default prompt (e.g., "You are a helpful assistant.").
    from langgraph_codeact import create_default_prompt
    
    # Example usage
    custom_prompt = create_default_prompt(tools=[my_tool], base_prompt="You are a helpful assistant.")