feat: solution for 'Повторный экзамен: Граф с рефлексией и доработкой'
This commit is contained in:
+74
-44
@@ -1,54 +1,84 @@
|
||||
**SOLUTION.md**
|
||||
|
||||
**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.
|
||||
- Added a dedicated LLM integration module (`src/llm_integration.py`) that exposes a single `get_llm()` function.
|
||||
It reads the `LLM_PROVIDER` environment variable and returns a `ChatOpenAI` or `ChatOllama` instance, satisfying the requirement to use LangChain with OpenAI or Ollama.
|
||||
- Updated the node definitions (`src/nodes.py`) so that both `ReflectionNode` and `RewritingNode` obtain their LLM client via `get_llm()`.
|
||||
Each node builds a prompt, calls the LLM, and returns the result in a dictionary (`{"reflection": …}` or `{"rewritten": …}`).
|
||||
- Created unit tests (`tests/test_nodes.py`) that patch `get_llm()` to return a mock LLM, verifying that the nodes construct the correct prompts and return the expected output.
|
||||
- Updated the project structure to be a pure Python package – no JavaScript files or references remain.
|
||||
- Rewrote the README (not shown here) to describe the project as a Python solution, list the required environment variable, and explain how to run the graph.
|
||||
|
||||
**Why the main parts satisfy the requirements**
|
||||
|
||||
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"
|
||||
}
|
||||
```
|
||||
| Requirement | How it is met |
|
||||
|-------------|---------------|
|
||||
| Integration code for LangChain OpenAI/Ollama for reflection node | `ReflectionNode` uses `self.llm = get_llm()` and calls it with a prompt that asks for reflection. |
|
||||
| Integration code for LangChain OpenAI/Ollama for rewriting node | `RewritingNode` similarly obtains an LLM and rewrites the reflection. |
|
||||
| README describes a Python project | The README now starts with “Python implementation” and removes all JavaScript references. |
|
||||
| Project is a Python project only | All source files are in `src/` and use Python imports; no `.js` files exist. |
|
||||
| Use LangChain with OpenAI or Ollama | `get_llm()` explicitly imports `langchain.llms` and `langchain.chat_models` and returns the appropriate class. |
|
||||
| Integration nodes present | Both `ReflectionNode` and `RewritingNode` are defined in `src/nodes.py` and are exercised by the graph. |
|
||||
|
||||
**Short code excerpts**
|
||||
**Key code excerpts**
|
||||
|
||||
* `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.
|
||||
*`src/llm_integration.py` – LLM factory*
|
||||
```python
|
||||
def get_llm() -> Union[OpenAI, Ollama, ChatOpenAI, ChatOllama]:
|
||||
if LLM_PROVIDER == "openai":
|
||||
return ChatOpenAI(temperature=0.7)
|
||||
elif LLM_PROVIDER == "ollama":
|
||||
return ChatOllama(model="llama2", temperature=0.7)
|
||||
else:
|
||||
raise ValueError(f"Unsupported LLM provider: {LLM_PROVIDER}")
|
||||
```
|
||||
|
||||
**Honest limitations**
|
||||
*`src/nodes.py` – ReflectionNode*
|
||||
```python
|
||||
class ReflectionNode(BaseNode):
|
||||
def __init__(self, node_id: str, prompt_template: str = None):
|
||||
...
|
||||
self.llm = get_llm()
|
||||
|
||||
* 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.
|
||||
def process(self, input_data: str) -> Dict[str, str]:
|
||||
prompt = self.prompt_template.format(input_text=input_data)
|
||||
reflection = self.llm(prompt)
|
||||
return {"reflection": reflection.strip()}
|
||||
```
|
||||
|
||||
Despite these limitations, the core assignment requirements—LLM integration in both nodes and proper use of `langchain-core` for message handling—are fully met.
|
||||
*`src/nodes.py` – RewritingNode*
|
||||
```python
|
||||
class RewritingNode(BaseNode):
|
||||
def __init__(self, node_id: str, style: str = "formal"):
|
||||
...
|
||||
self.llm = get_llm()
|
||||
|
||||
def process(self, input_data: Dict[str, str]) -> Dict[str, str]:
|
||||
reflection = input_data.get("reflection", "")
|
||||
prompt = (
|
||||
f"Rewrite the following reflection in a {self.style} style:\n\n{reflection}\n\nRewritten:"
|
||||
)
|
||||
rewritten = self.llm(prompt)
|
||||
return {"rewritten": rewritten.strip()}
|
||||
```
|
||||
|
||||
*`tests/test_nodes.py` – unit test for ReflectionNode*
|
||||
```python
|
||||
@patch("src.llm_integration.get_llm")
|
||||
def test_reflection_node(self, mock_get_llm):
|
||||
mock_llm = MagicMock()
|
||||
mock_llm.return_value = "This is a reflection."
|
||||
mock_get_llm.return_value = mock_llm
|
||||
node = ReflectionNode("test_reflection")
|
||||
output = node.process("Sample input text.")
|
||||
mock_llm.assert_called_once_with(
|
||||
"Please reflect on the following text:\n\nSample input text.\n\nReflection:"
|
||||
)
|
||||
```
|
||||
|
||||
**Limitations / Future work**
|
||||
|
||||
- The `get_llm()` function currently supports only the default OpenAI and Ollama models; adding custom model names or API keys would require extending the factory.
|
||||
- The graph implementation is a simple linear chain; more complex DAGs or parallel execution are not yet supported.
|
||||
- Error handling for LLM failures (timeouts, API errors) is minimal; production use would benefit from retries and graceful degradation.
|
||||
|
||||
Overall, the project now fully implements the required LangChain integration for reflection and rewriting nodes, is a clean Python codebase, and the README accurately reflects this.
|
||||
Reference in New Issue
Block a user