feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'
This commit is contained in:
@@ -1,19 +1,60 @@
|
|||||||
# Project Title
|
# LangGraph Agent with OpenAI Integration
|
||||||
|
|
||||||
This project demonstrates a simple usage of the `langgraph` library.
|
This project demonstrates a simple LangGraph agent that integrates with the OpenAI LLM via the `langchain-openai` package. The agent processes a single prompt and returns the model's response.
|
||||||
|
|
||||||
## Setup
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.10+
|
||||||
|
- `langchain-openai` (automatically installed via `requirements.txt`)
|
||||||
|
- `langgraph`
|
||||||
|
- `langchain`
|
||||||
|
- `openai`
|
||||||
|
|
||||||
|
Install the dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run
|
## Configuration
|
||||||
|
|
||||||
|
Set your OpenAI API key as an environment variable:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python main.py
|
export OPENAI_API_KEY="your-openai-api-key"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Notes
|
Alternatively, you can create a `.env` file in the project root with the following content:
|
||||||
|
|
||||||
The `langgraph` package is required for this project. It is specified in `requirements.txt` with a minimum version of 0.0.1.
|
```
|
||||||
|
OPENAI_API_KEY=your-openai-api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Agent
|
||||||
|
|
||||||
|
You can run the agent from the command line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m src.agent "Hello, how are you?"
|
||||||
|
```
|
||||||
|
|
||||||
|
The agent will send the prompt to the OpenAI model and print the response.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
├── requirements.txt
|
||||||
|
├── src
|
||||||
|
│ └── agent.py
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
- `requirements.txt` – lists all Python package dependencies.
|
||||||
|
- `src/agent.py` – contains the LangGraph agent implementation and a simple CLI.
|
||||||
|
- `README.md` – this documentation file.
|
||||||
|
|
||||||
|
## Extending the Agent
|
||||||
|
|
||||||
|
The current graph contains a single node that calls the LLM. You can extend it by adding more nodes (e.g., for tool usage, memory, or custom logic) and connecting them in the graph.
|
||||||
|
|
||||||
|
Happy coding!
|
||||||
+35
-16
@@ -1,26 +1,45 @@
|
|||||||
**Что реализовано**
|
**Что реализовано**
|
||||||
- В файл `requirements.txt` добавлен пакет `langgraph` с минимальной версией `>=0.0.1`.
|
- Добавлен пакет `langchain-openai` в `requirements.txt`.
|
||||||
- В `main.py` импортируется `langgraph` и выводится его версия, чтобы убедиться, что пакет действительно установлен.
|
- В `src/agent.py` реализован вызов модели OpenAI (или Ollama) через `ChatOpenAI` внутри узла графа LangGraph.
|
||||||
|
- Создан простейший граф: один узел `llm`, который принимает текущее состояние сообщений, отправляет его в LLM и добавляет ответ.
|
||||||
|
- Функция `run_agent` формирует начальное состояние, запускает граф и возвращает последний ответ LLM.
|
||||||
|
|
||||||
**Почему это удовлетворяет требованиям**
|
**Почему это удовлетворяет требованиям**
|
||||||
- Указание `langgraph>=0.0.1` гарантирует, что при установке зависимостей будет установлена хотя бы любая версия, начиная с 0.0.1, что соответствует заданной спецификации.
|
- **Интеграция LLM**: узел `llm_node` явно использует `ChatOpenAI` (или можно заменить на Ollama) и делает вызов `llm.invoke(messages)`.
|
||||||
- `main.py` демонстрирует, что проект корректно использует пакет, и выводит его версию, что подтверждает успешную интеграцию.
|
- **LangGraph‑агент**: граф создаётся через `StateGraph`, узел добавляется через `graph.add_node`, а запуск осуществляется через `graph.invoke`.
|
||||||
|
- **Пакет в требованиях**: упоминание `langchain-openai` в `requirements.txt` гарантирует, что зависимость будет установлена при развёртывании.
|
||||||
|
|
||||||
**Короткие фрагменты кода**
|
**Ключевые фрагменты кода**
|
||||||
|
|
||||||
`requirements.txt`
|
`src/agent.py` – инициализация LLM
|
||||||
```
|
|
||||||
langgraph>=0.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
`main.py`
|
|
||||||
```python
|
```python
|
||||||
import langgraph
|
llm = ChatOpenAI(
|
||||||
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
|
model="gpt-4o-mini",
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
def main():
|
`src/agent.py` – узел, который отправляет запрос в LLM
|
||||||
print("Langgraph version:", langgraph.__version__)
|
```python
|
||||||
|
def llm_node(state: Dict[str, List[BaseMessage]]) -> Dict[str, List[BaseMessage]]:
|
||||||
|
messages = state["messages"]
|
||||||
|
response: AIMessage = llm.invoke(messages)
|
||||||
|
new_messages = messages + [response]
|
||||||
|
return {"messages": new_messages}
|
||||||
|
```
|
||||||
|
|
||||||
|
`src/agent.py` – создание и запуск графа
|
||||||
|
```python
|
||||||
|
def create_agent() -> StateGraph:
|
||||||
|
graph = StateGraph(GraphState)
|
||||||
|
graph.add_node("llm", llm_node)
|
||||||
|
graph.set_entry_point("llm")
|
||||||
|
graph.add_edge("llm", END)
|
||||||
|
return graph
|
||||||
```
|
```
|
||||||
|
|
||||||
**Ограничения**
|
**Ограничения**
|
||||||
- В текущей реализации не проверяется наличие других зависимостей, но это не требуется по заданию.
|
- Нет обработки ошибок при вызове LLM (например, таймауты, недоступность сервиса).
|
||||||
- Если в будущем понадобится более строгая версия, её можно уточнить в `requirements.txt`.
|
- Нет поддержки потокового вывода (streaming).
|
||||||
|
- Для использования Ollama нужно заменить `ChatOpenAI` на соответствующий класс и задать URL‑адрес сервера.
|
||||||
|
- В текущей реализации граф состоит только из одного узла, поэтому рефлексия и более сложные сценарии пока не реализованы.
|
||||||
+4
-1
@@ -1 +1,4 @@
|
|||||||
langgraph>=0.0.1
|
langchain-openai>=0.0.1
|
||||||
|
langgraph>=0.0.1
|
||||||
|
langchain>=0.1.0
|
||||||
|
openai>=1.0.0
|
||||||
+61
-120
@@ -1,141 +1,82 @@
|
|||||||
"""
|
import os
|
||||||
Self-Correcting Agent implementation using LangGraph.
|
from typing import Dict, List
|
||||||
|
|
||||||
This module defines a simple LangGraph that:
|
from langgraph.graph import StateGraph, END
|
||||||
1. Generates an answer to a user question.
|
from langchain_openai import ChatOpenAI
|
||||||
2. Checks the quality of the answer.
|
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
|
||||||
3. Corrects the answer if needed.
|
|
||||||
4. Returns the final answer.
|
|
||||||
|
|
||||||
The graph is intentionally simple to satisfy the assignment specification
|
|
||||||
and to remain fully importable without external API keys.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
# Define the state type for the graph
|
||||||
from typing import Any, Dict
|
class GraphState:
|
||||||
|
messages: List[BaseMessage]
|
||||||
|
|
||||||
# Import LangGraph components
|
|
||||||
try:
|
|
||||||
from langgraph.graph import StateGraph, State, END
|
|
||||||
except ImportError as exc:
|
|
||||||
raise ImportError(
|
|
||||||
"langgraph is required. Install it via 'pip install langgraph==0.0.1'"
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
def llm_node(state: Dict[str, List[BaseMessage]]) -> Dict[str, List[BaseMessage]]:
|
||||||
# State definition
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
@dataclass
|
|
||||||
class AgentState(State):
|
|
||||||
"""
|
"""
|
||||||
Holds the state of the agent during execution.
|
Node that sends the current conversation to the LLM and appends the response.
|
||||||
"""
|
"""
|
||||||
question: str = ""
|
# Retrieve the current messages
|
||||||
answer: str = ""
|
messages = state["messages"]
|
||||||
feedback: str = ""
|
|
||||||
final_answer: str = ""
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# Initialize the LLM (OpenAI)
|
||||||
# Node implementations
|
llm = ChatOpenAI(
|
||||||
# --------------------------------------------------------------------------- #
|
api_key=os.getenv("OPENAI_API_KEY"),
|
||||||
def ask(state: AgentState) -> AgentState:
|
model="gpt-4o-mini", # You can change the model as needed
|
||||||
"""
|
)
|
||||||
Generates an answer to the provided question.
|
|
||||||
"""
|
|
||||||
# In a real implementation, this would call an LLM.
|
|
||||||
# Here we use a deterministic placeholder.
|
|
||||||
state.answer = f"Answer to: {state.question}"
|
|
||||||
return state
|
|
||||||
|
|
||||||
def check(state: AgentState) -> AgentState:
|
# Call the LLM with the conversation history
|
||||||
"""
|
response: AIMessage = llm.invoke(messages)
|
||||||
Checks the quality of the generated answer.
|
|
||||||
"""
|
|
||||||
# Simple heuristic: if the answer contains the word 'bad', flag it.
|
|
||||||
if "bad" in state.answer.lower():
|
|
||||||
state.feedback = "Needs correction"
|
|
||||||
else:
|
|
||||||
state.feedback = "Good"
|
|
||||||
return state
|
|
||||||
|
|
||||||
def correct(state: AgentState) -> AgentState:
|
# Append the LLM response to the conversation
|
||||||
|
new_messages = messages + [response]
|
||||||
|
return {"messages": new_messages}
|
||||||
|
|
||||||
|
|
||||||
|
def create_agent() -> StateGraph:
|
||||||
"""
|
"""
|
||||||
Corrects the answer if the feedback indicates a problem.
|
Creates a simple LangGraph agent that uses the LLM node.
|
||||||
"""
|
"""
|
||||||
if state.feedback == "Needs correction":
|
# Initialize the graph
|
||||||
# In a real scenario, this would call an LLM to rewrite the answer.
|
graph = StateGraph(GraphState)
|
||||||
state.final_answer = f"Corrected: {state.answer}"
|
|
||||||
else:
|
|
||||||
state.final_answer = state.answer
|
|
||||||
return state
|
|
||||||
|
|
||||||
def final(state: AgentState) -> str:
|
# Add the LLM node
|
||||||
"""
|
graph.add_node("llm", llm_node)
|
||||||
Returns the final answer to the user.
|
|
||||||
"""
|
|
||||||
return state.final_answer
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# Set the entry point and end condition
|
||||||
# Graph construction
|
graph.set_entry_point("llm")
|
||||||
# --------------------------------------------------------------------------- #
|
graph.add_edge("llm", END)
|
||||||
def build_agent_graph() -> StateGraph:
|
|
||||||
"""
|
|
||||||
Builds and returns the LangGraph for the self-correcting agent.
|
|
||||||
"""
|
|
||||||
graph = StateGraph(AgentState)
|
|
||||||
|
|
||||||
# Add nodes
|
|
||||||
graph.add_node("ask", ask)
|
|
||||||
graph.add_node("check", check)
|
|
||||||
graph.add_node("correct", correct)
|
|
||||||
graph.add_node("final", final)
|
|
||||||
|
|
||||||
# Define edges
|
|
||||||
graph.set_entry_point("ask")
|
|
||||||
graph.add_edge("ask", "check")
|
|
||||||
|
|
||||||
# Conditional transition from check to either correct or final
|
|
||||||
def check_transition(state: AgentState) -> str:
|
|
||||||
return "correct" if state.feedback != "Good" else "final"
|
|
||||||
|
|
||||||
graph.add_conditional_edges("check", check_transition)
|
|
||||||
|
|
||||||
graph.add_edge("correct", "final")
|
|
||||||
graph.add_edge("final", END)
|
|
||||||
|
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Public API
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
def run_agent(question: str) -> str:
|
|
||||||
"""
|
|
||||||
Runs the self-correcting agent on the given question.
|
|
||||||
|
|
||||||
Parameters
|
def run_agent(prompt: str) -> str:
|
||||||
----------
|
|
||||||
question : str
|
|
||||||
The user question to answer.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
The final answer produced by the agent.
|
|
||||||
"""
|
"""
|
||||||
graph = build_agent_graph()
|
Runs the agent with the given prompt and returns the LLM's final response.
|
||||||
# Initialize state
|
"""
|
||||||
init_state = AgentState(question=question)
|
# Create the graph
|
||||||
|
graph = create_agent()
|
||||||
|
|
||||||
|
# Build the initial state
|
||||||
|
initial_state = {"messages": [HumanMessage(content=prompt)]}
|
||||||
|
|
||||||
# Run the graph
|
# Run the graph
|
||||||
result = graph.invoke(init_state)
|
final_state = graph.invoke(initial_state)
|
||||||
# The result is the final answer string
|
|
||||||
return result
|
|
||||||
|
|
||||||
__all__ = [
|
# Extract the last AI message
|
||||||
"AgentState",
|
ai_messages = [msg for msg in final_state["messages"] if isinstance(msg, AIMessage)]
|
||||||
"ask",
|
if not ai_messages:
|
||||||
"check",
|
return "No response from LLM."
|
||||||
"correct",
|
return ai_messages[-1].content
|
||||||
"final",
|
|
||||||
"build_agent_graph",
|
|
||||||
"run_agent",
|
if __name__ == "__main__":
|
||||||
]
|
# Simple CLI usage
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Run the LangGraph agent with OpenAI LLM.")
|
||||||
|
parser.add_argument("prompt", type=str, help="The prompt to send to the agent.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
response = run_agent(args.prompt)
|
||||||
|
print("Agent response:")
|
||||||
|
print(response)
|
||||||
Reference in New Issue
Block a user