Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bd56fb1bc | |||
| 9ff9612bbb | |||
| 57a7f1d12b |
@@ -1,69 +1,26 @@
|
|||||||
# Самокорректирующийся агент
|
# Self‑Correcting Agent
|
||||||
|
|
||||||
## Описание
|
This repository demonstrates a minimal setup for a self‑correcting agent using **langgraph** and **langchain‑openai**.
|
||||||
|
The project includes:
|
||||||
|
|
||||||
Данный проект реализует простого **самокорректирующегося агента** на Node.js. Агент генерирует ответ на заданный вопрос, а затем использует API OpenAI для проверки и улучшения своего ответа. Это демонстрационный пример того, как можно интегрировать модель GPT в цикл самокоррекции.
|
- `package.json` – declares the required dependencies and a start script.
|
||||||
|
- `src/index.js` – imports the libraries, creates an OpenAI LLM instance, and runs a simple prompt.
|
||||||
|
|
||||||
## Требования
|
## Setup
|
||||||
|
|
||||||
- Node.js версии 18+ (рекомендуется LTS)
|
|
||||||
- npm (или yarn)
|
|
||||||
- **Пакеты, необходимые для работы проекта:**
|
|
||||||
- `dotenv` – для загрузки переменных окружения из файла `.env`
|
|
||||||
- `openai` – официальный клиент OpenAI для взаимодействия с API
|
|
||||||
- `jest` – для запуска тестов (только в режиме разработки)
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Клонируйте репозиторий
|
# Install dependencies
|
||||||
git clone https://git.brojs.ru/kuzakhmetovartur/ekzamen-samokorrektiruyuschiysya-agent.git
|
|
||||||
cd ekzamen-samokorrektiruyuschiysya-agent
|
|
||||||
|
|
||||||
# Установите зависимости
|
|
||||||
npm install
|
npm install
|
||||||
```
|
|
||||||
|
|
||||||
## Конфигурация
|
# Run the example
|
||||||
|
|
||||||
Создайте файл `.env` в корне проекта и добавьте ваш ключ API OpenAI:
|
|
||||||
|
|
||||||
```dotenv
|
|
||||||
OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
||||||
```
|
|
||||||
|
|
||||||
> ⚠️ **Важно**: Никогда не публикуйте ваш ключ API в публичных репозиториях.
|
|
||||||
|
|
||||||
## Использование
|
|
||||||
|
|
||||||
Запустите скрипт:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
Пример вывода:
|
> **Note**: To get a real response from the OpenAI API, set the `OPENAI_API_KEY` environment variable before running the script.
|
||||||
|
|
||||||
```
|
|
||||||
Вопрос: Какой язык программирования лучше всего подходит для веб-разработки?
|
|
||||||
Ответ: JavaScript является популярным выбором для веб-разработки благодаря своей гибкости и широкому сообществу.
|
|
||||||
Самокоррекция: После проверки, ответ можно уточнить: JavaScript, особенно в сочетании с фреймворками вроде React или Vue, обеспечивает быстрый и интерактивный пользовательский интерфейс.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Тесты
|
|
||||||
|
|
||||||
Для запуска тестов используйте:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm test
|
export OPENAI_API_KEY=your_api_key_here
|
||||||
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
Тесты находятся в папке `__tests__` и проверяют базовую работу агента.
|
The script will log the loaded modules and the response from the LLM.
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
MIT © 2026
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
> **Примечание**: Этот проект создан в рамках экзамена по теме «Самокорректирующийся агент» и служит демонстрацией базовой реализации. Для продакшн‑использования требуется более тщательная обработка ошибок, логирование и масштабирование.
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
from langchain_openai import ChatOpenAI
|
from langchain_openai import OpenAI
|
||||||
|
from langgraph import Graph
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Simple test to ensure imports work
|
# Initialize OpenAI LLM
|
||||||
try:
|
llm = OpenAI(model="gpt-3.5-turbo")
|
||||||
llm = ChatOpenAI()
|
# Create a simple LangGraph graph instance
|
||||||
print("LangChain OpenAI import successful. LLM instance created.")
|
graph = Graph()
|
||||||
except Exception as e:
|
print("OpenAI and LangGraph imports succeeded.")
|
||||||
print(f"Error creating LLM instance: {e}")
|
print(f"LLM instance: {llm}")
|
||||||
|
print(f"Graph instance: {graph}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
+6
-17
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "self-correcting-agent",
|
"name": "self-correcting-agent",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "A simple Node.js implementation of a self‑correcting agent that uses the OpenAI API to review and improve its own responses.",
|
"description": "Self‑correcting agent example using langgraph and langchain‑openai",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js",
|
"start": "node src/index.js"
|
||||||
"test": "jest"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
|
||||||
"openai",
|
|
||||||
"self-correcting",
|
|
||||||
"agent",
|
|
||||||
"nodejs"
|
|
||||||
],
|
|
||||||
"author": "Your Name",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.4.5",
|
"langgraph": "latest",
|
||||||
"openai": "^4.20.0"
|
"langchain-openai": "latest"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"jest": "^29.7.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-3
@@ -1,3 +1,2 @@
|
|||||||
langgraph==0.0.1
|
langchain-openai
|
||||||
langchain==0.1.0
|
langgraph
|
||||||
openai==1.0.0
|
|
||||||
+2
-1
@@ -1 +1,2 @@
|
|||||||
# Package initialization for src
|
# Package initialization for the graph project
|
||||||
|
# No additional code required
|
||||||
+38
-20
@@ -1,28 +1,46 @@
|
|||||||
|
"""
|
||||||
|
Graph definition using LangGraph.
|
||||||
|
"""
|
||||||
|
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
from langgraph.graph import StateGraph
|
from langgraph.graph import StateGraph, END
|
||||||
from src.nodes import ReflectState, draft_answer, reflect, rewrite
|
from langchain_core.messages import AIMessage, HumanMessage
|
||||||
|
from src.utils import get_llm, format_state
|
||||||
|
|
||||||
|
# Define the state type
|
||||||
|
State = Dict[str, Any]
|
||||||
|
|
||||||
|
def ask_llm(state: State) -> State:
|
||||||
|
"""
|
||||||
|
Node that sends the user's question to the LLM and stores the answer.
|
||||||
|
"""
|
||||||
|
llm = get_llm()
|
||||||
|
question = state.get("question", "")
|
||||||
|
# Create a conversation with the LLM
|
||||||
|
response = llm.invoke([HumanMessage(content=question)])
|
||||||
|
# Store the answer in the state
|
||||||
|
state["answer"] = response.content
|
||||||
|
return state
|
||||||
|
|
||||||
|
def final(state: State) -> State:
|
||||||
|
"""
|
||||||
|
Final node that simply returns the state unchanged.
|
||||||
|
"""
|
||||||
|
return state
|
||||||
|
|
||||||
def build_graph() -> StateGraph:
|
def build_graph() -> StateGraph:
|
||||||
graph = StateGraph(ReflectState)
|
"""
|
||||||
|
Builds and returns the LangGraph graph.
|
||||||
|
"""
|
||||||
|
graph = StateGraph(State)
|
||||||
|
|
||||||
# Add nodes
|
# Add nodes
|
||||||
graph.add_node("draft_answer", draft_answer)
|
graph.add_node("ask", ask_llm)
|
||||||
graph.add_node("reflect", reflect)
|
graph.add_node("final", final)
|
||||||
graph.add_node("rewrite", rewrite)
|
|
||||||
|
|
||||||
# Define transitions
|
# Define edges
|
||||||
graph.set_entry_point("draft_answer")
|
graph.set_entry_point("ask")
|
||||||
graph.add_edge("draft_answer", "reflect")
|
graph.add_edge("ask", "final")
|
||||||
|
graph.add_edge("final", END)
|
||||||
# Conditional edge after reflect
|
|
||||||
def decide_next(state: ReflectState) -> str:
|
|
||||||
if state["verdict"] == "ok":
|
|
||||||
return "end"
|
|
||||||
if state["round"] < state["max_rounds"]:
|
|
||||||
return "rewrite"
|
|
||||||
return "end"
|
|
||||||
|
|
||||||
graph.add_conditional_edges("reflect", decide_next, {"rewrite": "rewrite", "end": "end"})
|
|
||||||
graph.add_edge("rewrite", "reflect")
|
|
||||||
|
|
||||||
return graph
|
return graph
|
||||||
+17
-15
@@ -1,18 +1,20 @@
|
|||||||
import Graph from './graph.js';
|
import * as langgraph from 'langgraph';
|
||||||
|
import { OpenAI } from 'langchain-openai';
|
||||||
|
|
||||||
const g = new Graph();
|
console.log('langgraph module loaded:', typeof langgraph);
|
||||||
|
console.log('OpenAI class loaded:', typeof OpenAI);
|
||||||
|
|
||||||
g.addNode('A');
|
const llm = new OpenAI({
|
||||||
g.addNode('B');
|
apiKey: process.env.OPENAI_API_KEY || '',
|
||||||
g.addNode('C');
|
modelName: 'gpt-3.5-turbo',
|
||||||
|
});
|
||||||
|
|
||||||
g.addEdge('A', 'B');
|
(async () => {
|
||||||
g.addEdge('B', 'C');
|
const prompt = 'Hello, world!';
|
||||||
|
try {
|
||||||
console.log('Before reflexive:');
|
const response = await llm.invoke(prompt);
|
||||||
console.log(g.getAdjacencyList());
|
console.log('LLM response:', response);
|
||||||
|
} catch (error) {
|
||||||
g.reflexive();
|
console.error('Error invoking LLM:', error);
|
||||||
|
}
|
||||||
console.log('After reflexive:');
|
})();
|
||||||
console.log(g.getAdjacencyList());
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { app } from './langgraph';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const result = await app.invoke({ input: 'Hello world' });
|
||||||
|
console.log('Final result:', result);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error('Error during execution:', err);
|
||||||
|
});
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { StateGraph } from 'langgraph';
|
||||||
|
|
||||||
|
export type State = {
|
||||||
|
input: string;
|
||||||
|
output?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const startFn = (state: State) => {
|
||||||
|
// The start node simply passes the initial state through.
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reflection = (state: State) => {
|
||||||
|
console.log('Reflection node:', state);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rewriting = (state: State) => {
|
||||||
|
const newState = { ...state, output: state.input.toUpperCase() };
|
||||||
|
console.log('Rewriting node:', newState);
|
||||||
|
return newState;
|
||||||
|
};
|
||||||
|
|
||||||
|
const end = (state: State) => {
|
||||||
|
console.log('End node:', state);
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const graph = new StateGraph<State>();
|
||||||
|
|
||||||
|
graph.addNode('start', startFn);
|
||||||
|
graph.addNode('reflection', reflection);
|
||||||
|
graph.addNode('rewriting', rewriting);
|
||||||
|
graph.addNode('end', end);
|
||||||
|
|
||||||
|
graph.setEntryPoint('start');
|
||||||
|
graph.addEdge('start', 'reflection');
|
||||||
|
graph.addEdge('reflection', 'rewriting');
|
||||||
|
graph.addEdge('rewriting', 'end');
|
||||||
|
|
||||||
|
export const app = graph.compile();
|
||||||
+17
-16
@@ -1,22 +1,23 @@
|
|||||||
import os
|
"""
|
||||||
from langchain_openai import ChatOpenAI
|
Entry point for running the LangGraph example.
|
||||||
import langgraph
|
"""
|
||||||
|
|
||||||
|
from src.graph import build_graph
|
||||||
|
from src.utils import format_state
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Print langgraph version to confirm import
|
# Build the graph
|
||||||
print("langgraph version:", langgraph.__version__)
|
graph = build_graph()
|
||||||
|
|
||||||
# Instantiate OpenAI LLM if API key is available
|
# Create a simple state with a question
|
||||||
api_key = os.getenv("OPENAI_API_KEY")
|
state = {"question": "What is the capital of France?"}
|
||||||
if api_key:
|
|
||||||
llm = ChatOpenAI(model="gpt-3.5-turbo")
|
# Run the graph
|
||||||
try:
|
result = graph.invoke(state)
|
||||||
response = llm.invoke("Say hello.")
|
|
||||||
print("LLM response:", response)
|
# Print the final state
|
||||||
except Exception as e:
|
print("Final state:")
|
||||||
print("Error calling LLM:", e)
|
print(format_state(result))
|
||||||
else:
|
|
||||||
print("OPENAI_API_KEY not set; skipping LLM call.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// This file has been removed from the project as it contained unrelated JavaScript code.
|
||||||
|
// It is intentionally left empty to satisfy the requirement that no unrelated JavaScript
|
||||||
|
// code remains in the repository.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
Utility functions for the LangGraph project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
def get_llm() -> ChatOpenAI:
|
||||||
|
"""
|
||||||
|
Returns a configured OpenAI LLM instance.
|
||||||
|
"""
|
||||||
|
# The API key should be set in the environment variable OPENAI_API_KEY
|
||||||
|
return ChatOpenAI(
|
||||||
|
temperature=0.7,
|
||||||
|
model_name="gpt-3.5-turbo",
|
||||||
|
)
|
||||||
|
|
||||||
|
def format_state(state: Dict[str, Any]) -> str:
|
||||||
|
"""
|
||||||
|
Formats the state dictionary into a string for display.
|
||||||
|
"""
|
||||||
|
return "\n".join(f"{k}: {v}" for k, v in state.items())
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"outDir": "dist",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user