# AI Agents

> Agentes de IA: LangGraph4J, SpringAI, LangGraph Python, LangChain. Use para criar agentes RAG, grafos de estado, tool calling, multi-agent, human-in-the-loop, ou testar/observar pipelines de LLM.

- Skill: `nxs-cafi/ai-agents` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nxs-cafi/ai-agents`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nxs-cafi/ai-agents/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: nxs-cafi (https://skillmd.com/u/nxs-cafi)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/nxs-cafi/ai-agents

---


# AI Agents — LangGraph / SpringAI / LangChain

## Princípios para Agentes em Produção

1. **Determinismo onde possível:** temperature baixa, seeds, mocks em testes.
2. **Tool calling seguro:** validar inputs de tools; nunca executar código arbitrário do LLM.
3. **Observabilidade obrigatória:** trace cada chamada LLM (tokens, latência, custo).
4. **Human-in-the-loop** para ações irreversíveis (pagamento, delete, deploy).
5. **Fallback graceful:** timeout, retry com backoff, resposta degradada.

## LangGraph Python

```python
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
import operator

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]
    next_action: str

def router(state: AgentState) -> str:
    if state["next_action"] == "search":
        return "search"
    return "respond"

def search_node(state: AgentState) -> AgentState:
    # RAG retrieval
    return {"messages": [("assistant", "Resultado da busca...")]}

def respond_node(state: AgentState) -> AgentState:
    return {"messages": [("assistant", "Resposta final")]}

graph = StateGraph(AgentState)
graph.add_node("search", search_node)
graph.add_node("respond", respond_node)
graph.add_conditional_edges("router", router, {"search": "search", "respond": "respond"})
graph.add_edge("search", "respond")
graph.add_edge("respond", END)
app = graph.compile()

# Human-in-the-loop com interrupt
app = graph.compile(interrupt_before=["respond"])
```

## LangChain Python (LCEL + RAG)

```python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_community.vectorstores import Chroma

llm = ChatOpenAI(model="gpt-4o", temperature=0)
retriever = Chroma.from_documents(docs, OpenAIEmbeddings()).as_retriever()

prompt = ChatPromptTemplate.from_messages([
    ("system", "Responda com base no contexto. Se não souber, diga que não sabe."),
    ("human", "Contexto: {context}\n\nPergunta: {question}"),
])

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)
```

## SpringAI (Java)

```java
@Configuration
public class AiConfig {
    @Bean
    ChatClient chatClient(ChatClient.Builder builder, VectorStore vectorStore) {
        return builder
            .defaultSystem("Você é um assistente. Use apenas o contexto fornecido.")
            .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
            .build();
    }
}

@Service
@RequiredArgsConstructor
public class RagService {
    private final ChatClient chatClient;

    public String ask(String question) {
        return chatClient.prompt()
            .user(question)
            .call()
            .content();
    }
}

// Tool Calling
@Bean
@Description("Busca pedido por ID")
Function<OrderQuery, OrderSummary> findOrder(OrderRepository repo) {
    return query -> OrderSummary.from(repo.findById(query.orderId()).orElseThrow());
}
```

## LangGraph4J (Java)

```java
// Conceito equivalente ao LangGraph Python
StateGraph<AgentState> graph = new StateGraph<>(AgentState.class);
graph.addNode("retrieve", state -> retrieveDocuments(state));
graph.addNode("generate", state -> generateAnswer(state));
graph.addEdge(START, "retrieve");
graph.addEdge("retrieve", "generate");
graph.addEdge("generate", END);

CompiledGraph<AgentState> app = graph.compile();

// Checkpointing para conversas longas
var config = RunnableConfig.builder().threadId(sessionId).build();
app.invoke(initialState, config);
```

## Padrões Agnósticos

| Padrão | Descrição | Quando usar |
|--------|-----------|-------------|
| **ReAct** | Reason + Act em loop | Tarefas com tools externas |
| **Reflection** | Crítica e refinamento da resposta | Qualidade de texto/código |
| **Planning** | Plano antes de executar | Tarefas multi-step complexas |
| **Multi-Agent** | Agentes especializados | Domínios distintos (research + code) |
| **RAG** | Retrieval + Generation | Q&A sobre documentos internos |

## Testes de Agentes

```python
# Mock LLM para testes determinísticos
from unittest.mock import AsyncMock, patch

@pytest.mark.asyncio
async def test_agent_returns_answer_from_context():
    with patch("langchain_openai.ChatOpenAI.ainvoke") as mock_llm:
        mock_llm.return_value = AIMessage(content="Resposta esperada")
        result = await agent.ainvoke({"question": "Qual o prazo?"})
        assert "Resposta esperada" in result["answer"]

# Avaliação com LangSmith (opcional)
# - dataset de perguntas/respostas gold
# - métricas: faithfulness, relevance, latency
```

```java
@Test
void should_call_find_order_tool_when_asked_about_order() {
  when(chatClient.prompt().user(anyString()).call().content())
      .thenReturn("Pedido #123 está em trânsito");

  var response = agentService.handle("Onde está meu pedido 123?");

  assertThat(response).contains("trânsito");
  verify(orderRepository).findById(OrderId.of("123"));
}
```

## Observabilidade de LLM

```python
# OpenTelemetry + LangSmith
from langsmith import traceable

@traceable(name="rag_pipeline")
async def rag_pipeline(question: str) -> str:
    docs = await retriever.ainvoke(question)
    return await chain.ainvoke({"context": docs, "question": question})
```

**Métricas essenciais:**
- `llm.tokens.input` / `llm.tokens.output`
- `llm.latency.seconds`
- `llm.errors.total`
- `rag.retrieval.count`

## Anti-Patterns

| Anti-Pattern | Risco |
|---|---|
| Prompt injection sem sanitização | Execução indevida |
| Tool sem validação de input | SQL/command injection |
| Sem timeout em chamadas LLM | Request hang |
| Context window ilimitado | Custo e latência |
| Sem eval em CI | Regressão silenciosa de qualidade |

## Referências

- `~/.cursor/skills/references/langgraph-patterns.md`

