Files

129 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Deep research agent: веб-поиск, виртуальные файлы, экспорт на диск.
Основано на курсе deep agents from scratch:
https://github.com/langchain-ai/deep-agents-from-scratch/blob/main/notebooks/4_full_agent.ipynb
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
from deepagents import create_deep_agent
from deepagents.backends import LocalShellBackend
from langchain_openai import ChatOpenAI
from export_tools import DEFAULT_OUTPUT_DIR, WORKSPACE_DIR, export_virtual_files, export_workspace_to_disk
from web_tools import get_page_content, web_search
load_dotenv()
BROJS_INFERENCE_URL = "https://platform.brojs.ru/jrnl-bh/api/inference/v1"
DEFAULT_MODEL = "openai/gpt-oss-20b:free"
SYSTEM_PROMPT = """\
Ты исследовательский deep-агент (стиль deep agents from scratch).
Твоя работа:
1. Искать информацию в интернете через web_search и при необходимости get_page_content.
2. Сохранять результаты во ВИРТУАЛЬНЫЕ файлы через write_file (например /research/query.md,
/research/summary.md, /research/sources.txt). Не оставляй всё только в чате.
3. Используй write_todos для плана исследования.
4. В КОНЦЕ обязательно вызови export_virtual_files с target_dir="./output",
чтобы выгрузить виртуальные файлы в реальную файловую систему.
Правила:
- Один инструмент за шаг, если возможно.
- В файлах — структурированные заметки: запрос, источники, выводы.
- Отвечай на русском.
"""
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.2,
)
def build_agent():
WORKSPACE_DIR.mkdir(parents=True, exist_ok=True)
backend = LocalShellBackend(
root_dir=str(WORKSPACE_DIR.resolve()),
virtual_mode=True,
inherit_env=True,
)
llm = build_llm()
return create_deep_agent(
model=llm,
tools=[web_search, get_page_content, export_virtual_files],
system_prompt=SYSTEM_PROMPT,
backend=backend,
)
def run_research(query: str) -> None:
agent = build_agent()
print(f"Запрос: {query}\n")
print("Агент работает (поиск → виртуальные файлы → экспорт)...\n")
result = agent.invoke(
{
"messages": [
{
"role": "human",
"content": (
f"Исследуй тему: {query}. "
"Сохрани заметки в виртуальные файлы и в конце выгрузи их через export_virtual_files."
),
}
]
}
)
last = result["messages"][-1]
print("\n--- Ответ агента ---")
print(getattr(last, "content", last))
# Гарантированный экспорт, даже если агент не вызвал инструмент
exported = export_workspace_to_disk(output_dir=DEFAULT_OUTPUT_DIR)
print(f"\n--- Файлы на диске ({DEFAULT_OUTPUT_DIR}) ---")
if exported:
for path in exported:
print(f" {path}")
else:
print(" (пусто — агент не создал файлов в agent_workspace)")
def main() -> None:
query = " ".join(sys.argv[1:]).strip() or "LangGraph deep agents и context engineering"
run_research(query)
if __name__ == "__main__":
main()