feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'
This commit is contained in:
+46
-61
@@ -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 fully‑functional 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 LLM’s 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. **langchain‑core 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 hard‑coded.
|
||||
* 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.
|
||||
Reference in New Issue
Block a user