RepoMeituan (LongCat)Meituan (LongCat)published Sep 22, 2026seen 2w

meituan-longcat/LongCat-DeepResearch

Python

Open original ↗

Captured source

source ↗

meituan-longcat/LongCat-DeepResearch

Language: Python

License: MIT

Stars: 0

Forks: 0

Open issues: 0

Created: 2026-09-22T12:49:35Z

Pushed: 2026-09-23T07:03:19Z

Default branch: main

Fork: no

Archived: no

README: ![LongCat-DeepResearch](assets/longcat_logo.png)

Plan globally. Research independently. Synthesize coherently.

English | 简体中文

Overview

This repository open-sources the LongCat-DeepResearch harness for open-ended research. It captures evolving research requirements in an executable ResearchSpec, supports independent section research and writing, and makes targeted revisions to the assembled report instead of repeatedly rewriting it. Users can run deep-research tasks with their own services by implementing llm, web_search, and web_fetch according to the backend contract. Paired with our LongCat-2.0-based model enhanced for deep research, the complete system outperforms the Deep Research offerings from ChatGPT, Claude, and Gemini on DeepResearchBench, DeepResearchBench II, and ResearchRubrics.

![LongCat-DeepResearch benchmark overview](assets/benchmark_overview.png)

Research Harness

![LongCat-DeepResearch harness](assets/harness_overview.png)

The harness follows three stages:

1. Explore and plan. Multiple planners inspect the problem and early sources. A judge merges proposals, a critic identifies gaps, and a reviser produces the executable ResearchSpec. 2. Research sections. Independent researchers investigate and write assigned sections in separate contexts while retaining the full specification. 3. Assemble and edit. Completed sections are assembled directly. A global editor identifies ownership and consistency issues, and local editors apply section-scoped revisions.

Research Data Construction

![Evidence-grounded research data construction](assets/data_construction.png)

The evidence-grounded research-data construction pipeline builds research questions and task-specific rubrics from an independent CC-BY review article or a frozen multi-source brief, validates evidence support, searchability, leakage, and task quality, and collects and filters stage-specific harness trajectories from accepted queries.

This repository releases only the research harness. The data-construction pipeline, generated datasets, hidden rubrics, training trajectories, and training code are not included.

Quick Start

The harness itself uses only the Python standard library and supports Python 3.10 or newer. Install the repository in editable mode:

~~~bash python3 -m venv .venv source .venv/bin/activate python -m pip install -e . ~~~

To run it, provide one Python object with three methods:

  • llm sends an OpenAI Chat Completions/function-calling request to your model and returns the provider's response as a dictionary.
  • web_search receives one query and returns normalized search results.
  • web_fetch receives one or more URLs and returns normalized readable pages in the same order.

No hosted backend is bundled because model, search, and page-fetching services normally require account-specific credentials. Create local_backend.py in the repository root. It is already ignored by Git, so credentials and provider-specific code remain local:

~~~python class Backend: def llm(self, request: dict) -> dict:

request: {"model", "messages", "stream", "max_tokens", ...}

return: {"choices": [{"message": {...}, "finish_reason": "..."}]}

raise NotImplementedError

def web_search(self, request: dict) -> dict:

request: {"query": str, "top_k": int}

return: {"results": [{"title": str, "url": str,

"snippet": str, "published_at": str | None}]}

raise NotImplementedError

def web_fetch(self, request: dict) -> dict:

request: {"urls": [str, ...]}

return: {"pages": [{"url": str, "title": str,

"text": str, "error": str | None}, ...]}

raise NotImplementedError

def create_backend(): return Backend() ~~~

Set the module and model name, then verify the three configured services. The smoke command makes one small model request, one search request, and one page-fetch request; it does not generate a research report:

~~~bash export DR_BACKEND_MODULE=local_backend export DR_MODEL=your-model-name

scripts/run_smoke.sh ~~~

Run the complete research harness separately:

~~~bash export DR_BACKEND_MODULE=local_backend export DR_MODEL=your-model-name

printf '%s\n' 'What question should the system research?' > question.txt scripts/run_standard.sh question.txt runs ~~~

The final report is written to runs/<question_hash>/report_final.md. Intermediate planning, section research, and editing artifacts remain under the same run directory for inspection.

For library use, inject the backend directly:

~~~python from longcat_deepresearch import LongCatDeepResearch

result = LongCatDeepResearch(backend=my_backend).run( "What question should the system research?", run_root="runs", ) print(result.final_path) ~~~

Backend Contract

The backend is a transport boundary: the harness decides when to call the model and tools, while your backend decides how to authenticate and talk to external services. The harness passes web_search and web_fetch as OpenAI-style function tools in llm requests. If the model returns a tool call, the harness executes the corresponding backend method and sends the tool result back to the model in the next turn.

The public interface is defined in [longcat_deepresearch/backend.py](longcat_deepresearch/backend.py). Every method accepts one Python dictionary and returns one Python dictionary:

| Method | Request supplied by the harness | Required response | |---|---|---| | llm | model, messages, stream=false, max_tokens, and optional tools/tool_choice | An OpenAI Chat Completions-compatible object containing choices[0].message | | web_search | {"query": "...", "top_k": 8}; top_k is between 1 and 10 | {"results": [...]} with normalized title, URL, snippet, and optional publication date | | web_fetch | {"urls": ["https://..."]} with 1–10 HTTP(S) URLs | {"pages": [...]} with exactly one page per requested URL, in request order |

For a terminal model answer, llm returns:

~~~json { "choices": [ { "message": {"role": "assistant", "content": "The answer or report text."}, "finish_reason": "stop" } ] } ~~~

For a model-requested tool call, message.content may be null. The function arguments value must be a JSON-encoded string, not a nested object:

~~~json { "choices": [ { "message": { "role": "assistant", "content": null, "tool_calls": [ {...

Excerpt shown — open the source for the full document.