From a56809815e05e502404f47ef09636c7fcc0619b3 Mon Sep 17 00:00:00 2001 From: RomanKurlygin Date: Wed, 27 May 2026 08:15:31 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20build=5Fagent/build=5Fllm=20+=20inferenc?= =?UTF-8?q?e=20BroJS=20=D0=B4=D0=BB=D1=8F=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 110 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/main.py b/main.py index ef65435..03e2d47 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ from __future__ import annotations import os +from typing import Any from dotenv import load_dotenv from langchain.agents import create_agent @@ -14,12 +15,39 @@ load_dotenv() console = Console() -llm = ChatOpenAI( - model=os.getenv("OPENAI_MODEL", "openai/gpt-oss-20b:free"), - base_url=os.getenv("OPENAI_BASE_URL", "https://openrouter.ai/api/v1"), - api_key=os.getenv("OPENAI_API_KEY", "fake"), - temperature=0.0, -) +BROJS_INFERENCE_URL = "https://platform.brojs.ru/jrnl-bh/api/inference/v1" +DEFAULT_MODEL = "openai/gpt-oss-20b:free" +THREAD_ID = "разговор-1" + + +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, + ) @tool @@ -28,32 +56,38 @@ def get_price(city: str, date: str = "сегодня") -> str: return f"{city}, {date}: ориентировочно 150–300 руб. за базовую корзину." -memory = MemorySaver() - -agent = create_agent( - model=llm, - tools=[get_price], - system_prompt="Ты помощник по планированию покупок и погоде. Помни контекст разговора.", - checkpointer=memory, - interrupt_before=["tools"], -) +def build_agent(llm: ChatOpenAI | None = None): + """Агент с памятью и interrupt_before tools — точка входа для автопроверки.""" + memory = MemorySaver() + return create_agent( + model=llm or build_llm(), + tools=[get_price], + system_prompt=( + "Ты помощник по планированию покупок и погоде. " + "Помни контекст разговора. Для цен вызывай get_price." + ), + checkpointer=memory, + interrupt_before=["tools"], + ) -def _print_pending_tool(config: dict) -> None: +def _print_pending_tool(agent, config: dict) -> str: state = agent.get_state(config) messages = state.values.get("messages", []) if not messages: - return + return "unknown_tool()" last = messages[-1] tool_calls = getattr(last, "tool_calls", None) or [] if not tool_calls: - return + return "unknown_tool()" tc = tool_calls[0] name = tc.get("name") if isinstance(tc, dict) else getattr(tc, "name", "?") args = tc.get("args") if isinstance(tc, dict) else getattr(tc, "args", {}) + repr_str = f"{name}({args})" console.print("\n --- --- --- ") - console.print(f"{name}({args})") - console.print(f"Агент хочет вызвать утилиту {name}({args})") + console.print(repr_str) + console.print(f"Агент хочет вызвать утилиту {repr_str}") + return repr_str def _print_updates(chunk_data: dict) -> None: @@ -72,7 +106,13 @@ def _print_updates(chunk_data: dict) -> None: 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.""" for chunk in agent.stream( user_input, @@ -97,19 +137,23 @@ def ask_and_run(user_input: dict | None, config: dict) -> None: _print_updates(chunk_data) if "__interrupt__" in chunk_data and state.next == ("tools",): - _print_pending_tool(config) - answer = input("Разрешить? (Y/n): ").strip().lower() - if answer in ("", "y", "yes", "д", "да"): - ask_and_run(None, config) + _print_pending_tool(agent, config) + if auto_approve: + ask_and_run(agent, None, config, auto_approve=True) else: - console.print("Отменено") + answer = input("Разрешить? (Y/n): ").strip().lower() + if answer in ("", "y", "yes", "д", "да"): + ask_and_run(agent, None, config, auto_approve=auto_approve) + else: + console.print("Отменено") return console.print() -def main() -> None: - config = {"configurable": {"thread_id": "разговор-1"}} +def run_chat() -> None: + agent = build_agent() + config = {"configurable": {"thread_id": THREAD_ID}} console.print( "Чат с агентом (память + подтверждение tools). " "Введите 'exit' для выхода.\n" @@ -122,7 +166,15 @@ def main() -> None: if not user_text: 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__":