En este tutorial, implementamos un agente AI avanzado basado en gráficos utilizando el marco Graphagent y el modelo Gemini 1.5 Flash. Definimos un gráfico dirigido de nodos, cada uno responsable de una función específica: un planificador para desglosar la tarea, un enrutador para controlar el flujo, la investigación y los nodos matemáticos para proporcionar evidencia externa y cálculo, un escritor para sintetizar la respuesta y un crítico para validar y refinar la producción. Integramos Gemini a través de un envoltorio que maneja las indicaciones estructuradas de JSON, mientras que las funciones locales de Python actúan como herramientas para una evaluación de matemáticas seguras y la búsqueda de documentos. Al ejecutar esta tubería de extremo a extremo, demostramos cómo el razonamiento, la recuperación y la validación se modularizan dentro de un solo sistema cohesivo. Mira el Códigos completos aquí.
import os, json, time, ast, math, getpass
from dataclasses import dataclass, field
from typing import Dict, List, Callable, Any
import google.generativeai as genai
try:
import networkx as nx
except ImportError:
nx = None
Comenzamos importando bibliotecas de Python Core para el manejo de datos, el tiempo y la evaluación segura, junto con los datos de datos y los ayudantes de escritura para estructurar nuestro estado. También cargamos el cliente Google.Generativeai para acceder a Gemini y, opcionalmente, Networkx para la visualización de gráficos. Mira el Códigos completos aquí.
def make_model(api_key: str, model_name: str = "gemini-1.5-flash"):
genai.configure(api_key=api_key)
return genai.GenerativeModel(model_name, system_instruction=(
"You are GraphAgent, a principled planner-executor. "
"Prefer structured, concise outputs; use provided tools when asked."
))
def call_llm(model, prompt: str, temperature=0.2) -> str:
r = model.generate_content(prompt, generation_config={"temperature": temperature})
return (r.text or "").strip()
Definimos un ayudante para configurar y devolver un modelo Gemini con una instrucción del sistema personalizado, y otra función que llama al LLM con un aviso mientras controla la temperatura. Utilizamos esta configuración para garantizar que nuestro agente reciba salidas estructuradas y concisas de manera consistente. Mira el Códigos completos aquí.
def safe_eval_math(expr: str) -> str:
node = ast.parse(expr, mode="eval")
allowed = (ast.Expression, ast.BinOp, ast.UnaryOp, ast.Num, ast.Constant,
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.Mod,
ast.USub, ast.UAdd, ast.FloorDiv, ast.AST)
def check(n):
if not isinstance(n, allowed): raise ValueError("Unsafe expression")
for c in ast.iter_child_nodes(n): check(c)
check(node)
return str(eval(compile(node, "<math>", "eval"), {"__builtins__": {}}, {}))
DOCS = [
"Solar panels convert sunlight to electricity; capacity factor ~20%.",
"Wind turbines harvest kinetic energy; onshore capacity factor ~35%.",
"RAG = retrieval-augmented generation joins search with prompting.",
"LangGraph enables cyclic graphs of agents; good for tool orchestration.",
]
def search_docs(q: str, k: int = 3) -> List[str]:
ql = q.lower()
scored = sorted(DOCS, key=lambda d: -sum(w in d.lower() for w in ql.split()))
return scored[:k]
Implementamos dos herramientas clave para el agente: un evaluador de matemáticas seguras que analiza y verifica expresiones aritméticas con AST antes de la ejecución, y una búsqueda de documentos simple que recupera los fragmentos más relevantes de un pequeño corpus en la memoria. Los usamos para dar al agente capacidades confiables de cálculo y recuperación sin dependencias externas. Mira el Códigos completos aquí.
@dataclass
class State:
task: str
plan: str = ""
scratch: List[str] = field(default_factory=list)
evidence: List[str] = field(default_factory=list)
result: str = ""
step: int = 0
done: bool = False
def node_plan(state: State, model) -> str:
prompt = f"""Plan step-by-step to solve the user task.
Task: {state.task}
Return JSON: {{"subtasks": ["..."], "tools": {{"search": true/false, "math": true/false}}, "success_criteria": ["..."]}}"""
js = call_llm(model, prompt)
try:
plan = json.loads(js[js.find("{"): js.rfind("}")+1])
except Exception:
plan = {"subtasks": ["Research", "Synthesize"], "tools": {"search": True, "math": False}, "success_criteria": ["clear answer"]}
state.plan = json.dumps(plan, indent=2)
state.scratch.append("PLAN:\n"+state.plan)
return "route"
def node_route(state: State, model) -> str:
prompt = f"""You are a router. Decide next node.
Context scratch:\n{chr(10).join(state.scratch[-5:])}
If math needed -> 'math', if research needed -> 'research', if ready -> 'write'.
Return one token from [research, math, write]. Task: {state.task}"""
choice = call_llm(model, prompt).lower()
if "math" in choice and any(ch.isdigit() for ch in state.task):
return "math"
if "research" in choice or not state.evidence:
return "research"
return "write"
def node_research(state: State, model) -> str:
prompt = f"""Generate 3 focused search queries for:
Task: {state.task}
Return as JSON list of strings."""
qjson = call_llm(model, prompt)
try:
queries = json.loads(qjson[qjson.find("["): qjson.rfind("]")+1])[:3]
except Exception:
queries = [state.task, "background "+state.task, "pros cons "+state.task]
hits = []
for q in queries:
hits.extend(search_docs(q, k=2))
state.evidence.extend(list(dict.fromkeys(hits)))
state.scratch.append("EVIDENCE:\n- " + "\n- ".join(hits))
return "route"
def node_math(state: State, model) -> str:
prompt = "Extract a single arithmetic expression from this task:\n"+state.task
expr = call_llm(model, prompt)
expr = "".join(ch for ch in expr if ch in "0123456789+-*/().%^ ")
try:
val = safe_eval_math(expr)
state.scratch.append(f"MATH: {expr} = {val}")
except Exception as e:
state.scratch.append(f"MATH-ERROR: {expr} ({e})")
return "route"
def node_write(state: State, model) -> str:
prompt = f"""Write the final answer.
Task: {state.task}
Use the evidence and any math results below, cite inline like [1],[2].
Evidence:\n{chr(10).join(f'[{i+1}] '+e for i,e in enumerate(state.evidence))}
Notes:\n{chr(10).join(state.scratch[-5:])}
Return a concise, structured answer."""
draft = call_llm(model, prompt, temperature=0.3)
state.result = draft
state.scratch.append("DRAFT:\n"+draft)
return "critic"
def node_critic(state: State, model) -> str:
prompt = f"""Critique and improve the answer for factuality, missing steps, and clarity.
If fix needed, return improved answer. Else return 'OK'.
Answer:\n{state.result}\nCriteria:\n{state.plan}"""
crit = call_llm(model, prompt)
if crit.strip().upper() != "OK" and len(crit) > 30:
state.result = crit.strip()
state.scratch.append("REVISED")
state.done = True
return "end"
NODES: Dict[str, Callable[[State, Any], str]] = {
"plan": node_plan, "route": node_route, "research": node_research,
"math": node_math, "write": node_write, "critic": node_critic
}
def run_graph(task: str, api_key: str) -> State:
model = make_model(api_key)
state = State(task=task)
cur = "plan"
max_steps = 12
while not state.done and state.step < max_steps:
state.step += 1
nxt = NODES[cur](state, model)
if nxt == "end": break
cur = nxt
return state
def ascii_graph():
return """
START -> plan -> route -> (research <-> route) & (math <-> route) -> write -> critic -> END
"""
Definimos un estado de datos de estado escrito para persistir la tarea, el plan, la evidencia, las notas de rascar y los indicadores de control a medida que se ejecuta el gráfico. Implementamos funciones de nodos, un planificador, un enrutador, investigación, matemáticas, escritor y crítico. Estas funciones mutan el estado y devuelven la siguiente etiqueta de nodo. Luego los registramos en nodos e iteramos en run_graph hasta que se hagan. También exponemos ASCII_GRAPH () para visualizar el flujo de control que seguimos mientras enrutamos entre la investigación/matemáticas y finalizamos con una crítica. Mira el Códigos completos aquí.
if __name__ == "__main__":
key = os.getenv("GEMINI_API_KEY") or getpass.getpass("🔐 Enter GEMINI_API_KEY: ")
task = input("📝 Enter your task: ").strip() or "Compare solar vs wind for reliability; compute 5*7."
t0 = time.time()
state = run_graph(task, key)
dt = time.time() - t0
print("\n=== GRAPH ===", ascii_graph())
print(f"\n✅ Result in {dt:.2f}s:\n{state.result}\n")
print("---- Evidence ----")
print("\n".join(state.evidence))
print("\n---- Scratch (last 5) ----")
print("\n".join(state.scratch[-5:]))
Definimos el punto de entrada del programa: leemos de forma segura la clave de la API de Gemini, tomamos una tarea como entrada y luego ejecutamos el gráfico a través de Run_Graph. Medimos el tiempo de ejecución, imprimimos el gráfico ASCII del flujo de trabajo, mostramos el resultado final y también emitimos evidencia de soporte y las últimas notas de rascar para la transparencia. Mira el Códigos completos aquí.
En conclusión, demostramos cómo un agente estructurado con gráficos permite el diseño de flujos de trabajo deterministas alrededor de un LLM probabilístico. Observamos cómo el nodo del planificador hace cumplir la descomposición de la tarea, el enrutador selecciona dinámicamente entre la investigación y las matemáticas, y el crítico proporciona una mejora iterativa para la realidad y la claridad. Gemini actúa como el motor de razonamiento central, mientras que los nodos gráficos de suministro de suministro, las verificaciones de seguridad y la gestión del estado transparente. Concluimos con un agente totalmente funcional que muestra los beneficios de combinar la orquestación gráfica con un LLM moderno, habilitando extensiones como cadenas de herramientas personalizadas, memoria múltiple o ejecución de nodos paralelos en implementaciones más complejas.
Mira el Códigos completos aquí. No dude en ver nuestro Página de Github para tutoriales, códigos y cuadernos. Además, siéntete libre de seguirnos Gorjeo Y no olvides unirte a nuestro Subreddit de 100k+ ml y suscribirse a Nuestro boletín.
Asif Razzaq es el CEO de MarktechPost Media Inc .. Como empresario e ingeniero visionario, ASIF se compromete a aprovechar el potencial de la inteligencia artificial para el bien social. Su esfuerzo más reciente es el lanzamiento de una plataforma de medios de inteligencia artificial, MarktechPost, que se destaca por su cobertura profunda de noticias de aprendizaje automático y de aprendizaje profundo que es técnicamente sólido y fácilmente comprensible por una audiencia amplia. La plataforma cuenta con más de 2 millones de vistas mensuales, ilustrando su popularidad entre el público.