mirror of
https://github.com/hoshikawa2/agent-ai-mcp-server.git
synced 2026-03-03 16:19:35 +00:00
167 lines
6.4 KiB
Python
167 lines
6.4 KiB
Python
import asyncio
|
|
from langchain_core.prompts import ChatPromptTemplate
|
|
from langchain_community.chat_models.oci_generative_ai import ChatOCIGenAI
|
|
from langgraph.prebuilt import create_react_agent
|
|
from mcp import ClientSession, StdioServerParameters
|
|
from mcp.client.stdio import stdio_client
|
|
from langchain_mcp_adapters.tools import load_mcp_tools
|
|
|
|
from langgraph.graph import StateGraph
|
|
from langgraph.prebuilt import create_react_agent
|
|
from langchain_core.runnables import Runnable
|
|
from langchain_core.messages import HumanMessage, AIMessage
|
|
|
|
import phoenix as px
|
|
from opentelemetry import trace
|
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
from opentelemetry.sdk.resources import Resource
|
|
from opentelemetry.sdk.trace import TracerProvider
|
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
# Multiple Servers
|
|
from langchain_mcp_adapters.client import MultiServerMCPClient
|
|
|
|
# 1. Inicia o Phoenix (ele abre o servidor OTLP na porta 6006)
|
|
px.launch_app()
|
|
|
|
# 2. Configura o OpenTelemetry
|
|
resource = Resource(attributes={"service.name": "ollama_oraclegenai_trace"})
|
|
provider = TracerProvider(resource=resource)
|
|
trace.set_tracer_provider(provider)
|
|
|
|
# 3. Configura o exportador para mandar spans para o Phoenix
|
|
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:6006/v1/traces")
|
|
span_processor = BatchSpanProcessor(otlp_exporter)
|
|
provider.add_span_processor(span_processor)
|
|
|
|
# 4. Cria o tracer
|
|
tracer = trace.get_tracer(__name__)
|
|
|
|
class MemoryState:
|
|
def __init__(self):
|
|
self.messages = []
|
|
|
|
# Define the language model
|
|
llm = ChatOCIGenAI(
|
|
model_id="cohere.command-r-08-2024",
|
|
service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
|
|
compartment_id="ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
auth_profile="DEFAULT",
|
|
model_kwargs={"temperature": 0.1, "top_p": 0.75, "max_tokens": 2000}
|
|
)
|
|
|
|
# Prompt
|
|
prompt = ChatPromptTemplate.from_messages([
|
|
("system", """Você é um agente responsável por resolver inconsistências em notas fiscais de devolução de clientes.
|
|
Seu objetivo é encontrar a nota fiscal de **saída original da empresa**,
|
|
com base nas informações da **nota de devolução do cliente**.
|
|
|
|
### Importante:
|
|
1. Use o servidor `InvoiceItemResolver` para todas as consultas.
|
|
2. Primeiro, utilize a ferramenta de **busca vetorial ou fuzzy** para encontrar o **EAN mais provável**,
|
|
a partir da descrição fornecida pelo cliente. O atributo codigo vindo do resultado da lista de busca vetorial
|
|
pode ser entendida como EAN.
|
|
- Ferramentas: `buscar_produto_vetorizado` ou `resolve_ean`
|
|
- Retorne o EAN mais provável com sua descrição e grau de similaridade.
|
|
Use resolve_ean para obter o EAN mais provável. Se retornar um dicionário com erro, interrompa a operação.
|
|
3. Só após encontrar um EAN válido, use a ferramenta `buscar_notas_por_criterios` para procurar a nota fiscal de saída
|
|
original.
|
|
- Use o EAN junto com cliente, preço e local (estado) para fazer a busca.
|
|
|
|
### Exemplo de entrada:
|
|
```json
|
|
{{
|
|
"customer": "Cliente 43",
|
|
"description": "Harry Poter",
|
|
"price": 139.55,
|
|
"location": "RJ"
|
|
}}
|
|
Se encontrar uma nota fiscal de saída correspondente, retorne:
|
|
• número da nota,
|
|
• cliente,
|
|
• estado,
|
|
• EAN,
|
|
• descrição do produto,
|
|
• preço unitário.
|
|
|
|
Se não encontrar nenhuma correspondência, responda exatamente:
|
|
“EAN não encontrado com os critérios fornecidos.”
|
|
"""),
|
|
("placeholder", "{messages}")
|
|
])
|
|
|
|
# Run the client with the MCP server
|
|
async def main():
|
|
async with MultiServerMCPClient(
|
|
{
|
|
"InvoiceItemResolver": {
|
|
"command": "python",
|
|
"args": ["server_nf_items.py"],
|
|
"transport": "stdio",
|
|
},
|
|
}
|
|
) as client:
|
|
tools = client.get_tools()
|
|
if not tools:
|
|
print("❌ No MCP tools were loaded. Please check if the server is running.")
|
|
return
|
|
|
|
print("🛠️ Loaded tools:", [t.name for t in tools])
|
|
|
|
# Creating the LangGraph agent with in-memory state
|
|
memory_state = MemoryState()
|
|
|
|
agent_executor = create_react_agent(
|
|
model=llm,
|
|
tools=tools,
|
|
prompt=prompt,
|
|
)
|
|
|
|
print("🤖 READY")
|
|
while True:
|
|
query = input("You: ")
|
|
if query.lower() in ["quit", "exit"]:
|
|
break
|
|
if not query.strip():
|
|
continue
|
|
|
|
memory_state.messages.append(HumanMessage(content=query))
|
|
try:
|
|
result = await agent_executor.ainvoke({"messages": memory_state.messages})
|
|
new_messages = result.get("messages", [])
|
|
|
|
# Store new messages
|
|
# memory_state.messages.extend(new_messages)
|
|
memory_state.messages = []
|
|
|
|
print("Assist:", new_messages[-1].content)
|
|
|
|
formatted_messages = prompt.format_messages()
|
|
|
|
# Convertendo cada mensagem em string
|
|
formatted_messages_str = "\n".join([str(msg) for msg in formatted_messages])
|
|
with tracer.start_as_current_span("Server NF Items") as span:
|
|
# Anexa o prompt e resposta como atributos no trace
|
|
span.set_attribute("llm.prompt", formatted_messages_str)
|
|
span.set_attribute("llm.response", new_messages[-1].content)
|
|
span.set_attribute("llm.model", "ocigenai")
|
|
|
|
executed_tools = []
|
|
if "intermediate_steps" in result:
|
|
for step in result["intermediate_steps"]:
|
|
tool_call = step.get("tool_input") or step.get("action")
|
|
if tool_call:
|
|
tool_name = tool_call.get("tool") or step.get("tool")
|
|
if tool_name:
|
|
executed_tools.append(tool_name)
|
|
|
|
if not executed_tools:
|
|
executed_tools = [t.name for t in tools] # fallback
|
|
|
|
span.set_attribute("llm.executed_tools", ", ".join(executed_tools))
|
|
|
|
except Exception as e:
|
|
print("Error:", e)
|
|
|
|
# Run the agent with asyncio
|
|
if __name__ == "__main__":
|
|
asyncio.run(main()) |