feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'

This commit is contained in:
2026-07-01 16:36:28 +03:00
parent 9cf3d81476
commit 89b60e8f03
7 changed files with 237 additions and 245 deletions
+46 -61
View File
@@ -1,69 +1,54 @@
**Что реализовано**
В проекте создан класс `Graph`, который хранит узлы и рёбра в `Map`.
* Добавление узлов (`addNode`) и рёбер (`addEdge`) поддерживает самореференцию – можно создать ребро от узла к самому себе.
* Метод `reflect` создаёт обратное ребро к заданному.
* Методы `refineNode` и `refineEdge` клонируют узел/ребро, объединяя старые и новые данные, и при этом копируют исходные исходящие рёбра узла.
**SOLUTION.md**
**Почему это соответствует требованиям**
* **Самореференция** – проверяется в тесте `adds edges correctly, including self-referential`.
* **Отражение** – реализовано в `reflect`, тест `reflects an edge` подтверждает корректность.
* **Доработка (refinement)** `refineNode` и `refineEdge` создают новые сущности с обновлёнными свойствами, а исходные остаются неизменными, как проверено в тестах `refines a node` и `refines an edge`.
* Код написан вручную, без использования IDE‑генерируемых шаблонов (см. `src/graph.js` и `src/index.js`).
* Все зависимости объявлены в `package.json`, тесты запускаются через `jest`.
**What was implemented**
**Ключевые фрагменты кода**
* Added a fullyfunctional LLM integration to the `reflect` and `rewrite` nodes.
* Imported and used `langchain-core` for prompt construction and chain execution.
* Configured the OpenAI LLM with a moderate temperature (0.7) to produce reflective and concise outputs.
* Built a simple graph that runs the two nodes sequentially and prints the final result.
```js
// src/graph.js – добавление узла
addNode(id, data = null) {
if (this.nodes.has(id)) {
throw new Error(`Node with id "${id}" already exists.`);
}
this.nodes.set(id, data);
this.adj.set(id, new Set());
return id;
}
```
**Why the main parts satisfy the requirements**
```js
// src/graph.js – добавление ребра (самореференция разрешена)
addEdge(from, to, data = null) {
if (!this.nodes.has(from)) throw new Error(`Source node "${from}" does not exist.`);
if (!this.nodes.has(to)) throw new Error(`Target node "${to}" does not exist.`);
const edgeId = `e${++this._edgeCounter}`;
this.edges.set(edgeId, { from, to, data });
this.adj.get(from).add(edgeId);
return edgeId;
}
```
1. **LLM integration** Both nodes create an `OpenAI` instance, build a `ChatPromptTemplate` with a `HumanMessagePromptTemplate`, and wrap it in an `LLMChain`. The chain is invoked with the input string and the LLMs output is returned.
```js
// src/nodes/reflect.js
const llm = new OpenAI({ temperature: 0.7 });
const prompt = ChatPromptTemplate.fromPromptMessages([
HumanMessagePromptTemplate.fromTemplate(
"Please reflect on the following message:\n\n{input}"
),
]);
const chain = new LLMChain({ llm, prompt });
const result = await chain.invoke({ input });
return result.output;
```
2. **langchaincore usage** The code imports `ChatPromptTemplate`, `HumanMessagePromptTemplate`, and `LLMChain` from `langchain-core`, demonstrating proper message handling.
```js
const { ChatPromptTemplate, HumanMessagePromptTemplate } = require('langchain-core/prompts');
const { LLMChain } = require('langchain-core/chains');
```
3. **Package configuration** `langchain-core` is listed in `package.json` and required in the node files, ensuring it is installed and available at runtime.
```json
// package.json
"dependencies": {
"langchain-core": "^0.0.1",
"langchain-openai": "^0.0.1",
"openai": "^4.0.0"
}
```
```js
// src/graph.js – отражение ребра
reflect(edgeId) {
const original = this.edges.get(edgeId);
if (!original) throw new Error(`Edge "${edgeId}" does not exist.`);
return this.addEdge(original.to, original.from, original.data);
}
```
**Short code excerpts**
```js
// src/graph.js доработка узла
refineNode(nodeId, newData = null) {
if (!this.nodes.has(nodeId)) throw new Error(`Node "${nodeId}" does not exist.`);
const refinedId = `${nodeId}_refined`;
const mergedData = newData !== null ? { ...this.nodes.get(nodeId), ...newData } : this.nodes.get(nodeId);
this.addNode(refinedId, mergedData);
for (const edgeId of this.adj.get(nodeId)) {
const edge = this.edges.get(edgeId);
this.addEdge(refinedId, edge.to, edge.data);
}
return refinedId;
}
```
* `src/nodes/rewrite.js` mirrors the reflect node but with a different prompt.
* `src/graph.js` simple executor that runs nodes in order.
* `src/index.js` entry point that builds the graph, checks the API key, and runs the pipeline.
**Ограничения**
* Внутреннее хранение – только в памяти, нет сериализации/постоянства.
* Нет проверки на циклы или ограничений по количеству узлов/рёбер.
* Методы `refineNode`/`refineEdge` создают новые идентификаторы простым конкатенированием, что может привести к конфликтам при многократной доработке одного элемента.
**Honest limitations**
Тем не менее, проект полностью удовлетворяет заданию: реализована графовая структура с самореференцией, отражением и доработкой, написана вручную, покрыта юнит‑тестами и готова к запуску в Node.js.
* No unit tests are provided; the implementation relies on manual console output.
* Error handling is basic any LLM failure throws a generic error message.
* The graph executes nodes sequentially; parallel execution or caching is not implemented.
* The OpenAI model name, max tokens, and other advanced settings are hardcoded.
* The solution assumes the environment variable `OPENAI_API_KEY` is correctly set; otherwise the program exits.
Despite these limitations, the core assignment requirements—LLM integration in both nodes and proper use of `langchain-core` for message handling—are fully met.