139 lines
5.9 KiB
Python
139 lines
5.9 KiB
Python
from langchain_openai import ChatOpenAI
|
|
from langchain_core.messages import HumanMessage, SystemMessage
|
|
from state import ReflectState
|
|
|
|
# Инициализация LLM
|
|
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
|
|
|
|
# Промпты для узлов
|
|
DRAFT_PROMPT = """Ты — эксперт, который дает краткие, но содержательные ответы.
|
|
Ответь на вопрос пользователя в 5-10 предложений.
|
|
Будь конкретным, избегай общих фраз и "воды".
|
|
|
|
Вопрос: {question}"""
|
|
|
|
REFLECT_PROMPT = """Ты — строгий критик, оценивающий качество ответа.
|
|
Критерии оценки:
|
|
1. Полнота: ответ покрывает все аспекты вопроса
|
|
2. Конкретика: есть примеры, детали, точные формулировки
|
|
3. Отсутствие воды: нет общих фраз, повторений, лишних слов
|
|
|
|
Ответ для оценки: {draft}
|
|
|
|
Твой ответ должен быть в формате:
|
|
VERDICT: [ok/needs_revision]
|
|
CRITIQUE: [2-3 конкретных пункта замечаний или похвала]
|
|
|
|
Примеры:
|
|
VERDICT: ok
|
|
CRITIQUE: Ответ полный, есть примеры, нет воды.
|
|
|
|
VERDICT: needs_revision
|
|
CRITIQUE: 1. Нет конкретных примеров. 2. Слишком много общих фраз. 3. Не раскрыта разница в контексте MCP."""
|
|
|
|
REWRITE_PROMPT = """Улучши ответ на вопрос, учитывая замечания критика.
|
|
Вопрос: {question}
|
|
Предыдущая версия: {draft}
|
|
Замечания критика: {critique}
|
|
|
|
Дай исправленную версию (5-10 предложений), которая:
|
|
- Учитывает все замечания
|
|
- Становится более полной и конкретной
|
|
- Убирает воду
|
|
- Сохраняет краткость (5-10 предложений)"""
|
|
|
|
def draft_answer(state: ReflectState) -> ReflectState:
|
|
"""
|
|
Узел: первичная генерация ответа с try/except
|
|
При ошибке заполняет поле error
|
|
"""
|
|
print(f"\n📝 [Раунд {state['round']}] Генерация черновика...")
|
|
|
|
try:
|
|
prompt = DRAFT_PROMPT.format(question=state["question"])
|
|
response = llm.invoke([HumanMessage(content=prompt)])
|
|
|
|
# Проверка качества ответа (базовая валидация)
|
|
if len(response.content.split()) < 50:
|
|
raise ValueError("Слишком короткий ответ (<50 слов)")
|
|
|
|
if len(response.content.split()) > 300:
|
|
raise ValueError("Слишком длинный ответ (>300 слов)")
|
|
|
|
print(f" ✅ Черновик успешно создан")
|
|
new_state = state.copy()
|
|
new_state["draft"] = response.content
|
|
new_state["error"] = None # Очищаем ошибку
|
|
return new_state
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Ошибка генерации: {str(e)}")
|
|
new_state = state.copy()
|
|
new_state["draft"] = ""
|
|
new_state["error"] = str(e)
|
|
return new_state
|
|
|
|
def reflect(state: ReflectState) -> ReflectState:
|
|
"""Узел: критическая оценка ответа LLM-критиком"""
|
|
print(f"🔍 [Раунд {state['round']}] Рефлексия критика...")
|
|
|
|
try:
|
|
prompt = REFLECT_PROMPT.format(draft=state["draft"])
|
|
response = llm.invoke([HumanMessage(content=prompt)])
|
|
|
|
# Парсим ответ критика
|
|
lines = response.content.strip().split('\n')
|
|
verdict = "needs_revision"
|
|
critique = response.content
|
|
|
|
for line in lines:
|
|
if line.startswith("VERDICT:"):
|
|
verdict = line.replace("VERDICT:", "").strip().lower()
|
|
elif line.startswith("CRITIQUE:"):
|
|
critique = line.replace("CRITIQUE:", "").strip()
|
|
|
|
if verdict not in ["ok", "needs_revision"]:
|
|
verdict = "needs_revision"
|
|
|
|
new_state = state.copy()
|
|
new_state["verdict"] = verdict
|
|
new_state["critique"] = critique
|
|
|
|
print(f" Вердикт: {verdict.upper()}")
|
|
if verdict == "needs_revision":
|
|
print(f" Замечания: {critique[:100]}...")
|
|
|
|
return new_state
|
|
|
|
except Exception as e:
|
|
# Если критик сломался, считаем что нужна доработка
|
|
print(f" ⚠️ Ошибка критика: {str(e)}")
|
|
new_state = state.copy()
|
|
new_state["verdict"] = "needs_revision"
|
|
new_state["critique"] = f"Критик не смог оценить ответ: {str(e)}"
|
|
return new_state
|
|
|
|
def rewrite(state: ReflectState) -> ReflectState:
|
|
"""Узел: переписывание ответа с учетом замечаний"""
|
|
print(f"✏️ [Раунд {state['round']}] Переписывание с учетом критики...")
|
|
|
|
try:
|
|
prompt = REWRITE_PROMPT.format(
|
|
question=state["question"],
|
|
draft=state["draft"],
|
|
critique=state["critique"]
|
|
)
|
|
response = llm.invoke([HumanMessage(content=prompt)])
|
|
|
|
new_state = state.copy()
|
|
new_state["draft"] = response.content
|
|
new_state["round"] = state["round"] + 1
|
|
new_state["error"] = None
|
|
return new_state
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Ошибка переписывания: {str(e)}")
|
|
new_state = state.copy()
|
|
new_state["error"] = str(e)
|
|
new_state["round"] = state["round"] + 1
|
|
return new_state |