fix: build_agent/build_llm + inference BroJS для автопроверки
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from langchain.agents import create_agent
|
from langchain.agents import create_agent
|
||||||
@@ -14,10 +15,37 @@ load_dotenv()
|
|||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
llm = ChatOpenAI(
|
BROJS_INFERENCE_URL = "https://platform.brojs.ru/jrnl-bh/api/inference/v1"
|
||||||
model=os.getenv("OPENAI_MODEL", "openai/gpt-oss-20b:free"),
|
DEFAULT_MODEL = "openai/gpt-oss-20b:free"
|
||||||
base_url=os.getenv("OPENAI_BASE_URL", "https://openrouter.ai/api/v1"),
|
THREAD_ID = "разговор-1"
|
||||||
api_key=os.getenv("OPENAI_API_KEY", "fake"),
|
|
||||||
|
|
||||||
|
def _api_key() -> str:
|
||||||
|
return (
|
||||||
|
os.getenv("OPENAI_API_KEY")
|
||||||
|
or os.getenv("JOURNAL_MCP_PAT")
|
||||||
|
or os.getenv("JOURNAL_TOKEN")
|
||||||
|
or ""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _base_url() -> str:
|
||||||
|
if os.getenv("OPENAI_BASE_URL"):
|
||||||
|
return os.environ["OPENAI_BASE_URL"]
|
||||||
|
if os.getenv("OPENAI_API_KEY"):
|
||||||
|
return os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1")
|
||||||
|
return BROJS_INFERENCE_URL
|
||||||
|
|
||||||
|
|
||||||
|
def _model() -> str:
|
||||||
|
return os.getenv("OPENAI_MODEL") or os.getenv("OPENROUTER_MODEL") or DEFAULT_MODEL
|
||||||
|
|
||||||
|
|
||||||
|
def build_llm() -> ChatOpenAI:
|
||||||
|
return ChatOpenAI(
|
||||||
|
model=_model(),
|
||||||
|
base_url=_base_url(),
|
||||||
|
api_key=_api_key(),
|
||||||
temperature=0.0,
|
temperature=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,32 +56,38 @@ def get_price(city: str, date: str = "сегодня") -> str:
|
|||||||
return f"{city}, {date}: ориентировочно 150–300 руб. за базовую корзину."
|
return f"{city}, {date}: ориентировочно 150–300 руб. за базовую корзину."
|
||||||
|
|
||||||
|
|
||||||
|
def build_agent(llm: ChatOpenAI | None = None):
|
||||||
|
"""Агент с памятью и interrupt_before tools — точка входа для автопроверки."""
|
||||||
memory = MemorySaver()
|
memory = MemorySaver()
|
||||||
|
return create_agent(
|
||||||
agent = create_agent(
|
model=llm or build_llm(),
|
||||||
model=llm,
|
|
||||||
tools=[get_price],
|
tools=[get_price],
|
||||||
system_prompt="Ты помощник по планированию покупок и погоде. Помни контекст разговора.",
|
system_prompt=(
|
||||||
|
"Ты помощник по планированию покупок и погоде. "
|
||||||
|
"Помни контекст разговора. Для цен вызывай get_price."
|
||||||
|
),
|
||||||
checkpointer=memory,
|
checkpointer=memory,
|
||||||
interrupt_before=["tools"],
|
interrupt_before=["tools"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _print_pending_tool(config: dict) -> None:
|
def _print_pending_tool(agent, config: dict) -> str:
|
||||||
state = agent.get_state(config)
|
state = agent.get_state(config)
|
||||||
messages = state.values.get("messages", [])
|
messages = state.values.get("messages", [])
|
||||||
if not messages:
|
if not messages:
|
||||||
return
|
return "unknown_tool()"
|
||||||
last = messages[-1]
|
last = messages[-1]
|
||||||
tool_calls = getattr(last, "tool_calls", None) or []
|
tool_calls = getattr(last, "tool_calls", None) or []
|
||||||
if not tool_calls:
|
if not tool_calls:
|
||||||
return
|
return "unknown_tool()"
|
||||||
tc = tool_calls[0]
|
tc = tool_calls[0]
|
||||||
name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "?")
|
name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "?")
|
||||||
args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {})
|
args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {})
|
||||||
|
repr_str = f"{name}({args})"
|
||||||
console.print("\n --- --- --- ")
|
console.print("\n --- --- --- ")
|
||||||
console.print(f"{name}({args})")
|
console.print(repr_str)
|
||||||
console.print(f"Агент хочет вызвать утилиту {name}({args})")
|
console.print(f"Агент хочет вызвать утилиту {repr_str}")
|
||||||
|
return repr_str
|
||||||
|
|
||||||
|
|
||||||
def _print_updates(chunk_data: dict) -> None:
|
def _print_updates(chunk_data: dict) -> None:
|
||||||
@@ -72,7 +106,13 @@ def _print_updates(chunk_data: dict) -> None:
|
|||||||
console.print(f"{name}({args})")
|
console.print(f"{name}({args})")
|
||||||
|
|
||||||
|
|
||||||
def ask_and_run(user_input: dict | None, config: dict) -> None:
|
def ask_and_run(
|
||||||
|
agent,
|
||||||
|
user_input: dict | None,
|
||||||
|
config: dict,
|
||||||
|
*,
|
||||||
|
auto_approve: bool = False,
|
||||||
|
) -> None:
|
||||||
"""Запуск или возобновление агента с обработкой паузы перед tools."""
|
"""Запуск или возобновление агента с обработкой паузы перед tools."""
|
||||||
for chunk in agent.stream(
|
for chunk in agent.stream(
|
||||||
user_input,
|
user_input,
|
||||||
@@ -97,10 +137,13 @@ def ask_and_run(user_input: dict | None, config: dict) -> None:
|
|||||||
_print_updates(chunk_data)
|
_print_updates(chunk_data)
|
||||||
|
|
||||||
if "__interrupt__" in chunk_data and state.next == ("tools",):
|
if "__interrupt__" in chunk_data and state.next == ("tools",):
|
||||||
_print_pending_tool(config)
|
_print_pending_tool(agent, config)
|
||||||
|
if auto_approve:
|
||||||
|
ask_and_run(agent, None, config, auto_approve=True)
|
||||||
|
else:
|
||||||
answer = input("Разрешить? (Y/n): ").strip().lower()
|
answer = input("Разрешить? (Y/n): ").strip().lower()
|
||||||
if answer in ("", "y", "yes", "д", "да"):
|
if answer in ("", "y", "yes", "д", "да"):
|
||||||
ask_and_run(None, config)
|
ask_and_run(agent, None, config, auto_approve=auto_approve)
|
||||||
else:
|
else:
|
||||||
console.print("Отменено")
|
console.print("Отменено")
|
||||||
return
|
return
|
||||||
@@ -108,8 +151,9 @@ def ask_and_run(user_input: dict | None, config: dict) -> None:
|
|||||||
console.print()
|
console.print()
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def run_chat() -> None:
|
||||||
config = {"configurable": {"thread_id": "разговор-1"}}
|
agent = build_agent()
|
||||||
|
config = {"configurable": {"thread_id": THREAD_ID}}
|
||||||
console.print(
|
console.print(
|
||||||
"Чат с агентом (память + подтверждение tools). "
|
"Чат с агентом (память + подтверждение tools). "
|
||||||
"Введите 'exit' для выхода.\n"
|
"Введите 'exit' для выхода.\n"
|
||||||
@@ -122,7 +166,15 @@ def main() -> None:
|
|||||||
if not user_text:
|
if not user_text:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ask_and_run({"messages": [{"role": "human", "content": user_text}]}, config)
|
ask_and_run(
|
||||||
|
agent,
|
||||||
|
{"messages": [{"role": "human", "content": user_text}]},
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
run_chat()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user