Go library provides a framework for building agentic systems, allowing for the creation of modular, composable, and potentially asynchronous workflows involving interconnected nodes. It's designed to orchestrate complex tasks, including those involving external services like Large Language Models (LLMs) and web search.
(Inspired by the PocketFlow - Agent)
The framework is built around the concept of Nodes and Flows:
- Nodes: Represent individual units of work or decision points within a workflow.
BaseNode: The fundamental building block, containing parameters and successors (next nodes based on actions).Node: ExtendsBaseNodewith retry logic for handling transient errors.BatchNode: Processes a slice of items, applying the node's logic to each item.AsyncNode: Executes its logic asynchronously, returning a channel. Supports retry logic.AsyncBatchNode: Processes a slice of items asynchronously.AsyncParallelBatchNode: Processes a slice of items asynchronously and in parallel.
- Flows: Orchestrate the execution of a sequence of connected nodes.
Flow: Manages the execution path, determining the next node based on the action returned by the current node.BatchFlow: Executes the defined flow for each item in an input batch.AsyncFlow: Orchestrates a flow potentially containingAsyncNodes, managing the asynchronous execution.AsyncBatchFlow: Runs the asynchronous flow for each item in an input batch.AsyncParallelBatchFlow: Runs the asynchronous flow for each item in an input batch in parallel.
Each node typically follows a Prep -> Exec -> Post lifecycle:
Prep: Prepares input data, often interacting with a shared data map.Exec: Performs the main work of the node. For flows, this involves orchestrating the sub-nodes.Post: Processes the results and updates the shared data map.
Transitions between nodes are determined by the action string returned by a node's execution or post-processing step.
The example directory demonstrates how to use the framework to build a simple research agent:
DecideActionNode: Takes a question and current context (previous search results). It uses an LLM (like Google's Gemini model via thegoogle/generative-ai-golibrary) to decide whether tosearchfor more information oranswerthe question based on the current context. It formulates a specific prompt for the LLM and parses the YAML response to determine the next action and any necessary parameters (like a search query or the final answer).SearchWebNodeNode: If the decision is to search, this node takes thesearch_queryprovided by theDecideActionnode. It executes a web search using theSearchWebutility function (which attempts to scrape Google and Brave search results) and converts the HTML results to Markdown. The results are added to the shared context.AnswerQuestionNode: If the decision is to answer, this node takes the question and the accumulated context. It prompts the LLM to generate a comprehensive answer based only on the provided information.- Flow Orchestration: A
Flowconnects these nodes:- Starts with
DecideAction. - If
DecideActionreturns "search", it goes toSearchWebNode. - If
DecideActionreturns "answer", it goes toAnswerQuestion. SearchWebNodealways returns the action "decide", looping back toDecideActionwith the updated context.AnswerQuestionreturns "done", completing the flow.
- Starts with
- Utilities (
utils.go): Provides helper functions for:- Setting up the Gemini LLM client (
SetLlmApi). - Sending prompts to the LLM with retry logic (
SentLlmPrompt). - Performing web searches (
SearchWeb) - Note: Relies on potentially fragile web scraping. - Converting HTML to Markdown (
ParseHtmlToMarkdown).
- Setting up the Gemini LLM client (
- Set the
GEMINI_API_KEYenvironment variable with your API key. - Navigate to the
exampledirectory. - Run the example with
go run . "Your question here". If no question is provided, it uses a default question.