Crafting Your Agent πŸ› οΈ

Three ingredients make a great agent: a local model, custom tools, and solid instructions.

Building agents doesn't need to be over-engineered. We recommend starting with a simple recipe: a model, some tools, and direct instructions. Once the basics are humming, you can layer in memory, databases, and structured schemas.

Here's the simplest agent with access to HackerNews, connected to a local Ollama server running Llama 3.2 3B:

1from kern.agent import Agent
2from kern.models.openai import OpenAIChat
3from kern.tools.hackernews import HackerNewsTools
4
5# Let's boot up our local-first helper πŸ€–
6agent = Agent(
7 model=OpenAIChat(
8 id="llama3.2:3b",
9 base_url="http://localhost:11434/v1", # Hello, Ollama! πŸ‘‹
10 ),
11 tools=[HackerNewsTools()],
12 instructions="You are a tech journalist. Keep summaries brief and facts straight.",
13 markdown=True,
14)
15
16# Run and stream the output directly to the terminal! πŸš€
17agent.print_response("What are the trending startups today?", stream=True)

πŸƒ Running Your Agent

During development, Agent.print_response() is your best friend. It formats the markdown beautifully and prints it directly to your terminal.

For production or custom interfaces, use Agent.run() or Agent.arun() (the async version) to retrieve the structured stream:

1from typing import Iterator
2from kern.agent import Agent, RunOutputEvent, RunEvent
3from kern.models.openai import OpenAIChat
4from kern.tools.hackernews import HackerNewsTools
5
6agent = Agent(
7 model=OpenAIChat(id="llama3.2:3b", base_url="http://localhost:11434/v1"),
8 tools=[HackerNewsTools()],
9 instructions="Write a report on the topic. Output only the report.",
10 markdown=True,
11)
12
13# Stream the event outputs
14stream: Iterator[RunOutputEvent] = agent.run("Trending tech news", stream=True)
15for chunk in stream:
16 if chunk.event == RunEvent.run_content:
17 print(chunk.content, end="", flush=True)

πŸ’‘ Dynamic Tools (Callable Factories)

What if you don't want the exact same tools for every session? For example, admins should get file editing access, while standard users only get web search.

Instead of passing a static list, pass a callable function! Kern calls this function at the start of each run, allowing you to customize the tools or knowledge based on the user session. 🀯

1from kern.agent import Agent
2from kern.models.openai import OpenAIChat
3from kern.run import RunContext
4from kern.tools.duckduckgo import DuckDuckGoTools
5from kern.tools.yfinance import YFinanceTools
6
7# Determine tools on the fly ✈️
8def get_tools_for_user(run_context: RunContext):
9 role = (run_context.session_state or {}).get("role", "general")
10 if role == "finance":
11 return [YFinanceTools()]
12 return [DuckDuckGoTools()]
13
14# Wire up the dynamic tool factory
15agent = Agent(
16 model=OpenAIChat(id="llama3.2:3b", base_url="http://localhost:11434/v1"),
17 tools=get_tools_for_user,
18)
19
20# This run gets financial tools πŸ“ˆ
21agent.print_response("AAPL stock price?", session_state={"role": "finance"}, stream=True)
22
23# This run gets general web search 🌐
24agent.print_response("Latest AI news?", session_state={"role": "general"}, stream=True)

βš™οΈ Callable Caching Settings

Factory results are cached by default. The cache key is resolved in this order: custom key function > user_id > session_id. If none are available, caching is skipped and the factory runs every time.

SettingDefaultDescription
cache_callablesTrueEnable or disable caching for all callable factories
callable_tools_cache_keyNoneCustom cache key function for tools factory
callable_knowledge_cache_keyNoneCustom cache key function for knowledge factory
callable_members_cache_keyNoneCustom cache key function for members factory (Team only)

Set cache_callables=False when session_state changes between runs and the factory should re-evaluate each time.

Clear cached results programmatically:

1from kern.utils.callables import clear_callable_cache
2
3clear_callable_cache(team) # Clear all caches
4clear_callable_cache(team, kind="tools") # Clear tools cache only
5clear_callable_cache(team, kind="tools", close=True) # Clear and call .close() on cached resources

Use aclear_callable_cache() in async code.


πŸ—ΊοΈ Next Steps

Ready to upgrade your agent's capabilities? Explore the guides below: