Skip to main content

A lightweight node-flow runtime for building tool-using agents. Import name: agent_core.

Project description

Agent Core Runtime

PyPI CI License: MIT Python

Agent Core Runtime is a small Python runtime for building tool-using agents from a few explicit pieces: Node, Flow, RunContext, Tool, and Agent.

Chinese README · API Reference · Design · Changelog · Contributing

Why This Exists

The runtime is meant to be easy to read and easy to replace:

  • Node is one unit of work.
  • Flow connects nodes by action names.
  • Agent is also a Node, so an agent can run alone or sit inside a larger flow.
  • RunContext carries scoped messages, one event stream, metadata, artifacts, and cumulative model usage for one run.
  • payload carries explicit business data between nodes and is returned by the flow.
  • @tool turns typed Python functions into OpenAI-compatible tool schemas.
  • LLM is the default OpenAI-compatible model adapter. Configuration comes from constructor arguments or the process environment.

You can build a normal chat agent in one declaration, or wire your own flow when the loop needs custom logic.

Scope and Boundary

The runtime's story is deliberately small: it is the minimal unit that runs one agent, and the same pieces compose into workflows and multi-agent systems because Agent is a Node. It owns execution — flows, model and tool calls, and the per-run RunContext.

Everything that makes a product an agent product lives above it, in a harness: prompt layering, session persistence, context compaction, memory, permissions, verification loops, and user surfaces. Friday is one such harness; its architecture doc describes this boundary from the consumer side.

The contract for harness authors: drive the runtime through its public API — compose flows from published nodes, run them via Agent, and use RunContext through add_message / get_messages / metadata / artifacts / emit, usage snapshots, and the event subscriptions. When conversation history must change (compaction, resume), build a fresh context and replay messages through that API instead of editing the runtime's internal bookkeeping.

Runtime Shape

flowchart TD
    App["Application"] --> Agent["Agent"]
    Agent -->|"direct chat"| BuiltIn["built-in model/tool loop"]
    Agent -->|"custom"| Flow["Flow"]
    Agent -. "Agent is a Node" .-> OuterFlow["Another Flow"]

    Flow --> Data["payload"]
    Data --> Node["Node"]
    Node -->|"action + payload"| Next["Next Node"]
    Next --> Node

    Flow -. "runtime context" .-> Context["RunContext"]
    Context --> Messages["messages"]
    Context --> Events["events"]
    Context --> Usage["usage"]
    Context --> Artifacts["artifacts"]
    Context --> Metadata["metadata"]

    BuiltIn --> ModelNode["ModelNode"]
    ModelNode --> ChatModel["ChatModel"]
    ChatModel --> OpenAI["OpenAI-compatible API"]
    ModelNode --> Router["ToolRouterNode"]
    Router -->|"tool_calls"| ToolNode["ToolCallNode"]
    ToolNode --> Tools["@tool functions"]
    ToolNode --> ModelNode
    Router -->|"no tool_calls"| Answer["answer"]

Package Layout

src/agent_core/
  agent.py              # Agent: direct chat runner and embeddable Node
  core/                 # Node, Flow, RunContext, trace events
  llm/                  # LLM, ChatModel protocol, ModelNode, router
  tools/                # @tool, ToolExecutor, ToolCallNode
examples/
  01_basic_agent.py     # Node and Flow only
  02_custom_prompt.py   # Real model call with a custom prompt
  03_custom_tool.py     # Tool schema and execution
  04_tool_agent.py      # Manually wired model-tool-model loop
  05_custom_agent.py    # Direct Agent(instructions, tools)
tests/

Install

uv add friday-agent-core

Or with pip:

pip install friday-agent-core

The distribution name is friday-agent-core; the import name stays agent_core. To develop the runtime itself, clone this repository and run uv sync.

Set model credentials in the process environment or pass them to LLM(...):

$env:LLM_API_KEY = "..."
$env:LLM_BASE_URL = "https://api.example.com"
$env:LLM_MODEL = "model-name"

Quick Agent

from typing import Annotated

from agent_core import Agent, tool

@tool(description="Search private notes.")
def search_notes(topic: Annotated[str, "Topic to search."]) -> dict[str, str]:
    return {"topic": topic, "result": "mock note"}

agent = Agent(
    instructions="You are a concise research assistant.",
    tools=[search_notes],
    stream=True,
    chat_kwargs={"tool_choice": "auto"},
)

context = agent.new_context()
answer = agent.chat("Draft a short evaluation plan.", context=context)
print(answer)

Custom Flow

Use explicit nodes when the agent loop is not a simple chat loop:

from agent_core import Agent, CallableNode, Flow

def classify(payload: dict) -> tuple[str, dict]:
    return "question" if payload["text"].endswith("?") else "statement", payload

def answer(payload: dict) -> dict:
    payload["answer"] = "received"
    return payload

router = CallableNode(classify)
answer_node = CallableNode(answer)

router - "question" >> answer_node
router - "statement" >> answer_node

result = Agent(Flow(router)).run({"text": "Hello?"})
print(result.payload["answer"])

Because Agent is a Node, you can compose agents:

researcher = Agent(model=model, instructions="Research.", tools=[search_notes])
writer = Agent(model=model, instructions="Write the final response.")

researcher - "final" >> writer
team = Agent(Flow(researcher))

When an Agent is used as a node, it exposes the final action from its inner flow. Pass action="some_action" only when you want to force a fixed outward action.

Examples

Run the examples in order:

uv run python examples/01_basic_agent.py
uv run python examples/02_custom_prompt.py
uv run python examples/03_custom_tool.py
uv run python examples/04_tool_agent.py --context messages
uv run python examples/05_custom_agent.py

LLM examples stream by default. Use --no-stream to print full responses after completion.

04_tool_agent.py also supports:

  • --interactive: start an interactive loop.
  • --context summary|messages|events|artifacts|all|none: inspect the run context.

In your own agent, use Agent(..., stream=False) to disable streaming by default, or override one call with agent.chat(..., stream=False).

Runtime Events

Each run returns a RunContext:

result = agent.run({"text": "hello"})
messages = result.context.messages
events = [event.to_dict() for event in result.context.events]
usage = result.usage.to_dict()

RunUsage accumulates every model request in the flow, including streamed responses. Input and output totals are exact when every provider response includes usage; otherwise the totals are reported as unknown instead of a misleading partial sum.

Nodes can also write to the active context:

from agent_core import get_current_context

context = get_current_context()
if context:
    context.set_artifact("note", "saved")

Keep business state in payload and runtime/session data in RunContext. For example, a router decision, plan, or artifact path belongs in result.payload; streamed model deltas, messages, UI events, and artifact metadata belong in result.context. Large artifacts such as full reports should live in files, databases, or object storage, with payload/context carrying references rather than the full content.

In multi-agent flows, RunContext is shared for events, usage, artifacts, and metadata, but each Agent gets an isolated message scope for LLM input. This keeps the observable run unified without leaking one agent's prompt/history into another agent's model call.

Validate

uv run python -m unittest discover -s tests
uv run python -m compileall src tests examples

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

friday_agent_core-0.1.1.tar.gz (54.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

friday_agent_core-0.1.1-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file friday_agent_core-0.1.1.tar.gz.

File metadata

  • Download URL: friday_agent_core-0.1.1.tar.gz
  • Upload date:
  • Size: 54.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for friday_agent_core-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b642d206a97d78bb1a7881c33f3ea857ed161eb9b3cf2b31df993e700f572c85
MD5 fc5ad5fb93c46a7b44b12bc535b6a371
BLAKE2b-256 10817b760606821afdf716f96df1918cdc7047e0d8811bd6df4a3be726fd317b

See more details on using hashes here.

Provenance

The following attestation bundles were made for friday_agent_core-0.1.1.tar.gz:

Publisher: release.yml on Lancetwang/agent-core-runtime

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file friday_agent_core-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for friday_agent_core-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4a2740b6d6762a1c57b60ef5f1805264e07cc66735d62d847ffb63d713d1a121
MD5 5dd3a616ae3010f8b2bac1bd3319dff1
BLAKE2b-256 bd9c5430672c38be0cd0dcfafa3f57c464e9b4b23c490d35d5d232dd877e2c7d

See more details on using hashes here.

Provenance

The following attestation bundles were made for friday_agent_core-0.1.1-py3-none-any.whl:

Publisher: release.yml on Lancetwang/agent-core-runtime

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page