mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-02 22:02:13 +08:00
Prepare to merge deer-flow-2
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
.env
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
.venv/
|
||||
|
||||
# Web
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.next
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Project specific
|
||||
conf.yaml
|
||||
web/
|
||||
docs/
|
||||
examples/
|
||||
assets/
|
||||
tests/
|
||||
*.log
|
||||
128
.env.example
128
.env.example
@@ -1,128 +0,0 @@
|
||||
# Application Settings
|
||||
# Set to True to enable debug-level logging (shows detailed LLM prompts and responses)
|
||||
# Recommended for development and troubleshooting
|
||||
DEBUG=True
|
||||
APP_ENV=development
|
||||
|
||||
# Frontend API URL (used as Docker build arg for Next.js)
|
||||
# This is a BUILD-TIME variable: it gets embedded into the frontend JS bundle during build.
|
||||
# Default works for local development (localhost). For remote/LAN deployment, change to your host IP or domain:
|
||||
# NEXT_PUBLIC_API_URL=http://192.168.1.100:8000/api
|
||||
# NEXT_PUBLIC_API_URL=https://your-domain.com/api
|
||||
# Note: When using docker-compose, only this root .env is used (not web/.env).
|
||||
# If you change this value after building, you must rebuild: docker compose build
|
||||
NEXT_PUBLIC_API_URL="http://localhost:8000/api"
|
||||
|
||||
AGENT_RECURSION_LIMIT=30
|
||||
|
||||
# CORS settings
|
||||
# Comma-separated list of allowed origins for CORS requests
|
||||
# Example: ALLOWED_ORIGINS=http://localhost:3000,http://example.com
|
||||
ALLOWED_ORIGINS=http://localhost:3000
|
||||
|
||||
# Enable or disable MCP server configuration, the default is false.
|
||||
# Please enable this feature before securing your front-end and back-end in a managed environment.
|
||||
# Otherwise, you system could be compromised.
|
||||
ENABLE_MCP_SERVER_CONFIGURATION=false
|
||||
|
||||
# Enable or disable PYTHON_REPL configuration, the default is false.
|
||||
# Please enable this feature before securing your in a managed environment.
|
||||
# Otherwise, you system could be compromised.
|
||||
ENABLE_PYTHON_REPL=false
|
||||
|
||||
# Search Engine, Supported values: tavily, infoquest (recommended), duckduckgo, brave_search, arxiv, searx, serper
|
||||
SEARCH_API=tavily
|
||||
TAVILY_API_KEY=tvly-xxx
|
||||
INFOQUEST_API_KEY="infoquest-xxx"
|
||||
# SERPER_API_KEY=xxx # Required only if SEARCH_API is serper
|
||||
# SEARX_HOST=xxx # Required only if SEARCH_API is searx.(compatible with both Searx and SearxNG)
|
||||
# BRAVE_SEARCH_API_KEY=xxx # Required only if SEARCH_API is brave_search
|
||||
# JINA_API_KEY=jina_xxx # Optional, default is None
|
||||
|
||||
# Optional, RAG provider
|
||||
# RAG_PROVIDER=vikingdb_knowledge_base
|
||||
# VIKINGDB_KNOWLEDGE_BASE_API_URL="api-knowledgebase.mlp.cn-beijing.volces.com"
|
||||
# VIKINGDB_KNOWLEDGE_BASE_API_AK="AKxxx"
|
||||
# VIKINGDB_KNOWLEDGE_BASE_API_SK=""
|
||||
# VIKINGDB_KNOWLEDGE_BASE_RETRIEVAL_SIZE=15
|
||||
|
||||
# RAG_PROVIDER=ragflow
|
||||
# RAGFLOW_API_URL="http://localhost:9388"
|
||||
# RAGFLOW_API_KEY="ragflow-xxx"
|
||||
# RAGFLOW_RETRIEVAL_SIZE=10
|
||||
# RAGFLOW_CROSS_LANGUAGES=English,Chinese,Spanish,French,German,Japanese,Korean # Optional. To use RAGFlow's cross-language search, please separate each language with a single comma
|
||||
|
||||
# RAG_PROVIDER=dify
|
||||
# DIFY_API_URL="https://api.dify.ai/v1"
|
||||
# DIFY_API_KEY="dataset-xxx"
|
||||
|
||||
# MOI is a hybrid database that mainly serves enterprise users (https://www.matrixorigin.io/matrixone-intelligence)
|
||||
# RAG_PROVIDER=moi
|
||||
# MOI_API_URL="https://cluster.matrixonecloud.cn"
|
||||
# MOI_API_KEY="xxx-xxx-xxx-xxx"
|
||||
# MOI_RETRIEVAL_SIZE=10
|
||||
# MOI_LIST_LIMIT=10
|
||||
|
||||
|
||||
# RAG_PROVIDER: milvus (using free milvus instance on zilliz cloud: https://docs.zilliz.com/docs/quick-start )
|
||||
# RAG_PROVIDER=milvus
|
||||
# MILVUS_URI=<endpoint_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
# MILVUS_USER=<username_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
# MILVUS_PASSWORD=<password_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
# MILVUS_COLLECTION=documents
|
||||
# MILVUS_EMBEDDING_PROVIDER=openai # support openai,dashscope
|
||||
# MILVUS_EMBEDDING_BASE_URL=
|
||||
# MILVUS_EMBEDDING_MODEL=
|
||||
# MILVUS_EMBEDDING_API_KEY=
|
||||
# MILVUS_AUTO_LOAD_EXAMPLES=true
|
||||
|
||||
# RAG_PROVIDER: milvus (using milvus lite on Mac or Linux)
|
||||
# RAG_PROVIDER=milvus
|
||||
# MILVUS_URI=./milvus_demo.db
|
||||
# MILVUS_COLLECTION=documents
|
||||
# MILVUS_EMBEDDING_PROVIDER=openai # support openai,dashscope
|
||||
# MILVUS_EMBEDDING_BASE_URL=
|
||||
# MILVUS_EMBEDDING_MODEL=
|
||||
# MILVUS_EMBEDDING_API_KEY=
|
||||
# MILVUS_AUTO_LOAD_EXAMPLES=true
|
||||
|
||||
# RAG_PROVIDER: qdrant (using qdrant cloud or self-hosted: https://qdrant.tech/documentation/quick-start/)
|
||||
# RAG_PROVIDER=qdrant
|
||||
# QDRANT_LOCATION=https://xyz-example.eu-central.aws.cloud.qdrant.io:6333
|
||||
# QDRANT_API_KEY=<your_qdrant_api_key> # Optional, only for cloud/authenticated instances
|
||||
# QDRANT_COLLECTION=documents
|
||||
# QDRANT_EMBEDDING_PROVIDER=openai # support openai,dashscope
|
||||
# QDRANT_EMBEDDING_BASE_URL=
|
||||
# QDRANT_EMBEDDING_MODEL=text-embedding-ada-002
|
||||
# QDRANT_EMBEDDING_API_KEY=
|
||||
# QDRANT_AUTO_LOAD_EXAMPLES=true
|
||||
|
||||
# Optional, volcengine TTS for generating podcast
|
||||
VOLCENGINE_TTS_APPID=xxx
|
||||
VOLCENGINE_TTS_ACCESS_TOKEN=xxx
|
||||
# VOLCENGINE_TTS_CLUSTER=volcano_tts # Optional, default is volcano_tts
|
||||
# VOLCENGINE_TTS_VOICE_TYPE=BV700_V2_streaming # Optional, default is BV700_V2_streaming
|
||||
|
||||
# Optional, for langsmith tracing and monitoring
|
||||
# Highly recommended for production debugging and performance monitoring
|
||||
# Get your API key from https://smith.langchain.com/
|
||||
# LANGSMITH_TRACING=true
|
||||
# LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
# LANGSMITH_API_KEY="xxx"
|
||||
# LANGSMITH_PROJECT="xxx"
|
||||
|
||||
# Optional, LangChain verbose logging
|
||||
# Enable these to see detailed LLM interactions in console/logs
|
||||
# Useful for debugging but can be very verbose
|
||||
# LANGCHAIN_VERBOSE=true
|
||||
# LANGCHAIN_DEBUG=true
|
||||
|
||||
# [!NOTE]
|
||||
# For model settings and other configurations, please refer to `docs/configuration_guide.md`
|
||||
|
||||
# Option, for langgraph mongodb checkpointer
|
||||
# Enable LangGraph checkpoint saver, supports MongoDB, Postgres
|
||||
#LANGGRAPH_CHECKPOINT_SAVER=true
|
||||
# Set the database URL for saving checkpoints
|
||||
#LANGGRAPH_CHECKPOINT_DB_URL=mongodb://localhost:27017/
|
||||
#LANGGRAPH_CHECKPOINT_DB_URL=postgresql://localhost:5432/postgres
|
||||
303
.github/copilot-instructions.md
vendored
303
.github/copilot-instructions.md
vendored
@@ -1,303 +0,0 @@
|
||||
# GitHub Copilot Instructions for DeerFlow
|
||||
|
||||
This file provides guidance to GitHub Copilot when working with the DeerFlow repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**DeerFlow** (Deep Exploration and Efficient Research Flow) is a community-driven Deep Research framework built on LangGraph. It orchestrates AI agents to conduct deep research, generate reports, and create content like podcasts and presentations.
|
||||
|
||||
### Technology Stack
|
||||
|
||||
- **Backend**: Python 3.12+, FastAPI, LangGraph, LangChain
|
||||
- **Frontend**: Next.js (React), TypeScript, pnpm
|
||||
- **Package Management**: uv (Python), pnpm (Node.js)
|
||||
- **Testing**: pytest (Python), Jest (JavaScript)
|
||||
- **Linting/Formatting**: Ruff (Python), ESLint/Prettier (JavaScript)
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core Components
|
||||
|
||||
1. **Multi-Agent System**: Built on LangGraph with state-based workflows
|
||||
- **Coordinator**: Entry point managing workflow lifecycle
|
||||
- **Planner**: Decomposes research objectives into structured plans
|
||||
- **Research Team**: Specialized agents (Researcher, Coder) executing plans
|
||||
- **Reporter**: Aggregates findings and generates final reports
|
||||
- **Human-in-the-loop**: Interactive plan modification and approval
|
||||
|
||||
2. **State Management**
|
||||
- Uses LangGraph StateGraph for agent communication
|
||||
- MemorySaver for conversation persistence
|
||||
- Checkpointing with MongoDB/PostgreSQL support
|
||||
|
||||
3. **External Integrations**
|
||||
- Search engines: Tavily, Brave Search, DuckDuckGo
|
||||
- Web crawling: Jina for content extraction
|
||||
- TTS: Volcengine TTS API
|
||||
- RAG: RAGFlow and VikingDB support
|
||||
- MCP: Model Context Protocol integration
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── agents/ # Agent definitions and behaviors
|
||||
├── config/ # Configuration management (YAML, env vars)
|
||||
├── crawler/ # Web crawling and content extraction
|
||||
├── graph/ # LangGraph workflow definitions
|
||||
├── llms/ # LLM provider integrations (OpenAI, DeepSeek, etc.)
|
||||
├── prompts/ # Agent prompt templates
|
||||
├── server/ # FastAPI web server and endpoints
|
||||
├── tools/ # External tools (search, TTS, Python REPL)
|
||||
└── rag/ # RAG integration for private knowledgebases
|
||||
|
||||
web/ # Next.js web UI (React, TypeScript)
|
||||
├── src/app/ # Next.js pages and API routes
|
||||
├── src/components/ # UI components and design system
|
||||
└── src/core/ # Frontend utilities and state management
|
||||
|
||||
tests/ # Test suite
|
||||
├── unit/ # Unit tests
|
||||
└── integration/ # Integration tests
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Environment Setup
|
||||
|
||||
1. **Python Environment**:
|
||||
```bash
|
||||
# Use uv for dependency management
|
||||
uv sync
|
||||
|
||||
# For development dependencies
|
||||
uv pip install -e ".[dev]"
|
||||
uv pip install -e ".[test]"
|
||||
```
|
||||
|
||||
2. **Configuration Files**:
|
||||
```bash
|
||||
# Copy and configure environment files
|
||||
cp .env.example .env
|
||||
cp conf.yaml.example conf.yaml
|
||||
```
|
||||
|
||||
3. **Frontend Setup**:
|
||||
```bash
|
||||
cd web/
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
|
||||
- **Backend Development Server**: `uv run server.py --reload`
|
||||
- **Console UI**: `uv run main.py`
|
||||
- **Frontend Development**: `cd web && pnpm dev`
|
||||
- **Full Stack**: `./bootstrap.sh -d` (macOS/Linux) or `bootstrap.bat -d` (Windows)
|
||||
- **LangGraph Studio**: `make langgraph-dev`
|
||||
|
||||
### Testing
|
||||
|
||||
- **Python Tests**: `make test` or `pytest tests/`
|
||||
- **Python Coverage**: `make coverage`
|
||||
- **Frontend Tests**: `cd web && pnpm test:run`
|
||||
- **Frontend Lint**: `make lint-frontend`
|
||||
|
||||
### Code Quality
|
||||
|
||||
- **Python Formatting**: `make format` (uses Ruff)
|
||||
- **Python Linting**: `make lint` (uses Ruff)
|
||||
- **Frontend Linting**: `cd web && pnpm lint`
|
||||
- **Frontend Type Check**: `cd web && pnpm typecheck`
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Python Code
|
||||
|
||||
1. **Style Guidelines**:
|
||||
- Follow PEP 8 guidelines
|
||||
- Use type hints wherever possible
|
||||
- Line length: 88 characters (Ruff default)
|
||||
- Python version requirement: >= 3.12
|
||||
|
||||
2. **Code Organization**:
|
||||
- Write clear, documented code with descriptive docstrings
|
||||
- Keep functions and methods focused and single-purpose
|
||||
- Comment complex logic
|
||||
- Use meaningful variable and function names
|
||||
|
||||
3. **Testing Requirements**:
|
||||
- Add tests for new features in `tests/` directory
|
||||
- Maintain test coverage (minimum 25%)
|
||||
- Use pytest fixtures for test setup
|
||||
- Test both unit and integration scenarios
|
||||
|
||||
4. **LangGraph Patterns**:
|
||||
- Agents communicate via LangGraph state
|
||||
- Each agent has specific tool permissions
|
||||
- Use persistent checkpoints for conversation history
|
||||
- Follow the node → edge → state pattern
|
||||
|
||||
### TypeScript/JavaScript Code
|
||||
|
||||
1. **Style Guidelines**:
|
||||
- Use TypeScript for type safety
|
||||
- Follow ESLint configuration
|
||||
- Use Prettier for consistent formatting
|
||||
- Prefer functional components with hooks
|
||||
|
||||
2. **Component Structure**:
|
||||
- Place UI components in `web/src/components/`
|
||||
- Use the established design system
|
||||
- Keep components focused and reusable
|
||||
- Export types alongside components
|
||||
|
||||
3. **API Integration**:
|
||||
- API utilities in `web/src/core/api/`
|
||||
- Handle errors gracefully
|
||||
- Use proper TypeScript types for API responses
|
||||
|
||||
## Configuration Management
|
||||
|
||||
### Environment Variables (.env)
|
||||
|
||||
Key environment variables to configure:
|
||||
- `TAVILY_API_KEY`: Web search integration
|
||||
- `BRAVE_SEARCH_API_KEY`: Alternative search engine
|
||||
- `LANGSMITH_API_KEY`: LangSmith tracing (optional)
|
||||
- `LANGGRAPH_CHECKPOINT_DB_URL`: MongoDB/PostgreSQL for persistence
|
||||
- `RAGFLOW_API_URL`: RAG integration
|
||||
|
||||
### Application Configuration (conf.yaml)
|
||||
|
||||
- LLM model configurations
|
||||
- Provider-specific settings
|
||||
- Search engine preferences
|
||||
- MCP server configurations
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding New Features
|
||||
|
||||
1. **New Agent**:
|
||||
- Add agent definition in `src/agents/`
|
||||
- Update graph in `src/graph/builder.py`
|
||||
- Register agent tools in prompts
|
||||
|
||||
2. **New Tool**:
|
||||
- Implement tool in `src/tools/`
|
||||
- Register in agent prompts
|
||||
- Add tests for tool functionality
|
||||
|
||||
3. **New Workflow**:
|
||||
- Create graph builder in `src/[feature]/graph/builder.py`
|
||||
- Define state management
|
||||
- Add nodes and edges
|
||||
|
||||
4. **Frontend Component**:
|
||||
- Add component to `web/src/components/`
|
||||
- Update API in `web/src/core/api/`
|
||||
- Add corresponding types
|
||||
|
||||
### Debugging
|
||||
|
||||
- **LangGraph Studio**: `make langgraph-dev` for visual workflow debugging
|
||||
- **LangSmith**: Configure `LANGSMITH_API_KEY` for tracing
|
||||
- **Server Logs**: Check FastAPI server output for backend issues
|
||||
- **Browser DevTools**: Use for frontend debugging
|
||||
|
||||
## Important Patterns
|
||||
|
||||
### Agent Communication
|
||||
- Agents communicate through LangGraph state
|
||||
- State is preserved across checkpoints
|
||||
- Use proper type annotations for state
|
||||
|
||||
### Content Generation Pipeline
|
||||
1. Planning: Planner creates research plan
|
||||
2. Research: Researcher gathers information
|
||||
3. Processing: Coder analyzes data/code
|
||||
4. Reporting: Reporter synthesizes findings
|
||||
5. Post-processing: Optional podcast/PPT generation
|
||||
|
||||
### Error Handling
|
||||
- Use try-except blocks with specific exception types
|
||||
- Log errors with appropriate context
|
||||
- Provide meaningful error messages to users
|
||||
- Handle API failures gracefully
|
||||
|
||||
### Async Operations
|
||||
- Use async/await for I/O operations
|
||||
- Properly handle concurrent operations
|
||||
- Use appropriate timeout values
|
||||
- Clean up resources in finally blocks
|
||||
|
||||
## Pre-commit Hooks
|
||||
|
||||
The repository uses pre-commit hooks for code quality:
|
||||
```bash
|
||||
chmod +x pre-commit
|
||||
ln -s ../../pre-commit .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Adding New Dependencies
|
||||
|
||||
- **Python**: Add to `pyproject.toml` dependencies, then run `uv sync`
|
||||
- **JavaScript**: Use `pnpm add <package>` in the `web/` directory
|
||||
|
||||
### Dependency Updates
|
||||
|
||||
- Keep dependencies up to date
|
||||
- Test thoroughly after updates
|
||||
- Check compatibility with Python 3.12+ and Node.js 22+
|
||||
|
||||
## Documentation
|
||||
|
||||
### When to Update Documentation
|
||||
|
||||
- New features: Update relevant docs in `docs/` directory
|
||||
- API changes: Update `docs/API.md`
|
||||
- Configuration changes: Update `docs/configuration_guide.md`
|
||||
- Breaking changes: Clearly document in README and CONTRIBUTING
|
||||
|
||||
### Documentation Style
|
||||
|
||||
- Use clear, concise language
|
||||
- Include code examples where applicable
|
||||
- Keep documentation in sync with code
|
||||
- Use markdown formatting consistently
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Never commit API keys or secrets to the repository
|
||||
- Use `.env` files for sensitive configuration
|
||||
- Validate and sanitize user inputs
|
||||
- Follow security best practices for web applications
|
||||
- Be cautious with code execution features
|
||||
|
||||
## Community Guidelines
|
||||
|
||||
- Be respectful and inclusive
|
||||
- Follow the MIT License terms
|
||||
- Give constructive feedback in code reviews
|
||||
- Help others learn and grow
|
||||
- Stay focused on improving the project
|
||||
|
||||
## Getting Help
|
||||
|
||||
- Check existing documentation in `docs/`
|
||||
- Review `Agent.md` for architecture details
|
||||
- See `CONTRIBUTING` for contribution guidelines
|
||||
- Check GitHub issues for known problems
|
||||
- Join community discussions for support
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- Main README: Comprehensive project overview
|
||||
- Agent.md: Detailed architecture and agent guidance
|
||||
- CONTRIBUTING: Full contribution guidelines
|
||||
- docs/configuration_guide.md: Configuration details
|
||||
- docs/API.md: API documentation
|
||||
- docs/mcp_integrations.md: MCP integration guide
|
||||
95
.github/workflows/container.yaml
vendored
95
.github/workflows/container.yaml
vendored
@@ -1,95 +0,0 @@
|
||||
name: Publish Containers
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
backend-container:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 #v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
frontend-container:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}-web
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 #v5.7.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
||||
with:
|
||||
context: web
|
||||
file: web/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
69
.github/workflows/lint.yaml
vendored
69
.github/workflows/lint.yaml
vendored
@@ -1,69 +0,0 @@
|
||||
name: Lint Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
pull_request:
|
||||
branches: [ '*' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 #v6.5.0
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv venv --python 3.12
|
||||
uv pip install -e ".[dev]"
|
||||
|
||||
- name: Run linters
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
make lint
|
||||
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
cd web
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run frontend linting
|
||||
run: |
|
||||
cd web
|
||||
pnpm lint
|
||||
|
||||
- name: Check TypeScript types
|
||||
run: |
|
||||
cd web
|
||||
pnpm typecheck
|
||||
|
||||
- name: Running the frontend tests
|
||||
run: |
|
||||
cd web
|
||||
pnpm test:run
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
cd web
|
||||
pnpm build
|
||||
73
.github/workflows/unittest.yaml
vendored
73
.github/workflows/unittest.yaml
vendored
@@ -1,73 +0,0 @@
|
||||
name: Test Cases Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
pull_request:
|
||||
branches: [ '*' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_DB: checkpointing_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports: ["5432:5432"]
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
mongodb:
|
||||
image: mongo:6
|
||||
env:
|
||||
MONGO_INITDB_ROOT_USERNAME: admin
|
||||
MONGO_INITDB_ROOT_PASSWORD: admin
|
||||
MONGO_INITDB_DATABASE: checkpointing_db
|
||||
ports: ["27017:27017"]
|
||||
options: >-
|
||||
--health-cmd "mongosh --eval 'db.runCommand(\"ping\").ok'"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 3
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 #v6.5.0
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv venv --python 3.12
|
||||
uv pip install -e ".[dev]"
|
||||
uv pip install -e ".[test]"
|
||||
|
||||
- name: Run test cases with coverage
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
TAVILY_API_KEY=mock-key DB_TESTS_ENABLED=true make coverage
|
||||
|
||||
- name: Generate HTML Coverage Report
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
python -m coverage html -d coverage_html
|
||||
|
||||
- name: Upload Coverage Report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report
|
||||
path: coverage_html/
|
||||
|
||||
- name: Display Coverage Summary
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
python -m coverage report
|
||||
32
.gitignore
vendored
32
.gitignore
vendored
@@ -1,32 +0,0 @@
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
.coverage
|
||||
.coverage.*
|
||||
agent_history.gif
|
||||
static/browser_history/*.gif
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
venv/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
# user conf
|
||||
conf.yaml
|
||||
|
||||
.idea/
|
||||
.langgraph_api/
|
||||
.DS_Store
|
||||
|
||||
# coverage report
|
||||
coverage.xml
|
||||
coverage/
|
||||
|
||||
# Temporary PPT content files
|
||||
ppt_content_*.md
|
||||
@@ -1 +0,0 @@
|
||||
3.12
|
||||
121
.vscode/launch.json
vendored
121
.vscode/launch.json
vendored
@@ -1,121 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "Debug Tests",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "pytest",
|
||||
"args": [
|
||||
"${workspaceFolder}/tests",
|
||||
"-v",
|
||||
"-s"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug Current Test File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "pytest",
|
||||
"args": [
|
||||
"${file}",
|
||||
"-v",
|
||||
"-s"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "Python: 当前文件",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "Python: main.py",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/main.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
},
|
||||
"args": [
|
||||
"--debug", "--max_plan_iterations", "1", "--max_step_num", "1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Python: llm.py",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/llms/llm.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Python: server.py",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/server.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Python: graph.py",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/ppt/graph/builder.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug: python server",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/server.py",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false,
|
||||
"env": {
|
||||
"PYTHONPATH": "${workspaceFolder}"
|
||||
},
|
||||
"args": [
|
||||
"--reload"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug: nodejs web",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "pnpm",
|
||||
"runtimeArgs": [
|
||||
"dev"
|
||||
],
|
||||
"cwd": "${workspaceFolder}/web",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Launch Deerflow",
|
||||
"configurations": ["Debug: python server", "Debug: nodejs web"]
|
||||
}
|
||||
]
|
||||
}
|
||||
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"python.testing.pytestArgs": [
|
||||
"tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
186
Agent.md
186
Agent.md
@@ -1,186 +0,0 @@
|
||||
# Agent.md
|
||||
|
||||
This file provides guidance to AI agents when working with code in this repository.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
**DeerFlow** is a multi-agent research framework built on LangGraph that orchestrates AI agents to conduct deep research, generate reports, and create content like podcasts and presentations.
|
||||
|
||||
### Core Architecture
|
||||
|
||||
The system uses a **modular multi-agent architecture** with these key components:
|
||||
|
||||
- **Coordinator**: Entry point managing workflow lifecycle
|
||||
- **Planner**: Decomposes research objectives into structured plans
|
||||
- **Research Team**: Specialized agents (Researcher, Coder) executing plans
|
||||
- **Reporter**: Aggregates findings and generates final reports
|
||||
- **Human-in-the-loop**: Interactive plan modification and approval
|
||||
|
||||
### Graph Structure
|
||||
|
||||
Built on **LangGraph** with state-based workflows:
|
||||
- **StateGraph** manages agent communication
|
||||
- **MemorySaver** provides conversation persistence
|
||||
- **Checkpointing** supports MongoDB/PostgreSQL storage
|
||||
- **Nodes**: coordinator → planner → research_team → reporter
|
||||
|
||||
### Key Directories
|
||||
|
||||
```
|
||||
src/
|
||||
├── agents/ # Agent definitions and behaviors
|
||||
├── config/ # Configuration management (YAML, env vars)
|
||||
├── crawler/ # Web crawling and content extraction
|
||||
├── graph/ # LangGraph workflow definitions
|
||||
├── llms/ # LLM provider integrations (OpenAI, DeepSeek, etc.)
|
||||
├── prompts/ # Agent prompt templates
|
||||
├── server/ # FastAPI web server and endpoints
|
||||
├── tools/ # External tools (search, TTS, Python REPL)
|
||||
└── rag/ # RAG integration for private knowledgebases
|
||||
|
||||
web/ # Next.js web UI (React, TypeScript)
|
||||
├── src/app/ # Next.js pages and API routes
|
||||
├── src/components/ # UI components and design system
|
||||
└── src/core/ # Frontend utilities and state management
|
||||
```
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Backend (Python)
|
||||
```bash
|
||||
# Install dependencies
|
||||
uv sync
|
||||
|
||||
# Development server
|
||||
uv run server.py --reload
|
||||
|
||||
# Console UI
|
||||
uv run main.py
|
||||
|
||||
# Run tests
|
||||
make test # Run all tests
|
||||
make coverage # Run tests with coverage
|
||||
pytest tests/unit/test_*.py # Run specific test file
|
||||
|
||||
# Code quality
|
||||
make lint # Ruff linting
|
||||
make format # Ruff formatting
|
||||
|
||||
# LangGraph Studio (debugging)
|
||||
make langgraph-dev # Start LangGraph development server
|
||||
```
|
||||
|
||||
### Frontend (Web UI)
|
||||
```bash
|
||||
cd web/
|
||||
pnpm install # Install dependencies
|
||||
pnpm dev # Development server (localhost:3000)
|
||||
pnpm build # Production build
|
||||
pnpm typecheck # Type checking
|
||||
pnpm lint # ESLint
|
||||
pnpm format:write # Prettier formatting
|
||||
```
|
||||
|
||||
### Full Stack Development
|
||||
```bash
|
||||
# Run both backend and frontend
|
||||
./bootstrap.sh -d # macOS/Linux
|
||||
bootstrap.bat -d # Windows
|
||||
```
|
||||
|
||||
### Docker
|
||||
```bash
|
||||
# Build and run
|
||||
make build # Build Docker image
|
||||
docker compose up # Run with Docker Compose
|
||||
|
||||
# Production deployment
|
||||
docker build -t deer-flow-api .
|
||||
docker run -p 8000:8000 deer-flow-api
|
||||
```
|
||||
|
||||
### Fix GitHub issues
|
||||
create a branch named `fix/<issue-number>` to address specific GitHub issues.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Setup
|
||||
```bash
|
||||
# Required: Copy example configs
|
||||
cp .env.example .env
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Key environment variables:
|
||||
# TAVILY_API_KEY # Web search
|
||||
# BRAVE_SEARCH_API_KEY # Alternative search
|
||||
# LANGSMITH_API_KEY # LangSmith tracing (optional)
|
||||
# LANGGRAPH_CHECKPOINT_DB_URL # MongoDB/PostgreSQL for persistence
|
||||
```
|
||||
|
||||
### LangGraph Studio
|
||||
```bash
|
||||
# Local debugging with checkpointing
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Unit tests
|
||||
pytest tests/unit/
|
||||
|
||||
# Integration tests
|
||||
pytest tests/integration/
|
||||
|
||||
# Specific component
|
||||
pytest tests/unit/config/test_configuration.py
|
||||
|
||||
# With coverage
|
||||
pytest --cov=src tests/ --cov-report=html
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
```bash
|
||||
# Format code
|
||||
make format
|
||||
|
||||
# Check linting
|
||||
make lint
|
||||
|
||||
# Type checking (frontend)
|
||||
cd web && pnpm typecheck
|
||||
```
|
||||
|
||||
### Adding New Features
|
||||
1. **New Agent**: Add agent in `src/agents/` + update graph in `src/graph/builder.py`
|
||||
2. **New Tool**: Add tool in `src/tools/` + register in agent prompts
|
||||
3. **New Workflow**: Create graph builder in `src/[feature]/graph/builder.py`
|
||||
4. **Frontend Component**: Add to `web/src/components/` + update API in `web/src/core/api/`
|
||||
|
||||
### Configuration Changes
|
||||
- **LLM Models**: Update `conf.yaml` with new providers
|
||||
- **Search Engines**: Modify `.env` SEARCH_API variable
|
||||
- **RAG Integration**: Configure RAGFLOW_API_URL in `.env`
|
||||
- **MCP Servers**: Add MCP settings in configuration
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Agent Communication
|
||||
- **Message Passing**: Agents communicate via LangGraph state
|
||||
- **Tool Access**: Each agent has specific tool permissions
|
||||
- **State Management**: Persistent checkpoints for conversation history
|
||||
|
||||
### Content Generation Pipeline
|
||||
1. **Planning**: Planner creates research plan
|
||||
2. **Research**: Researcher gathers information
|
||||
3. **Processing**: Coder analyzes data/code
|
||||
4. **Reporting**: Reporter synthesizes findings
|
||||
5. **Post-processing**: Optional podcast/PPT generation
|
||||
|
||||
### External Integrations
|
||||
- **Search**: Tavily, Brave Search, DuckDuckGo
|
||||
- **Crawling**: Jina for web content extraction
|
||||
- **TTS**: Volcengine TTS API
|
||||
- **RAG**: RAGFlow and VikingDB support
|
||||
- **MCP**: Model Context Protocol integration
|
||||
144
CONTRIBUTING
144
CONTRIBUTING
@@ -1,144 +0,0 @@
|
||||
# Contributing to DeerFlow
|
||||
|
||||
Thank you for your interest in contributing to DeerFlow! We welcome contributions of all kinds from the community.
|
||||
|
||||
## Ways to Contribute
|
||||
|
||||
There are many ways you can contribute to DeerFlow:
|
||||
|
||||
- **Code Contributions**: Add new features, fix bugs, or improve performance
|
||||
- **Documentation**: Improve README, add code comments, or create examples
|
||||
- **Bug Reports**: Submit detailed bug reports through issues
|
||||
- **Feature Requests**: Suggest new features or improvements
|
||||
- **Code Reviews**: Review pull requests from other contributors
|
||||
- **Community Support**: Help others in discussions and issues
|
||||
|
||||
## Development Setup
|
||||
|
||||
1. Fork the repository
|
||||
2. Clone your fork:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
```
|
||||
|
||||
3. Set up your development environment:
|
||||
|
||||
```bash
|
||||
# Install dependencies, uv will take care of the python interpreter and venv creation
|
||||
uv sync
|
||||
|
||||
# For development, install additional dependencies
|
||||
uv pip install -e ".[dev]"
|
||||
uv pip install -e ".[test]"
|
||||
```
|
||||
|
||||
4. Configure pre-commit hooks:
|
||||
|
||||
```bash
|
||||
chmod +x pre-commit
|
||||
ln -s ../../pre-commit .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
## Development Process
|
||||
|
||||
1. Create a new branch:
|
||||
|
||||
```bash
|
||||
git checkout -b feature/amazing-feature
|
||||
```
|
||||
|
||||
2. Make your changes following our coding standards:
|
||||
- Write clear, documented code
|
||||
- Follow PEP 8 style guidelines
|
||||
- Add tests for new features
|
||||
- Update documentation as needed
|
||||
|
||||
3. Run tests and checks:
|
||||
|
||||
```bash
|
||||
make test # Run tests
|
||||
make lint # Run linting
|
||||
make format # Format code
|
||||
make coverage # Check test coverage
|
||||
```
|
||||
|
||||
4. Commit your changes:
|
||||
|
||||
```bash
|
||||
git commit -m 'Add some amazing feature'
|
||||
```
|
||||
|
||||
5. Push to your fork:
|
||||
|
||||
```bash
|
||||
git push origin feature/amazing-feature
|
||||
```
|
||||
|
||||
6. Open a Pull Request
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Fill in the pull request template completely
|
||||
- Include tests for new features
|
||||
- Update documentation as needed
|
||||
- Ensure all tests pass and there are no linting errors
|
||||
- Keep pull requests focused on a single feature or fix
|
||||
- Reference any related issues
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow PEP 8 guidelines
|
||||
- Use type hints where possible
|
||||
- Write descriptive docstrings
|
||||
- Keep functions and methods focused and single-purpose
|
||||
- Comment complex logic
|
||||
- Python version requirement: >= 3.12
|
||||
|
||||
## Testing
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
make test
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Run with coverage
|
||||
make coverage
|
||||
```
|
||||
|
||||
## Code Quality
|
||||
|
||||
```bash
|
||||
# Run linting
|
||||
make lint
|
||||
|
||||
# Format code
|
||||
make format
|
||||
```
|
||||
|
||||
## Community Guidelines
|
||||
|
||||
- Be respectful and inclusive
|
||||
- Follow our code of conduct
|
||||
- Help others learn and grow
|
||||
- Give constructive feedback
|
||||
- Stay focused on improving the project
|
||||
|
||||
## Need Help?
|
||||
|
||||
If you need help with anything:
|
||||
|
||||
- Check existing issues and discussions
|
||||
- Join our community channels
|
||||
- Ask questions in discussions
|
||||
|
||||
## License
|
||||
|
||||
By contributing to DeerFlow, you agree that your contributions will be licensed under the MIT License.
|
||||
|
||||
We appreciate your contributions to making DeerFlow better!
|
||||
32
Dockerfile
32
Dockerfile
@@ -1,32 +0,0 @@
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm
|
||||
|
||||
# Install uv.
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
||||
|
||||
# Install system dependencies including libpq
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Pre-cache the application dependencies.
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
uv sync --locked --no-install-project
|
||||
|
||||
# Copy the application into the container.
|
||||
COPY . /app
|
||||
|
||||
# Install the application dependencies.
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --locked
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the application.
|
||||
RUN useradd -m appuser
|
||||
USER appuser
|
||||
|
||||
CMD ["uv", "run", "python", "server.py", "--host", "0.0.0.0", "--port", "8000"]
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,2 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
@@ -1,2 +0,0 @@
|
||||
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
// SPDX-License-Identifier: MIT
|
||||
44
Makefile
44
Makefile
@@ -1,44 +0,0 @@
|
||||
.PHONY: help lint format install-dev serve test coverage langgraph-dev lint-frontend add-license-all check-license-all
|
||||
|
||||
help: ## Show this help message
|
||||
@echo "Deer Flow - Available Make Targets:"
|
||||
@echo ""
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'
|
||||
@echo ""
|
||||
@echo "Usage: make <target>"
|
||||
|
||||
install-dev: ## Install development dependencies which could be optional for normal usage
|
||||
uv pip install -e ".[dev]" && uv pip install -e ".[test]"
|
||||
|
||||
format: ## Format code using ruff
|
||||
uv run ruff format --config pyproject.toml .
|
||||
|
||||
lint: ## Lint and fix code using ruff
|
||||
uv run ruff check --fix --select I --config pyproject.toml .
|
||||
|
||||
lint-frontend: ## Lint frontend code, run tests, and check build
|
||||
cd web && pnpm install --frozen-lockfile
|
||||
cd web && pnpm lint
|
||||
cd web && pnpm typecheck
|
||||
cd web && pnpm test:run
|
||||
cd web && pnpm build
|
||||
|
||||
serve: ## Start development server with reload
|
||||
uv run server.py --reload
|
||||
|
||||
test: ## Run tests with pytest, need to run after 'make install-dev' for first time
|
||||
uv run pytest tests/
|
||||
|
||||
langgraph-dev: ## Start langgraph development server
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
|
||||
coverage: ## Run tests with coverage report
|
||||
uv run pytest --cov=src tests/ --cov-report=term-missing --cov-report=xml
|
||||
|
||||
add-license-all: ## Add license headers to all Python and TypeScript files
|
||||
@echo "Adding license headers to all source files..."
|
||||
@uv run python scripts/license_header.py src/ tests/ server.py main.py web/src/ web/tests/ --verbose
|
||||
|
||||
check-license-all: ## Check if all Python and TypeScript files have license headers
|
||||
@echo "Checking license headers in all source files..."
|
||||
@uv run python scripts/license_header.py src/ tests/ server.py main.py web/src/ web/tests/ --check
|
||||
721
README.md
721
README.md
@@ -1,721 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/bytedance/deer-flow)
|
||||
|
||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> Originated from Open Source, give back to Open Source.
|
||||
|
||||
> [!NOTE]
|
||||
> As we're [moving to DeerFlow 2.0](https://github.com/bytedance/deer-flow/issues/824) in February, it's time to wrap up DeerFlow 1.0 on the main branch.
|
||||
|
||||
**DeerFlow** (**D**eep **E**xploration and **E**fficient **R**esearch **Flow**) is a community-driven Deep Research framework that builds upon the incredible work of the open source community. Our goal is to combine language models with specialized tools for tasks like web search, crawling, and Python code execution, while giving back to the community that made this possible.
|
||||
|
||||
Currently, DeerFlow has officially entered the [FaaS Application Center of Volcengine](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market). Users can experience it online through the [experience link](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market/deerflow/?channel=github&source=deerflow) to intuitively feel its powerful functions and convenient operations. At the same time, to meet the deployment needs of different users, DeerFlow supports one-click deployment based on Volcengine. Click the [deployment link](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/application/create?templateId=683adf9e372daa0008aaed5c&channel=github&source=deerflow) to quickly complete the deployment process and start an efficient research journey.
|
||||
|
||||
DeerFlow has newly integrated the intelligent search and crawling toolset independently developed by BytePlus--[InfoQuest (supports free online experience)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
Please visit [our official website](https://deerflow.tech/) for more details.
|
||||
|
||||
## Demo
|
||||
|
||||
### Video
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
In this demo, we showcase how to use DeerFlow to:
|
||||
|
||||
- Seamlessly integrate with MCP services
|
||||
- Conduct the Deep Research process and produce a comprehensive report with images
|
||||
- Create podcast audio based on the generated report
|
||||
|
||||
### Replays
|
||||
|
||||
- [How tall is Eiffel Tower compared to the tallest building?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [What are the top trending repositories on GitHub?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [Write an article about Nanjing's traditional dishes](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [How to decorate a rental apartment?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [Visit our official website to explore more replays.](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 Table of Contents
|
||||
|
||||
- [🚀 Quick Start](#quick-start)
|
||||
- [🌟 Features](#features)
|
||||
- [🏗️ Architecture](#architecture)
|
||||
- [🛠️ Development](#development)
|
||||
- [🐳 Docker](#docker)
|
||||
- [🗣️ Text-to-Speech Integration](#text-to-speech-integration)
|
||||
- [📚 Examples](#examples)
|
||||
- [❓ FAQ](#faq)
|
||||
- [📜 License](#license)
|
||||
- [💖 Acknowledgments](#acknowledgments)
|
||||
- [⭐ Star History](#star-history)
|
||||
|
||||
## Quick Start
|
||||
|
||||
DeerFlow is developed in Python, and comes with a web UI written in Node.js. To ensure a smooth setup process, we recommend using the following tools:
|
||||
|
||||
### Recommended Tools
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Simplify Python environment and dependency management. `uv` automatically creates a virtual environment in the root directory and installs all required packages for you—no need to manually install Python environments.
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
Manage multiple versions of the Node.js runtime effortlessly.
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Install and manage dependencies of Node.js project.
|
||||
|
||||
### Environment Requirements
|
||||
|
||||
Make sure your system meets the following minimum requirements:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** Version `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** Version `22+`
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# Install dependencies, uv will take care of the python interpreter and venv creation, and install the required packages
|
||||
uv sync
|
||||
|
||||
# Configure .env with your API keys
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# volcengine TTS: Add your TTS credentials if you have them
|
||||
cp .env.example .env
|
||||
|
||||
# See the 'Supported Search Engines' and 'Text-to-Speech Integration' sections below for all available options
|
||||
|
||||
# Configure conf.yaml for your LLM model and API keys
|
||||
# Please refer to 'docs/configuration_guide.md' for more details
|
||||
# For local development, you can use Ollama or other local models
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Install marp for ppt generation
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
Optionally, install web UI dependencies via [pnpm](https://pnpm.io/installation):
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Configurations
|
||||
|
||||
Please refer to the [Configuration Guide](docs/configuration_guide.md) for more details.
|
||||
|
||||
> [!NOTE]
|
||||
> Before you start the project, read the guide carefully, and update the configurations to match your specific settings and requirements.
|
||||
|
||||
### Console UI
|
||||
|
||||
The quickest way to run the project is to use the console UI.
|
||||
|
||||
```bash
|
||||
# Run the project in a bash-like shell
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
This project also includes a Web UI, offering a more dynamic and engaging interactive experience.
|
||||
|
||||
> [!NOTE]
|
||||
> You need to install the dependencies of web UI first.
|
||||
|
||||
```bash
|
||||
# Run both the backend and frontend servers in development mode
|
||||
# On macOS/Linux
|
||||
./bootstrap.sh -d
|
||||
|
||||
# On Windows
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!Note]
|
||||
> By default, the backend server binds to 127.0.0.1 (localhost) for security reasons. If you need to allow external connections (e.g., when deploying on Linux server), you can modify the server host to 0.0.0.0 in the bootstrap script(uv run server.py --host 0.0.0.0).
|
||||
> Please ensure your environment is properly secured before exposing the service to external networks.
|
||||
|
||||
Open your browser and visit [`http://localhost:3000`](http://localhost:3000) to explore the web UI.
|
||||
|
||||
Explore more details in the [`web`](./web/) directory.
|
||||
|
||||
## Supported Search Engines
|
||||
|
||||
### Web Search
|
||||
|
||||
DeerFlow supports multiple search engines that can be configured in your `.env` file using the `SEARCH_API` variable:
|
||||
|
||||
- **Tavily** (default): A specialized search API for AI applications
|
||||
- Requires `TAVILY_API_KEY` in your `.env` file
|
||||
- Sign up at: https://app.tavily.com/home
|
||||
|
||||
- **InfoQuest** (recommended): AI-optimized intelligent search and crawling toolset independently developed by BytePlus
|
||||
- Requires `INFOQUEST_API_KEY` in your `.env` file
|
||||
- Support for time range filtering and site filtering
|
||||
- Provides high-quality search results and content extraction
|
||||
- Sign up at: https://console.byteplus.com/infoquest/infoquests
|
||||
- Visit https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest to learn more
|
||||
|
||||
- **DuckDuckGo**: Privacy-focused search engine
|
||||
- No API key required
|
||||
|
||||
- **Brave Search**: Privacy-focused search engine with advanced features
|
||||
- Requires `BRAVE_SEARCH_API_KEY` in your `.env` file
|
||||
- Sign up at: https://brave.com/search/api/
|
||||
|
||||
- **Arxiv**: Scientific paper search for academic research
|
||||
- No API key required
|
||||
- Specialized for scientific and academic papers
|
||||
|
||||
- **Searx/SearxNG**: Self-hosted metasearch engine
|
||||
- Requires `SEARX_HOST` to be set in the `.env` file
|
||||
- Supports connecting to either Searx or SearxNG
|
||||
|
||||
To configure your preferred search engine, set the `SEARCH_API` variable in your `.env` file:
|
||||
|
||||
```bash
|
||||
# Choose one: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### Crawling Tools
|
||||
|
||||
DeerFlow supports multiple crawling tools that can be configured in your `conf.yaml` file:
|
||||
|
||||
- **Jina** (default): Freely accessible web content crawling tool
|
||||
|
||||
- **InfoQuest** (recommended): AI-optimized intelligent search and crawling toolset developed by BytePlus
|
||||
- Requires `INFOQUEST_API_KEY` in your `.env` file
|
||||
- Provides configurable crawling parameters
|
||||
- Supports custom timeout settings
|
||||
- Offers more powerful content extraction capabilities
|
||||
- Visit https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest to learn more
|
||||
|
||||
To configure your preferred crawling tool, set the following in your `conf.yaml` file:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# Engine type: "jina" (default) or "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
### Private Knowledgebase
|
||||
|
||||
DeerFlow supports private knowledgebase such as RAGFlow, Qdrant, Milvus, and VikingDB, so that you can use your private documents to answer questions.
|
||||
|
||||
- **[RAGFlow](https://ragflow.io/docs/dev/)**: open source RAG engine
|
||||
```bash
|
||||
# examples in .env.example
|
||||
RAG_PROVIDER=ragflow
|
||||
RAGFLOW_API_URL="http://localhost:9388"
|
||||
RAGFLOW_API_KEY="ragflow-xxx"
|
||||
RAGFLOW_RETRIEVAL_SIZE=10
|
||||
RAGFLOW_CROSS_LANGUAGES=English,Chinese,Spanish,French,German,Japanese,Korean
|
||||
```
|
||||
|
||||
- **[Qdrant](https://qdrant.tech/)**: open source vector database
|
||||
```bash
|
||||
# Using Qdrant Cloud or self-hosted
|
||||
RAG_PROVIDER=qdrant
|
||||
QDRANT_LOCATION=https://xyz-example.eu-central.aws.cloud.qdrant.io:6333
|
||||
QDRANT_API_KEY=your_qdrant_api_key
|
||||
QDRANT_COLLECTION=documents
|
||||
QDRANT_EMBEDDING_PROVIDER=openai
|
||||
QDRANT_EMBEDDING_MODEL=text-embedding-ada-002
|
||||
QDRANT_EMBEDDING_API_KEY=your_openai_api_key
|
||||
QDRANT_AUTO_LOAD_EXAMPLES=true
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Core Capabilities
|
||||
|
||||
- 🤖 **LLM Integration**
|
||||
- It supports the integration of most models through [litellm](https://docs.litellm.ai/docs/providers).
|
||||
- Support for open source models like Qwen, you need to read the [configuration](docs/configuration_guide.md) for more details.
|
||||
- OpenAI-compatible API interface
|
||||
- Multi-tier LLM system for different task complexities
|
||||
|
||||
### Tools and MCP Integrations
|
||||
|
||||
- 🔍 **Search and Retrieval**
|
||||
- Web search via Tavily, InfoQuest, Brave Search and more
|
||||
- Crawling with Jina and InfoQuest
|
||||
- Advanced content extraction
|
||||
- Support for private knowledgebase
|
||||
|
||||
- 📃 **RAG Integration**
|
||||
|
||||
- Supports multiple vector databases: [Qdrant](https://qdrant.tech/), [Milvus](https://milvus.io/), [RAGFlow](https://github.com/infiniflow/ragflow), VikingDB, MOI, and Dify
|
||||
- Supports mentioning files from RAG providers within the input box
|
||||
- Easy switching between different vector databases through configuration
|
||||
|
||||
- 🔗 **MCP Seamless Integration**
|
||||
- Expand capabilities for private domain access, knowledge graph, web browsing and more
|
||||
- Facilitates integration of diverse research tools and methodologies
|
||||
|
||||
### Human Collaboration
|
||||
|
||||
- 💬 **Intelligent Clarification Feature**
|
||||
- Multi-turn dialogue to clarify vague research topics
|
||||
- Improve research precision and report quality
|
||||
- Reduce ineffective searches and token usage
|
||||
- Configurable switch for flexible enable/disable control
|
||||
- See [Configuration Guide - Clarification](./docs/configuration_guide.md#multi-turn-clarification-feature) for details
|
||||
|
||||
- 🧠 **Human-in-the-loop**
|
||||
- Supports interactive modification of research plans using natural language
|
||||
- Supports auto-acceptance of research plans
|
||||
|
||||
- 📝 **Report Post-Editing**
|
||||
- Supports Notion-like block editing
|
||||
- Allows AI refinements, including AI-assisted polishing, sentence shortening, and expansion
|
||||
- Powered by [tiptap](https://tiptap.dev/)
|
||||
|
||||
### Content Creation
|
||||
|
||||
- 🎙️ **Podcast and Presentation Generation**
|
||||
- AI-powered podcast script generation and audio synthesis
|
||||
- Automated creation of simple PowerPoint presentations
|
||||
- Customizable templates for tailored content
|
||||
|
||||
## Architecture
|
||||
|
||||
DeerFlow implements a modular multi-agent system architecture designed for automated research and code analysis. The system is built on LangGraph, enabling a flexible state-based workflow where components communicate through a well-defined message passing system.
|
||||
|
||||

|
||||
|
||||
> See it live at [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)
|
||||
|
||||
The system employs a streamlined workflow with the following components:
|
||||
|
||||
1. **Coordinator**: The entry point that manages the workflow lifecycle
|
||||
|
||||
- Initiates the research process based on user input
|
||||
- Delegates tasks to the planner when appropriate
|
||||
- Acts as the primary interface between the user and the system
|
||||
|
||||
2. **Planner**: Strategic component for task decomposition and planning
|
||||
|
||||
- Analyzes research objectives and creates structured execution plans
|
||||
- Determines if enough context is available or if more research is needed
|
||||
- Manages the research flow and decides when to generate the final report
|
||||
|
||||
3. **Research Team**: A collection of specialized agents that execute the plan:
|
||||
- **Researcher**: Conducts web searches and information gathering using tools like web search engines, crawling and even MCP services.
|
||||
- **Coder**: Handles code analysis, execution, and technical tasks using Python REPL tool.
|
||||
Each agent has access to specific tools optimized for their role and operates within the LangGraph framework
|
||||
|
||||
4. **Reporter**: Final stage processor for research outputs
|
||||
- Aggregates findings from the research team
|
||||
- Processes and structures the collected information
|
||||
- Generates comprehensive research reports
|
||||
|
||||
## Text-to-Speech Integration
|
||||
|
||||
DeerFlow now includes a Text-to-Speech (TTS) feature that allows you to convert research reports to speech. This feature uses the volcengine TTS API to generate high-quality audio from text. Features like speed, volume, and pitch are also customizable.
|
||||
|
||||
### Using the TTS API
|
||||
|
||||
You can access the TTS functionality through the `/api/tts` endpoint:
|
||||
|
||||
```bash
|
||||
# Example API call using curl
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "This is a test of the text-to-speech functionality.",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Testing
|
||||
Install development dependencies:
|
||||
|
||||
```bash
|
||||
uv pip install -e ".[test]"
|
||||
```
|
||||
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
make test
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Run with coverage
|
||||
make coverage
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# Run linting
|
||||
make lint
|
||||
|
||||
# Format code
|
||||
make format
|
||||
```
|
||||
|
||||
### Debugging with LangGraph Studio
|
||||
|
||||
DeerFlow uses LangGraph for its workflow architecture. You can use LangGraph Studio to debug and visualize the workflow in real-time.
|
||||
|
||||
#### Running LangGraph Studio Locally
|
||||
|
||||
DeerFlow includes a `langgraph.json` configuration file that defines the graph structure and dependencies for the LangGraph Studio. This file points to the workflow graphs defined in the project and automatically loads environment variables from the `.env` file.
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# Install uv package manager if you don't have it
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Install dependencies and start the LangGraph server
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# Start the LangGraph server
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
After starting the LangGraph server, you'll see several URLs in the terminal:
|
||||
|
||||
- API: http://127.0.0.1:2024
|
||||
- Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
|
||||
- API Docs: http://127.0.0.1:2024/docs
|
||||
|
||||
Open the Studio UI link in your browser to access the debugging interface.
|
||||
|
||||
#### Using LangGraph Studio
|
||||
|
||||
In the Studio UI, you can:
|
||||
|
||||
1. Visualize the workflow graph and see how components connect
|
||||
2. Trace execution in real-time to see how data flows through the system
|
||||
3. Inspect the state at each step of the workflow
|
||||
4. Debug issues by examining inputs and outputs of each component
|
||||
5. Provide feedback during the planning phase to refine research plans
|
||||
|
||||
When you submit a research topic in the Studio UI, you'll be able to see the entire workflow execution, including:
|
||||
|
||||
- The planning phase where the research plan is created
|
||||
- The feedback loop where you can modify the plan
|
||||
- The research and writing phases for each section
|
||||
- The final report generation
|
||||
|
||||
### Enabling LangSmith Tracing
|
||||
|
||||
DeerFlow supports LangSmith tracing to help you debug and monitor your workflows. To enable LangSmith tracing:
|
||||
|
||||
1. Make sure your `.env` file has the following configurations (see `.env.example`):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. Start tracing and visualize the graph locally with LangSmith by running:
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
This will enable trace visualization in LangGraph Studio and send your traces to LangSmith for monitoring and analysis.
|
||||
|
||||
### Checkpointing
|
||||
1. Postgres and MongoDB implementation of LangGraph checkpoint saver.
|
||||
2. In-memory store is used to cache the streaming messages before persisting to database; If finish_reason is "stop" or "interrupt", it triggers persistence.
|
||||
3. Supports saving and loading checkpoints for workflow execution.
|
||||
4. Supports saving chat stream events for replaying conversations.
|
||||
|
||||
*Note: About langgraph issue #5557*
|
||||
The latest langgraph-checkpoint-postgres-2.0.23 have checkpointing issue, you can check the open issue: "TypeError: Object of type HumanMessage is not JSON serializable" [https://github.com/langchain-ai/langgraph/issues/5557].
|
||||
|
||||
To use postgres checkpoint, you should install langgraph-checkpoint-postgres-2.0.21
|
||||
|
||||
*Note: About psycopg dependencies*
|
||||
Please read the following document before using postgres: https://www.psycopg.org/psycopg3/docs/basic/install.html
|
||||
|
||||
BY default, psycopg needs libpq to be installed on your system. If you don't have libpq installed, you can install psycopg with the `binary` extra to include a statically linked version of libpq manually:
|
||||
|
||||
```bash
|
||||
pip install psycopg[binary]
|
||||
```
|
||||
This will install a self-contained package with all the libraries needed, but binary not supported for all platform, you check the supported platform: https://pypi.org/project/psycopg-binary/#files
|
||||
|
||||
If not supported, you can select local-installation: https://www.psycopg.org/psycopg3/docs/basic/install.html#local-installation
|
||||
|
||||
|
||||
The default database and collection will be automatically created if not exists.
|
||||
Default database: checkpoing_db
|
||||
Default collection: checkpoint_writes_aio (langgraph checkpoint writes)
|
||||
Default collection: checkpoints_aio (langgraph checkpoints)
|
||||
Default collection: chat_streams (chat stream events for replaying conversations)
|
||||
|
||||
You need to set the following environment variables in your `.env` file:
|
||||
|
||||
```bash
|
||||
# Enable LangGraph checkpoint saver, supports MongoDB, Postgres
|
||||
LANGGRAPH_CHECKPOINT_SAVER=true
|
||||
# Set the database URL for saving checkpoints
|
||||
LANGGRAPH_CHECKPOINT_DB_URL="mongodb://localhost:27017/"
|
||||
#LANGGRAPH_CHECKPOINT_DB_URL=postgresql://localhost:5432/postgres
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
You can also run this project with Docker.
|
||||
|
||||
First, you need to read the [configuration](docs/configuration_guide.md) below. Make sure `.env`, `.conf.yaml` files are ready.
|
||||
|
||||
Second, to build a Docker image of your own web server:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
Finally, start up a docker container running the web server:
|
||||
```bash
|
||||
# Replace deer-flow-api-app with your preferred container name
|
||||
# Start the server then bind to localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# stop the server
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose (include both backend and frontend)
|
||||
|
||||
DeerFlow provides a docker-compose setup to easily run both the backend and frontend together.
|
||||
|
||||
#### Configuration
|
||||
|
||||
Before building, configure the root `.env` file (copied from `.env.example`):
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
cp conf.yaml.example conf.yaml
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The `docker-compose.yml` only uses the **root `.env`** file (not `web/.env`). You do **not** need to create or modify `web/.env` when using Docker Compose.
|
||||
|
||||
If you are deploying on a **remote server** or accessing from a **LAN IP** (not `localhost`), you **must** update `NEXT_PUBLIC_API_URL` in the root `.env` to your actual host IP or domain:
|
||||
|
||||
```bash
|
||||
# Example: accessing from LAN IP
|
||||
NEXT_PUBLIC_API_URL=http://192.168.1.100:8000/api
|
||||
|
||||
# Example: remote deployment with domain
|
||||
NEXT_PUBLIC_API_URL=https://your-domain.com/api
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `NEXT_PUBLIC_API_URL` is a **build-time** variable for Next.js — it gets embedded into the frontend JavaScript bundle during `docker compose build`. If you change this value later, you must rebuild with `docker compose build` for the change to take effect.
|
||||
|
||||
#### Build and Run
|
||||
|
||||
```bash
|
||||
# building docker image
|
||||
docker compose build
|
||||
|
||||
# start the server
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> If you want to deploy the deer flow into production environments, please add authentication to the website and evaluate your security check of the MCPServer and Python Repl.
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples demonstrate the capabilities of DeerFlow:
|
||||
|
||||
### Research Reports
|
||||
|
||||
1. **OpenAI Sora Report** - Analysis of OpenAI's Sora AI tool
|
||||
|
||||
- Discusses features, access, prompt engineering, limitations, and ethical considerations
|
||||
- [View full report](examples/openai_sora_report.md)
|
||||
|
||||
2. **Google's Agent to Agent Protocol Report** - Overview of Google's Agent to Agent (A2A) protocol
|
||||
|
||||
- Discusses its role in AI agent communication and its relationship with Anthropic's Model Context Protocol (MCP)
|
||||
- [View full report](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **What is MCP?** - A comprehensive analysis of the term "MCP" across multiple contexts
|
||||
|
||||
- Explores Model Context Protocol in AI, Monocalcium Phosphate in chemistry, and Micro-channel Plate in electronics
|
||||
- [View full report](examples/what_is_mcp.md)
|
||||
|
||||
4. **Bitcoin Price Fluctuations** - Analysis of recent Bitcoin price movements
|
||||
|
||||
- Examines market trends, regulatory influences, and technical indicators
|
||||
- Provides recommendations based on historical data
|
||||
- [View full report](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **What is LLM?** - An in-depth exploration of Large Language Models
|
||||
|
||||
- Discusses architecture, training, applications, and ethical considerations
|
||||
- [View full report](examples/what_is_llm.md)
|
||||
|
||||
6. **How to Use Claude for Deep Research?** - Best practices and workflows for using Claude in deep research
|
||||
|
||||
- Covers prompt engineering, data analysis, and integration with other tools
|
||||
- [View full report](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **AI Adoption in Healthcare: Influencing Factors** - Analysis of factors driving AI adoption in healthcare
|
||||
|
||||
- Discusses AI technologies, data quality, ethical considerations, economic evaluations, organizational readiness, and digital infrastructure
|
||||
- [View full report](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **Quantum Computing Impact on Cryptography** - Analysis of quantum computing's impact on cryptography
|
||||
|
||||
- Discusses vulnerabilities of classical cryptography, post-quantum cryptography, and quantum-resistant cryptographic solutions
|
||||
- [View full report](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **Cristiano Ronaldo's Performance Highlights** - Analysis of Cristiano Ronaldo's performance highlights
|
||||
- Discusses his career achievements, international goals, and performance in various matches
|
||||
- [View full report](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
To run these examples or create your own research reports, you can use the following commands:
|
||||
|
||||
```bash
|
||||
# Run with a specific query
|
||||
uv run main.py "What factors are influencing AI adoption in healthcare?"
|
||||
|
||||
# Run with custom planning parameters
|
||||
uv run main.py --max_plan_iterations 3 "How does quantum computing impact cryptography?"
|
||||
|
||||
# Run in interactive mode with built-in questions
|
||||
uv run main.py --interactive
|
||||
|
||||
# Or run with basic interactive prompt
|
||||
uv run main.py
|
||||
|
||||
# View all available options
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### Interactive Mode
|
||||
|
||||
The application now supports an interactive mode with built-in questions in both English and Chinese:
|
||||
|
||||
1. Launch the interactive mode:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. Select your preferred language (English or 中文)
|
||||
|
||||
3. Choose from a list of built-in questions or select the option to ask your own question
|
||||
|
||||
4. The system will process your question and generate a comprehensive research report
|
||||
|
||||
### Human in the Loop
|
||||
|
||||
DeerFlow includes a human in the loop mechanism that allows you to review, edit, and approve research plans before they are executed:
|
||||
|
||||
1. **Plan Review**: When human in the loop is enabled, the system will present the generated research plan for your review before execution
|
||||
|
||||
2. **Providing Feedback**: You can:
|
||||
|
||||
- Accept the plan by responding with `[ACCEPTED]`
|
||||
- Edit the plan by providing feedback (e.g., `[EDIT PLAN] Add more steps about technical implementation`)
|
||||
- The system will incorporate your feedback and generate a revised plan
|
||||
|
||||
3. **Auto-acceptance**: You can enable auto-acceptance to skip the review process:
|
||||
|
||||
- Via API: Set `auto_accepted_plan: true` in your request
|
||||
|
||||
4. **API Integration**: When using the API, you can provide feedback through the `feedback` parameter:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{ "role": "user", "content": "What is quantum computing?" }],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] Include more about quantum algorithms"
|
||||
}
|
||||
```
|
||||
|
||||
### Command Line Arguments
|
||||
|
||||
The application supports several command-line arguments to customize its behavior:
|
||||
|
||||
- **query**: The research query to process (can be multiple words)
|
||||
- **--interactive**: Run in interactive mode with built-in questions
|
||||
- **--max_plan_iterations**: Maximum number of planning cycles (default: 1)
|
||||
- **--max_step_num**: Maximum number of steps in a research plan (default: 3)
|
||||
- **--debug**: Enable detailed debug logging
|
||||
|
||||
## FAQ
|
||||
|
||||
Please refer to the [FAQ.md](docs/FAQ.md) for more details.
|
||||
|
||||
## License
|
||||
|
||||
This project is open source and available under the [MIT License](./LICENSE).
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
DeerFlow is built upon the incredible work of the open-source community. We are deeply grateful to all the projects and contributors whose efforts have made DeerFlow possible. Truly, we stand on the shoulders of giants.
|
||||
|
||||
We would like to extend our sincere appreciation to the following projects for their invaluable contributions:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**: Their exceptional framework powers our LLM interactions and chains, enabling seamless integration and functionality.
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: Their innovative approach to multi-agent orchestration has been instrumental in enabling DeerFlow's sophisticated workflows.
|
||||
- **[Novel](https://github.com/steven-tey/novel)**: Their Notion-style WYSIWYG editor supports our report editing and AI-assisted rewriting.
|
||||
- **[RAGFlow](https://github.com/infiniflow/ragflow)**: We have achieved support for research on users' private knowledge bases through integration with RAGFlow.
|
||||
|
||||
These projects exemplify the transformative power of open-source collaboration, and we are proud to build upon their foundations.
|
||||
|
||||
### Key Contributors
|
||||
|
||||
A heartfelt thank you goes out to the core authors of `DeerFlow`, whose vision, passion, and dedication have brought this project to life:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
Your unwavering commitment and expertise have been the driving force behind DeerFlow's success. We are honored to have you at the helm of this journey.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
610
README_de.md
610
README_de.md
@@ -1,610 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/bytedance/deer-flow)
|
||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> Aus Open Source entstanden, an Open Source zurückgeben.
|
||||
|
||||
**DeerFlow** (**D**eep **E**xploration and **E**fficient **R**esearch **Flow**) ist ein Community-getriebenes Framework für tiefgehende Recherche, das auf der großartigen Arbeit der Open-Source-Community aufbaut. Unser Ziel ist es, Sprachmodelle mit spezialisierten Werkzeugen für Aufgaben wie Websuche, Crawling und Python-Code-Ausführung zu kombinieren und gleichzeitig der Community, die dies möglich gemacht hat, etwas zurückzugeben.
|
||||
|
||||
Derzeit ist DeerFlow offiziell in das [FaaS-Anwendungszentrum von Volcengine](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market) eingezogen. Benutzer können es über den [Erfahrungslink](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market/deerflow/?channel=github&source=deerflow) online erleben, um seine leistungsstarken Funktionen und bequemen Operationen intuitiv zu spüren. Gleichzeitig unterstützt DeerFlow zur Erfüllung der Bereitstellungsanforderungen verschiedener Benutzer die Ein-Klick-Bereitstellung basierend auf Volcengine. Klicken Sie auf den [Bereitstellungslink](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/application/create?templateId=683adf9e372daa0008aaed5c&channel=github&source=deerflow), um den Bereitstellungsprozess schnell abzuschließen und eine effiziente Forschungsreise zu beginnen.
|
||||
|
||||
DeerFlow hat neu die intelligente Such- und Crawling-Toolset von BytePlus integriert - [InfoQuest (unterstützt kostenlose Online-Erfahrung)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
Besuchen Sie [unsere offizielle Website](https://deerflow.tech/) für weitere Details.
|
||||
|
||||
## Demo
|
||||
|
||||
### Video
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
In dieser Demo zeigen wir, wie man DeerFlow nutzt, um:
|
||||
|
||||
- Nahtlos mit MCP-Diensten zu integrieren
|
||||
- Den Prozess der tiefgehenden Recherche durchzuführen und einen umfassenden Bericht mit Bildern zu erstellen
|
||||
- Podcast-Audio basierend auf dem generierten Bericht zu erstellen
|
||||
|
||||
### Wiedergaben
|
||||
|
||||
- [Wie hoch ist der Eiffelturm im Vergleich zum höchsten Gebäude?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [Was sind die angesagtesten Repositories auf GitHub?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [Einen Artikel über traditionelle Gerichte aus Nanjing schreiben](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [Wie dekoriert man eine Mietwohnung?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [Besuchen Sie unsere offizielle Website, um weitere Wiedergaben zu entdecken.](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 Inhaltsverzeichnis
|
||||
|
||||
- [🚀 Schnellstart](#schnellstart)
|
||||
- [🌟 Funktionen](#funktionen)
|
||||
- [🏗️ Architektur](#architektur)
|
||||
- [🛠️ Entwicklung](#entwicklung)
|
||||
- [🐳 Docker](#docker)
|
||||
- [🗣️ Text-zu-Sprache-Integration](#text-zu-sprache-integration)
|
||||
- [📚 Beispiele](#beispiele)
|
||||
- [❓ FAQ](#faq)
|
||||
- [📜 Lizenz](#lizenz)
|
||||
- [💖 Danksagungen](#danksagungen)
|
||||
- [⭐ Star-Verlauf](#star-verlauf)
|
||||
|
||||
## Schnellstart
|
||||
|
||||
DeerFlow ist in Python entwickelt und kommt mit einer in Node.js geschriebenen Web-UI. Um einen reibungslosen Einrichtungsprozess zu gewährleisten, empfehlen wir die Verwendung der folgenden Tools:
|
||||
|
||||
### Empfohlene Tools
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Vereinfacht die Verwaltung von Python-Umgebungen und Abhängigkeiten. `uv` erstellt automatisch eine virtuelle Umgebung im Stammverzeichnis und installiert alle erforderlichen Pakete für Sie—keine manuelle Installation von Python-Umgebungen notwendig.
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
Verwalten Sie mühelos mehrere Versionen der Node.js-Laufzeit.
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Installieren und verwalten Sie Abhängigkeiten des Node.js-Projekts.
|
||||
|
||||
### Umgebungsanforderungen
|
||||
|
||||
Stellen Sie sicher, dass Ihr System die folgenden Mindestanforderungen erfüllt:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** Version `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** Version `22+`
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Repository klonen
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# Abhängigkeiten installieren, uv kümmert sich um den Python-Interpreter und die Erstellung der venv sowie die Installation der erforderlichen Pakete
|
||||
uv sync
|
||||
|
||||
# Konfigurieren Sie .env mit Ihren API-Schlüsseln
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# volcengine TTS: Fügen Sie Ihre TTS-Anmeldedaten hinzu, falls vorhanden
|
||||
cp .env.example .env
|
||||
|
||||
# Siehe die Abschnitte 'Unterstützte Suchmaschinen' und 'Text-zu-Sprache-Integration' unten für alle verfügbaren Optionen
|
||||
|
||||
# Konfigurieren Sie conf.yaml für Ihr LLM-Modell und API-Schlüssel
|
||||
# Weitere Details finden Sie unter 'docs/configuration_guide.md'
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Installieren Sie marp für PPT-Generierung
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
Optional können Sie Web-UI-Abhängigkeiten über [pnpm](https://pnpm.io/installation) installieren:
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Konfigurationen
|
||||
|
||||
Weitere Informationen finden Sie im [Konfigurationsleitfaden](docs/configuration_guide.md).
|
||||
|
||||
> [!HINWEIS]
|
||||
> Lesen Sie den Leitfaden sorgfältig, bevor Sie das Projekt starten, und aktualisieren Sie die Konfigurationen entsprechend Ihren spezifischen Einstellungen und Anforderungen.
|
||||
|
||||
### Konsolen-UI
|
||||
|
||||
Der schnellste Weg, um das Projekt auszuführen, ist die Verwendung der Konsolen-UI.
|
||||
|
||||
```bash
|
||||
# Führen Sie das Projekt in einer bash-ähnlichen Shell aus
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Web-UI
|
||||
|
||||
Dieses Projekt enthält auch eine Web-UI, die ein dynamischeres und ansprechenderes interaktives Erlebnis bietet.
|
||||
|
||||
> [!HINWEIS]
|
||||
> Sie müssen zuerst die Abhängigkeiten der Web-UI installieren.
|
||||
|
||||
```bash
|
||||
# Führen Sie sowohl den Backend- als auch den Frontend-Server im Entwicklungsmodus aus
|
||||
# Unter macOS/Linux
|
||||
./bootstrap.sh -d
|
||||
|
||||
# Unter Windows
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!HINWEIS]
|
||||
> Standardmäßig bindet sich der Backend-Server aus Sicherheitsgründen an 127.0.0.1 (localhost). Wenn Sie externe Verbindungen zulassen müssen (z. B. bei der Bereitstellung auf einem Linux-Server), können Sie den Server-Host im Bootstrap-Skript auf 0.0.0.0 ändern (uv run server.py --host 0.0.0.0).
|
||||
> Bitte stellen Sie sicher, dass Ihre Umgebung ordnungsgemäß gesichert ist, bevor Sie den Service externen Netzwerken aussetzen.
|
||||
|
||||
Öffnen Sie Ihren Browser und besuchen Sie [`http://localhost:3000`](http://localhost:3000), um die Web-UI zu erkunden.
|
||||
|
||||
Weitere Details finden Sie im Verzeichnis [`web`](./web/).
|
||||
|
||||
## Unterstützte Suchmaschinen
|
||||
|
||||
### Websuche
|
||||
|
||||
DeerFlow unterstützt mehrere Suchmaschinen, die in Ihrer `.env`-Datei über die Variable `SEARCH_API` konfiguriert werden können:
|
||||
|
||||
- **Tavily** (Standard): Eine spezialisierte Such-API für KI-Anwendungen
|
||||
- Erfordert `TAVILY_API_KEY` in Ihrer `.env`-Datei
|
||||
- Registrieren Sie sich unter: https://app.tavily.com/home
|
||||
|
||||
- **InfoQuest** (empfohlen): Ein KI-optimiertes intelligentes Such- und Crawling-Toolset, entwickelt von BytePlus
|
||||
- Erfordert `INFOQUEST_API_KEY` in Ihrer `.env`-Datei
|
||||
- Unterstützung für Zeitbereichsfilterung und Seitenfilterung
|
||||
- Bietet qualitativ hochwertige Suchergebnisse und Inhaltsextraktion
|
||||
- Registrieren Sie sich unter: https://console.byteplus.com/infoquest/infoquests
|
||||
- Besuchen Sie https://docs.byteplus.com/de/docs/InfoQuest/What_is_Info_Quest für weitere Informationen
|
||||
|
||||
- **DuckDuckGo**: Datenschutzorientierte Suchmaschine
|
||||
- Kein API-Schlüssel erforderlich
|
||||
|
||||
- **Brave Search**: Datenschutzorientierte Suchmaschine mit erweiterten Funktionen
|
||||
- Erfordert `BRAVE_SEARCH_API_KEY` in Ihrer `.env`-Datei
|
||||
- Registrieren Sie sich unter: https://brave.com/search/api/
|
||||
|
||||
- **Arxiv**: Wissenschaftliche Papiersuche für akademische Forschung
|
||||
- Kein API-Schlüssel erforderlich
|
||||
- Spezialisiert auf wissenschaftliche und akademische Papiere
|
||||
|
||||
- **Searx/SearxNG**: Selbstgehostete Metasuchmaschine
|
||||
- Erfordert `SEARX_HOST` in Ihrer `.env`-Datei
|
||||
- Unterstützt die Anbindung an Searx oder SearxNG
|
||||
|
||||
Um Ihre bevorzugte Suchmaschine zu konfigurieren, setzen Sie die Variable `SEARCH_API` in Ihrer `.env`-Datei:
|
||||
|
||||
```bash
|
||||
# Wählen Sie eine: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### Crawling-Tools
|
||||
|
||||
- **Jina** (Standard): Kostenloses, zugängliches Webinhalts-Crawling-Tool
|
||||
- Kein API-Schlüssel erforderlich für grundlegende Funktionen
|
||||
- Mit API-Schlüssel erhalten Sie höhere Zugriffsraten
|
||||
- Weitere Informationen unter <https://jina.ai/reader>
|
||||
|
||||
- **InfoQuest** (empfohlen): KI-optimiertes intelligentes Such- und Crawling-Toolset, entwickelt von BytePlus
|
||||
- Erfordert `INFOQUEST_API_KEY` in Ihrer `.env`-Datei
|
||||
- Bietet konfigurierbare Crawling-Parameter
|
||||
- Unterstützt benutzerdefinierte Timeout-Einstellungen
|
||||
- Bietet stärkere Inhaltsextraktionsfähigkeiten
|
||||
- Weitere Informationen unter <https://docs.byteplus.com/de/docs/InfoQuest/What_is_Info_Quest>
|
||||
|
||||
Um Ihr bevorzugtes Crawling-Tool zu konfigurieren, setzen Sie Folgendes in Ihrer `conf.yaml`-Datei:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# Engine-Typ: "jina" (Standard) oder "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
### Private Wissensbasis
|
||||
|
||||
DeerFlow unterstützt private Wissensbasen wie RAGFlow und VikingDB, sodass Sie Ihre privaten Dokumente zur Beantwortung von Fragen verwenden können.
|
||||
|
||||
- **[RAGFlow](https://ragflow.io/docs/dev/)**:Open-Source-RAG-Engine
|
||||
```
|
||||
# Beispiele in .env.example
|
||||
RAG_PROVIDER=ragflow
|
||||
RAGFLOW_API_URL="http://localhost:9388"
|
||||
RAGFLOW_API_KEY="ragflow-xxx"
|
||||
RAGFLOW_RETRIEVAL_SIZE=10
|
||||
RAGFLOW_CROSS_LANGUAGES=English,Chinese,Spanish,French,German,Japanese,Korean
|
||||
```
|
||||
|
||||
## Funktionen
|
||||
|
||||
### Kernfähigkeiten
|
||||
|
||||
- 🤖 **LLM-Integration**
|
||||
- Unterstützt die Integration der meisten Modelle über [litellm](https://docs.litellm.ai/docs/providers).
|
||||
- Unterstützung für Open-Source-Modelle wie Qwen
|
||||
- OpenAI-kompatible API-Schnittstelle
|
||||
- Mehrstufiges LLM-System für unterschiedliche Aufgabenkomplexitäten
|
||||
|
||||
### Tools und MCP-Integrationen
|
||||
|
||||
- 🔍 **Suche und Abruf**
|
||||
- Websuche über Tavily, InfoQuest, Brave Search und mehr
|
||||
- Crawling mit Jina und InfoQuest
|
||||
- Fortgeschrittene Inhaltsextraktion
|
||||
- Unterstützung für private Wissensbasis
|
||||
|
||||
- 📃 **RAG-Integration**
|
||||
|
||||
- Unterstützt die Erwähnung von Dateien aus [RAGFlow](https://github.com/infiniflow/ragflow) innerhalb der Eingabebox. [RAGFlow-Server starten](https://ragflow.io/docs/dev/).
|
||||
|
||||
- 🔗 **MCP Nahtlose Integration**
|
||||
- Erweiterte Fähigkeiten für privaten Domänenzugriff, Wissensgraphen, Webbrowsing und mehr
|
||||
- Erleichtert die Integration verschiedener Forschungswerkzeuge und -methoden
|
||||
|
||||
### Menschliche Zusammenarbeit
|
||||
|
||||
- 🧠 **Mensch-in-der-Schleife**
|
||||
- Unterstützt interaktive Modifikation von Forschungsplänen mit natürlicher Sprache
|
||||
- Unterstützt automatische Akzeptanz von Forschungsplänen
|
||||
|
||||
- 📝 **Bericht-Nachbearbeitung**
|
||||
- Unterstützt Notion-ähnliche Blockbearbeitung
|
||||
- Ermöglicht KI-Verfeinerungen, einschließlich KI-unterstützter Polierung, Satzkürzung und -erweiterung
|
||||
- Angetrieben von [tiptap](https://tiptap.dev/)
|
||||
|
||||
### Inhaltserstellung
|
||||
|
||||
- 🎙️ **Podcast- und Präsentationserstellung**
|
||||
- KI-gestützte Podcast-Skripterstellung und Audiosynthese
|
||||
- Automatisierte Erstellung einfacher PowerPoint-Präsentationen
|
||||
- Anpassbare Vorlagen für maßgeschneiderte Inhalte
|
||||
|
||||
## Architektur
|
||||
|
||||
DeerFlow implementiert eine modulare Multi-Agenten-Systemarchitektur, die für automatisierte Forschung und Codeanalyse konzipiert ist. Das System basiert auf LangGraph und ermöglicht einen flexiblen zustandsbasierten Workflow, bei dem Komponenten über ein klar definiertes Nachrichtenübermittlungssystem kommunizieren.
|
||||
|
||||

|
||||
|
||||
> Sehen Sie es live auf [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)
|
||||
|
||||
Das System verwendet einen optimierten Workflow mit den folgenden Komponenten:
|
||||
|
||||
1. **Koordinator**: Der Einstiegspunkt, der den Workflow-Lebenszyklus verwaltet
|
||||
- Initiiert den Forschungsprozess basierend auf Benutzereingaben
|
||||
- Delegiert Aufgaben bei Bedarf an den Planer
|
||||
- Fungiert als primäre Schnittstelle zwischen dem Benutzer und dem System
|
||||
|
||||
2. **Planer**: Strategische Komponente für Aufgabenzerlegung und -planung
|
||||
- Analysiert Forschungsziele und erstellt strukturierte Ausführungspläne
|
||||
- Bestimmt, ob ausreichend Kontext verfügbar ist oder ob weitere Forschung benötigt wird
|
||||
- Verwaltet den Forschungsablauf und entscheidet, wann der endgültige Bericht erstellt wird
|
||||
|
||||
3. **Forschungsteam**: Eine Sammlung spezialisierter Agenten, die den Plan ausführen:
|
||||
- **Forscher**: Führt Websuchen und Informationssammlung mit Tools wie Websuchmaschinen, Crawling und sogar MCP-Diensten durch.
|
||||
- **Codierer**: Behandelt Codeanalyse, -ausführung und technische Aufgaben mit dem Python REPL Tool.
|
||||
Jeder Agent hat Zugriff auf spezifische Tools, die für seine Rolle optimiert sind, und operiert innerhalb des LangGraph-Frameworks
|
||||
|
||||
4. **Reporter**: Endphasenprozessor für Forschungsergebnisse
|
||||
- Aggregiert Erkenntnisse vom Forschungsteam
|
||||
- Verarbeitet und strukturiert die gesammelten Informationen
|
||||
- Erstellt umfassende Forschungsberichte
|
||||
|
||||
## Text-zu-Sprache-Integration
|
||||
|
||||
DeerFlow enthält jetzt eine Text-zu-Sprache (TTS)-Funktion, mit der Sie Forschungsberichte in Sprache umwandeln können. Diese Funktion verwendet die volcengine TTS API, um hochwertige Audios aus Text zu generieren. Funktionen wie Geschwindigkeit, Lautstärke und Tonhöhe können ebenfalls angepasst werden.
|
||||
|
||||
### Verwendung der TTS API
|
||||
|
||||
Sie können auf die TTS-Funktionalität über den Endpunkt `/api/tts` zugreifen:
|
||||
|
||||
```bash
|
||||
# Beispiel API-Aufruf mit curl
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "Dies ist ein Test der Text-zu-Sprache-Funktionalität.",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### Testen
|
||||
|
||||
Führen Sie die Testsuite aus:
|
||||
|
||||
```bash
|
||||
# Alle Tests ausführen
|
||||
make test
|
||||
|
||||
# Spezifische Testdatei ausführen
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Mit Abdeckung ausführen
|
||||
make coverage
|
||||
```
|
||||
|
||||
### Codequalität
|
||||
|
||||
```bash
|
||||
# Lint ausführen
|
||||
make lint
|
||||
|
||||
# Code formatieren
|
||||
make format
|
||||
```
|
||||
|
||||
### Debugging mit LangGraph Studio
|
||||
|
||||
DeerFlow verwendet LangGraph für seine Workflow-Architektur. Sie können LangGraph Studio verwenden, um den Workflow in Echtzeit zu debuggen und zu visualisieren.
|
||||
|
||||
#### LangGraph Studio lokal ausführen
|
||||
|
||||
DeerFlow enthält eine `langgraph.json`-Konfigurationsdatei, die die Graphstruktur und Abhängigkeiten für das LangGraph Studio definiert. Diese Datei verweist auf die im Projekt definierten Workflow-Graphen und lädt automatisch Umgebungsvariablen aus der `.env`-Datei.
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# Installieren Sie den uv-Paketmanager, wenn Sie ihn noch nicht haben
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Installieren Sie Abhängigkeiten und starten Sie den LangGraph-Server
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# Abhängigkeiten installieren
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# LangGraph-Server starten
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Nach dem Start des LangGraph-Servers sehen Sie mehrere URLs im Terminal:
|
||||
|
||||
- API: http://127.0.0.1:2024
|
||||
- Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
|
||||
- API-Dokumentation: http://127.0.0.1:2024/docs
|
||||
|
||||
Öffnen Sie den Studio UI-Link in Ihrem Browser, um auf die Debugging-Schnittstelle zuzugreifen.
|
||||
|
||||
#### Verwendung von LangGraph Studio
|
||||
|
||||
In der Studio UI können Sie:
|
||||
|
||||
1. Den Workflow-Graphen visualisieren und sehen, wie Komponenten verbunden sind
|
||||
2. Die Ausführung in Echtzeit verfolgen, um zu sehen, wie Daten durch das System fließen
|
||||
3. Den Zustand in jedem Schritt des Workflows inspizieren
|
||||
4. Probleme durch Untersuchung von Ein- und Ausgaben jeder Komponente debuggen
|
||||
5. Feedback während der Planungsphase geben, um Forschungspläne zu verfeinern
|
||||
|
||||
Wenn Sie ein Forschungsthema in der Studio UI einreichen, können Sie die gesamte Workflow-Ausführung sehen, einschließlich:
|
||||
|
||||
- Die Planungsphase, in der der Forschungsplan erstellt wird
|
||||
- Die Feedback-Schleife, in der Sie den Plan ändern können
|
||||
- Die Forschungs- und Schreibphasen für jeden Abschnitt
|
||||
- Die Erstellung des endgültigen Berichts
|
||||
|
||||
### Aktivieren von LangSmith-Tracing
|
||||
|
||||
DeerFlow unterstützt LangSmith-Tracing, um Ihnen beim Debuggen und Überwachen Ihrer Workflows zu helfen. Um LangSmith-Tracing zu aktivieren:
|
||||
|
||||
1. Stellen Sie sicher, dass Ihre `.env`-Datei die folgenden Konfigurationen enthält (siehe `.env.example`):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. Starten Sie das Tracing mit LangSmith lokal, indem Sie folgenden Befehl ausführen:
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Dies aktiviert die Trace-Visualisierung in LangGraph Studio und sendet Ihre Traces zur Überwachung und Analyse an LangSmith.
|
||||
|
||||
## Docker
|
||||
|
||||
Sie können dieses Projekt auch mit Docker ausführen.
|
||||
|
||||
Zuerst müssen Sie die [Konfiguration](docs/configuration_guide.md) unten lesen. Stellen Sie sicher, dass die Dateien `.env` und `.conf.yaml` bereit sind.
|
||||
|
||||
Zweitens, um ein Docker-Image Ihres eigenen Webservers zu erstellen:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
Schließlich starten Sie einen Docker-Container, der den Webserver ausführt:
|
||||
|
||||
```bash
|
||||
# Ersetzen Sie deer-flow-api-app durch Ihren bevorzugten Container-Namen
|
||||
# Starten Sie den Server und binden Sie ihn an localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# Server stoppen
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose (umfasst sowohl Backend als auch Frontend)
|
||||
|
||||
DeerFlow bietet ein docker-compose-Setup, um sowohl das Backend als auch das Frontend einfach zusammen auszuführen:
|
||||
|
||||
```bash
|
||||
# Docker-Image erstellen
|
||||
docker compose build
|
||||
|
||||
# Server starten
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Wenn Sie DeerFlow in Produktionsumgebungen bereitstellen möchten, fügen Sie bitte Authentifizierung zur Website hinzu und bewerten Sie Ihre Sicherheitsüberprüfung des MCPServer und Python Repl.
|
||||
|
||||
## Beispiele
|
||||
|
||||
Die folgenden Beispiele demonstrieren die Fähigkeiten von DeerFlow:
|
||||
|
||||
### Forschungsberichte
|
||||
|
||||
1. **OpenAI Sora Bericht** - Analyse von OpenAIs Sora KI-Tool
|
||||
- Diskutiert Funktionen, Zugang, Prompt-Engineering, Einschränkungen und ethische Überlegungen
|
||||
- [Vollständigen Bericht ansehen](examples/openai_sora_report.md)
|
||||
|
||||
2. **Googles Agent-to-Agent-Protokoll Bericht** - Überblick über Googles Agent-to-Agent (A2A)-Protokoll
|
||||
- Diskutiert seine Rolle in der KI-Agentenkommunikation und seine Beziehung zum Model Context Protocol (MCP) von Anthropic
|
||||
- [Vollständigen Bericht ansehen](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **Was ist MCP?** - Eine umfassende Analyse des Begriffs "MCP" in mehreren Kontexten
|
||||
- Untersucht Model Context Protocol in KI, Monocalciumphosphat in der Chemie und Micro-channel Plate in der Elektronik
|
||||
- [Vollständigen Bericht ansehen](examples/what_is_mcp.md)
|
||||
|
||||
4. **Bitcoin-Preisschwankungen** - Analyse der jüngsten Bitcoin-Preisbewegungen
|
||||
- Untersucht Markttrends, regulatorische Einflüsse und technische Indikatoren
|
||||
- Bietet Empfehlungen basierend auf historischen Daten
|
||||
- [Vollständigen Bericht ansehen](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **Was ist LLM?** - Eine eingehende Erforschung großer Sprachmodelle
|
||||
- Diskutiert Architektur, Training, Anwendungen und ethische Überlegungen
|
||||
- [Vollständigen Bericht ansehen](examples/what_is_llm.md)
|
||||
|
||||
6. **Wie nutzt man Claude für tiefgehende Recherche?** - Best Practices und Workflows für die Verwendung von Claude in der tiefgehenden Forschung
|
||||
- Behandelt Prompt-Engineering, Datenanalyse und Integration mit anderen Tools
|
||||
- [Vollständigen Bericht ansehen](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **KI-Adoption im Gesundheitswesen: Einflussfaktoren** - Analyse der Faktoren, die die KI-Adoption im Gesundheitswesen vorantreiben
|
||||
- Diskutiert KI-Technologien, Datenqualität, ethische Überlegungen, wirtschaftliche Bewertungen, organisatorische Bereitschaft und digitale Infrastruktur
|
||||
- [Vollständigen Bericht ansehen](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **Auswirkungen des Quantencomputing auf die Kryptographie** - Analyse der Auswirkungen des Quantencomputing auf die Kryptographie
|
||||
- Diskutiert Schwachstellen der klassischen Kryptographie, Post-Quanten-Kryptographie und quantenresistente kryptographische Lösungen
|
||||
- [Vollständigen Bericht ansehen](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **Cristiano Ronaldos Leistungshöhepunkte** - Analyse der Leistungshöhepunkte von Cristiano Ronaldo
|
||||
- Diskutiert seine Karriereerfolge, internationalen Tore und Leistungen in verschiedenen Spielen
|
||||
- [Vollständigen Bericht ansehen](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
Um diese Beispiele auszuführen oder Ihre eigenen Forschungsberichte zu erstellen, können Sie die folgenden Befehle verwenden:
|
||||
|
||||
```bash
|
||||
# Mit einer spezifischen Anfrage ausführen
|
||||
uv run main.py "Welche Faktoren beeinflussen die KI-Adoption im Gesundheitswesen?"
|
||||
|
||||
# Mit benutzerdefinierten Planungsparametern ausführen
|
||||
uv run main.py --max_plan_iterations 3 "Wie wirkt sich Quantencomputing auf die Kryptographie aus?"
|
||||
|
||||
# Im interaktiven Modus mit eingebauten Fragen ausführen
|
||||
uv run main.py --interactive
|
||||
|
||||
# Oder mit grundlegendem interaktiven Prompt ausführen
|
||||
uv run main.py
|
||||
|
||||
# Alle verfügbaren Optionen anzeigen
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### Interaktiver Modus
|
||||
|
||||
Die Anwendung unterstützt jetzt einen interaktiven Modus mit eingebauten Fragen in Englisch und Chinesisch:
|
||||
|
||||
1. Starten Sie den interaktiven Modus:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. Wählen Sie Ihre bevorzugte Sprache (English oder 中文)
|
||||
|
||||
3. Wählen Sie aus einer Liste von eingebauten Fragen oder wählen Sie die Option, Ihre eigene Frage zu stellen
|
||||
|
||||
4. Das System wird Ihre Frage verarbeiten und einen umfassenden Forschungsbericht generieren
|
||||
|
||||
### Mensch-in-der-Schleife
|
||||
DeerFlow enthält einen Mensch-in-der-Schleife-Mechanismus, der es Ihnen ermöglicht, Forschungspläne vor ihrer Ausführung zu überprüfen, zu bearbeiten und zu genehmigen:
|
||||
|
||||
1. **Planüberprüfung**: Wenn Mensch-in-der-Schleife aktiviert ist, präsentiert das System den generierten Forschungsplan zur Überprüfung vor der Ausführung
|
||||
|
||||
2. **Feedback geben**: Sie können:
|
||||
- Den Plan akzeptieren, indem Sie mit `[ACCEPTED]` antworten
|
||||
- Den Plan bearbeiten, indem Sie Feedback geben (z.B., `[EDIT PLAN] Fügen Sie mehr Schritte zur technischen Implementierung hinzu`)
|
||||
- Das System wird Ihr Feedback einarbeiten und einen überarbeiteten Plan generieren
|
||||
|
||||
3. **Automatische Akzeptanz**: Sie können die automatische Akzeptanz aktivieren, um den Überprüfungsprozess zu überspringen:
|
||||
- Über API: Setzen Sie `auto_accepted_plan: true` in Ihrer Anfrage
|
||||
|
||||
4. **API-Integration**: Bei Verwendung der API können Sie Feedback über den Parameter `feedback` geben:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Was ist Quantencomputing?"}],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] Mehr über Quantenalgorithmen aufnehmen"
|
||||
}
|
||||
```
|
||||
|
||||
### Kommandozeilenargumente
|
||||
|
||||
Die Anwendung unterstützt mehrere Kommandozeilenargumente, um ihr Verhalten anzupassen:
|
||||
|
||||
- **query**: Die zu verarbeitende Forschungsanfrage (kann mehrere Wörter umfassen)
|
||||
- **--interactive**: Im interaktiven Modus mit eingebauten Fragen ausführen
|
||||
- **--max_plan_iterations**: Maximale Anzahl von Planungszyklen (Standard: 1)
|
||||
- **--max_step_num**: Maximale Anzahl von Schritten in einem Forschungsplan (Standard: 3)
|
||||
- **--debug**: Detaillierte Debug-Protokollierung aktivieren
|
||||
|
||||
## FAQ
|
||||
|
||||
Weitere Informationen finden Sie in der [FAQ.md](docs/FAQ.md).
|
||||
|
||||
## Lizenz
|
||||
|
||||
Dieses Projekt ist Open Source und unter der [MIT-Lizenz](./LICENSE) verfügbar.
|
||||
|
||||
## Danksagungen
|
||||
|
||||
DeerFlow baut auf der unglaublichen Arbeit der Open-Source-Community auf. Wir sind allen Projekten und Mitwirkenden zutiefst dankbar, deren Bemühungen DeerFlow möglich gemacht haben. Wahrhaftig stehen wir auf den Schultern von Riesen.
|
||||
|
||||
Wir möchten unsere aufrichtige Wertschätzung den folgenden Projekten für ihre unschätzbaren Beiträge aussprechen:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**: Ihr außergewöhnliches Framework unterstützt unsere LLM-Interaktionen und -Ketten und ermöglicht nahtlose Integration und Funktionalität.
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: Ihr innovativer Ansatz zur Multi-Agenten-Orchestrierung war maßgeblich für die Ermöglichung der ausgeklügelten Workflows von DeerFlow.
|
||||
- **[Novel](https://github.com/steven-tey/novel)**: Ihr Notion-artiger WYSIWYG-Editor unterstützt unsere Berichtbearbeitung und KI-unterstützte Umschreibung.
|
||||
- **[RAGFlow](https://github.com/infiniflow/ragflow)**: Wir haben durch die Integration mit RAGFlow die Unterstützung für Forschung auf privaten Wissensdatenbanken der Benutzer erreicht.
|
||||
|
||||
Diese Projekte veranschaulichen die transformative Kraft der Open-Source-Zusammenarbeit, und wir sind stolz darauf, auf ihren Grundlagen aufzubauen.
|
||||
|
||||
### Hauptmitwirkende
|
||||
|
||||
Ein herzliches Dankeschön geht an die Hauptautoren von `DeerFlow`, deren Vision, Leidenschaft und Engagement dieses Projekt zum Leben erweckt haben:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
Ihr unerschütterliches Engagement und Fachwissen waren die treibende Kraft hinter dem Erfolg von DeerFlow. Wir fühlen uns geehrt, Sie an der Spitze dieser Reise zu haben.
|
||||
|
||||
## Star-Verlauf
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
607
README_es.md
607
README_es.md
@@ -1,607 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/bytedance/deer-flow)
|
||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> Originado del código abierto, retribuido al código abierto.
|
||||
|
||||
**DeerFlow** (**D**eep **E**xploration and **E**fficient **R**esearch **Flow**) es un marco de Investigación Profunda impulsado por la comunidad que se basa en el increíble trabajo de la comunidad de código abierto. Nuestro objetivo es combinar modelos de lenguaje con herramientas especializadas para tareas como búsqueda web, rastreo y ejecución de código Python, mientras devolvemos a la comunidad que hizo esto posible.
|
||||
|
||||
Actualmente, DeerFlow ha ingresado oficialmente al Centro de Aplicaciones FaaS de Volcengine. Los usuarios pueden experimentarlo en línea a través del enlace de experiencia para sentir intuitivamente sus potentes funciones y operaciones convenientes. Al mismo tiempo, para satisfacer las necesidades de implementación de diferentes usuarios, DeerFlow admite la implementación con un clic basada en Volcengine. Haga clic en el enlace de implementación para completar rápidamente el proceso de implementación y comenzar un viaje de investigación eficiente.
|
||||
|
||||
DeerFlow ha integrado recientemente el conjunto de herramientas de búsqueda y rastreo inteligente desarrollado independientemente por BytePlus - [InfoQuest (admite experiencia gratuita en línea)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
Por favor, visita [nuestra página web oficial](https://deerflow.tech/) para más detalles.
|
||||
|
||||
## Demostración
|
||||
|
||||
### Video
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
En esta demostración, mostramos cómo usar DeerFlow para:
|
||||
|
||||
- Integrar perfectamente con servicios MCP
|
||||
- Realizar el proceso de Investigación Profunda y producir un informe completo con imágenes
|
||||
- Crear audio de podcast basado en el informe generado
|
||||
|
||||
### Repeticiones
|
||||
|
||||
- [¿Qué altura tiene la Torre Eiffel comparada con el edificio más alto?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [¿Cuáles son los repositorios más populares en GitHub?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [Escribir un artículo sobre los platos tradicionales de Nanjing](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [¿Cómo decorar un apartamento de alquiler?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [Visita nuestra página web oficial para explorar más repeticiones.](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 Tabla de Contenidos
|
||||
|
||||
- [🚀 Inicio Rápido](#inicio-rápido)
|
||||
- [🌟 Características](#características)
|
||||
- [🏗️ Arquitectura](#arquitectura)
|
||||
- [🛠️ Desarrollo](#desarrollo)
|
||||
- [🐳 Docker](#docker)
|
||||
- [🗣️ Integración de Texto a Voz](#integración-de-texto-a-voz)
|
||||
- [📚 Ejemplos](#ejemplos)
|
||||
- [❓ Preguntas Frecuentes](#preguntas-frecuentes)
|
||||
- [📜 Licencia](#licencia)
|
||||
- [💖 Agradecimientos](#agradecimientos)
|
||||
- [⭐ Historial de Estrellas](#historial-de-estrellas)
|
||||
|
||||
## Inicio Rápido
|
||||
|
||||
DeerFlow está desarrollado en Python y viene con una interfaz web escrita en Node.js. Para garantizar un proceso de configuración sin problemas, recomendamos utilizar las siguientes herramientas:
|
||||
|
||||
### Herramientas Recomendadas
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Simplifica la gestión del entorno Python y las dependencias. `uv` crea automáticamente un entorno virtual en el directorio raíz e instala todos los paquetes necesarios por ti—sin necesidad de instalar entornos Python manualmente.
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
Gestiona múltiples versiones del entorno de ejecución Node.js sin esfuerzo.
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Instala y gestiona dependencias del proyecto Node.js.
|
||||
|
||||
### Requisitos del Entorno
|
||||
|
||||
Asegúrate de que tu sistema cumple con los siguientes requisitos mínimos:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** Versión `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** Versión `22+`
|
||||
|
||||
### Instalación
|
||||
|
||||
```bash
|
||||
# Clonar el repositorio
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# Instalar dependencias, uv se encargará del intérprete de python, la creación del entorno virtual y la instalación de los paquetes necesarios
|
||||
uv sync
|
||||
|
||||
# Configurar .env con tus claves API
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# volcengine TTS: Añade tus credenciales TTS si las tienes
|
||||
cp .env.example .env
|
||||
|
||||
# Ver las secciones 'Motores de Búsqueda Compatibles' e 'Integración de Texto a Voz' a continuación para todas las opciones disponibles
|
||||
|
||||
# Configurar conf.yaml para tu modelo LLM y claves API
|
||||
# Por favor, consulta 'docs/configuration_guide.md' para más detalles
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Instalar marp para la generación de presentaciones
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
Opcionalmente, instala las dependencias de la interfaz web vía [pnpm](https://pnpm.io/installation):
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Configuraciones
|
||||
|
||||
Por favor, consulta la [Guía de Configuración](docs/configuration_guide.md) para más detalles.
|
||||
|
||||
> [!NOTA]
|
||||
> Antes de iniciar el proyecto, lee la guía cuidadosamente y actualiza las configuraciones para que coincidan con tus ajustes y requisitos específicos.
|
||||
|
||||
### Interfaz de Consola
|
||||
|
||||
La forma más rápida de ejecutar el proyecto es utilizar la interfaz de consola.
|
||||
|
||||
```bash
|
||||
# Ejecutar el proyecto en un shell tipo bash
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Interfaz Web
|
||||
|
||||
Este proyecto también incluye una Interfaz Web, que ofrece una experiencia interactiva más dinámica y atractiva.
|
||||
|
||||
> [!NOTA]
|
||||
> Necesitas instalar primero las dependencias de la interfaz web.
|
||||
|
||||
```bash
|
||||
# Ejecutar tanto el servidor backend como el frontend en modo desarrollo
|
||||
# En macOS/Linux
|
||||
./bootstrap.sh -d
|
||||
|
||||
# En Windows
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!NOTA]
|
||||
> Por defecto, el servidor backend se enlaza a 127.0.0.1 (localhost) por razones de seguridad. Si necesitas permitir conexiones externas (por ejemplo, al desplegar en un servidor Linux), puedes modificar el host del servidor a 0.0.0.0 en el script de arranque (uv run server.py --host 0.0.0.0).
|
||||
> Por favor, asegúrate de que tu entorno esté correctamente protegido antes de exponer el servicio a redes externas.
|
||||
|
||||
Abre tu navegador y visita [`http://localhost:3000`](http://localhost:3000) para explorar la interfaz web.
|
||||
|
||||
Explora más detalles en el directorio [`web`](./web/).
|
||||
|
||||
## Motores de Búsqueda Compatibles
|
||||
|
||||
DeerFlow soporta múltiples motores de búsqueda que pueden configurarse en tu archivo `.env` usando la variable `SEARCH_API`:
|
||||
|
||||
- **Tavily** (predeterminado): Una API de búsqueda especializada para aplicaciones de IA
|
||||
|
||||
- Requiere `TAVILY_API_KEY` en tu archivo `.env`
|
||||
- Regístrate en: <https://app.tavily.com/home>
|
||||
|
||||
- **InfoQuest** (recomendado): Un conjunto de herramientas inteligentes de búsqueda y rastreo optimizadas para IA, desarrollado por BytePlus
|
||||
- Requiere `INFOQUEST_API_KEY` en tu archivo `.env`
|
||||
- Soporte para filtrado por rango de fecha y filtrado de sitios web
|
||||
- Proporciona resultados de búsqueda y extracción de contenido de alta calidad
|
||||
- Regístrate en: <https://console.byteplus.com/infoquest/infoquests>
|
||||
- Visita https://docs.byteplus.com/es/docs/InfoQuest/What_is_Info_Quest para obtener más información
|
||||
|
||||
- **DuckDuckGo**: Motor de búsqueda centrado en la privacidad
|
||||
|
||||
- No requiere clave API
|
||||
|
||||
- **Brave Search**: Motor de búsqueda centrado en la privacidad con características avanzadas
|
||||
|
||||
- Requiere `BRAVE_SEARCH_API_KEY` en tu archivo `.env`
|
||||
- Regístrate en: <https://brave.com/search/api/>
|
||||
|
||||
- **Arxiv**: Búsqueda de artículos científicos para investigación académica
|
||||
- No requiere clave API
|
||||
- Especializado en artículos científicos y académicos
|
||||
|
||||
- **Searx/SearxNG**: Motor de metabúsqueda autoalojado
|
||||
- Requiere `SEARX_HOST` en tu archivo `.env`
|
||||
- Compatible con Searx o SearxNG
|
||||
|
||||
Para configurar tu motor de búsqueda preferido, establece la variable `SEARCH_API` en tu archivo `.env`:
|
||||
|
||||
```bash
|
||||
# Elige uno: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### Herramientas de Rastreo
|
||||
|
||||
- **Jina** (predeterminado): Herramienta gratuita de rastreo de contenido web accesible
|
||||
- No se requiere clave API para usar funciones básicas
|
||||
- Al usar una clave API, se obtienen límites de tasa de acceso más altos
|
||||
- Visite <https://jina.ai/reader> para obtener más información
|
||||
|
||||
- **InfoQuest** (recomendado): Conjunto de herramientas inteligentes de búsqueda y rastreo optimizadas para IA, desarrollado por BytePlus
|
||||
- Requiere `INFOQUEST_API_KEY` en tu archivo `.env`
|
||||
- Proporciona parámetros de rastreo configurables
|
||||
- Admite configuración de tiempo de espera personalizada
|
||||
- Ofrece capacidades más potentes de extracción de contenido
|
||||
- Visita <https://docs.byteplus.com/es/docs/InfoQuest/What_is_Info_Quest> para obtener más información
|
||||
|
||||
Para configurar su herramienta de rastreo preferida, establezca lo siguiente en su archivo `conf.yaml`:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# Tipo de motor: "jina" (predeterminado) o "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
## Características
|
||||
|
||||
### Capacidades Principales
|
||||
|
||||
- 🤖 **Integración de LLM**
|
||||
- Soporta la integración de la mayoría de los modelos a través de [litellm](https://docs.litellm.ai/docs/providers).
|
||||
- Soporte para modelos de código abierto como Qwen
|
||||
- Interfaz API compatible con OpenAI
|
||||
- Sistema LLM de múltiples niveles para diferentes complejidades de tareas
|
||||
|
||||
### Herramientas e Integraciones MCP
|
||||
|
||||
- 🔍 **Búsqueda y Recuperación**
|
||||
|
||||
- Búsqueda web a través de Tavily, InfoQuest, Brave Search y más
|
||||
- Rastreo con Jina e InfoQuest
|
||||
- Extracción avanzada de contenido
|
||||
|
||||
- 🔗 **Integración Perfecta con MCP**
|
||||
- Amplía capacidades para acceso a dominio privado, gráfico de conocimiento, navegación web y más
|
||||
- Facilita la integración de diversas herramientas y metodologías de investigación
|
||||
|
||||
### Colaboración Humana
|
||||
|
||||
- 🧠 **Humano en el Bucle**
|
||||
|
||||
- Soporta modificación interactiva de planes de investigación usando lenguaje natural
|
||||
- Soporta aceptación automática de planes de investigación
|
||||
|
||||
- 📝 **Post-Edición de Informes**
|
||||
- Soporta edición de bloques tipo Notion
|
||||
- Permite refinamientos por IA, incluyendo pulido asistido por IA, acortamiento y expansión de oraciones
|
||||
- Impulsado por [tiptap](https://tiptap.dev/)
|
||||
|
||||
### Creación de Contenido
|
||||
|
||||
- 🎙️ **Generación de Podcasts y Presentaciones**
|
||||
- Generación de guiones de podcast y síntesis de audio impulsadas por IA
|
||||
- Creación automatizada de presentaciones PowerPoint simples
|
||||
- Plantillas personalizables para contenido a medida
|
||||
|
||||
## Arquitectura
|
||||
|
||||
DeerFlow implementa una arquitectura modular de sistema multi-agente diseñada para investigación automatizada y análisis de código. El sistema está construido sobre LangGraph, permitiendo un flujo de trabajo flexible basado en estados donde los componentes se comunican a través de un sistema de paso de mensajes bien definido.
|
||||
|
||||

|
||||
|
||||
> Vélo en vivo en [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)
|
||||
|
||||
El sistema emplea un flujo de trabajo racionalizado con los siguientes componentes:
|
||||
|
||||
1. **Coordinador**: El punto de entrada que gestiona el ciclo de vida del flujo de trabajo
|
||||
|
||||
- Inicia el proceso de investigación basado en la entrada del usuario
|
||||
- Delega tareas al planificador cuando corresponde
|
||||
- Actúa como la interfaz principal entre el usuario y el sistema
|
||||
|
||||
2. **Planificador**: Componente estratégico para descomposición y planificación de tareas
|
||||
|
||||
- Analiza objetivos de investigación y crea planes de ejecución estructurados
|
||||
- Determina si hay suficiente contexto disponible o si se necesita más investigación
|
||||
- Gestiona el flujo de investigación y decide cuándo generar el informe final
|
||||
|
||||
3. **Equipo de Investigación**: Una colección de agentes especializados que ejecutan el plan:
|
||||
|
||||
- **Investigador**: Realiza búsquedas web y recopilación de información utilizando herramientas como motores de búsqueda web, rastreo e incluso servicios MCP.
|
||||
- **Programador**: Maneja análisis de código, ejecución y tareas técnicas utilizando la herramienta Python REPL.
|
||||
Cada agente tiene acceso a herramientas específicas optimizadas para su rol y opera dentro del marco LangGraph
|
||||
|
||||
4. **Reportero**: Procesador de etapa final para los resultados de la investigación
|
||||
- Agrega hallazgos del equipo de investigación
|
||||
- Procesa y estructura la información recopilada
|
||||
- Genera informes de investigación completos
|
||||
|
||||
## Integración de Texto a Voz
|
||||
|
||||
DeerFlow ahora incluye una función de Texto a Voz (TTS) que te permite convertir informes de investigación a voz. Esta función utiliza la API TTS de volcengine para generar audio de alta calidad a partir de texto. Características como velocidad, volumen y tono también son personalizables.
|
||||
|
||||
### Usando la API TTS
|
||||
|
||||
Puedes acceder a la funcionalidad TTS a través del punto final `/api/tts`:
|
||||
|
||||
```bash
|
||||
# Ejemplo de llamada API usando curl
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "Esto es una prueba de la funcionalidad de texto a voz.",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## Desarrollo
|
||||
|
||||
### Pruebas
|
||||
|
||||
Ejecuta el conjunto de pruebas:
|
||||
|
||||
```bash
|
||||
# Ejecutar todas las pruebas
|
||||
make test
|
||||
|
||||
# Ejecutar archivo de prueba específico
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Ejecutar con cobertura
|
||||
make coverage
|
||||
```
|
||||
|
||||
### Calidad del Código
|
||||
|
||||
```bash
|
||||
# Ejecutar linting
|
||||
make lint
|
||||
|
||||
# Formatear código
|
||||
make format
|
||||
```
|
||||
|
||||
### Depuración con LangGraph Studio
|
||||
|
||||
DeerFlow utiliza LangGraph para su arquitectura de flujo de trabajo. Puedes usar LangGraph Studio para depurar y visualizar el flujo de trabajo en tiempo real.
|
||||
|
||||
#### Ejecutando LangGraph Studio Localmente
|
||||
|
||||
DeerFlow incluye un archivo de configuración `langgraph.json` que define la estructura del grafo y las dependencias para LangGraph Studio. Este archivo apunta a los grafos de flujo de trabajo definidos en el proyecto y carga automáticamente variables de entorno desde el archivo `.env`.
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# Instala el gestor de paquetes uv si no lo tienes
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Instala dependencias e inicia el servidor LangGraph
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# Instalar dependencias
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# Iniciar el servidor LangGraph
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Después de iniciar el servidor LangGraph, verás varias URLs en la terminal:
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- UI de Studio: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- Docs de API: <http://127.0.0.1:2024/docs>
|
||||
|
||||
Abre el enlace de UI de Studio en tu navegador para acceder a la interfaz de depuración.
|
||||
|
||||
#### Usando LangGraph Studio
|
||||
|
||||
En la UI de Studio, puedes:
|
||||
|
||||
1. Visualizar el grafo de flujo de trabajo y ver cómo se conectan los componentes
|
||||
2. Rastrear la ejecución en tiempo real para ver cómo fluyen los datos a través del sistema
|
||||
3. Inspeccionar el estado en cada paso del flujo de trabajo
|
||||
4. Depurar problemas examinando entradas y salidas de cada componente
|
||||
5. Proporcionar retroalimentación durante la fase de planificación para refinar planes de investigación
|
||||
|
||||
Cuando envías un tema de investigación en la UI de Studio, podrás ver toda la ejecución del flujo de trabajo, incluyendo:
|
||||
|
||||
- La fase de planificación donde se crea el plan de investigación
|
||||
- El bucle de retroalimentación donde puedes modificar el plan
|
||||
- Las fases de investigación y escritura para cada sección
|
||||
- La generación del informe final
|
||||
|
||||
### Habilitando el Rastreo de LangSmith
|
||||
|
||||
DeerFlow soporta el rastreo de LangSmith para ayudarte a depurar y monitorear tus flujos de trabajo. Para habilitar el rastreo de LangSmith:
|
||||
|
||||
1. Asegúrate de que tu archivo `.env` tenga las siguientes configuraciones (ver `.env.example`):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. Inicia el rastreo y visualiza el grafo localmente con LangSmith ejecutando:
|
||||
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Esto habilitará la visualización de rastros en LangGraph Studio y enviará tus rastros a LangSmith para monitoreo y análisis.
|
||||
|
||||
## Docker
|
||||
|
||||
También puedes ejecutar este proyecto con Docker.
|
||||
|
||||
Primero, necesitas leer la [configuración](docs/configuration_guide.md) a continuación. Asegúrate de que los archivos `.env` y `.conf.yaml` estén listos.
|
||||
|
||||
Segundo, para construir una imagen Docker de tu propio servidor web:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
Finalmente, inicia un contenedor Docker que ejecute el servidor web:
|
||||
|
||||
```bash
|
||||
# Reemplaza deer-flow-api-app con tu nombre de contenedor preferido
|
||||
# Inicia el servidor y enlázalo a localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# detener el servidor
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose (incluye tanto backend como frontend)
|
||||
|
||||
DeerFlow proporciona una configuración docker-compose para ejecutar fácilmente tanto el backend como el frontend juntos:
|
||||
|
||||
```bash
|
||||
# construir imagen docker
|
||||
docker compose build
|
||||
|
||||
# iniciar el servidor
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Si desea implementar DeerFlow en entornos de producción, agregue autenticación al sitio web y evalúe su verificación de seguridad del MCPServer y Python Repl.
|
||||
|
||||
## Ejemplos
|
||||
|
||||
Los siguientes ejemplos demuestran las capacidades de DeerFlow:
|
||||
|
||||
### Informes de Investigación
|
||||
|
||||
1. **Informe sobre OpenAI Sora** - Análisis de la herramienta IA Sora de OpenAI
|
||||
|
||||
- Discute características, acceso, ingeniería de prompts, limitaciones y consideraciones éticas
|
||||
- [Ver informe completo](examples/openai_sora_report.md)
|
||||
|
||||
2. **Informe sobre el Protocolo Agent to Agent de Google** - Visión general del protocolo Agent to Agent (A2A) de Google
|
||||
|
||||
- Discute su papel en la comunicación de agentes IA y su relación con el Model Context Protocol (MCP) de Anthropic
|
||||
- [Ver informe completo](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **¿Qué es MCP?** - Un análisis completo del término "MCP" en múltiples contextos
|
||||
|
||||
- Explora Model Context Protocol en IA, Fosfato Monocálcico en química y Placa de Microcanales en electrónica
|
||||
- [Ver informe completo](examples/what_is_mcp.md)
|
||||
|
||||
4. **Fluctuaciones del Precio de Bitcoin** - Análisis de los movimientos recientes del precio de Bitcoin
|
||||
|
||||
- Examina tendencias del mercado, influencias regulatorias e indicadores técnicos
|
||||
- Proporciona recomendaciones basadas en datos históricos
|
||||
- [Ver informe completo](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **¿Qué es LLM?** - Una exploración en profundidad de los Modelos de Lenguaje Grandes
|
||||
|
||||
- Discute arquitectura, entrenamiento, aplicaciones y consideraciones éticas
|
||||
- [Ver informe completo](examples/what_is_llm.md)
|
||||
|
||||
6. **¿Cómo usar Claude para Investigación Profunda?** - Mejores prácticas y flujos de trabajo para usar Claude en investigación profunda
|
||||
|
||||
- Cubre ingeniería de prompts, análisis de datos e integración con otras herramientas
|
||||
- [Ver informe completo](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **Adopción de IA en Salud: Factores de Influencia** - Análisis de factores que impulsan la adopción de IA en salud
|
||||
|
||||
- Discute tecnologías IA, calidad de datos, consideraciones éticas, evaluaciones económicas, preparación organizativa e infraestructura digital
|
||||
- [Ver informe completo](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **Impacto de la Computación Cuántica en la Criptografía** - Análisis del impacto de la computación cuántica en la criptografía
|
||||
|
||||
- Discute vulnerabilidades de la criptografía clásica, criptografía post-cuántica y soluciones criptográficas resistentes a la cuántica
|
||||
- [Ver informe completo](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **Aspectos Destacados del Rendimiento de Cristiano Ronaldo** - Análisis de los aspectos destacados del rendimiento de Cristiano Ronaldo
|
||||
- Discute sus logros profesionales, goles internacionales y rendimiento en varios partidos
|
||||
- [Ver informe completo](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
Para ejecutar estos ejemplos o crear tus propios informes de investigación, puedes usar los siguientes comandos:
|
||||
|
||||
```bash
|
||||
# Ejecutar con una consulta específica
|
||||
uv run main.py "¿Qué factores están influyendo en la adopción de IA en salud?"
|
||||
|
||||
# Ejecutar con parámetros de planificación personalizados
|
||||
uv run main.py --max_plan_iterations 3 "¿Cómo impacta la computación cuántica en la criptografía?"
|
||||
|
||||
# Ejecutar en modo interactivo con preguntas integradas
|
||||
uv run main.py --interactive
|
||||
|
||||
# O ejecutar con prompt interactivo básico
|
||||
uv run main.py
|
||||
|
||||
# Ver todas las opciones disponibles
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### Modo Interactivo
|
||||
|
||||
La aplicación ahora soporta un modo interactivo con preguntas integradas tanto en inglés como en chino:
|
||||
|
||||
1. Lanza el modo interactivo:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. Selecciona tu idioma preferido (English o 中文)
|
||||
|
||||
3. Elige de una lista de preguntas integradas o selecciona la opción para hacer tu propia pregunta
|
||||
|
||||
4. El sistema procesará tu pregunta y generará un informe de investigación completo
|
||||
|
||||
### Humano en el Bucle
|
||||
|
||||
DeerFlow incluye un mecanismo de humano en el bucle que te permite revisar, editar y aprobar planes de investigación antes de que sean ejecutados:
|
||||
|
||||
1. **Revisión del Plan**: Cuando el humano en el bucle está habilitado, el sistema presentará el plan de investigación generado para tu revisión antes de la ejecución
|
||||
|
||||
2. **Proporcionando Retroalimentación**: Puedes:
|
||||
|
||||
- Aceptar el plan respondiendo con `[ACCEPTED]`
|
||||
- Editar el plan proporcionando retroalimentación (p.ej., `[EDIT PLAN] Añadir más pasos sobre implementación técnica`)
|
||||
- El sistema incorporará tu retroalimentación y generará un plan revisado
|
||||
|
||||
3. **Auto-aceptación**: Puedes habilitar la auto-aceptación para omitir el proceso de revisión:
|
||||
|
||||
- Vía API: Establece `auto_accepted_plan: true` en tu solicitud
|
||||
|
||||
4. **Integración API**: Cuando uses la API, puedes proporcionar retroalimentación a través del parámetro `feedback`:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{ "role": "user", "content": "¿Qué es la computación cuántica?" }],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] Incluir más sobre algoritmos cuánticos"
|
||||
}
|
||||
```
|
||||
|
||||
### Argumentos de Línea de Comandos
|
||||
|
||||
La aplicación soporta varios argumentos de línea de comandos para personalizar su comportamiento:
|
||||
|
||||
- **query**: La consulta de investigación a procesar (puede ser múltiples palabras)
|
||||
- **--interactive**: Ejecutar en modo interactivo con preguntas integradas
|
||||
- **--max_plan_iterations**: Número máximo de ciclos de planificación (predeterminado: 1)
|
||||
- **--max_step_num**: Número máximo de pasos en un plan de investigación (predeterminado: 3)
|
||||
- **--debug**: Habilitar registro detallado de depuración
|
||||
|
||||
## Preguntas Frecuentes
|
||||
|
||||
Por favor, consulta [FAQ.md](docs/FAQ.md) para más detalles.
|
||||
|
||||
## Licencia
|
||||
|
||||
Este proyecto es de código abierto y está disponible bajo la [Licencia MIT](./LICENSE).
|
||||
|
||||
## Agradecimientos
|
||||
|
||||
DeerFlow está construido sobre el increíble trabajo de la comunidad de código abierto. Estamos profundamente agradecidos a todos los proyectos y contribuyentes cuyos esfuerzos han hecho posible DeerFlow. Verdaderamente, nos apoyamos en hombros de gigantes.
|
||||
|
||||
Nos gustaría extender nuestro sincero agradecimiento a los siguientes proyectos por sus invaluables contribuciones:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**: Su excepcional marco impulsa nuestras interacciones y cadenas LLM, permitiendo integración y funcionalidad sin problemas.
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: Su enfoque innovador para la orquestación multi-agente ha sido instrumental en permitir los sofisticados flujos de trabajo de DeerFlow.
|
||||
|
||||
Estos proyectos ejemplifican el poder transformador de la colaboración de código abierto, y estamos orgullosos de construir sobre sus cimientos.
|
||||
|
||||
### Contribuyentes Clave
|
||||
|
||||
Un sentido agradecimiento va para los autores principales de `DeerFlow`, cuya visión, pasión y dedicación han dado vida a este proyecto:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
Su compromiso inquebrantable y experiencia han sido la fuerza impulsora detrás del éxito de DeerFlow. Nos sentimos honrados de tenerlos al timón de este viaje.
|
||||
|
||||
## Historial de Estrellas
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
624
README_ja.md
624
README_ja.md
@@ -1,624 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> オープンソースから生まれ、オープンソースに還元する。
|
||||
|
||||
**DeerFlow**(**D**eep **E**xploration and **E**fficient **R**esearch **Flow**)は、オープンソースコミュニティの素晴らしい成果の上に構築されたコミュニティ主導の深層研究フレームワークです。私たちの目標は、言語モデルとウェブ検索、クローリング、Python コード実行などの専門ツールを組み合わせながら、これを可能にしたコミュニティに貢献することです。
|
||||
|
||||
現在、DeerFlow は火山引擎の FaaS アプリケーションセンターに正式に入居しています。ユーザーは体験リンクを通じてオンラインで体験し、その強力な機能と便利な操作を直感的に感じることができます。同時に、さまざまなユーザーの展開ニーズを満たすため、DeerFlow は火山引擎に基づくワンクリック展開をサポートしています。展開リンクをクリックして展開プロセスを迅速に完了し、効率的な研究の旅を始めましょう。
|
||||
|
||||
DeerFlow は新たにBytePlusが自主開発したインテリジェント検索・クローリングツールセットを統合しました--[InfoQuest (オンライン無料体験をサポート)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
詳細については[DeerFlow の公式ウェブサイト](https://deerflow.tech/)をご覧ください。
|
||||
|
||||
## デモ
|
||||
|
||||
### ビデオ
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
このデモでは、DeerFlowの使用方法を紹介しています:
|
||||
|
||||
- MCPサービスとのシームレスな統合
|
||||
- 深層研究プロセスの実施と画像を含む包括的なレポートの作成
|
||||
- 生成されたレポートに基づくポッドキャストオーディオの作成
|
||||
|
||||
### リプレイ例
|
||||
|
||||
- [エッフェル塔は世界一高いビルと比べてどれくらい高い?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [GitHub で最も人気のあるリポジトリは?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [南京の伝統料理に関する記事を書く](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [賃貸アパートの装飾方法は?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [公式ウェブサイトでより多くのリプレイ例をご覧ください。](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 目次
|
||||
|
||||
- [🚀 クイックスタート](#クイックスタート)
|
||||
- [🌟 特徴](#特徴)
|
||||
- [🏗️ アーキテクチャ](#アーキテクチャ)
|
||||
- [🛠️ 開発](#開発)
|
||||
- [🗣️ テキスト読み上げ統合](#テキスト読み上げ統合)
|
||||
- [📚 例](#例)
|
||||
- [❓ よくある質問](#よくある質問)
|
||||
- [📜 ライセンス](#ライセンス)
|
||||
- [💖 謝辞](#謝辞)
|
||||
- [⭐ スター履歴](#スター履歴)
|
||||
|
||||
## クイックスタート
|
||||
|
||||
DeerFlow は Python で開発され、Node.js で書かれた Web UI が付属しています。スムーズなセットアッププロセスを確保するために、以下のツールの使用をお勧めします:
|
||||
|
||||
### 推奨ツール
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Python 環境と依存関係の管理を簡素化します。`uv`はルートディレクトリに自動的に仮想環境を作成し、必要なパッケージをすべてインストールします—Python 環境を手動でインストールする必要はありません。
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
複数の Node.js ランタイムバージョンを簡単に管理します。
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Node.js プロジェクトの依存関係をインストールおよび管理します。
|
||||
|
||||
### 環境要件
|
||||
|
||||
システムが以下の最小要件を満たしていることを確認してください:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** バージョン `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** バージョン `22+`
|
||||
|
||||
### インストール
|
||||
|
||||
```bash
|
||||
# リポジトリをクローン
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# 依存関係をインストール、uvがPythonインタープリタと仮想環境の作成、必要なパッケージのインストールを担当
|
||||
uv sync
|
||||
|
||||
# APIキーで.envを設定
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# 火山引擎TTS: TTSの資格情報がある場合は追加
|
||||
cp .env.example .env
|
||||
|
||||
# 下記の「サポートされている検索エンジン」と「テキスト読み上げ統合」セクションですべての利用可能なオプションを確認
|
||||
|
||||
# LLMモデルとAPIキーのconf.yamlを設定
|
||||
# 詳細は「docs/configuration_guide.md」を参照
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# PPT生成用にmarpをインストール
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
オプションで、[pnpm](https://pnpm.io/installation)を使用して Web UI 依存関係をインストール:
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 設定
|
||||
|
||||
詳細については[設定ガイド](docs/configuration_guide.md)を参照してください。
|
||||
|
||||
> [!注意]
|
||||
> プロジェクトを開始する前に、ガイドを注意深く読み、特定の設定と要件に合わせて構成を更新してください。
|
||||
|
||||
### コンソール UI
|
||||
|
||||
プロジェクトを実行する最も迅速な方法は、コンソール UI を使用することです。
|
||||
|
||||
```bash
|
||||
# bashライクなシェルでプロジェクトを実行
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
このプロジェクトには Web UI も含まれており、より動的で魅力的なインタラクティブ体験を提供します。
|
||||
|
||||
> [!注意]
|
||||
> 先に Web UI の依存関係をインストールする必要があります。
|
||||
|
||||
```bash
|
||||
# 開発モードでバックエンドとフロントエンドサーバーの両方を実行
|
||||
# macOS/Linuxの場合
|
||||
./bootstrap.sh -d
|
||||
|
||||
# Windowsの場合
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!NOTE]
|
||||
> デフォルトでは、セキュリティ上の理由からバックエンドサーバーは 127.0.0.1 (localhost) にバインドされます。外部接続を許可する必要がある場合 (例: Linux サーバーにデプロイする場合) は、ブートストラップスクリプトでサーバーホストを 0.0.0.0 に変更できます (uv run server.py --host 0.0.0.0)。
|
||||
> サービスを外部ネットワークに公開する前に、環境が適切に保護されていることを確認してください。
|
||||
|
||||
ブラウザを開き、[`http://localhost:3000`](http://localhost:3000)にアクセスして Web UI を探索してください。
|
||||
|
||||
[`web`](./web/)ディレクトリで詳細を確認できます。
|
||||
|
||||
## サポートされている検索エンジン
|
||||
|
||||
DeerFlow は複数の検索エンジンをサポートしており、`.env`ファイルの`SEARCH_API`変数で設定できます:
|
||||
|
||||
- **Tavily**(デフォルト):AI アプリケーション向けの専門検索 API
|
||||
- `.env`ファイルに`TAVILY_API_KEY`が必要
|
||||
- 登録先:<https://app.tavily.com/home>
|
||||
|
||||
- **InfoQuest**(推奨):BytePlusが開発したAI最適化のインテリジェント検索とクローリングツールセット
|
||||
- `.env`ファイルに`INFOQUEST_API_KEY`が必要
|
||||
- 時間範囲フィルタリングとサイトフィルタリングをサポート
|
||||
- 高品質な検索結果とコンテンツ抽出を提供
|
||||
- 登録先:<https://console.byteplus.com/infoquest/infoquests>
|
||||
- ドキュメント:<https://docs.byteplus.com/ja/docs/InfoQuest/What_is_Info_Quest>
|
||||
|
||||
- **DuckDuckGo**:プライバシー重視の検索エンジン
|
||||
- APIキー不要
|
||||
|
||||
- **Brave Search**:高度な機能を備えたプライバシー重視の検索エンジン
|
||||
- `.env`ファイルに`BRAVE_SEARCH_API_KEY`が必要
|
||||
- 登録先:<https://brave.com/search/api/>
|
||||
|
||||
- **Arxiv**:学術研究用の科学論文検索
|
||||
- APIキー不要
|
||||
- 科学・学術論文専用
|
||||
|
||||
- **Searx/SearxNG**セルフホスト型メタ検索エンジン
|
||||
- `.env`ファイルに`SEARX_HOST`が必要
|
||||
- Searx または SearxNG に接続可能
|
||||
|
||||
お好みの検索エンジンを設定するには、`.env`ファイルで`SEARCH_API`変数を設定します:
|
||||
|
||||
```bash
|
||||
# 選択肢: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### クローリングツール
|
||||
|
||||
- **Jina**(デフォルト):無料でアクセス可能なウェブコンテンツクローリングツール
|
||||
- 基本機能を使用するにはAPIキーは不要
|
||||
- APIキーを使用するとより高いアクセスレート制限が適用されます
|
||||
- 詳細については <https://jina.ai/reader> を参照してください
|
||||
|
||||
- **InfoQuest**(推奨):BytePlusが開発したAI最適化のインテリジェント検索とクローリングツールセット
|
||||
- `.env`ファイルに`INFOQUEST_API_KEY`が必要
|
||||
- 設定可能なクローリングパラメータを提供
|
||||
- カスタムタイムアウト設定をサポート
|
||||
- より強力なコンテンツ抽出機能を提供
|
||||
- 詳細については <https://docs.byteplus.com/ja/docs/InfoQuest/What_is_Info_Quest> を参照してください
|
||||
|
||||
お好みのクローリングツールを設定するには、`conf.yaml`ファイルで以下を設定します:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# エンジンタイプ:"jina"(デフォルト)または "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
## 特徴
|
||||
|
||||
### コア機能
|
||||
|
||||
- 🤖 **LLM統合**
|
||||
- [litellm](https://docs.litellm.ai/docs/providers)を通じてほとんどのモデルの統合をサポート
|
||||
- Qwenなどのオープンソースモデルをサポート
|
||||
- OpenAI互換のAPIインターフェース
|
||||
- 異なるタスクの複雑さに対応するマルチティアLLMシステム
|
||||
|
||||
### ツールと MCP 統合
|
||||
|
||||
- 🔍 **検索と取得**
|
||||
- Tavily、InfoQuest、Brave Searchなどを通じたWeb検索
|
||||
- JinaとInfoQuestを使用したクローリング
|
||||
- 高度なコンテンツ抽出
|
||||
|
||||
- 🔗 **MCPシームレス統合**
|
||||
- プライベートドメインアクセス、ナレッジグラフ、Webブラウジングなどの機能を拡張
|
||||
- 多様な研究ツールと方法論の統合を促進
|
||||
|
||||
### 人間との協力
|
||||
|
||||
- 🧠 **人間参加型ループ**
|
||||
- 自然言語を使用した研究計画の対話的修正をサポート
|
||||
- 研究計画の自動承認をサポート
|
||||
|
||||
- 📝 **レポート後編集**
|
||||
- Notionライクなブロック編集をサポート
|
||||
- AI支援による洗練、文の短縮、拡張などのAI改良を可能に
|
||||
- [tiptap](https://tiptap.dev/)を活用
|
||||
|
||||
### コンテンツ作成
|
||||
|
||||
- 🎙️ **ポッドキャストとプレゼンテーション生成**
|
||||
- AI駆動のポッドキャストスクリプト生成と音声合成
|
||||
- シンプルなPowerPointプレゼンテーションの自動作成
|
||||
- カスタマイズ可能なテンプレートで個別のコンテンツに対応
|
||||
|
||||
## アーキテクチャ
|
||||
|
||||
DeerFlow は、自動研究とコード分析のためのモジュラーなマルチエージェントシステムアーキテクチャを実装しています。システムは LangGraph 上に構築され、コンポーネントが明確に定義されたメッセージパッシングシステムを通じて通信する柔軟な状態ベースのワークフローを実現しています。
|
||||
|
||||

|
||||
|
||||
> [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)でライブで確認できます
|
||||
|
||||
システムは以下のコンポーネントを含む合理化されたワークフローを採用しています:
|
||||
|
||||
1. **コーディネーター**:ワークフローのライフサイクルを管理するエントリーポイント
|
||||
|
||||
- ユーザー入力に基づいて研究プロセスを開始
|
||||
- 適切なタイミングでプランナーにタスクを委託
|
||||
- ユーザーとシステム間の主要なインターフェースとして機能
|
||||
|
||||
2. **プランナー**:タスク分解と計画のための戦略的コンポーネント
|
||||
|
||||
- 研究目標を分析し、構造化された実行計画を作成
|
||||
- 十分なコンテキストが利用可能か、さらなる研究が必要かを判断
|
||||
- 研究フローを管理し、最終レポート生成のタイミングを決定
|
||||
|
||||
3. **研究チーム**:計画を実行する専門エージェントの集合:
|
||||
|
||||
- **研究者**:Web 検索エンジン、クローリング、さらには MCP サービスなどのツールを使用して Web 検索と情報収集を行う。
|
||||
- **コーダー**:Python REPL ツールを使用してコード分析、実行、技術的タスクを処理する。
|
||||
各エージェントは自分の役割に最適化された特定のツールにアクセスでき、LangGraph フレームワーク内で動作する
|
||||
|
||||
4. **レポーター**:研究出力の最終段階プロセッサ
|
||||
- 研究チームの調査結果を集約
|
||||
- 収集した情報を処理および構造化
|
||||
- 包括的な研究レポートを生成
|
||||
|
||||
## テキスト読み上げ統合
|
||||
|
||||
DeerFlowには現在、研究レポートを音声に変換できるテキスト読み上げ(TTS)機能が含まれています。この機能は火山引擎TTS APIを使用して高品質なテキストオーディオを生成します。速度、音量、ピッチなどの特性もカスタマイズ可能です。
|
||||
|
||||
### TTS APIの使用
|
||||
|
||||
`/api/tts`エンドポイントからTTS機能にアクセスできます:
|
||||
|
||||
```bash
|
||||
# curlを使用したAPI呼び出し例
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "これはテキスト読み上げ機能のテストです。",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## 開発
|
||||
|
||||
### テスト
|
||||
|
||||
テストスイートの実行:
|
||||
|
||||
```bash
|
||||
# すべてのテストを実行
|
||||
make test
|
||||
|
||||
# 特定のテストファイルを実行
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# カバレッジテストを実行
|
||||
make coverage
|
||||
```
|
||||
|
||||
### コード品質
|
||||
|
||||
```bash
|
||||
# コードチェックを実行
|
||||
make lint
|
||||
|
||||
# コードをフォーマット
|
||||
make format
|
||||
```
|
||||
|
||||
### LangGraph Studio によるデバッグ
|
||||
|
||||
DeerFlow はワークフローアーキテクチャとして LangGraph を使用しています。LangGraph Studio を使用してワークフローをリアルタイムでデバッグおよび可視化できます。
|
||||
|
||||
#### ローカルで LangGraph Studio を実行
|
||||
|
||||
DeerFlow には`langgraph.json`設定ファイルが含まれており、これが LangGraph Studio のグラフ構造と依存関係を定義しています。このファイルはプロジェクトで定義されたワークフローグラフを指し、`.env`ファイルから環境変数を自動的に読み込みます。
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# uvパッケージマネージャがない場合はインストール
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# 依存関係をインストールしLangGraphサーバーを開始
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# 依存関係をインストール
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# LangGraphサーバーを開始
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
LangGraphサーバーを開始すると、端末にいくつかのURLが表示されます:
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- Studio UI: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- APIドキュメント: <http://127.0.0.1:2024/docs>
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- Studio UI: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- APIドキュメント: <http://127.0.0.1:2024/docs>
|
||||
|
||||
ブラウザで Studio UI リンクを開いてデバッグインターフェースにアクセスします。
|
||||
|
||||
#### LangGraph Studio の使用
|
||||
|
||||
Studio UI では、次のことができます:
|
||||
|
||||
1. ワークフローグラフを可視化し、コンポーネントの接続方法を確認
|
||||
2. 実行をリアルタイムで追跡し、データがシステム内をどのように流れるかを理解
|
||||
3. ワークフローの各ステップの状態を検査
|
||||
4. 各コンポーネントの入力と出力を検査して問題をデバッグ
|
||||
5. 計画段階でフィードバックを提供して研究計画を洗練
|
||||
|
||||
Studio UIで研究トピックを送信すると、次を含む全ワークフロー実行プロセスを見ることができます:
|
||||
|
||||
- 研究計画を作成する計画段階
|
||||
- 計画を修正できるフィードバックループ
|
||||
- 各セクションの研究と執筆段階
|
||||
- 最終レポート生成
|
||||
|
||||
### LangSmith トレースの有効化
|
||||
|
||||
DeerFlow は LangSmith トレース機能をサポートしており、ワークフローのデバッグとモニタリングに役立ちます。LangSmith トレースを有効にするには:
|
||||
|
||||
1. `.env` ファイルに次の設定があることを確認してください(`.env.example` を参照):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. 次のコマンドを実行して LangSmith トレースを開始します:
|
||||
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
これにより、LangGraph Studio でトレース可視化が有効になり、トレースがモニタリングと分析のために LangSmith に送信されます。
|
||||
|
||||
## Docker
|
||||
|
||||
このプロジェクトは Docker でも実行できます。
|
||||
|
||||
まず、以下の[設定](#設定)セクションを読んでください。`.env`と`.conf.yaml`ファイルが準備できていることを確認してください。
|
||||
|
||||
次に、独自の Web サーバーの Docker イメージをビルドします:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
最後に、Web サーバーを実行する Docker コンテナを起動します:
|
||||
|
||||
```bash
|
||||
# deer-flow-api-appを希望のコンテナ名に置き換えてください
|
||||
# サーバーを起動してlocalhost:8000にバインド
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# サーバーを停止
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
このプロジェクトは docker compose でも設定できます:
|
||||
|
||||
```bash
|
||||
# dockerイメージをビルド
|
||||
docker compose build
|
||||
|
||||
# サーバーを起動
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> DeerFlow を本番環境にデプロイする場合は、ウェブサイトに認証を追加し、MCPServer と Python Repl のセキュリティチェックを評価してください。
|
||||
|
||||
## テキスト読み上げ統合
|
||||
|
||||
DeerFlow には現在、研究レポートを音声に変換できるテキスト読み上げ(TTS)機能が含まれています。この機能は火山引擎 TTS API を使用して高品質なテキストオーディオを生成します。速度、音量、ピッチなどの特性もカスタマイズ可能です。
|
||||
|
||||
### TTS API の使用
|
||||
|
||||
`/api/tts`エンドポイントから TTS 機能にアクセスできます:
|
||||
|
||||
```bash
|
||||
# curlを使用したAPI呼び出し例
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "これはテキスト読み上げ機能のテストです。",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## 例
|
||||
|
||||
以下の例は DeerFlow の機能を示しています:
|
||||
|
||||
### 研究レポート
|
||||
|
||||
1. **OpenAI Sora レポート** - OpenAI の Sora AI ツールの分析
|
||||
|
||||
- 機能、アクセス方法、プロンプトエンジニアリング、制限、倫理的考慮について議論
|
||||
- [完全なレポートを見る](examples/openai_sora_report.md)
|
||||
|
||||
2. **Google の Agent to Agent プロトコルレポート** - Google の Agent to Agent(A2A)プロトコルの概要
|
||||
|
||||
- AI エージェント通信における役割と、Anthropic の Model Context Protocol(MCP)との関係について議論
|
||||
- [完全なレポートを見る](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **MCP とは何か?** - 複数のコンテキストにおける「MCP」という用語の包括的分析
|
||||
|
||||
- AI における Model Context Protocol、化学における Monocalcium Phosphate、電子工学における Micro-channel Plate を探る
|
||||
- [完全なレポートを見る](examples/what_is_mcp.md)
|
||||
|
||||
4. **ビットコイン価格変動** - 最近のビットコイン価格動向の分析
|
||||
|
||||
- 市場動向、規制の影響、テクニカル指標の調査
|
||||
- 歴史的データに基づく提言
|
||||
- [完全なレポートを見る](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **LLM とは何か?** - 大規模言語モデルの詳細な探求
|
||||
|
||||
- アーキテクチャ、トレーニング、応用、倫理的考慮について議論
|
||||
- [完全なレポートを見る](examples/what_is_llm.md)
|
||||
|
||||
6. **Claude を使った深層研究の方法は?** - 深層研究での Claude の使用に関するベストプラクティスとワークフロー
|
||||
|
||||
- プロンプトエンジニアリング、データ分析、他のツールとの統合
|
||||
- [完全なレポートを見る](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **医療における AI 採用:影響要因** - 医療における AI 採用に影響する要因の分析
|
||||
|
||||
- AI テクノロジー、データ品質、倫理的考慮、経済的評価、組織の準備状況、デジタルインフラについて議論
|
||||
- [完全なレポートを見る](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **量子コンピューティングの暗号学への影響** - 量子コンピューティングの暗号学への影響の分析
|
||||
|
||||
- 古典的暗号の脆弱性、ポスト量子暗号学、耐量子暗号ソリューションについて議論
|
||||
- [完全なレポートを見る](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **クリスティアーノ・ロナウドのパフォーマンスハイライト** - クリスティアーノ・ロナウドのパフォーマンスハイライトの分析
|
||||
- 彼のキャリア達成、国際ゴール、さまざまな大会でのパフォーマンスについて議論
|
||||
- [完全なレポートを見る](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
これらの例を実行したり、独自の研究レポートを作成したりするには、次のコマンドを使用できます:
|
||||
|
||||
```bash
|
||||
# 特定のクエリで実行
|
||||
uv run main.py "医療におけるAI採用に影響する要因は何か?"
|
||||
|
||||
# カスタム計画パラメータで実行
|
||||
uv run main.py --max_plan_iterations 3 "量子コンピューティングは暗号学にどのように影響するか?"
|
||||
|
||||
# 組み込み質問を使用したインタラクティブモードで実行
|
||||
uv run main.py --interactive
|
||||
|
||||
# または基本的なインタラクティブプロンプトで実行
|
||||
uv run main.py
|
||||
|
||||
# 利用可能なすべてのオプションを表示
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### インタラクティブモード
|
||||
|
||||
アプリケーションは現在、英語と中国語の組み込み質問を使用したインタラクティブモードをサポートしています:
|
||||
|
||||
1. インタラクティブモードを開始:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. 好みの言語(English または Chinese)を選択
|
||||
|
||||
3. 組み込み質問リストから選択するか、独自の質問を提示するオプションを選択
|
||||
|
||||
4. システムが質問を処理し、包括的な研究レポートを生成
|
||||
|
||||
### 人間参加型ループ
|
||||
|
||||
DeerFlow には人間参加型ループメカニズムが含まれており、研究計画を実行する前にレビュー、編集、承認することができます:
|
||||
|
||||
1. **計画レビュー**:人間参加型ループが有効な場合、システムは実行前に生成された研究計画を表示
|
||||
|
||||
2. **フィードバック提供**:次のことができます:
|
||||
|
||||
- `[ACCEPTED]`と返信して計画を承認
|
||||
- フィードバックを提供して計画を編集(例:`[EDIT PLAN] 技術実装に関するステップをさらに追加する`)
|
||||
- システムはフィードバックを統合し、修正された計画を生成
|
||||
|
||||
3. **自動承認**:レビュープロセスをスキップするために自動承認を有効にできます:
|
||||
|
||||
4. **API統合**:APIを使用する場合、`feedback`パラメータでフィードバックを提供できます:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [
|
||||
{ "role": "user", "content": "量子コンピューティングとは何ですか?" }
|
||||
],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] 量子アルゴリズムについてもっと含める"
|
||||
}
|
||||
```
|
||||
|
||||
### コマンドライン引数
|
||||
|
||||
アプリケーションは動作をカスタマイズするための複数のコマンドライン引数をサポートしています:
|
||||
|
||||
- **query**:処理する研究クエリ(複数の単語でも可)
|
||||
- **--interactive**:組み込み質問を使用したインタラクティブモードで実行
|
||||
- **--max_plan_iterations**:最大計画サイクル数(デフォルト:1)
|
||||
- **--max_step_num**:研究計画の最大ステップ数(デフォルト:3)
|
||||
- **--debug**:詳細なデバッグログを有効化
|
||||
|
||||
## よくある質問
|
||||
|
||||
詳細については[FAQ.md](docs/FAQ.md)を参照してください。
|
||||
|
||||
## ライセンス
|
||||
|
||||
このプロジェクトはオープンソースであり、[MIT ライセンス](./LICENSE)に従っています。
|
||||
|
||||
## 謝辞
|
||||
|
||||
DeerFlow はオープンソースコミュニティの素晴らしい成果の上に構築されています。DeerFlow を可能にしたすべてのプロジェクトと貢献者に深く感謝します。私たちは確かに巨人の肩の上に立っています。
|
||||
|
||||
以下のプロジェクトに心からの感謝を表します:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**:彼らの優れたフレームワークは、シームレスな統合と機能性を実現する LLM 相互作用とチェーンに力を与えています。
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**:マルチエージェントオーケストレーションへの革新的アプローチは、DeerFlow の複雑なワークフローの実現に不可欠でした。
|
||||
|
||||
これらのプロジェクトはオープンソースコラボレーションの変革力を示しており、その基盤の上に構築できることを誇りに思います。
|
||||
|
||||
### 主要貢献者
|
||||
|
||||
`DeerFlow`の主要な作者に心から感謝します。彼らのビジョン、情熱、献身がこのプロジェクトを実現しました:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
あなたの揺るぎない取り組みと専門知識が DeerFlow の成功を推進しています。この旅をリードしていただき光栄です。
|
||||
|
||||
## スター履歴
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
593
README_pt.md
593
README_pt.md
@@ -1,593 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/bytedance/deer-flow)
|
||||
|
||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> Originado do Open Source, de volta ao Open Source
|
||||
|
||||
**DeerFlow** (**D**eep **E**xploration and **E**fficient **R**esearch **Flow**) é um framework de Pesquisa Profunda orientado-a-comunidade que baseia-se em um íncrivel trabalho da comunidade open source. Nosso objetivo é combinar modelos de linguagem com ferramentas especializadas para tarefas como busca na web, crawling, e execução de código Python, enquanto retribui com a comunidade que o tornou possível.
|
||||
|
||||
Atualmente, o DeerFlow entrou oficialmente no Centro de Aplicações FaaS da Volcengine. Os usuários podem experimentá-lo online através do link de experiência para sentir intuitivamente suas funções poderosas e operações convenientes. Ao mesmo tempo, para atender às necessidades de implantação de diferentes usuários, o DeerFlow suporta implantação com um clique baseada na Volcengine. Clique no link de implantação para completar rapidamente o processo de implantação e iniciar uma jornada de pesquisa eficiente.
|
||||
|
||||
O DeerFlow recentemente integrou o conjunto de ferramentas de busca e rastreamento inteligente desenvolvido independentemente pela BytePlus — [InfoQuest (oferece experiência gratuita online)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
Por favor, visite [Nosso Site Oficial](https://deerflow.tech/) para maiores detalhes.
|
||||
|
||||
## Demo
|
||||
|
||||
### Video
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
Nesse demo, nós demonstramos como usar o DeerFlow para:
|
||||
In this demo, we showcase how to use DeerFlow to:
|
||||
|
||||
- Integração fácil com serviços MCP
|
||||
- Conduzir o processo de Pesquisa Profunda e produzir um relatório abrangente com imagens
|
||||
- Criar um áudio podcast baseado no relatório gerado
|
||||
|
||||
### Replays
|
||||
|
||||
- [Quão alta é a Torre Eiffel comparada ao prédio mais alto?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [Quais são os top repositórios tendência no GitHub?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [Escreva um artigo sobre os pratos tradicionais de Nanjing's](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [Como decorar um apartamento alugado?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [Visite nosso site oficial para explorar mais replays.](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 Tabela de Conteúdos
|
||||
|
||||
- [🚀 Início Rápido](#Início-Rápido)
|
||||
- [🌟 Funcionalidades](#funcionalidades)
|
||||
- [🏗️ Arquitetura](#arquitetura)
|
||||
- [🛠️ Desenvolvimento](#desenvolvimento)
|
||||
- [🐳 Docker](#docker)
|
||||
- [🗣️ Texto-para-fala Integração](#texto-para-fala-integração)
|
||||
- [📚 Exemplos](#exemplos)
|
||||
- [❓ FAQ](#faq)
|
||||
- [📜 Licença](#licença)
|
||||
- [💖 Agradecimentos](#agradecimentos)
|
||||
- [🏆 Contribuidores-Chave](#contribuidores-chave)
|
||||
- [⭐ Histórico de Estrelas](#Histórico-Estrelas)
|
||||
|
||||
## Início-Rápido
|
||||
|
||||
DeerFlow é desenvolvido em Python, e vem com uma IU web escrita em Node.js. Para garantir um processo de configuração fácil, nós recomendamos o uso das seguintes ferramentas:
|
||||
|
||||
### Ferramentas Recomendadas
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Simplifica o gerenciamento de dependência de ambientes Python. `uv` automaticamente cria um ambiente virtual no diretório raiz e instala todos os pacotes necessários para não haver a necessidade de instalar ambientes Python manualmente
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
Gerencia múltiplas versões do ambiente de execução do Node.js sem esforço.
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Instala e gerencia dependências do projeto Node.js.
|
||||
|
||||
### Requisitos de Ambiente
|
||||
|
||||
Certifique-se de que seu sistema atenda os seguintes requisitos mínimos:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** Versão `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** Versão `22+`
|
||||
|
||||
### Instalação
|
||||
|
||||
```bash
|
||||
# Clone o repositório
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# Instale as dependências, uv irá lidar com o interpretador do python e a criação do venv, e instalar os pacotes necessários
|
||||
uv sync
|
||||
|
||||
# Configure .env com suas chaves de API
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# volcengine TTS: Adicione sua credencial TTS caso você a possua
|
||||
cp .env.example .env
|
||||
|
||||
# Veja as seções abaixo 'Supported Search Engines' and 'Texto-para-Fala Integração' para todas as opções disponíveis
|
||||
|
||||
# Configure o conf.yaml para o seu modelo LLM e chaves API
|
||||
# Por favor, consulte 'docs/configuration_guide.md' para maiores detalhes
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Instale marp para geração de ppt
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
Opcionalmente, instale as dependências IU web via [pnpm](https://pnpm.io/installation):
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Configurações
|
||||
|
||||
Por favor, consulte o [Guia de Configuração](docs/configuration_guide.md) para maiores detalhes.
|
||||
|
||||
> [!NOTA]
|
||||
> Antes de iniciar o projeto, leia o guia detalhadamente, e atualize as configurações para baterem com os seus requisitos e configurações específicas.
|
||||
|
||||
### Console IU
|
||||
|
||||
A maneira mais rápida de rodar o projeto é usar o console IU.
|
||||
|
||||
```bash
|
||||
# Execute o projeto em um shell tipo-bash
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Web IU
|
||||
|
||||
Esse projeto também inclui uma IU Web, trazendo uma experiência mais interativa, dinâmica e engajadora.
|
||||
|
||||
> [!NOTA]
|
||||
> Você precisa instalar as dependências do IU web primeiro.
|
||||
|
||||
```bash
|
||||
# Execute ambos os servidores de backend e frontend em modo desenvolvimento
|
||||
# No macOS/Linux
|
||||
./bootstrap.sh -d
|
||||
|
||||
# No Windows
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!NOTA]
|
||||
> Por padrão, o servidor backend se vincula a 127.0.0.1 (localhost) por motivos de segurança. Se você precisar permitir conexões externas (por exemplo, ao implantar em um servidor Linux), poderá modificar o host do servidor para 0.0.0.0 no script de inicialização (uv run server.py --host 0.0.0.0).
|
||||
> Certifique-se de que seu ambiente esteja devidamente protegido antes de expor o serviço a redes externas.
|
||||
|
||||
Abra seu navegador e visite [`http://localhost:3000`](http://localhost:3000) para explorar a IU web.
|
||||
|
||||
Explore mais detalhes no diretório [`web`](./web/) .
|
||||
|
||||
## Mecanismos de Busca Suportados
|
||||
|
||||
DeerFlow suporta múltiplos mecanismos de busca que podem ser configurados no seu arquivo `.env` usando a variável `SEARCH_API`:
|
||||
|
||||
- **Tavily** (padrão): Uma API de busca especializada para aplicações de IA
|
||||
|
||||
- Requer `TAVILY_API_KEY` no seu arquivo `.env`
|
||||
- Inscreva-se em: <https://app.tavily.com/home>
|
||||
|
||||
- **InfoQuest** (recomendado): Um conjunto de ferramentas inteligentes de busca e crawling otimizadas para IA, desenvolvido pela BytePlus
|
||||
- Requer `INFOQUEST_API_KEY` no seu arquivo `.env`
|
||||
- Suporte para filtragem por intervalo de tempo e filtragem de sites
|
||||
- Fornece resultados de busca e extração de conteúdo de alta qualidade
|
||||
- Inscreva-se em: <https://console.byteplus.com/infoquest/infoquests>
|
||||
- Visite https://docs.byteplus.com/pt/docs/InfoQuest/What_is_Info_Quest para obter mais informações
|
||||
|
||||
- **DuckDuckGo**: Mecanismo de busca focado em privacidade
|
||||
|
||||
- Não requer chave API
|
||||
|
||||
- **Brave Search**: Mecanismo de busca focado em privacidade com funcionalidades avançadas
|
||||
|
||||
- Requer `BRAVE_SEARCH_API_KEY` no seu arquivo `.env`
|
||||
- Inscreva-se em: <https://brave.com/search/api/>
|
||||
|
||||
- **Arxiv**: Busca de artigos científicos para pesquisa acadêmica
|
||||
- Não requer chave API
|
||||
- Especializado em artigos científicos e acadêmicos
|
||||
|
||||
- **Searx/SearxNG**: Mecanismo de metabusca auto-hospedado
|
||||
- Requer `SEARX_HOST` no seu arquivo `.env`
|
||||
- Suporta integração com Searx ou SearxNG
|
||||
|
||||
Para configurar o seu mecanismo preferido, defina a variável `SEARCH_API` no seu arquivo:
|
||||
|
||||
```bash
|
||||
# Escolha uma: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### Ferramentas de Crawling
|
||||
|
||||
- **Jina** (padrão): Ferramenta gratuita de crawling de conteúdo web acessível
|
||||
- Não é necessária chave API para usar recursos básicos
|
||||
- Ao usar uma chave API, você obtém limites de taxa de acesso mais altos
|
||||
- Visite <https://jina.ai/reader> para obter mais informações
|
||||
|
||||
- **InfoQuest** (recomendado): Conjunto de ferramentas inteligentes de busca e crawling otimizadas para IA, desenvolvido pela BytePlus
|
||||
- Requer `INFOQUEST_API_KEY` no seu arquivo `.env`
|
||||
- Fornece parâmetros de crawling configuráveis
|
||||
- Suporta configurações de timeout personalizadas
|
||||
- Oferece capacidades mais poderosas de extração de conteúdo
|
||||
- Visite <https://docs.byteplus.com/pt/docs/InfoQuest/What_is_Info_Quest> para obter mais informações
|
||||
|
||||
Para configurar sua ferramenta de crawling preferida, defina o seguinte em seu arquivo `conf.yaml`:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# Tipo de mecanismo: "jina" (padrão) ou "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
### Principais Funcionalidades
|
||||
|
||||
- 🤖 **Integração LLM**
|
||||
|
||||
- Suporta a integração da maioria dos modelos através de [litellm](https://docs.litellm.ai/docs/providers).
|
||||
- Suporte a modelos open source como Qwen
|
||||
- Interface API compatível com a OpenAI
|
||||
- Sistema LLM multicamadas para diferentes complexidades de tarefa
|
||||
|
||||
### Ferramentas e Integrações MCP
|
||||
|
||||
- 🔍 **Busca e Recuperação**
|
||||
|
||||
- Busca web com Tavily, InfoQuest, Brave Search e mais
|
||||
- Crawling com Jina e InfoQuest
|
||||
- Extração de Conteúdo avançada
|
||||
|
||||
- 🔗 **Integração MCP perfeita**
|
||||
|
||||
- Expansão de capacidades de acesso para acesso a domínios privados, grafo de conhecimento, navegação web e mais
|
||||
- Integração facilitdade de diversas ferramentas de pesquisa e metodologias
|
||||
|
||||
### Colaboração Humana
|
||||
|
||||
- 🧠 **Humano-no-processo**
|
||||
|
||||
- Suporta modificação interativa de planos de pesquisa usando linguagem natural
|
||||
- Suporta auto-aceite de planos de pesquisa
|
||||
|
||||
- 📝 **Relatório Pós-Edição**
|
||||
- Suporta edição de edição de blocos estilo Notion
|
||||
- Permite refinamentos de IA, incluindo polimento de IA assistida, encurtamento de frase, e expansão
|
||||
- Distribuído por [tiptap](https://tiptap.dev/)
|
||||
|
||||
### Criação de Conteúdo
|
||||
|
||||
- 🎙️ **Geração de Podcast e apresentação**
|
||||
|
||||
- Script de geração de podcast e síntese de áudio movido por IA
|
||||
- Criação automatizada de apresentações PowerPoint simples
|
||||
- Templates customizáveis para conteúdo personalizado
|
||||
|
||||
## Arquitetura
|
||||
|
||||
DeerFlow implementa uma arquitetura de sistema multi-agente modular designada para pesquisa e análise de código automatizada. O sistema é construído em LangGraph, possibilitando um fluxo de trabalho flexível baseado-em-estado onde os componentes se comunicam através de um sistema de transmissão de mensagens bem-definido.
|
||||
|
||||

|
||||
|
||||
> Veja ao vivo em [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)
|
||||
|
||||
O sistema emprega um fluxo de trabalho simplificado com os seguintes componentes:
|
||||
|
||||
1. **Coordenador**: O ponto de entrada que gerencia o ciclo de vida do fluxo de trabalho
|
||||
|
||||
- Inicia o processo de pesquisa baseado na entrada do usuário
|
||||
- Delega tarefas so planejador quando apropriado
|
||||
- Atua como a interface primária entre o usuário e o sistema
|
||||
|
||||
2. **Planejador**: Componente estratégico para a decomposição e planejamento
|
||||
|
||||
- Analisa objetivos de pesquisa e cria planos de execução estruturados
|
||||
- Determina se há contexto suficiente disponível ou se mais pesquisa é necessária
|
||||
- Gerencia o fluxo de pesquisa e decide quando gerar o relatório final
|
||||
|
||||
3. **Time de Pesquisa**: Uma coleção de agentes especializados que executam o plano:
|
||||
|
||||
- **Pesquisador**: Conduz buscas web e coleta informações utilizando ferramentas como mecanismos de busca web, crawling e mesmo serviços MCP.
|
||||
- **Programador**: Lida com a análise de código, execução e tarefas técnicas como usar a ferramenta Python REPL.
|
||||
Cada agente tem acesso à ferramentas específicas otimizadas para seu papel e opera dentro do fluxo de trabalho LangGraph.
|
||||
|
||||
4. **Repórter**: Estágio final do processador de estágio para saídas de pesquisa
|
||||
- Resultados agregados do time de pesquisa
|
||||
- Processa e estrutura as informações coletadas
|
||||
- Gera relatórios abrangentes de pesquisas
|
||||
|
||||
## Texto-para-Fala Integração
|
||||
|
||||
DeerFlow agora inclui uma funcionalidade Texto-para-Fala (TTS) que permite que você converta relatórios de busca para voz. Essa funcionalidade usa o mecanismo de voz da API TTS para gerar áudio de alta qualidade a partir do texto. Funcionalidades como velocidade, volume e tom também são customizáveis.
|
||||
|
||||
### Usando a API TTS
|
||||
|
||||
Você pode acessar a funcionalidade TTS através do endpoint `/api/tts`:
|
||||
|
||||
```bash
|
||||
# Exemplo de chamada da API usando curl
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "This is a test of the text-to-speech functionality.",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
### Testando
|
||||
|
||||
Rode o conjunto de testes:
|
||||
|
||||
```bash
|
||||
# Roda todos os testes
|
||||
make test
|
||||
|
||||
# Roda um arquivo de teste específico
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Roda com coverage
|
||||
make coverage
|
||||
```
|
||||
|
||||
### Qualidade de Código
|
||||
|
||||
```bash
|
||||
# Roda o linting
|
||||
make lint
|
||||
|
||||
# Formata de código
|
||||
make format
|
||||
```
|
||||
|
||||
### Debugando com o LangGraph Studio
|
||||
|
||||
DeerFlow usa LangGraph para sua arquitetura de fluxo de trabalho. Nós podemos usar o LangGraph Studio para debugar e visualizar o fluxo de trabalho em tempo real.
|
||||
|
||||
#### Rodando o LangGraph Studio Localmente
|
||||
|
||||
DeerFlow inclui um arquivo de configuração `langgraph.json` que define a estrutura do grafo e dependências para o LangGraph Studio. Esse arquivo aponta para o grafo do fluxo de trabalho definido no projeto e automaticamente carrega as variáveis de ambiente do arquivo `.env`.
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# Instala o gerenciador de pacote uv caso você não o possua
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Instala as dependências e inicia o servidor LangGraph
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# Instala as dependências
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# Inicia o servidor LangGraph
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Após iniciar o servidor LangGraph, você verá diversas URLs no seu terminal:
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- Studio UI: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- API Docs: <http://127.0.0.1:2024/docs>
|
||||
|
||||
Abra o link do Studio UI no seu navegador para acessar a interface de depuração.
|
||||
|
||||
#### Usando o LangGraph Studio
|
||||
|
||||
No Studio UI, você pode:
|
||||
|
||||
1. Visualizar o grafo do fluxo de trabalho e como seus componentes se conectam
|
||||
2. Rastrear a execução em tempo-real e ver como os dados fluem através do sistema
|
||||
3. Inspecionar o estado de cada passo do fluxo de trabalho
|
||||
4. Depurar problemas ao examinar entradas e saídas de cada componente
|
||||
5. Coletar feedback durante a fase de planejamento para refinar os planos de pesquisa
|
||||
|
||||
Quando você envia um tópico de pesquisa ao Studio UI, você será capaz de ver toda a execução do fluxo de trabalho, incluindo:
|
||||
|
||||
- A fase de planejamento onde o plano de pesquisa foi criado
|
||||
- O processo de feedback onde você pode modificar o plano
|
||||
- As fases de pesquisa e escrita de cada seção
|
||||
- A geração do relatório final
|
||||
|
||||
## Docker
|
||||
|
||||
Você também pode executar esse projeto via Docker.
|
||||
|
||||
Primeiro, voce deve ler a [configuração](#configuration) below. Make sure `.env`, `.conf.yaml` files are ready.
|
||||
|
||||
Segundo, para fazer o build de sua imagem docker em seu próprio servidor:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
E por fim, inicie um container docker rodando o servidor web:
|
||||
|
||||
```bash
|
||||
# substitua deer-flow-api-app com seu nome de container preferido
|
||||
# Inicie o servidor e faça o bind com localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# pare o servidor
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose (inclui ambos backend e frontend)
|
||||
|
||||
DeerFlow fornece uma estrutura docker-compose para facilmente executar ambos o backend e frontend juntos:
|
||||
|
||||
```bash
|
||||
# building docker image
|
||||
docker compose build
|
||||
|
||||
# start the server
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Se você quiser implantar o DeerFlow em ambientes de produção, adicione autenticação ao site e avalie sua verificação de segurança do MCPServer e Python Repl.
|
||||
|
||||
## Exemplos
|
||||
|
||||
Os seguintes exemplos demonstram as capacidades do DeerFlow:
|
||||
|
||||
### Relatórios de Pesquisa
|
||||
|
||||
1. **Relatório OpenAI Sora** - Análise da ferramenta Sora da OpenAI
|
||||
|
||||
- Discute funcionalidades, acesso, engenharia de prompt, limitações e considerações éticas
|
||||
|
||||
- [Veja o relatório completo](examples/openai_sora_report.md)
|
||||
|
||||
2. **Relatório Protocolo Agent-to-Agent do Google** - Visão geral do protocolo Agent-to-Agent (A2A) do Google
|
||||
|
||||
- Discute o seu papel na comunicação de Agente de IA e seu relacionamento com o Protocolo de Contexto de Modelo ( MCP ) da Anthropic
|
||||
- [Veja o relatório completo](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **O que é MCP?** - Uma análise abrangente to termo "MCP" através de múltiplos contextos
|
||||
|
||||
- Explora o Protocolo de Contexto de Modelo em IA, Fosfato Monocálcio em Química, e placa de microcanal em eletrônica
|
||||
- [Veja o relatório completo](examples/what_is_mcp.md)
|
||||
|
||||
4. **Bitcoin Price Fluctuations** - Análise das recentes movimentações de preço do Bitcoin
|
||||
|
||||
- Examina tendências de mercado, influências regulatórias, e indicadores técnicos
|
||||
- Fornece recomendações baseadas nos dados históricos
|
||||
- [Veja o relatório completo](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **O que é LLM?** - Uma exploração em profundidade de Large Language Models
|
||||
|
||||
- Discute arquitetura, treinamento, aplicações, e considerações éticas
|
||||
- [Veja o relatório completo](examples/what_is_llm.md)
|
||||
|
||||
6. **Como usar Claude para Pesquisa Aprofundada?** - Melhores práticas e fluxos de trabalho para usar Claude em pesquisa aprofundada
|
||||
|
||||
- Cobre engenharia de prompt, análise de dados, e integração com outras ferramentas
|
||||
- [Veja o relatório completo](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **Adoção de IA na Área da Saúde: Fatores de Influência** - Análise dos fatores que levam à adoção de IA na área da saúde
|
||||
|
||||
- Discute tecnologias de IA, qualidade de dados, considerações éticas, avaliações econômicas, prontidão organizacional, e infraestrutura digital
|
||||
- [Veja o relatório completo](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **Impacto da Computação Quântica em Criptografia** - Análise dos impactos da computação quântica em criptografia
|
||||
|
||||
- Discture vulnerabilidades da criptografia clássica, criptografia pós-quântica, e soluções criptográficas de resistência-quântica
|
||||
- [Veja o relatório completo](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **Destaques da Performance do Cristiano Ronaldo** - Análise dos destaques da performance do Cristiano Ronaldo
|
||||
- Discute as suas conquistas de carreira, objetivos internacionais, e performance em diversas partidas
|
||||
- [Veja o relatório completo](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
Para executar esses exemplos ou criar seus próprios relatórios de pesquisa, você deve utilizar os seguintes comandos:
|
||||
|
||||
```bash
|
||||
# Executa com uma consulta específica
|
||||
uv run main.py "Quais fatores estão influenciando a adoção de IA na área da saúde?"
|
||||
|
||||
# Executa com parâmetros de planejamento customizados
|
||||
uv run main.py --max_plan_iterations 3 "Como a computação quântica impacta na criptografia?"
|
||||
|
||||
# Executa em modo interativo com questões embutidas
|
||||
uv run main.py --interactive
|
||||
|
||||
# Ou executa com um prompt interativo básico
|
||||
uv run main.py
|
||||
|
||||
# Vê todas as opções disponíveis
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### Modo Interativo
|
||||
|
||||
A aplicação agora suporta um modo interativo com questões embutidas tanto em Inglês quanto Chinês:
|
||||
|
||||
1. Inicie o modo interativo:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. Selecione sua linguagem de preferência (English or 中文)
|
||||
|
||||
3. Escolha uma das questões embutidas da lista ou selecione a opção para perguntar sua própria questão
|
||||
|
||||
4. O sistema irá processar sua questão e gerar um relatório abrangente de pesquisa
|
||||
|
||||
### Humano no processo
|
||||
|
||||
DeerFlow inclue um mecanismo de humano no processo que permite a você revisar, editar e aprovar planos de pesquisa antes que estes sejam executados:
|
||||
|
||||
1. **Revisão de Plano**: Quando o humano no processo está habilitado, o sistema irá apresentar o plano de pesquisa gerado para sua revisão antes da execução
|
||||
|
||||
2. **Fornecimento de Feedback**: Você pode:
|
||||
|
||||
- Aceitar o plano respondendo com `[ACCEPTED]`
|
||||
- Edite o plano fornecendo feedback (e.g., `[EDIT PLAN] Adicione mais passos sobre a implementação técnica`)
|
||||
- O sistema irá incorporar seu feedback e gerar um plano revisado
|
||||
|
||||
3. **Auto-aceite**: Você pode habilitar o auto-aceite ou pular o processo de revisão:
|
||||
|
||||
- Via API: Defina `auto_accepted_plan: true` na sua requisição
|
||||
|
||||
4. **Integração de API**: Quanto usar a API, você pode fornecer um feedback através do parâmetro `feedback`:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{ "role": "user", "content": "O que é computação quântica?" }],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] Inclua mais sobre algoritmos quânticos"
|
||||
}
|
||||
```
|
||||
|
||||
### Argumentos via Linha de Comando
|
||||
|
||||
A aplicação suporta diversos argumentos via linha de comando para customizar o seu comportamento:
|
||||
|
||||
- **consulta**: A consulta de pesquisa a ser processada (podem ser múltiplas palavras)
|
||||
- **--interativo**: Roda no modo interativo com questões embutidas
|
||||
- **--max_plan_iterations**: Número máximo de ciclos de planejamento (padrão: 1)
|
||||
- **--max_step_num**: Número máximo de passos em um plano de pesquisa (padrão: 3)
|
||||
- **--debug**: Habilita Enable um log de depuração detalhado
|
||||
|
||||
## FAQ
|
||||
|
||||
Por favor consulte a [FAQ.md](docs/FAQ.md) para maiores detalhes.
|
||||
|
||||
## Licença
|
||||
|
||||
Esse projeto é open source e disponível sob a [MIT License](./LICENSE).
|
||||
|
||||
## Agradecimentos
|
||||
|
||||
DeerFlow é construído através do incrível trabalho da comunidade open-source. Nós somos profundamente gratos a todos os projetos e contribuidores cujos esforços tornaram o DeerFlow possível. Realmente, nós estamos apoiados nos ombros de gigantes.
|
||||
|
||||
Nós gostaríamos de extender nossos sinceros agradecimentos aos seguintes projetos por suas invaloráveis contribuições:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**: O framework excepcional deles empodera nossas interações via LLM e correntes, permitindo uma integração perfeita e funcional.
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: A abordagem inovativa para orquestração multi-agente deles tem sido foi fundamental em permitir o acesso dos fluxos de trabalho sofisticados do DeerFlow.
|
||||
|
||||
Esses projetos exemplificam o poder transformador da colaboração open-source, e nós temos orgulho de construir baseado em suas fundações.
|
||||
|
||||
### Contribuidores-Chave
|
||||
|
||||
Um sincero muito obrigado vai para os principais autores do `DeerFlow`, cuja visão, paixão, e dedicação trouxe esse projeto à vida:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
O seu compromisso inabalável e experiência tem sido a força por trás do sucesso do DeerFlow. Nós estamos honrados em tê-los no comando dessa trajetória.
|
||||
|
||||
## Histórico-Estrelas
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
607
README_ru.md
607
README_ru.md
@@ -1,607 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/bytedance/deer-flow)
|
||||
<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) | [Portuguese](./README_pt.md)
|
||||
|
||||
> Создано на базе открытого кода, возвращено в открытый код.
|
||||
|
||||
**DeerFlow** (**D**eep **E**xploration and **E**fficient **R**esearch **Flow**) - это фреймворк для глубокого исследования, разработанный сообществом и основанный на впечатляющей работе сообщества открытого кода. Наша цель - объединить языковые модели со специализированными инструментами для таких задач, как веб-поиск, сканирование и выполнение кода Python, одновременно возвращая пользу сообществу, которое сделало это возможным.
|
||||
|
||||
В настоящее время DeerFlow официально вошел в Центр приложений FaaS Volcengine. Пользователи могут испытать его онлайн через ссылку для опыта, чтобы интуитивно почувствовать его мощные функции и удобные операции. В то же время, для удовлетворения потребностей развертывания различных пользователей, DeerFlow поддерживает развертывание одним кликом на основе Volcengine. Нажмите на ссылку развертывания, чтобы быстро завершить процесс развертывания и начать эффективное исследовательское путешествие.
|
||||
|
||||
DeerFlow недавно интегрировал интеллектуальный набор инструментов поиска и краулинга, разработанный самостоятельно компанией BytePlus — [InfoQuest (поддерживает бесплатное онлайн-опробование)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-160108.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
Пожалуйста, посетите [наш официальный сайт](https://deerflow.tech/) для получения дополнительной информации.
|
||||
|
||||
## Демонстрация
|
||||
|
||||
### Видео
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
В этой демонстрации мы показываем, как использовать DeerFlow для:
|
||||
|
||||
- Бесшовной интеграции с сервисами MCP
|
||||
- Проведения процесса глубокого исследования и создания комплексного отчета с изображениями
|
||||
- Создания аудио подкаста на основе сгенерированного отчета
|
||||
|
||||
### Повторы
|
||||
|
||||
- [Какова высота Эйфелевой башни по сравнению с самым высоким зданием?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [Какие репозитории самые популярные на GitHub?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [Написать статью о традиционных блюдах Нанкина](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [Как украсить съемную квартиру?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [Посетите наш официальный сайт, чтобы изучить больше повторов.](https://deerflow.tech/#case-studies)
|
||||
|
||||
---
|
||||
|
||||
## 📑 Оглавление
|
||||
|
||||
- [🚀 Быстрый старт](#быстрый-старт)
|
||||
- [🌟 Особенности](#особенности)
|
||||
- [🏗️ Архитектура](#архитектура)
|
||||
- [🛠️ Разработка](#разработка)
|
||||
- [🐳 Docker](#docker)
|
||||
- [🗣️ Интеграция преобразования текста в речь](#интеграция-преобразования-текста-в-речь)
|
||||
- [📚 Примеры](#примеры)
|
||||
- [❓ FAQ](#faq)
|
||||
- [📜 Лицензия](#лицензия)
|
||||
- [💖 Благодарности](#благодарности)
|
||||
- [⭐ История звезд](#история-звезд)
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
DeerFlow разработан на Python и поставляется с веб-интерфейсом, написанным на Node.js. Для обеспечения плавного процесса настройки мы рекомендуем использовать следующие инструменты:
|
||||
|
||||
### Рекомендуемые инструменты
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
Упрощает управление средой Python и зависимостями. `uv` автоматически создает виртуальную среду в корневом каталоге и устанавливает все необходимые пакеты за вас—без необходимости вручную устанавливать среды Python.
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
Легко управляйте несколькими версиями среды выполнения Node.js.
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
Установка и управление зависимостями проекта Node.js.
|
||||
|
||||
### Требования к среде
|
||||
|
||||
Убедитесь, что ваша система соответствует следующим минимальным требованиям:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** Версия `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** Версия `22+`
|
||||
|
||||
### Установка
|
||||
|
||||
```bash
|
||||
# Клонировать репозиторий
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# Установить зависимости, uv позаботится об интерпретаторе python и создании venv, и установит необходимые пакеты
|
||||
uv sync
|
||||
|
||||
# Настроить .env с вашими API-ключами
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# volcengine TTS: Добавьте ваши учетные данные TTS, если они у вас есть
|
||||
cp .env.example .env
|
||||
|
||||
# См. разделы 'Поддерживаемые поисковые системы' и 'Интеграция преобразования текста в речь' ниже для всех доступных опций
|
||||
|
||||
# Настроить conf.yaml для вашей модели LLM и API-ключей
|
||||
# Пожалуйста, обратитесь к 'docs/configuration_guide.md' для получения дополнительной информации
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# Установить marp для генерации презентаций
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
По желанию установите зависимости веб-интерфейса через [pnpm](https://pnpm.io/installation):
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Конфигурации
|
||||
|
||||
Пожалуйста, обратитесь к [Руководству по конфигурации](docs/configuration_guide.md) для получения дополнительной информации.
|
||||
|
||||
> [!ПРИМЕЧАНИЕ]
|
||||
> Прежде чем запустить проект, внимательно прочитайте руководство и обновите конфигурации в соответствии с вашими конкретными настройками и требованиями.
|
||||
|
||||
### Консольный интерфейс
|
||||
|
||||
Самый быстрый способ запустить проект - использовать консольный интерфейс.
|
||||
|
||||
```bash
|
||||
# Запустить проект в оболочке, похожей на bash
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Веб-интерфейс
|
||||
|
||||
Этот проект также включает веб-интерфейс, предлагающий более динамичный и привлекательный интерактивный опыт.
|
||||
|
||||
> [!ПРИМЕЧАНИЕ]
|
||||
> Сначала вам нужно установить зависимости веб-интерфейса.
|
||||
|
||||
```bash
|
||||
# Запустить оба сервера, бэкенд и фронтенд, в режиме разработки
|
||||
# На macOS/Linux
|
||||
./bootstrap.sh -d
|
||||
|
||||
# На Windows
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [!Примечание]
|
||||
> По умолчанию сервер бэкенда привязывается к 127.0.0.1 (localhost) по соображениям безопасности. Если вам нужно разрешить внешние подключения (например, при развертывании на сервере Linux), вы можете изменить хост сервера на 0.0.0.0 в скрипте загрузки (uv run server.py --host 0.0.0.0).
|
||||
> Пожалуйста, убедитесь, что ваша среда должным образом защищена, прежде чем подвергать сервис внешним сетям.
|
||||
|
||||
Откройте ваш браузер и посетите [`http://localhost:3000`](http://localhost:3000), чтобы исследовать веб-интерфейс.
|
||||
|
||||
Исследуйте больше деталей в каталоге [`web`](./web/).
|
||||
|
||||
## Поддерживаемые поисковые системы
|
||||
|
||||
DeerFlow поддерживает несколько поисковых систем, которые можно настроить в файле `.env` с помощью переменной `SEARCH_API`:
|
||||
|
||||
- **Tavily** (по умолчанию): Специализированный поисковый API для приложений ИИ
|
||||
|
||||
- Требуется `TAVILY_API_KEY` в вашем файле `.env`
|
||||
- Зарегистрируйтесь на: <https://app.tavily.com/home>
|
||||
|
||||
- **InfoQuest** (рекомендуется): Набор интеллектуальных инструментов для поиска и сканирования, оптимизированных для ИИ, разработанный компанией BytePlus
|
||||
- Требуется `INFOQUEST_API_KEY` в вашем файле `.env`
|
||||
- Поддержка фильтрации по диапазону времени и фильтрации сайтов
|
||||
- Предоставляет высококачественные результаты поиска и извлечение контента
|
||||
- Зарегистрируйтесь на: <https://console.byteplus.com/infoquest/infoquests>
|
||||
- Посетите https://docs.byteplus.com/ru/docs/InfoQuest/What_is_Info_Quest для получения дополнительной информации
|
||||
|
||||
- **DuckDuckGo**: Поисковая система, ориентированная на конфиденциальность
|
||||
|
||||
- Не требуется API-ключ
|
||||
|
||||
- **Brave Search**: Поисковая система, ориентированная на конфиденциальность, с расширенными функциями
|
||||
|
||||
- Требуется `BRAVE_SEARCH_API_KEY` в вашем файле `.env`
|
||||
- Зарегистрируйтесь на: <https://brave.com/search/api/>
|
||||
|
||||
- **Arxiv**: Поиск научных статей для академических исследований
|
||||
- Не требуется API-ключ
|
||||
- Специализируется на научных и академических статьях
|
||||
|
||||
- **Searx/SearxNG**: Самостоятельно размещённая метапоисковая система
|
||||
- Требуется `SEARX_HOST` в вашем файле `.env`
|
||||
- Поддерживает подключение к Searx или SearxNG
|
||||
|
||||
Чтобы настроить предпочитаемую поисковую систему, установите переменную `SEARCH_API` в вашем файле `.env`:
|
||||
|
||||
```bash
|
||||
# Выберите одно: tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### Инструменты сканирования
|
||||
|
||||
- **Jina** (по умолчанию): Бесплатный доступный инструмент для сканирования веб-контента
|
||||
- API-ключ не требуется для использования базовых функций
|
||||
- При использовании API-ключа вы получаете более высокие лимиты скорости доступа
|
||||
- Посетите <https://jina.ai/reader> для получения дополнительной информации
|
||||
|
||||
- **InfoQuest** (рекомендуется): Набор интеллектуальных инструментов для поиска и сканирования, оптимизированных для ИИ, разработанный компанией BytePlus
|
||||
- Требуется `INFOQUEST_API_KEY` в вашем файле `.env`
|
||||
- Предоставляет настраиваемые параметры сканирования
|
||||
- Поддерживает настройки пользовательских тайм-аутов
|
||||
- Предоставляет более мощные возможности извлечения контента
|
||||
- Посетите <https://docs.byteplus.com/ru/docs/InfoQuest/What_is_Info_Quest> для получения дополнительной информации
|
||||
|
||||
Чтобы настроить предпочитаемый инструмент сканирования, установите следующее в вашем файле `conf.yaml`:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# Тип движка: "jina" (по умолчанию) или "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
## Особенности
|
||||
|
||||
### Ключевые возможности
|
||||
|
||||
- 🤖 **Интеграция LLM**
|
||||
- Поддерживает интеграцию большинства моделей через [litellm](https://docs.litellm.ai/docs/providers).
|
||||
- Поддержка моделей с открытым исходным кодом, таких как Qwen
|
||||
- API-интерфейс, совместимый с OpenAI
|
||||
- Многоуровневая система LLM для задач различной сложности
|
||||
|
||||
### Инструменты и интеграции MCP
|
||||
|
||||
- 🔍 **Поиск и извлечение**
|
||||
|
||||
- Веб-поиск через Tavily, InfoQuest, Brave Search и другие
|
||||
- Сканирование с Jina и InfoQuest
|
||||
- Расширенное извлечение контента
|
||||
|
||||
- 🔗 **Бесшовная интеграция MCP**
|
||||
- Расширение возможностей для доступа к частным доменам, графам знаний, веб-браузингу и многому другому
|
||||
- Облегчает интеграцию различных исследовательских инструментов и методологий
|
||||
|
||||
### Человеческое взаимодействие
|
||||
|
||||
- 🧠 **Человек в контуре**
|
||||
|
||||
- Поддерживает интерактивное изменение планов исследования с использованием естественного языка
|
||||
- Поддерживает автоматическое принятие планов исследования
|
||||
|
||||
- 📝 **Пост-редактирование отчетов**
|
||||
- Поддерживает блочное редактирование в стиле Notion
|
||||
- Позволяет совершенствовать с помощью ИИ, включая полировку, сокращение и расширение предложений
|
||||
- Работает на [tiptap](https://tiptap.dev/)
|
||||
|
||||
### Создание контента
|
||||
|
||||
- 🎙️ **Генерация подкастов и презентаций**
|
||||
- Генерация сценариев подкастов и синтез аудио с помощью ИИ
|
||||
- Автоматическое создание простых презентаций PowerPoint
|
||||
- Настраиваемые шаблоны для индивидуального контента
|
||||
|
||||
## Архитектура
|
||||
|
||||
DeerFlow реализует модульную архитектуру системы с несколькими агентами, предназначенную для автоматизированных исследований и анализа кода. Система построена на LangGraph, обеспечивающей гибкий рабочий процесс на основе состояний, где компоненты взаимодействуют через четко определенную систему передачи сообщений.
|
||||
|
||||

|
||||
|
||||
> Посмотрите вживую на [deerflow.tech](https://deerflow.tech/#multi-agent-architecture)
|
||||
|
||||
В системе используется оптимизированный рабочий процесс со следующими компонентами:
|
||||
|
||||
1. **Координатор**: Точка входа, управляющая жизненным циклом рабочего процесса
|
||||
|
||||
- Инициирует процесс исследования на основе пользовательского ввода
|
||||
- Делегирует задачи планировщику, когда это необходимо
|
||||
- Выступает в качестве основного интерфейса между пользователем и системой
|
||||
|
||||
2. **Планировщик**: Стратегический компонент для декомпозиции и планирования задач
|
||||
|
||||
- Анализирует цели исследования и создает структурированные планы выполнения
|
||||
- Определяет, достаточно ли доступного контекста или требуется дополнительное исследование
|
||||
- Управляет потоком исследования и решает, когда генерировать итоговый отчет
|
||||
|
||||
3. **Исследовательская команда**: Набор специализированных агентов, которые выполняют план:
|
||||
|
||||
- **Исследователь**: Проводит веб-поиск и сбор информации с использованием таких инструментов, как поисковые системы, сканирование и даже сервисы MCP.
|
||||
- **Программист**: Обрабатывает анализ кода, выполнение и технические задачи с помощью инструмента Python REPL.
|
||||
Каждый агент имеет доступ к определенным инструментам, оптимизированным для его роли, и работает в рамках фреймворка LangGraph
|
||||
|
||||
4. **Репортер**: Процессор финальной стадии для результатов исследования
|
||||
- Агрегирует находки исследовательской команды
|
||||
- Обрабатывает и структурирует собранную информацию
|
||||
- Генерирует комплексные исследовательские отчеты
|
||||
|
||||
## Интеграция преобразования текста в речь
|
||||
|
||||
DeerFlow теперь включает функцию преобразования текста в речь (TTS), которая позволяет конвертировать исследовательские отчеты в речь. Эта функция использует API TTS volcengine для генерации высококачественного аудио из текста. Также можно настраивать такие параметры, как скорость, громкость и тон.
|
||||
|
||||
### Использование API TTS
|
||||
|
||||
Вы можете получить доступ к функциональности TTS через конечную точку `/api/tts`:
|
||||
|
||||
```bash
|
||||
# Пример вызова API с использованием curl
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "Это тест функциональности преобразования текста в речь.",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## Разработка
|
||||
|
||||
### Тестирование
|
||||
|
||||
Запустите набор тестов:
|
||||
|
||||
```bash
|
||||
# Запустить все тесты
|
||||
make test
|
||||
|
||||
# Запустить определенный тестовый файл
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# Запустить с покрытием
|
||||
make coverage
|
||||
```
|
||||
|
||||
### Качество кода
|
||||
|
||||
```bash
|
||||
# Запустить линтинг
|
||||
make lint
|
||||
|
||||
# Форматировать код
|
||||
make format
|
||||
```
|
||||
|
||||
### Отладка с LangGraph Studio
|
||||
|
||||
DeerFlow использует LangGraph для своей архитектуры рабочего процесса. Вы можете использовать LangGraph Studio для отладки и визуализации рабочего процесса в реальном времени.
|
||||
|
||||
#### Запуск LangGraph Studio локально
|
||||
|
||||
DeerFlow включает конфигурационный файл `langgraph.json`, который определяет структуру графа и зависимости для LangGraph Studio. Этот файл указывает на графы рабочего процесса, определенные в проекте, и автоматически загружает переменные окружения из файла `.env`.
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# Установите менеджер пакетов uv, если у вас его нет
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# Установите зависимости и запустите сервер LangGraph
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# Установить зависимости
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# Запустить сервер LangGraph
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
После запуска сервера LangGraph вы увидите несколько URL в терминале:
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- Studio UI: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- API Docs: <http://127.0.0.1:2024/docs>
|
||||
|
||||
Откройте ссылку Studio UI в вашем браузере для доступа к интерфейсу отладки.
|
||||
|
||||
#### Использование LangGraph Studio
|
||||
|
||||
В интерфейсе Studio вы можете:
|
||||
|
||||
1. Визуализировать граф рабочего процесса и видеть, как соединяются компоненты
|
||||
2. Отслеживать выполнение в реальном времени, чтобы видеть, как данные проходят через систему
|
||||
3. Исследовать состояние на каждом шаге рабочего процесса
|
||||
4. Отлаживать проблемы путем изучения входов и выходов каждого компонента
|
||||
5. Предоставлять обратную связь во время фазы планирования для уточнения планов исследования
|
||||
|
||||
Когда вы отправляете тему исследования в интерфейсе Studio, вы сможете увидеть весь процесс выполнения рабочего процесса, включая:
|
||||
|
||||
- Фазу планирования, где создается план исследования
|
||||
- Цикл обратной связи, где вы можете модифицировать план
|
||||
- Фазы исследования и написания для каждого раздела
|
||||
- Генерацию итогового отчета
|
||||
|
||||
### Включение трассировки LangSmith
|
||||
|
||||
DeerFlow поддерживает трассировку LangSmith, чтобы помочь вам отладить и контролировать ваши рабочие процессы. Чтобы включить трассировку LangSmith:
|
||||
|
||||
1. Убедитесь, что в вашем файле `.env` есть следующие конфигурации (см. `.env.example`):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. Запустите трассировку и визуализируйте граф локально с LangSmith, выполнив:
|
||||
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
Это включит визуализацию трассировки в LangGraph Studio и отправит ваши трассировки в LangSmith для мониторинга и анализа.
|
||||
|
||||
## Docker
|
||||
|
||||
Вы также можете запустить этот проект с Docker.
|
||||
|
||||
Во-первых, вам нужно прочитать [конфигурацию](docs/configuration_guide.md) ниже. Убедитесь, что файлы `.env`, `.conf.yaml` готовы.
|
||||
|
||||
Во-вторых, чтобы построить Docker-образ вашего собственного веб-сервера:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
Наконец, запустите Docker-контейнер с веб-сервером:
|
||||
|
||||
```bash
|
||||
# Замените deer-flow-api-app на предпочитаемое вами имя контейнера
|
||||
# Запустите сервер и привяжите к localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# остановить сервер
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose (включает как бэкенд, так и фронтенд)
|
||||
|
||||
DeerFlow предоставляет настройку docker-compose для легкого запуска бэкенда и фронтенда вместе:
|
||||
|
||||
```bash
|
||||
# сборка docker-образа
|
||||
docker compose build
|
||||
|
||||
# запуск сервера
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Если вы хотите развернуть DeerFlow в производственных средах, пожалуйста, добавьте аутентификацию к веб-сайту и оцените свою проверку безопасности MCPServer и Python Repl.
|
||||
|
||||
## Примеры
|
||||
|
||||
Следующие примеры демонстрируют возможности DeerFlow:
|
||||
|
||||
### Исследовательские отчеты
|
||||
|
||||
1. **Отчет о OpenAI Sora** - Анализ инструмента ИИ Sora от OpenAI
|
||||
|
||||
- Обсуждаются функции, доступ, инженерия промптов, ограничения и этические соображения
|
||||
- [Просмотреть полный отчет](examples/openai_sora_report.md)
|
||||
|
||||
2. **Отчет о протоколе Agent to Agent от Google** - Обзор протокола Agent to Agent (A2A) от Google
|
||||
|
||||
- Обсуждается его роль в коммуникации агентов ИИ и его отношение к протоколу Model Context Protocol (MCP) от Anthropic
|
||||
- [Просмотреть полный отчет](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **Что такое MCP?** - Комплексный анализ термина "MCP" в различных контекстах
|
||||
|
||||
- Исследует Model Context Protocol в ИИ, Монокальцийфосфат в химии и Микроканальные пластины в электронике
|
||||
- [Просмотреть полный отчет](examples/what_is_mcp.md)
|
||||
|
||||
4. **Колебания цены Биткоина** - Анализ недавних движений цены Биткоина
|
||||
|
||||
- Исследует рыночные тренды, регуляторные влияния и технические индикаторы
|
||||
- Предоставляет рекомендации на основе исторических данных
|
||||
- [Просмотреть полный отчет](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **Что такое LLM?** - Углубленное исследование больших языковых моделей
|
||||
|
||||
- Обсуждаются архитектура, обучение, приложения и этические соображения
|
||||
- [Просмотреть полный отчет](examples/what_is_llm.md)
|
||||
|
||||
6. **Как использовать Claude для глубокого исследования?** - Лучшие практики и рабочие процессы для использования Claude в глубоком исследовании
|
||||
|
||||
- Охватывает инженерию промптов, анализ данных и интеграцию с другими инструментами
|
||||
- [Просмотреть полный отчет](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **Внедрение ИИ в здравоохранении: Влияющие факторы** - Анализ факторов, движущих внедрением ИИ в здравоохранении
|
||||
|
||||
- Обсуждаются технологии ИИ, качество данных, этические соображения, экономические оценки, организационная готовность и цифровая инфраструктура
|
||||
- [Просмотреть полный отчет](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **Влияние квантовых вычислений на криптографию** - Анализ влияния квантовых вычислений на криптографию
|
||||
|
||||
- Обсуждаются уязвимости классической криптографии, пост-квантовая криптография и криптографические решения, устойчивые к квантовым вычислениям
|
||||
- [Просмотреть полный отчет](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **Ключевые моменты выступлений Криштиану Роналду** - Анализ выдающихся выступлений Криштиану Роналду
|
||||
- Обсуждаются его карьерные достижения, международные голы и выступления в различных матчах
|
||||
- [Просмотреть полный отчет](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
Чтобы запустить эти примеры или создать собственные исследовательские отчеты, вы можете использовать следующие команды:
|
||||
|
||||
```bash
|
||||
# Запустить с определенным запросом
|
||||
uv run main.py "Какие факторы влияют на внедрение ИИ в здравоохранении?"
|
||||
|
||||
# Запустить с пользовательскими параметрами планирования
|
||||
uv run main.py --max_plan_iterations 3 "Как квантовые вычисления влияют на криптографию?"
|
||||
|
||||
# Запустить в интерактивном режиме с встроенными вопросами
|
||||
uv run main.py --interactive
|
||||
|
||||
# Или запустить с базовым интерактивным приглашением
|
||||
uv run main.py
|
||||
|
||||
# Посмотреть все доступные опции
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### Интерактивный режим
|
||||
|
||||
Приложение теперь поддерживает интерактивный режим с встроенными вопросами как на английском, так и на китайском языках:
|
||||
|
||||
1. Запустите интерактивный режим:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. Выберите предпочитаемый язык (English или 中文)
|
||||
|
||||
3. Выберите из списка встроенных вопросов или выберите опцию задать собственный вопрос
|
||||
|
||||
4. Система обработает ваш вопрос и сгенерирует комплексный исследовательский отчет
|
||||
|
||||
### Человек в контуре
|
||||
|
||||
DeerFlow включает механизм "человек в контуре", который позволяет вам просматривать, редактировать и утверждать планы исследования перед их выполнением:
|
||||
|
||||
1. **Просмотр плана**: Когда активирован режим "человек в контуре", система представит сгенерированный план исследования для вашего просмотра перед выполнением
|
||||
|
||||
2. **Предоставление обратной связи**: Вы можете:
|
||||
|
||||
- Принять план, ответив `[ACCEPTED]`
|
||||
- Отредактировать план, предоставив обратную связь (например, `[EDIT PLAN] Добавить больше шагов о технической реализации`)
|
||||
- Система включит вашу обратную связь и сгенерирует пересмотренный план
|
||||
|
||||
3. **Автоматическое принятие**: Вы можете включить автоматическое принятие, чтобы пропустить процесс просмотра:
|
||||
|
||||
- Через API: Установите `auto_accepted_plan: true` в вашем запросе
|
||||
|
||||
4. **Интеграция API**: При использовании API вы можете предоставить обратную связь через параметр `feedback`:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{ "role": "user", "content": "Что такое квантовые вычисления?" }],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] Включить больше о квантовых алгоритмах"
|
||||
}
|
||||
```
|
||||
|
||||
### Аргументы командной строки
|
||||
|
||||
Приложение поддерживает несколько аргументов командной строки для настройки его поведения:
|
||||
|
||||
- **query**: Запрос исследования для обработки (может состоять из нескольких слов)
|
||||
- **--interactive**: Запустить в интерактивном режиме с встроенными вопросами
|
||||
- **--max_plan_iterations**: Максимальное количество циклов планирования (по умолчанию: 1)
|
||||
- **--max_step_num**: Максимальное количество шагов в плане исследования (по умолчанию: 3)
|
||||
- **--debug**: Включить подробное логирование отладки
|
||||
|
||||
## FAQ
|
||||
|
||||
Пожалуйста, обратитесь к [FAQ.md](docs/FAQ.md) для получения дополнительной информации.
|
||||
|
||||
## Лицензия
|
||||
|
||||
Этот проект имеет открытый исходный код и доступен под [Лицензией MIT](./LICENSE).
|
||||
|
||||
## Благодарности
|
||||
|
||||
DeerFlow создан на основе невероятной работы сообщества открытого кода. Мы глубоко благодарны всем проектам и контрибьюторам, чьи усилия сделали DeerFlow возможным. Поистине, мы стоим на плечах гигантов.
|
||||
|
||||
Мы хотели бы выразить искреннюю признательность следующим проектам за их неоценимый вклад:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**: Их исключительный фреймворк обеспечивает наши взаимодействия и цепочки LLM, позволяя бесшовную интеграцию и функциональность.
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**: Их инновационный подход к оркестровке многоагентных систем сыграл решающую роль в обеспечении сложных рабочих процессов DeerFlow.
|
||||
|
||||
Эти проекты являются примером преобразующей силы сотрудничества в области открытого кода, и мы гордимся тем, что строим на их основе.
|
||||
|
||||
### Ключевые контрибьюторы
|
||||
|
||||
Сердечная благодарность основным авторам `DeerFlow`, чье видение, страсть и преданность делу вдохнули жизнь в этот проект:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
Ваша непоколебимая приверженность и опыт стали движущей силой успеха DeerFlow. Мы считаем за честь иметь вас во главе этого путешествия.
|
||||
|
||||
## История звезд
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
686
README_zh.md
686
README_zh.md
@@ -1,686 +0,0 @@
|
||||
# 🦌 DeerFlow
|
||||
|
||||
[](https://www.python.org/downloads/)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
[English](./README.md) | [简体中文](./README_zh.md) | [日本語](./README_ja.md) | [Deutsch](./README_de.md) | [Español](./README_es.md) | [Русский](./README_ru.md) |[Portuguese](./README_pt.md)
|
||||
|
||||
> 源于开源,回馈开源。
|
||||
|
||||
**DeerFlow**(**D**eep **E**xploration and **E**fficient **R**esearch **Flow**)是一个社区驱动的深度研究框架,它建立在开源社区的杰出工作基础之上。我们的目标是将语言模型与专业工具(如网络搜索、爬虫和 Python 代码执行)相结合,同时回馈使这一切成为可能的社区。
|
||||
|
||||
目前,DeerFlow 已正式入驻[火山引擎的 FaaS 应用中心](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market),用户可通过[体验链接](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/market/deerflow/?channel=github&source=deerflow)进行在线体验,直观感受其强大功能与便捷操作;同时,为满足不同用户的部署需求,DeerFlow 支持基于火山引擎一键部署,点击[部署链接](https://console.volcengine.com/vefaas/region:vefaas+cn-beijing/application/create?templateId=683adf9e372daa0008aaed5c&channel=github&source=deerflow)即可快速完成部署流程,开启高效研究之旅。
|
||||
|
||||
DeerFlow 新接入BytePlus自主推出的智能搜索与爬取工具集--[InfoQuest(支持在线免费体验)](https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest)
|
||||
|
||||
<a href="https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest" target="_blank">
|
||||
<img
|
||||
src="https://sf16-sg.tiktokcdn.com/obj/eden-sg/hubseh7bsbps/20251208-141052.png" alt="infoquest_bannar"
|
||||
/>
|
||||
</a>
|
||||
|
||||
请访问[DeerFlow 的官方网站](https://deerflow.tech/)了解更多详情。
|
||||
|
||||
## 演示
|
||||
|
||||
### 视频
|
||||
|
||||
<https://github.com/user-attachments/assets/f3786598-1f2a-4d07-919e-8b99dfa1de3e>
|
||||
|
||||
在此演示中,我们展示了如何使用 DeerFlow:
|
||||
|
||||
- 无缝集成 MCP 服务
|
||||
- 进行深度研究过程并生成包含图像的综合报告
|
||||
- 基于生成的报告创建播客音频
|
||||
|
||||
### 回放示例
|
||||
|
||||
- [埃菲尔铁塔与最高建筑相比有多高?](https://deerflow.tech/chat?replay=eiffel-tower-vs-tallest-building)
|
||||
- [GitHub 上最热门的仓库有哪些?](https://deerflow.tech/chat?replay=github-top-trending-repo)
|
||||
- [撰写关于南京传统美食的文章](https://deerflow.tech/chat?replay=nanjing-traditional-dishes)
|
||||
- [如何装饰租赁公寓?](https://deerflow.tech/chat?replay=rental-apartment-decoration)
|
||||
- [访问我们的官方网站探索更多回放示例。](https://deerflow.tech/#case-studies)
|
||||
---
|
||||
|
||||
|
||||
## 📑 目录
|
||||
|
||||
- [🚀 快速开始](#快速开始)
|
||||
- [🌟 特性](#特性)
|
||||
- [🏗️ 架构](#架构)
|
||||
- [🛠️ 开发](#开发)
|
||||
- [🗣️ 文本转语音集成](#文本转语音集成)
|
||||
- [📚 示例](#示例)
|
||||
- [❓ 常见问题](#常见问题)
|
||||
- [📜 许可证](#许可证)
|
||||
- [💖 致谢](#致谢)
|
||||
- [⭐ Star History](#star-history)
|
||||
|
||||
## 快速开始
|
||||
|
||||
DeerFlow 使用 Python 开发,并配有用 Node.js 编写的 Web UI。为确保顺利的设置过程,我们推荐使用以下工具:
|
||||
|
||||
### 推荐工具
|
||||
|
||||
- **[`uv`](https://docs.astral.sh/uv/getting-started/installation/):**
|
||||
简化 Python 环境和依赖管理。`uv`会自动在根目录创建虚拟环境并为您安装所有必需的包—无需手动安装 Python 环境。
|
||||
|
||||
- **[`nvm`](https://github.com/nvm-sh/nvm):**
|
||||
轻松管理多个 Node.js 运行时版本。
|
||||
|
||||
- **[`pnpm`](https://pnpm.io/installation):**
|
||||
安装和管理 Node.js 项目的依赖。
|
||||
|
||||
### 环境要求
|
||||
|
||||
确保您的系统满足以下最低要求:
|
||||
|
||||
- **[Python](https://www.python.org/downloads/):** 版本 `3.12+`
|
||||
- **[Node.js](https://nodejs.org/en/download/):** 版本 `22+`
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone https://github.com/bytedance/deer-flow.git
|
||||
cd deer-flow
|
||||
|
||||
# 安装依赖,uv将负责Python解释器和虚拟环境的创建,并安装所需的包
|
||||
uv sync
|
||||
|
||||
# 使用您的API密钥配置.env
|
||||
# Tavily: https://app.tavily.com/home
|
||||
# Brave_SEARCH: https://brave.com/search/api/
|
||||
# 火山引擎TTS: 如果您有TTS凭证,请添加
|
||||
cp .env.example .env
|
||||
|
||||
# 查看下方的"支持的搜索引擎"和"文本转语音集成"部分了解所有可用选项
|
||||
|
||||
# 为您的LLM模型和API密钥配置conf.yaml
|
||||
# 请参阅'docs/configuration_guide.md'获取更多详情
|
||||
cp conf.yaml.example conf.yaml
|
||||
|
||||
# 安装marp用于PPT生成
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file#use-package-manager
|
||||
brew install marp-cli
|
||||
```
|
||||
|
||||
可选,通过[pnpm](https://pnpm.io/installation)安装 Web UI 依赖:
|
||||
|
||||
```bash
|
||||
cd deer-flow/web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
请参阅[配置指南](docs/configuration_guide.md)获取更多详情。
|
||||
|
||||
> [! 注意]
|
||||
> 在启动项目之前,请仔细阅读指南,并更新配置以匹配您的特定设置和要求。
|
||||
|
||||
### 控制台 UI
|
||||
|
||||
运行项目的最快方法是使用控制台 UI。
|
||||
|
||||
```bash
|
||||
# 在类bash的shell中运行项目
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
本项目还包括一个 Web UI,提供更加动态和引人入胜的交互体验。
|
||||
> [! 注意]
|
||||
> 您需要先安装 Web UI 的依赖。
|
||||
|
||||
```bash
|
||||
# 在开发模式下同时运行后端和前端服务器
|
||||
# 在macOS/Linux上
|
||||
./bootstrap.sh -d
|
||||
|
||||
# 在Windows上
|
||||
bootstrap.bat -d
|
||||
```
|
||||
> [! 注意]
|
||||
> 出于安全考虑,后端服务器默认绑定到 127.0.0.1 (localhost)。如果您需要允许外部连接(例如,在Linux服务器上部署时),您可以修改启动脚本中的主机地址为 0.0.0.0。(uv run server.py --host 0.0.0.0)
|
||||
> 请注意,在将服务暴露给外部网络之前,请务必确保您的环境已经过适当的安全加固。
|
||||
|
||||
打开浏览器并访问[`http://localhost:3000`](http://localhost:3000)探索 Web UI。
|
||||
|
||||
在[`web`](./web/)目录中探索更多详情。
|
||||
|
||||
## 支持的搜索引擎
|
||||
|
||||
### 公域搜索引擎
|
||||
|
||||
DeerFlow 支持多种搜索引擎,可以在`.env`文件中通过`SEARCH_API`变量进行配置:
|
||||
|
||||
- **Tavily**(默认):专为 AI 应用设计的专业搜索 API
|
||||
- 需要在`.env`文件中设置`TAVILY_API_KEY`
|
||||
- 注册地址:<https://app.tavily.com/home>
|
||||
|
||||
- **InfoQuest**(推荐):BytePlus自主研发的专为AI应用优化的智能搜索与爬取工具集
|
||||
- 需要在`.env`文件中设置`INFOQUEST_API_KEY`
|
||||
- 支持时间范围过滤和站点过滤
|
||||
- 提供高质量的搜索结果和内容提取
|
||||
- 注册地址:<https://console.byteplus.com/infoquest/infoquests>
|
||||
- 访问 <https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest> 了解更多信息
|
||||
|
||||
- **DuckDuckGo**:注重隐私的搜索引擎
|
||||
- 无需 API 密钥
|
||||
|
||||
- **Brave Search**:具有高级功能的注重隐私的搜索引擎
|
||||
- 需要在`.env`文件中设置`BRAVE_SEARCH_API_KEY`
|
||||
- 注册地址:<https://brave.com/search/api/>
|
||||
|
||||
- **Arxiv**:用于学术研究的科学论文搜索
|
||||
- 无需 API 密钥
|
||||
- 专为科学和学术论文设计
|
||||
|
||||
- **Searx/SearxNG**:自托管的元搜索引擎
|
||||
- 需要在`.env`文件中设置`SEARX_HOST`
|
||||
- 支持对接Searx或SearxNG
|
||||
|
||||
要配置您首选的搜索引擎,请在`.env`文件中设置`SEARCH_API`变量:
|
||||
|
||||
```bash
|
||||
# 选择一个:tavily, infoquest, duckduckgo, brave_search, arxiv
|
||||
SEARCH_API=tavily
|
||||
```
|
||||
|
||||
### 爬取工具
|
||||
|
||||
- **Jina**(默认):免费可访问的网页内容爬取工具
|
||||
- 无需 API 密钥即可使用基础功能
|
||||
- 使用 API 密钥可获得更高的访问速率限制
|
||||
- 访问 <https://jina.ai/reader> 了解更多信息
|
||||
|
||||
- **InfoQuest**(推荐):BytePlus自主研发的专为AI应用优化的智能搜索与爬取工具集
|
||||
- 需要在`.env`文件中设置`INFOQUEST_API_KEY`
|
||||
- 提供可配置的爬取参数
|
||||
- 支持自定义超时设置
|
||||
- 提供更强大的内容提取能力
|
||||
- 访问 <https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest> 了解更多信息
|
||||
|
||||
要配置您首选的爬取工具,请在`conf.yaml`文件中设置:
|
||||
|
||||
```yaml
|
||||
CRAWLER_ENGINE:
|
||||
# 引擎类型:"jina"(默认)或 "infoquest"
|
||||
engine: infoquest
|
||||
```
|
||||
|
||||
### 私域知识库引擎
|
||||
|
||||
DeerFlow 支持基于私有域知识的检索,您可以将文档上传到多种私有知识库中,以便在研究过程中使用,当前支持的私域知识库有:
|
||||
|
||||
- **[RAGFlow](https://ragflow.io/docs/dev/)**:开源的基于检索增强生成的知识库引擎
|
||||
```
|
||||
# 参照示例进行配置 .env.example
|
||||
RAG_PROVIDER=ragflow
|
||||
RAGFLOW_API_URL="http://localhost:9388"
|
||||
RAGFLOW_API_KEY="ragflow-xxx"
|
||||
RAGFLOW_RETRIEVAL_SIZE=10
|
||||
```
|
||||
|
||||
- **[MOI]**:AI 原生多模态数据智能平台
|
||||
```
|
||||
# 参照示例进行配置 .env.example
|
||||
RAG_PROVIDER=moi
|
||||
MOI_API_URL="https://freetier-01.cn-hangzhou.cluster.matrixonecloud.cn"
|
||||
MOI_API_KEY="xxx-xxx-xxx-xxx"
|
||||
MOI_RETRIEVAL_SIZE=10
|
||||
MOI_LIST_LIMIT=10
|
||||
```
|
||||
|
||||
- **[VikingDB 知识库](https://www.volcengine.com/docs/84313/1254457)**:火山引擎提供的公有云知识库引擎
|
||||
> 注意先从 [火山引擎](https://www.volcengine.com/docs/84313/1254485) 获取账号 AK/SK
|
||||
```
|
||||
# 参照示例进行配置 .env.example
|
||||
RAG_PROVIDER=vikingdb_knowledge_base
|
||||
VIKINGDB_KNOWLEDGE_BASE_API_URL="api-knowledgebase.mlp.cn-beijing.volces.com"
|
||||
VIKINGDB_KNOWLEDGE_BASE_API_AK="volcengine-ak-xxx"
|
||||
VIKINGDB_KNOWLEDGE_BASE_API_SK="volcengine-sk-xxx"
|
||||
VIKINGDB_KNOWLEDGE_BASE_RETRIEVAL_SIZE=15
|
||||
```
|
||||
|
||||
## 特性
|
||||
|
||||
### 核心能力
|
||||
|
||||
- 🤖 **LLM 集成**
|
||||
- 通过[litellm](https://docs.litellm.ai/docs/providers)支持集成大多数模型
|
||||
- 支持开源模型如 Qwen
|
||||
- 兼容 OpenAI 的 API 接口
|
||||
- 多层 LLM 系统适用于不同复杂度的任务
|
||||
|
||||
### 工具和 MCP 集成
|
||||
|
||||
- 🔍 **搜索和检索**
|
||||
- 通过 Tavily、InfoQuest、Brave Search 等进行网络搜索
|
||||
- 使用 Jina、InfoQuest 进行爬取
|
||||
- 高级内容提取
|
||||
- 支持检索指定私有知识库
|
||||
|
||||
- 📃 **RAG 集成**
|
||||
- 支持 [RAGFlow](https://github.com/infiniflow/ragflow) 知识库
|
||||
- 支持 [VikingDB](https://www.volcengine.com/docs/84313/1254457) 火山知识库
|
||||
|
||||
- 🔗 **MCP 无缝集成**
|
||||
- 扩展私有域访问、知识图谱、网页浏览等能力
|
||||
- 促进多样化研究工具和方法的集成
|
||||
|
||||
### 人机协作
|
||||
|
||||
- 💬 **智能澄清功能**
|
||||
- 多轮对话澄清模糊的研究主题
|
||||
- 提高研究精准度和报告质量
|
||||
- 减少无效搜索和 token 使用
|
||||
- 可配置开关,灵活控制启用/禁用
|
||||
- 详见 [配置指南 - 澄清功能](./docs/configuration_guide.md#multi-turn-clarification-feature)
|
||||
|
||||
- 🧠 **人在环中**
|
||||
- 支持使用自然语言交互式修改研究计划
|
||||
- 支持自动接受研究计划
|
||||
|
||||
- 📝 **报告后期编辑**
|
||||
- 支持类 Notion 的块编辑
|
||||
- 允许 AI 优化,包括 AI 辅助润色、句子缩短和扩展
|
||||
- 由[tiptap](https://tiptap.dev/)提供支持
|
||||
|
||||
### 内容创作
|
||||
|
||||
- 🎙️ **播客和演示文稿生成**
|
||||
- AI 驱动的播客脚本生成和音频合成
|
||||
- 自动创建简单的 PowerPoint 演示文稿
|
||||
- 可定制模板以满足个性化内容需求
|
||||
|
||||
## 架构
|
||||
|
||||
DeerFlow 实现了一个模块化的多智能体系统架构,专为自动化研究和代码分析而设计。该系统基于 LangGraph 构建,实现了灵活的基于状态的工作流,其中组件通过定义良好的消息传递系统进行通信。
|
||||
|
||||

|
||||
|
||||
> 在[deerflow.tech](https://deerflow.tech/#multi-agent-architecture)上查看实时演示
|
||||
|
||||
系统采用了精简的工作流程,包含以下组件:
|
||||
|
||||
1. **协调器**:管理工作流生命周期的入口点
|
||||
|
||||
- 根据用户输入启动研究过程
|
||||
- 在适当时候将任务委派给规划器
|
||||
- 作为用户和系统之间的主要接口
|
||||
|
||||
2. **规划器**:负责任务分解和规划的战略组件
|
||||
|
||||
- 分析研究目标并创建结构化执行计划
|
||||
- 确定是否有足够的上下文或是否需要更多研究
|
||||
- 管理研究流程并决定何时生成最终报告
|
||||
|
||||
3. **研究团队**:执行计划的专业智能体集合:
|
||||
- **研究员**:使用网络搜索引擎、爬虫甚至 MCP 服务等工具进行网络搜索和信息收集。
|
||||
- **编码员**:使用 Python REPL 工具处理代码分析、执行和技术任务。
|
||||
每个智能体都可以访问针对其角色优化的特定工具,并在 LangGraph 框架内运行
|
||||
|
||||
4. **报告员**:研究输出的最终阶段处理器
|
||||
- 汇总研究团队的发现
|
||||
- 处理和组织收集的信息
|
||||
- 生成全面的研究报告
|
||||
|
||||
## 文本转语音集成
|
||||
|
||||
DeerFlow 现在包含一个文本转语音 (TTS) 功能,允许您将研究报告转换为语音。此功能使用火山引擎 TTS API 生成高质量的文本音频。速度、音量和音调等特性也可以自定义。
|
||||
|
||||
### 使用 TTS API
|
||||
|
||||
您可以通过`/api/tts`端点访问 TTS 功能:
|
||||
|
||||
```bash
|
||||
# 使用curl的API调用示例
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "这是文本转语音功能的测试。",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## 开发
|
||||
|
||||
### 测试
|
||||
|
||||
运行测试套件:
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
make test
|
||||
|
||||
# 运行特定测试文件
|
||||
pytest tests/integration/test_workflow.py
|
||||
|
||||
# 运行覆盖率测试
|
||||
make coverage
|
||||
```
|
||||
|
||||
### 代码质量
|
||||
|
||||
```bash
|
||||
# 运行代码检查
|
||||
make lint
|
||||
|
||||
# 格式化代码
|
||||
make format
|
||||
```
|
||||
|
||||
### 使用 LangGraph Studio 进行调试
|
||||
|
||||
DeerFlow 使用 LangGraph 作为其工作流架构。您可以使用 LangGraph Studio 实时调试和可视化工作流。
|
||||
|
||||
#### 本地运行 LangGraph Studio
|
||||
|
||||
DeerFlow 包含一个`langgraph.json`配置文件,该文件定义了 LangGraph Studio 的图结构和依赖关系。该文件指向项目中定义的工作流图,并自动从`.env`文件加载环境变量。
|
||||
|
||||
##### Mac
|
||||
|
||||
```bash
|
||||
# 如果您没有uv包管理器,请安装它
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
|
||||
# 安装依赖并启动LangGraph服务器
|
||||
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking
|
||||
```
|
||||
|
||||
##### Windows / Linux
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
pip install -e .
|
||||
pip install -U "langgraph-cli[inmem]"
|
||||
|
||||
# 启动LangGraph服务器
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
启动 LangGraph 服务器后,您将在终端中看到几个 URL:
|
||||
|
||||
- API: <http://127.0.0.1:2024>
|
||||
- Studio UI: <https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024>
|
||||
- API 文档:<http://127.0.0.1:2024/docs>
|
||||
|
||||
在浏览器中打开 Studio UI 链接以访问调试界面。
|
||||
|
||||
#### 使用 LangGraph Studio
|
||||
|
||||
在 Studio UI 中,您可以:
|
||||
|
||||
1. 可视化工作流图并查看组件如何连接
|
||||
2. 实时跟踪执行情况,了解数据如何在系统中流动
|
||||
3. 检查工作流每个步骤的状态
|
||||
4. 通过检查每个组件的输入和输出来调试问题
|
||||
5. 在规划阶段提供反馈以完善研究计划
|
||||
|
||||
当您在 Studio UI 中提交研究主题时,您将能够看到整个工作流执行过程,包括:
|
||||
|
||||
- 创建研究计划的规划阶段
|
||||
- 可以修改计划的反馈循环
|
||||
- 每个部分的研究和写作阶段
|
||||
- 最终报告生成
|
||||
|
||||
### 启用 LangSmith 追踪
|
||||
|
||||
DeerFlow 支持 LangSmith 追踪功能,帮助您调试和监控工作流。要启用 LangSmith 追踪:
|
||||
|
||||
1. 确保您的 `.env` 文件中有以下配置(参见 `.env.example`):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="xxx"
|
||||
LANGSMITH_PROJECT="xxx"
|
||||
```
|
||||
|
||||
2. 通过运行以下命令本地启动 LangSmith 追踪:
|
||||
|
||||
```bash
|
||||
langgraph dev
|
||||
```
|
||||
|
||||
这将在 LangGraph Studio 中启用追踪可视化,并将您的追踪发送到 LangSmith 进行监控和分析。
|
||||
|
||||
## Docker
|
||||
|
||||
您也可以使用 Docker 运行此项目。
|
||||
|
||||
首先,您需要阅读下面的[配置](#配置)部分。确保`.env`和`.conf.yaml`文件已准备就绪。
|
||||
|
||||
其次,构建您自己的 Web 服务器 Docker 镜像:
|
||||
|
||||
```bash
|
||||
docker build -t deer-flow-api .
|
||||
```
|
||||
|
||||
最后,启动运行 Web 服务器的 Docker 容器:
|
||||
|
||||
```bash
|
||||
# 将deer-flow-api-app替换为您首选的容器名称
|
||||
# 启动服务器并绑定到localhost:8000
|
||||
docker run -d -t -p 127.0.0.1:8000:8000 --env-file .env --name deer-flow-api-app deer-flow-api
|
||||
|
||||
# 停止服务器
|
||||
docker stop deer-flow-api-app
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
|
||||
您也可以使用 docker compose 同时运行后端和前端。
|
||||
|
||||
#### 配置
|
||||
|
||||
构建前,先配置根目录的 `.env` 文件(从 `.env.example` 复制):
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
cp conf.yaml.example conf.yaml
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `docker-compose.yml` 只使用**根目录的 `.env`** 文件(不使用 `web/.env`)。使用 Docker Compose 时,您**不需要**创建或修改 `web/.env`。
|
||||
|
||||
如果您在**远程服务器**上部署或通过**局域网 IP**(非 `localhost`)访问,**必须**将根目录 `.env` 中的 `NEXT_PUBLIC_API_URL` 修改为实际的主机 IP 或域名:
|
||||
|
||||
```bash
|
||||
# 示例:通过局域网 IP 访问
|
||||
NEXT_PUBLIC_API_URL=http://192.168.1.100:8000/api
|
||||
|
||||
# 示例:使用域名的远程部署
|
||||
NEXT_PUBLIC_API_URL=https://your-domain.com/api
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> `NEXT_PUBLIC_API_URL` 是 Next.js 的**构建时**变量——它会在 `docker compose build` 时被嵌入到前端 JavaScript 包中。如果之后修改了此值,必须重新执行 `docker compose build` 才能生效。
|
||||
|
||||
#### 构建和运行
|
||||
|
||||
```bash
|
||||
# 构建docker镜像
|
||||
docker compose build
|
||||
|
||||
# 启动服务器
|
||||
docker compose up
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> 如果您想将 DeerFlow 部署到生产环境中,请为网站添加身份验证,并评估 MCPServer 和 Python Repl 的安全检查。
|
||||
|
||||
## 文本转语音集成
|
||||
|
||||
DeerFlow 现在包含一个文本转语音 (TTS) 功能,允许您将研究报告转换为语音。此功能使用火山引擎 TTS API 生成高质量的文本音频。速度、音量和音调等特性也可以自定义。
|
||||
|
||||
### 使用 TTS API
|
||||
|
||||
您可以通过`/api/tts`端点访问 TTS 功能:
|
||||
|
||||
```bash
|
||||
# 使用curl的API调用示例
|
||||
curl --location 'http://localhost:8000/api/tts' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"text": "这是文本转语音功能的测试。",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
以下示例展示了 DeerFlow 的功能:
|
||||
|
||||
### 研究报告
|
||||
|
||||
1. **OpenAI Sora 报告** - OpenAI 的 Sora AI 工具分析
|
||||
- 讨论功能、访问方式、提示工程、限制和伦理考虑
|
||||
- [查看完整报告](examples/openai_sora_report.md)
|
||||
|
||||
2. **Google 的 Agent to Agent 协议报告** - Google 的 Agent to Agent (A2A) 协议概述
|
||||
- 讨论其在 AI 智能体通信中的作用及其与 Anthropic 的 Model Context Protocol (MCP) 的关系
|
||||
- [查看完整报告](examples/what_is_agent_to_agent_protocol.md)
|
||||
|
||||
3. **什么是 MCP?** - 对"MCP"一词在多个上下文中的全面分析
|
||||
- 探讨 AI 中的 Model Context Protocol、化学中的 Monocalcium Phosphate 和电子学中的 Micro-channel Plate
|
||||
- [查看完整报告](examples/what_is_mcp.md)
|
||||
|
||||
4. **比特币价格波动** - 最近比特币价格走势分析
|
||||
|
||||
- 研究市场趋势、监管影响和技术指标
|
||||
- 基于历史数据提供建议
|
||||
- [查看完整报告](examples/bitcoin_price_fluctuation.md)
|
||||
|
||||
5. **什么是 LLM?** - 对大型语言模型的深入探索
|
||||
- 讨论架构、训练、应用和伦理考虑
|
||||
- [查看完整报告](examples/what_is_llm.md)
|
||||
|
||||
6. **如何使用 Claude 进行深度研究?** - 在深度研究中使用 Claude 的最佳实践和工作流程
|
||||
- 涵盖提示工程、数据分析和与其他工具的集成
|
||||
- [查看完整报告](examples/how_to_use_claude_deep_research.md)
|
||||
|
||||
7. **医疗保健中的 AI 采用:影响因素** - 影响医疗保健中 AI 采用的因素分析
|
||||
- 讨论 AI 技术、数据质量、伦理考虑、经济评估、组织准备度和数字基础设施
|
||||
- [查看完整报告](examples/AI_adoption_in_healthcare.md)
|
||||
|
||||
8. **量子计算对密码学的影响** - 量子计算对密码学影响的分析
|
||||
|
||||
- 讨论经典密码学的漏洞、后量子密码学和抗量子密码解决方案
|
||||
- [查看完整报告](examples/Quantum_Computing_Impact_on_Cryptography.md)
|
||||
|
||||
9. **克里斯蒂亚诺·罗纳尔多的表现亮点** - 克里斯蒂亚诺·罗纳尔多表现亮点的分析
|
||||
- 讨论他的职业成就、国际进球和在各种比赛中的表现
|
||||
- [查看完整报告](examples/Cristiano_Ronaldo's_Performance_Highlights.md)
|
||||
|
||||
要运行这些示例或创建您自己的研究报告,您可以使用以下命令:
|
||||
|
||||
```bash
|
||||
# 使用特定查询运行
|
||||
uv run main.py "哪些因素正在影响医疗保健中的AI采用?"
|
||||
|
||||
# 使用自定义规划参数运行
|
||||
uv run main.py --max_plan_iterations 3 "量子计算如何影响密码学?"
|
||||
|
||||
# 在交互模式下运行,带有内置问题
|
||||
uv run main.py --interactive
|
||||
|
||||
# 或者使用基本交互提示运行
|
||||
uv run main.py
|
||||
|
||||
# 查看所有可用选项
|
||||
uv run main.py --help
|
||||
```
|
||||
|
||||
### 交互模式
|
||||
|
||||
应用程序现在支持带有英文和中文内置问题的交互模式:
|
||||
|
||||
1. 启动交互模式:
|
||||
|
||||
```bash
|
||||
uv run main.py --interactive
|
||||
```
|
||||
|
||||
2. 选择您偏好的语言(English 或中文)
|
||||
|
||||
3. 从内置问题列表中选择或选择提出您自己问题的选项
|
||||
|
||||
4. 系统将处理您的问题并生成全面的研究报告
|
||||
|
||||
### 人在环中
|
||||
|
||||
DeerFlow 包含一个人在环中机制,允许您在执行研究计划前审查、编辑和批准:
|
||||
|
||||
1. **计划审查**:启用人在环中时,系统将在执行前向您展示生成的研究计划
|
||||
|
||||
2. **提供反馈**:您可以:
|
||||
|
||||
- 通过回复`[ACCEPTED]`接受计划
|
||||
- 通过提供反馈编辑计划(例如,`[EDIT PLAN] 添加更多关于技术实现的步骤`)
|
||||
- 系统将整合您的反馈并生成修订后的计划
|
||||
|
||||
3. **自动接受**:您可以启用自动接受以跳过审查过程:
|
||||
- 通过 API:在请求中设置`auto_accepted_plan: true`
|
||||
|
||||
4. **API 集成**:使用 API 时,您可以通过`feedback`参数提供反馈:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{ "role": "user", "content": "什么是量子计算?" }],
|
||||
"thread_id": "my_thread_id",
|
||||
"auto_accepted_plan": false,
|
||||
"feedback": "[EDIT PLAN] 包含更多关于量子算法的内容"
|
||||
}
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
应用程序支持多个命令行参数来自定义其行为:
|
||||
|
||||
- **query**:要处理的研究查询(可以是多个词)
|
||||
- **--interactive**:以交互模式运行,带有内置问题
|
||||
- **--max_plan_iterations**:最大规划周期数(默认:1)
|
||||
- **--max_step_num**:研究计划中的最大步骤数(默认:3)
|
||||
- **--debug**:启用详细调试日志
|
||||
|
||||
## 常见问题
|
||||
|
||||
请参阅[FAQ.md](docs/FAQ.md)获取更多详情。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目是开源的,遵循[MIT 许可证](./LICENSE)。
|
||||
|
||||
## 致谢
|
||||
|
||||
DeerFlow 建立在开源社区的杰出工作基础之上。我们深深感谢所有使 DeerFlow 成为可能的项目和贡献者。诚然,我们站在巨人的肩膀上。
|
||||
|
||||
我们要向以下项目表达诚挚的感谢,感谢他们的宝贵贡献:
|
||||
|
||||
- **[LangChain](https://github.com/langchain-ai/langchain)**:他们卓越的框架为我们的 LLM 交互和链提供动力,实现了无缝集成和功能。
|
||||
- **[LangGraph](https://github.com/langchain-ai/langgraph)**:他们在多智能体编排方面的创新方法对于实现 DeerFlow 复杂工作流至关重要。
|
||||
|
||||
这些项目展示了开源协作的变革力量,我们很自豪能够在他们的基础上构建。
|
||||
|
||||
### 核心贡献者
|
||||
|
||||
衷心感谢`DeerFlow`的核心作者,他们的愿景、热情和奉献使这个项目得以实现:
|
||||
|
||||
- **[Daniel Walnut](https://github.com/hetaoBackend/)**
|
||||
- **[Henry Li](https://github.com/magiccube/)**
|
||||
|
||||
您坚定不移的承诺和专业知识是 DeerFlow 成功的驱动力。我们很荣幸有您引领这一旅程。
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#bytedance/deer-flow&Date)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
As deer-flow doesn't provide an offical release yet, please use the latest version for the security updates.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please go to https://github.com/bytedance/deer-flow/security to report the vulnerability you find.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 151 KiB |
@@ -1,28 +0,0 @@
|
||||
@echo off
|
||||
SETLOCAL ENABLEEXTENSIONS
|
||||
|
||||
REM Check if argument is dev mode
|
||||
SET MODE=%1
|
||||
IF "%MODE%"=="--dev" GOTO DEV
|
||||
IF "%MODE%"=="-d" GOTO DEV
|
||||
IF "%MODE%"=="dev" GOTO DEV
|
||||
IF "%MODE%"=="development" GOTO DEV
|
||||
|
||||
:PROD
|
||||
echo Starting DeerFlow in [PRODUCTION] mode...
|
||||
start uv run server.py
|
||||
cd web
|
||||
start pnpm start
|
||||
REM Wait for user to close
|
||||
GOTO END
|
||||
|
||||
:DEV
|
||||
echo Starting DeerFlow in [DEVELOPMENT] mode...
|
||||
start uv run server.py --reload
|
||||
cd web
|
||||
start pnpm dev
|
||||
REM Wait for user to close
|
||||
pause
|
||||
|
||||
:END
|
||||
ENDLOCAL
|
||||
18
bootstrap.sh
18
bootstrap.sh
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start both of DeerFlow's backend and web UI server.
|
||||
# If the user presses Ctrl+C, kill them both.
|
||||
|
||||
if [ "$1" = "--dev" -o "$1" = "-d" -o "$1" = "dev" -o "$1" = "development" ]; then
|
||||
echo -e "Starting DeerFlow in [DEVELOPMENT] mode...\n"
|
||||
uv run server.py --reload & SERVER_PID=$$!
|
||||
cd web && pnpm dev & WEB_PID=$$!
|
||||
trap "kill $$SERVER_PID $$WEB_PID" SIGINT SIGTERM
|
||||
wait
|
||||
else
|
||||
echo -e "Starting DeerFlow in [PRODUCTION] mode...\n"
|
||||
uv run server.py & SERVER_PID=$$!
|
||||
cd web && pnpm start & WEB_PID=$$!
|
||||
trap "kill $$SERVER_PID $$WEB_PID" SIGINT SIGTERM
|
||||
wait
|
||||
fi
|
||||
@@ -1,127 +0,0 @@
|
||||
# [!NOTE]
|
||||
# Read the `docs/configuration_guide.md` carefully, and update the
|
||||
# configurations to match your specific settings and requirements.
|
||||
# - Replace `api_key` with your own credentials.
|
||||
# - Replace `base_url` and `model` name if you want to use a custom model.
|
||||
# - Set `verify_ssl` to `false` if your LLM server uses self-signed certificates
|
||||
# - A restart is required every time you change the `conf.yaml` file.
|
||||
|
||||
BASIC_MODEL:
|
||||
base_url: https://ark.cn-beijing.volces.com/api/v3
|
||||
model: "doubao-1-5-pro-32k-250115"
|
||||
api_key: xxxx
|
||||
# max_retries: 3 # Maximum number of retries for LLM calls
|
||||
# verify_ssl: false # Uncomment this line to disable SSL certificate verification for self-signed certificates
|
||||
# token_limit: 200000 # Maximum input tokens for context compression (prevents token overflow errors)
|
||||
|
||||
# Local model configuration example:
|
||||
|
||||
# Ollama (Tested and supported for local development)
|
||||
# BASIC_MODEL:
|
||||
# base_url: "http://localhost:11434/v1" # Ollama OpenAI compatible endpoint
|
||||
# model: "qwen3:14b" # or "llama3.2", etc.
|
||||
# api_key: "ollama" # Ollama doesn't need real API key
|
||||
# max_retries: 3
|
||||
# verify_ssl: false # Local deployment usually doesn't need SSL verification
|
||||
|
||||
# To use Google Ai Studio as your basic platform:
|
||||
# BASIC_MODEL:
|
||||
# platform: "google_aistudio"
|
||||
# model: "gemini-2.5-flash" # or "gemini-1.5-pro", "gemini-2.5-flash-exp", etc.
|
||||
# api_key: your_gemini_api_key # Get from https://aistudio.google.com/app/apikey
|
||||
# max_retries: 3
|
||||
|
||||
# Reasoning model is optional.
|
||||
# Uncomment the following settings if you want to use reasoning model
|
||||
# for planning.
|
||||
|
||||
# REASONING_MODEL:
|
||||
# base_url: https://ark.cn-beijing.volces.com/api/v3
|
||||
# model: "doubao-1-5-thinking-pro-m-250428"
|
||||
# api_key: xxxx
|
||||
# max_retries: 3 # Maximum number of retries for LLM calls
|
||||
# token_limit: 150000 # Maximum input tokens for context compression
|
||||
|
||||
|
||||
# OTHER SETTINGS:
|
||||
|
||||
# Tool-specific interrupts configuration (Issue #572)
|
||||
# Allows interrupting execution before specific tools are called.
|
||||
# Useful for reviewing sensitive operations like database queries or API calls.
|
||||
# Note: This can be overridden per-request via the API.
|
||||
# TOOL_INTERRUPTS:
|
||||
# # List of tool names to interrupt before execution
|
||||
# # Example: interrupt before database tools or sensitive API calls
|
||||
# interrupt_before:
|
||||
# - "db_tool" # Database operations
|
||||
# - "db_read_tool" # Database reads
|
||||
# - "db_write_tool" # Database writes
|
||||
# - "payment_api" # Payment-related API calls
|
||||
# - "admin_api" # Administrative API calls
|
||||
# # When interrupt is triggered, user will be prompted to approve/reject
|
||||
# # Approved keywords: "approved", "approve", "yes", "proceed", "continue", "ok", "okay", "accepted", "accept"
|
||||
|
||||
# Web search toggle (Issue #681)
|
||||
# Set to false to disable web search and use only local RAG knowledge base.
|
||||
# This is useful for environments without internet access.
|
||||
# WARNING: If you disable web search, make sure to configure local RAG resources;
|
||||
# otherwise, the researcher will operate in pure LLM reasoning mode without external data.
|
||||
# Note: This can be overridden per-request via the API parameter `enable_web_search`.
|
||||
# ENABLE_WEB_SEARCH: true
|
||||
|
||||
# Search engine configuration
|
||||
# Supported engines: tavily, infoquest
|
||||
# SEARCH_ENGINE:
|
||||
# # Engine type to use: "tavily" or "infoquest"
|
||||
# engine: tavily or infoquest
|
||||
#
|
||||
# # The following parameters are specific to Tavily
|
||||
# # Only include results from these domains
|
||||
# include_domains:
|
||||
# - example.com
|
||||
# - trusted-news.com
|
||||
# - reliable-source.org
|
||||
# - gov.cn
|
||||
# - edu.cn
|
||||
# # Exclude results from these domains
|
||||
# exclude_domains:
|
||||
# - example.com
|
||||
# # Include an answer in the search results
|
||||
# include_answer: false
|
||||
# # Search depth: "basic" or "advanced"
|
||||
# search_depth: "advanced"
|
||||
# # Include raw content from pages
|
||||
# include_raw_content: true
|
||||
# # Include images in search results
|
||||
# include_images: true
|
||||
# # Include descriptions for images
|
||||
# include_image_descriptions: true
|
||||
# # Minimum score threshold for results (0-1)
|
||||
# min_score_threshold: 0.0
|
||||
# # Maximum content length per page
|
||||
# max_content_length_per_page: 4000
|
||||
#
|
||||
# # The following parameters are specific to InfoQuest
|
||||
# # Used to limit the scope of search results, only returns content within the specified time range. Set to -1 to disable time filtering
|
||||
# time_range: 30
|
||||
# # Used to limit the scope of search results, only returns content from specified whitelisted domains. Set to empty string to disable site filtering
|
||||
# site: "example.com"
|
||||
|
||||
|
||||
# Crawler engine configuration
|
||||
# Supported engines: jina (default), infoquest
|
||||
# Uncomment the following section to configure crawler engine
|
||||
# CRAWLER_ENGINE:
|
||||
# # Engine type to use: "jina" (default) or "infoquest"
|
||||
# engine: infoquest
|
||||
#
|
||||
# # The following timeout parameters are only effective when engine is set to "infoquest"
|
||||
# # Waiting time after page loading (in seconds)
|
||||
# # Set to positive value to enable, -1 to disable
|
||||
# fetch_time: 10
|
||||
# # Overall timeout for the entire crawling process (in seconds)
|
||||
# # Set to positive value to enable, -1 to disable
|
||||
# timeout: 30
|
||||
# # Timeout for navigating to the page (in seconds)
|
||||
# # Set to positive value to enable, -1 to disable
|
||||
# navi_timeout: 15
|
||||
@@ -1,35 +0,0 @@
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: deer-flow-backend
|
||||
# Remove the ports section to not expose to host
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./conf.yaml:/app/conf.yaml:ro
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- deer-flow-network
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./web
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
- NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
|
||||
container_name: deer-flow-frontend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- deer-flow-network
|
||||
|
||||
networks:
|
||||
deer-flow-network:
|
||||
driver: bridge
|
||||
552
docs/API.md
552
docs/API.md
@@ -1,552 +0,0 @@
|
||||
# DeerFlow API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
DeerFlow API is a comprehensive backend service for advanced research and content generation. It provides endpoints for chat-based research, text-to-speech conversion, content generation (podcasts, presentations, prose), prompt enhancement, and RAG (Retrieval-Augmented Generation) functionality.
|
||||
|
||||
**API Version:** 0.1.0
|
||||
**Base URL:** `http://localhost:8000`
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Currently, the API does not require authentication. CORS is configured with origins defined by the `ALLOWED_ORIGINS` environment variable (default: `http://localhost:3000`).
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Chat & Research
|
||||
|
||||
#### `POST /api/chat/stream`
|
||||
|
||||
Initiates a streaming chat session with the research agent. Returns Server-Sent Events (SSE) with message chunks, tool calls, and intermediate results.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is quantum computing?"
|
||||
}
|
||||
],
|
||||
"thread_id": "__default__",
|
||||
"max_plan_iterations": 1,
|
||||
"max_step_num": 3,
|
||||
"max_search_results": 3,
|
||||
"auto_accepted_plan": false,
|
||||
"report_style": "ACADEMIC",
|
||||
"enable_background_investigation": true,
|
||||
"enable_deep_thinking": false,
|
||||
"enable_clarification": false
|
||||
}
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
- `messages` (required, array[ChatMessage]): History of messages between user and assistant
|
||||
- `resources` (optional, array[Resource]): Resources for the research
|
||||
- `thread_id` (optional, string, default: `"__default__"`): Conversation identifier
|
||||
- `max_plan_iterations` (optional, integer, default: 1): Maximum number of plan iterations
|
||||
- `max_step_num` (optional, integer, default: 3): Maximum number of steps in a plan
|
||||
- `max_search_results` (optional, integer, default: 3): Maximum number of search results
|
||||
- `auto_accepted_plan` (optional, boolean, default: false): Automatically accept the plan
|
||||
- `interrupt_feedback` (optional, string): User feedback on the plan
|
||||
- `mcp_settings` (optional, object): MCP settings (requires `ENABLE_MCP_SERVER_CONFIGURATION=true`)
|
||||
- `enable_background_investigation` (optional, boolean, default: true): Get background investigation before plan
|
||||
- `report_style` (optional, enum): Style of the report - `ACADEMIC`, `POPULAR_SCIENCE`, `NEWS`, `SOCIAL_MEDIA`, `STRATEGIC_INVESTMENT`
|
||||
- `enable_deep_thinking` (optional, boolean, default: false): Enable deep thinking
|
||||
- `enable_clarification` (optional, boolean): Enable multi-turn clarification
|
||||
- `max_clarification_rounds` (optional, integer): Maximum clarification rounds
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `text/event-stream`
|
||||
- Server-sent events with various message types:
|
||||
- `message_chunk`: Raw message tokens
|
||||
- `tool_calls`: Tool invocations
|
||||
- `tool_call_chunks`: Partial tool call data
|
||||
- `tool_call_result`: Result of a tool call
|
||||
- `interrupt`: Plan interruption for user feedback
|
||||
- `error`: Error messages
|
||||
|
||||
**Example Response (Server-Sent Events):**
|
||||
```
|
||||
event: message_chunk
|
||||
data: {"thread_id":"abc123","agent":"researcher","role":"assistant","content":"I'll search for information about quantum computing..."}
|
||||
|
||||
event: tool_calls
|
||||
data: {"thread_id":"abc123","agent":"researcher","tool_calls":[{"name":"web_search","args":"{\"query\":\"quantum computing\"}"}]}
|
||||
|
||||
event: tool_call_result
|
||||
data: {"thread_id":"abc123","agent":"researcher","content":"Found 10 results about quantum computing"}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `403`: MCP server configuration is disabled
|
||||
- `500`: Internal server error during graph execution
|
||||
|
||||
---
|
||||
|
||||
### Text-to-Speech
|
||||
|
||||
#### `POST /api/tts`
|
||||
|
||||
Converts text to speech using Volcengine TTS API.
|
||||
|
||||
**Requirements:**
|
||||
- Environment variables must be set:
|
||||
- `VOLCENGINE_TTS_APPID`
|
||||
- `VOLCENGINE_TTS_ACCESS_TOKEN`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is a test",
|
||||
"encoding": "mp3",
|
||||
"voice_type": "BV700_V2_streaming",
|
||||
"speed_ratio": 1.0,
|
||||
"volume_ratio": 1.0,
|
||||
"pitch_ratio": 1.0,
|
||||
"text_type": "plain",
|
||||
"with_frontend": 1,
|
||||
"frontend_type": "unitTson"
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `text` (required, string, max 1024 chars): Text to convert to speech
|
||||
- `encoding` (optional, string, default: `"mp3"`): Audio format - `mp3`, `wav`
|
||||
- `voice_type` (optional, string, default: `"BV700_V2_streaming"`): Voice type
|
||||
- `speed_ratio` (optional, float, default: 1.0): Speech speed ratio
|
||||
- `volume_ratio` (optional, float, default: 1.0): Speech volume ratio
|
||||
- `pitch_ratio` (optional, float, default: 1.0): Speech pitch ratio
|
||||
- `text_type` (optional, string, default: `"plain"`): `plain` or `ssml`
|
||||
- `with_frontend` (optional, integer, default: 1): Enable frontend processing
|
||||
- `frontend_type` (optional, string, default: `"unitTson"`): Frontend type
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `audio/mp3` or `audio/wav` (depends on encoding)
|
||||
- Binary audio data
|
||||
- Header: `Content-Disposition: attachment; filename=tts_output.{encoding}`
|
||||
|
||||
**Error Responses:**
|
||||
- `400`: Missing required environment variables
|
||||
- `500`: Internal server error during TTS processing
|
||||
|
||||
---
|
||||
|
||||
### Content Generation
|
||||
|
||||
#### `POST /api/podcast/generate`
|
||||
|
||||
Generates an audio podcast from provided text content.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"content": "# Podcast Content\nThis is the content of the podcast..."
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `content` (required, string): Podcast content in text format
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `audio/mp3`
|
||||
- Binary audio data
|
||||
|
||||
**Error Responses:**
|
||||
- `500`: Error during podcast generation
|
||||
|
||||
---
|
||||
|
||||
#### `POST /api/ppt/generate`
|
||||
|
||||
Generates a PowerPoint presentation from provided content.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"content": "# Presentation Title\n## Slide 1\nContent here..."
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `content` (required, string): Content for the presentation
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `application/vnd.openxmlformats-officedocument.presentationml.presentation`
|
||||
- Binary PowerPoint file (.pptx)
|
||||
- Header: `Content-Disposition: attachment; filename=output.pptx`
|
||||
|
||||
**Error Responses:**
|
||||
- `500`: Error during PPT generation
|
||||
|
||||
---
|
||||
|
||||
#### `POST /api/prose/generate`
|
||||
|
||||
Generates prose content with streaming output based on prompt and option.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"prompt": "Write a creative story about",
|
||||
"option": "story",
|
||||
"command": "make it exciting"
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `prompt` (required, string): Content/prompt for prose generation
|
||||
- `option` (required, string): Prose writing option
|
||||
- `command` (optional, string, default: `""`): User custom command
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `text/event-stream`
|
||||
- Server-sent events with prose content chunks
|
||||
|
||||
**Example Response:**
|
||||
```
|
||||
data: "Once upon a time, there was..."
|
||||
|
||||
data: " a mysterious kingdom..."
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `500`: Error during prose generation
|
||||
|
||||
---
|
||||
|
||||
### Prompt Enhancement
|
||||
|
||||
#### `POST /api/prompt/enhance`
|
||||
|
||||
Enhances and refines user prompts with specified report style and context.
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"prompt": "Tell me about climate change",
|
||||
"context": "For a scientific audience",
|
||||
"report_style": "ACADEMIC"
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `prompt` (required, string): Original prompt to enhance
|
||||
- `context` (optional, string, default: `""`): Additional context about intended use
|
||||
- `report_style` (optional, string, default: `"academic"`): Style - `academic`, `popular_science`, `news`, `social_media`, `strategic_investment`
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"result": "Enhanced and refined prompt here..."
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `500`: Error during prompt enhancement
|
||||
|
||||
---
|
||||
|
||||
### MCP Integration
|
||||
|
||||
#### `POST /api/mcp/server/metadata`
|
||||
|
||||
Retrieves metadata and available tools from a Model Context Protocol (MCP) server.
|
||||
|
||||
**Requirements:**
|
||||
- Environment variable: `ENABLE_MCP_SERVER_CONFIGURATION=true`
|
||||
|
||||
**Request Body - For stdio transport:**
|
||||
```json
|
||||
{
|
||||
"transport": "stdio",
|
||||
"command": "python",
|
||||
"args": ["-m", "mcp_server"],
|
||||
"env": {
|
||||
"VAR_NAME": "value"
|
||||
},
|
||||
"timeout_seconds": 300
|
||||
}
|
||||
```
|
||||
|
||||
**Request Body - For SSE transport:**
|
||||
```json
|
||||
{
|
||||
"transport": "sse",
|
||||
"url": "https://mcp-server.example.com",
|
||||
"headers": {
|
||||
"Authorization": "Bearer token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `transport` (required, string): `stdio`, `sse`, or `streamable_http`
|
||||
- `command` (optional, string): Command to execute (stdio type)
|
||||
- `args` (optional, array[string]): Command arguments (stdio type)
|
||||
- `url` (optional, string): Server URL (sse/streamable_http type)
|
||||
- `env` (optional, object): Environment variables (stdio type)
|
||||
- `headers` (optional, object): HTTP headers (sse/streamable_http type)
|
||||
- `timeout_seconds` (optional, integer): Custom timeout in seconds (default: 300)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"transport": "stdio",
|
||||
"command": "python",
|
||||
"args": ["-m", "mcp_server"],
|
||||
"tools": [
|
||||
{
|
||||
"name": "tool_1",
|
||||
"description": "Description of tool",
|
||||
"parameters": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- `403`: MCP server configuration is disabled
|
||||
- `500`: Error retrieving MCP server metadata
|
||||
|
||||
---
|
||||
|
||||
### RAG Configuration
|
||||
|
||||
#### `GET /api/rag/config`
|
||||
|
||||
Returns the current RAG (Retrieval-Augmented Generation) provider configuration.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"provider": "ragflow"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- None (always returns 200)
|
||||
|
||||
---
|
||||
|
||||
#### `GET /api/rag/resources`
|
||||
|
||||
Retrieves available resources from the RAG system based on optional query.
|
||||
|
||||
**Query Parameters:**
|
||||
- `query` (optional, string): Search query for resources
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"resources": [
|
||||
{
|
||||
"id": "resource_1",
|
||||
"name": "Document",
|
||||
"type": "pdf"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- None (returns empty resources array if retriever not configured)
|
||||
|
||||
---
|
||||
|
||||
### Server Configuration
|
||||
|
||||
#### `GET /api/config`
|
||||
|
||||
Returns the complete server configuration including RAG settings and available models.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"rag": {
|
||||
"provider": "ragflow"
|
||||
},
|
||||
"models": {
|
||||
"llm": ["gpt-4", "gpt-3.5-turbo"],
|
||||
"embedding": ["openai-embedding"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
- None (always returns 200)
|
||||
|
||||
---
|
||||
|
||||
## Data Structures
|
||||
|
||||
### ChatMessage
|
||||
```json
|
||||
{
|
||||
"role": "user or assistant",
|
||||
"content": "string or array of ContentItem"
|
||||
}
|
||||
```
|
||||
|
||||
### ContentItem
|
||||
```json
|
||||
{
|
||||
"type": "text or image",
|
||||
"text": "string (for text type)",
|
||||
"image_url": "string (for image type)"
|
||||
}
|
||||
```
|
||||
|
||||
### ReportStyle Enum
|
||||
- `ACADEMIC`
|
||||
- `POPULAR_SCIENCE`
|
||||
- `NEWS`
|
||||
- `SOCIAL_MEDIA`
|
||||
- `STRATEGIC_INVESTMENT`
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
All endpoints follow standard HTTP status codes:
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| 200 | Success |
|
||||
| 400 | Bad Request - Invalid parameters |
|
||||
| 403 | Forbidden - Feature disabled or unauthorized |
|
||||
| 500 | Internal Server Error |
|
||||
|
||||
Error response format:
|
||||
```json
|
||||
{
|
||||
"detail": "Error message describing what went wrong"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
Several endpoints return streaming responses using Server-Sent Events (SSE):
|
||||
|
||||
- `/api/chat/stream` - Chat streaming
|
||||
- `/api/prose/generate` - Prose generation streaming
|
||||
|
||||
To consume SSE in your client:
|
||||
```javascript
|
||||
const eventSource = new EventSource('/api/chat/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({...})
|
||||
});
|
||||
|
||||
eventSource.addEventListener('message_chunk', (event) => {
|
||||
console.log(event.data);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limiting & Quotas
|
||||
|
||||
Currently no rate limiting is implemented. The system respects the following limits:
|
||||
- TTS text input: max 1024 characters
|
||||
- Search results: configurable via `max_search_results`
|
||||
- Plan iterations: configurable via `max_plan_iterations`
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Key environment variables for API configuration:
|
||||
|
||||
```bash
|
||||
# CORS
|
||||
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
|
||||
|
||||
# TTS
|
||||
VOLCENGINE_TTS_APPID=your_app_id
|
||||
VOLCENGINE_TTS_ACCESS_TOKEN=your_access_token
|
||||
VOLCENGINE_TTS_CLUSTER=volcano_tts
|
||||
VOLCENGINE_TTS_VOICE_TYPE=BV700_V2_streaming
|
||||
|
||||
# MCP
|
||||
ENABLE_MCP_SERVER_CONFIGURATION=false
|
||||
|
||||
# Checkpointing
|
||||
LANGGRAPH_CHECKPOINT_SAVER=false
|
||||
LANGGRAPH_CHECKPOINT_DB_URL=postgresql://user:pass@localhost/db
|
||||
|
||||
# RAG
|
||||
RAG_PROVIDER=ragflow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Chat with Research
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/chat/stream \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [{"role": "user", "content": "What are the latest AI trends?"}],
|
||||
"thread_id": "conversation_1",
|
||||
"max_search_results": 5,
|
||||
"report_style": "POPULAR_SCIENCE"
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 2: Text-to-Speech
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/tts \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "Hello world",
|
||||
"encoding": "mp3",
|
||||
"speed_ratio": 1.2
|
||||
}' \
|
||||
--output audio.mp3
|
||||
```
|
||||
|
||||
### Example 3: Enhance Prompt
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/api/prompt/enhance \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"prompt": "Tell me about space",
|
||||
"context": "For kids aged 8-10",
|
||||
"report_style": "POPULAR_SCIENCE"
|
||||
}'
|
||||
```
|
||||
|
||||
### Example 4: Get Server Configuration
|
||||
```bash
|
||||
curl -X GET http://localhost:8000/api/config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 0.1.0
|
||||
- Initial API release
|
||||
- Chat streaming with research capabilities
|
||||
- Text-to-speech conversion
|
||||
- Content generation (podcasts, presentations, prose)
|
||||
- Prompt enhancement
|
||||
- MCP server integration
|
||||
- RAG configuration and resources
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions about the API, please refer to the project documentation or file an issue in the repository.
|
||||
@@ -1,317 +0,0 @@
|
||||
# Debugging Guide
|
||||
|
||||
This guide helps you debug DeerFlow workflows, view model outputs, and troubleshoot common issues.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Viewing Model Output](#viewing-model-output)
|
||||
- [Debug Logging Configuration](#debug-logging-configuration)
|
||||
- [LangChain Verbose Logging](#langchain-verbose-logging)
|
||||
- [LangSmith Tracing](#langsmith-tracing)
|
||||
- [Docker Compose Debugging](#docker-compose-debugging)
|
||||
- [Common Issues](#common-issues)
|
||||
|
||||
## Viewing Model Output
|
||||
|
||||
When you need to see the complete model output, including tool calls and internal reasoning, you have several options:
|
||||
|
||||
### 1. Enable Debug Logging
|
||||
|
||||
Set `DEBUG=True` in your `.env` file or configuration:
|
||||
|
||||
```bash
|
||||
DEBUG=True
|
||||
```
|
||||
|
||||
This enables debug-level logging throughout the application, showing detailed information about:
|
||||
- System prompts sent to LLMs
|
||||
- Model responses
|
||||
- Tool calls and results
|
||||
- Workflow state transitions
|
||||
|
||||
### 2. Enable LangChain Verbose Logging
|
||||
|
||||
Add these environment variables to your `.env` file for detailed LangChain output:
|
||||
|
||||
```bash
|
||||
# Enable verbose logging for LangChain
|
||||
LANGCHAIN_VERBOSE=true
|
||||
LANGCHAIN_DEBUG=true
|
||||
```
|
||||
|
||||
This will show:
|
||||
- Chain execution steps
|
||||
- LLM input/output for each call
|
||||
- Tool invocations
|
||||
- Intermediate results
|
||||
|
||||
### 3. Enable LangSmith Tracing (Recommended for Production)
|
||||
|
||||
For advanced debugging and visualization, configure LangSmith integration:
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="your-api-key"
|
||||
LANGSMITH_PROJECT="your-project-name"
|
||||
```
|
||||
|
||||
LangSmith provides:
|
||||
- Visual trace of workflow execution
|
||||
- Performance metrics
|
||||
- Token usage statistics
|
||||
- Error tracking
|
||||
- Comparison between runs
|
||||
|
||||
To get started with LangSmith:
|
||||
1. Sign up at [LangSmith](https://smith.langchain.com/)
|
||||
2. Create a project
|
||||
3. Copy your API key
|
||||
4. Add the configuration to your `.env` file
|
||||
|
||||
## Debug Logging Configuration
|
||||
|
||||
### Log Levels
|
||||
|
||||
DeerFlow uses Python's standard logging levels:
|
||||
|
||||
- **DEBUG**: Detailed diagnostic information
|
||||
- **INFO**: General informational messages
|
||||
- **WARNING**: Warning messages
|
||||
- **ERROR**: Error messages
|
||||
- **CRITICAL**: Critical errors
|
||||
|
||||
### Viewing Logs
|
||||
|
||||
**Development mode (console):**
|
||||
```bash
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
Logs will be printed to the console.
|
||||
|
||||
**Docker Compose:**
|
||||
```bash
|
||||
# View logs from all services
|
||||
docker compose logs -f
|
||||
|
||||
# View logs from backend only
|
||||
docker compose logs -f backend
|
||||
|
||||
# View logs with timestamps
|
||||
docker compose logs -f --timestamps
|
||||
```
|
||||
|
||||
## LangChain Verbose Logging
|
||||
|
||||
### What It Shows
|
||||
|
||||
When `LANGCHAIN_VERBOSE=true` is enabled, you'll see output like:
|
||||
|
||||
```
|
||||
> Entering new AgentExecutor chain...
|
||||
Thought: I need to search for information about quantum computing
|
||||
Action: web_search
|
||||
Action Input: "quantum computing basics 2024"
|
||||
|
||||
Observation: [Search results...]
|
||||
|
||||
Thought: I now have enough information to answer
|
||||
Final Answer: ...
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
```bash
|
||||
# Basic verbose mode
|
||||
LANGCHAIN_VERBOSE=true
|
||||
|
||||
# Full debug mode with internal details
|
||||
LANGCHAIN_DEBUG=true
|
||||
|
||||
# Both (recommended for debugging)
|
||||
LANGCHAIN_VERBOSE=true
|
||||
LANGCHAIN_DEBUG=true
|
||||
```
|
||||
|
||||
## LangSmith Tracing
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Create a LangSmith account**: Visit [smith.langchain.com](https://smith.langchain.com)
|
||||
|
||||
2. **Get your API key**: Navigate to Settings → API Keys
|
||||
|
||||
3. **Configure environment variables**:
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
|
||||
LANGSMITH_API_KEY="lsv2_pt_..."
|
||||
LANGSMITH_PROJECT="deerflow-debug"
|
||||
```
|
||||
|
||||
4. **Restart your application**
|
||||
|
||||
### Features
|
||||
|
||||
- **Visual traces**: See the entire workflow execution as a graph
|
||||
- **Performance metrics**: Identify slow operations
|
||||
- **Token tracking**: Monitor LLM token usage
|
||||
- **Error analysis**: Quickly identify failures
|
||||
- **Comparison**: Compare different runs side-by-side
|
||||
|
||||
### Viewing Traces
|
||||
|
||||
1. Run your workflow as normal
|
||||
2. Visit [smith.langchain.com](https://smith.langchain.com)
|
||||
3. Select your project
|
||||
4. View traces in the "Traces" tab
|
||||
|
||||
## Docker Compose Debugging
|
||||
|
||||
### Update docker-compose.yml
|
||||
|
||||
Add debug environment variables to your `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
# Debug settings
|
||||
- DEBUG=True
|
||||
- LANGCHAIN_VERBOSE=true
|
||||
- LANGCHAIN_DEBUG=true
|
||||
|
||||
# LangSmith (optional)
|
||||
- LANGSMITH_TRACING=true
|
||||
- LANGSMITH_ENDPOINT=https://api.smith.langchain.com
|
||||
- LANGSMITH_API_KEY=${LANGSMITH_API_KEY}
|
||||
- LANGSMITH_PROJECT=${LANGSMITH_PROJECT}
|
||||
```
|
||||
|
||||
### View Detailed Logs
|
||||
|
||||
```bash
|
||||
# Start with verbose output
|
||||
docker compose up
|
||||
|
||||
# Or in detached mode and follow logs
|
||||
docker compose up -d
|
||||
docker compose logs -f backend
|
||||
```
|
||||
|
||||
### Common Docker Commands
|
||||
|
||||
```bash
|
||||
# View logs from last 100 lines
|
||||
docker compose logs --tail=100 backend
|
||||
|
||||
# View logs with timestamps
|
||||
docker compose logs -f --timestamps
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
|
||||
# Restart services
|
||||
docker compose restart backend
|
||||
```
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: "Log information doesn't show complete content"
|
||||
|
||||
**Solution**: Enable debug logging as described above:
|
||||
```bash
|
||||
DEBUG=True
|
||||
LANGCHAIN_VERBOSE=true
|
||||
LANGCHAIN_DEBUG=true
|
||||
```
|
||||
|
||||
### Issue: "Can't see system prompts"
|
||||
|
||||
**Solution**: Debug logging will show system prompts. Look for log entries like:
|
||||
```
|
||||
[INFO] System Prompt:
|
||||
You are DeerFlow, a friendly AI assistant...
|
||||
```
|
||||
|
||||
### Issue: "Want to see token usage"
|
||||
|
||||
**Solution**: Enable LangSmith tracing or check model responses in verbose mode:
|
||||
```bash
|
||||
LANGCHAIN_VERBOSE=true
|
||||
```
|
||||
|
||||
### Issue: "Need to debug specific nodes"
|
||||
|
||||
**Solution**: Add custom logging in specific nodes. For example, in `src/graph/nodes.py`:
|
||||
```python
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def my_node(state, config):
|
||||
logger.debug(f"Node input: {state}")
|
||||
# ... your code ...
|
||||
logger.debug(f"Node output: {result}")
|
||||
return result
|
||||
```
|
||||
|
||||
### Issue: "Logs are too verbose"
|
||||
|
||||
**Solution**: Adjust log level for specific modules:
|
||||
```python
|
||||
# In your code
|
||||
logging.getLogger('langchain').setLevel(logging.WARNING)
|
||||
logging.getLogger('openai').setLevel(logging.WARNING)
|
||||
```
|
||||
|
||||
## Performance Debugging
|
||||
|
||||
### Measure Execution Time
|
||||
|
||||
Enable LangSmith or add timing logs:
|
||||
|
||||
```python
|
||||
import time
|
||||
start = time.time()
|
||||
result = some_function()
|
||||
logger.info(f"Execution time: {time.time() - start:.2f}s")
|
||||
```
|
||||
|
||||
### Monitor Token Usage
|
||||
|
||||
With LangSmith enabled, token usage is automatically tracked. Alternatively, check model responses:
|
||||
|
||||
```bash
|
||||
LANGCHAIN_VERBOSE=true
|
||||
```
|
||||
|
||||
Look for output like:
|
||||
```
|
||||
Tokens Used: 150
|
||||
Prompt Tokens: 100
|
||||
Completion Tokens: 50
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [LangSmith Documentation](https://docs.smith.langchain.com/)
|
||||
- [LangGraph Debugging](https://langchain-ai.github.io/langgraph/how-tos/debugging/)
|
||||
- [Configuration Guide](./configuration_guide.md)
|
||||
- [API Documentation](./API.md)
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you're still experiencing issues:
|
||||
|
||||
1. Check existing [GitHub Issues](https://github.com/bytedance/deer-flow/issues)
|
||||
2. Enable debug logging and LangSmith tracing
|
||||
3. Collect relevant log output
|
||||
4. Create a new issue with:
|
||||
- Description of the problem
|
||||
- Steps to reproduce
|
||||
- Log output
|
||||
- Configuration (without sensitive data)
|
||||
94
docs/FAQ.md
94
docs/FAQ.md
@@ -1,94 +0,0 @@
|
||||
# FAQ
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Where's the name DeerFlow come from?](#wheres-the-name-deerflow-come-from)
|
||||
- [Which models does DeerFlow support?](#which-models-does-deerflow-support)
|
||||
- [How do I view complete model output?](#how-do-i-view-complete-model-output)
|
||||
- [How do I enable debug logging?](#how-do-i-enable-debug-logging)
|
||||
- [How do I troubleshoot issues?](#how-do-i-troubleshoot-issues)
|
||||
|
||||
## Where's the name DeerFlow come from?
|
||||
|
||||
DeerFlow is short for **D**eep **E**xploration and **E**fficient **R**esearch **Flow**. It is named after the deer, which is a symbol of gentleness and elegance. We hope DeerFlow can bring a gentle and elegant deep research experience to you.
|
||||
|
||||
## Which models does DeerFlow support?
|
||||
|
||||
Please refer to the [Configuration Guide](configuration_guide.md) for more details.
|
||||
|
||||
## How do I view complete model output?
|
||||
|
||||
If you want to see the complete model output, including system prompts, tool calls, and LLM responses:
|
||||
|
||||
1. **Enable debug logging** by setting `DEBUG=True` in your `.env` file
|
||||
|
||||
2. **Enable LangChain verbose logging** by adding these to your `.env`:
|
||||
|
||||
```bash
|
||||
LANGCHAIN_VERBOSE=true
|
||||
LANGCHAIN_DEBUG=true
|
||||
```
|
||||
|
||||
3. **Use LangSmith tracing** for visual debugging (recommended for production):
|
||||
|
||||
```bash
|
||||
LANGSMITH_TRACING=true
|
||||
LANGSMITH_API_KEY="your-api-key"
|
||||
LANGSMITH_PROJECT="your-project-name"
|
||||
```
|
||||
|
||||
For detailed instructions, see the [Debugging Guide](DEBUGGING.md).
|
||||
|
||||
## How do I enable debug logging?
|
||||
|
||||
To enable debug logging:
|
||||
|
||||
1. Open your `.env` file
|
||||
2. Set `DEBUG=True`
|
||||
3. Restart your application
|
||||
|
||||
For Docker Compose:
|
||||
|
||||
```bash
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
For development:
|
||||
|
||||
```bash
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
You'll now see detailed logs including:
|
||||
|
||||
- System prompts sent to LLMs
|
||||
- Model responses
|
||||
- Tool execution details
|
||||
- Workflow state transitions
|
||||
|
||||
See the [Debugging Guide](DEBUGGING.md) for more options.
|
||||
|
||||
## How do I troubleshoot issues?
|
||||
|
||||
When encountering issues:
|
||||
|
||||
1. **Check the logs**: Enable debug logging as described above
|
||||
2. **Review configuration**: Ensure your `.env` and `conf.yaml` are correct
|
||||
3. **Check existing issues**: Search [GitHub Issues](https://github.com/bytedance/deer-flow/issues) for similar problems
|
||||
4. **Enable verbose logging**: Use `LANGCHAIN_VERBOSE=true` for detailed output
|
||||
5. **Use LangSmith**: For visual debugging, enable LangSmith tracing
|
||||
|
||||
For Docker-specific issues:
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
docker compose logs -f
|
||||
|
||||
# Check container status
|
||||
docker compose ps
|
||||
|
||||
# Restart services
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
For more detailed troubleshooting steps, see the [Debugging Guide](DEBUGGING.md).
|
||||
@@ -1,223 +0,0 @@
|
||||
# License Header Management
|
||||
|
||||
This document explains how to manage license headers in the DeerFlow project.
|
||||
|
||||
## License Header Format
|
||||
|
||||
All source files in this project should include license headers.
|
||||
|
||||
### Python Files
|
||||
|
||||
```python
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
```
|
||||
|
||||
For files with a shebang (`#!/usr/bin/env python3`), the header is placed after the shebang:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import something
|
||||
```
|
||||
|
||||
### TypeScript Files
|
||||
|
||||
```typescript
|
||||
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { something } from "somewhere";
|
||||
```
|
||||
|
||||
## Makefile Targets
|
||||
|
||||
### Check License Headers
|
||||
|
||||
Check if all Python and TypeScript files have the required license header:
|
||||
|
||||
```bash
|
||||
# Check all files (Python and TypeScript)
|
||||
make check-license-all
|
||||
|
||||
# Check only Python files
|
||||
make check-license
|
||||
|
||||
# Check only TypeScript files
|
||||
make check-license-ts
|
||||
```
|
||||
|
||||
These commands:
|
||||
- Scan all source files in `src/`, `tests/`, `web/src/`, `web/tests/`, and root-level files
|
||||
- Report files missing the license header
|
||||
- Return exit code 1 if any files are missing headers (useful for CI/CD)
|
||||
- Return exit code 0 if all files have headers
|
||||
|
||||
### Add License Headers
|
||||
|
||||
Automatically add license headers to files that don't have them:
|
||||
|
||||
```bash
|
||||
# Add to all files (Python and TypeScript)
|
||||
make add-license-all
|
||||
|
||||
# Add only to Python files
|
||||
make add-license
|
||||
|
||||
# Add only to TypeScript files
|
||||
make add-license-ts
|
||||
```
|
||||
|
||||
These commands:
|
||||
- Add the appropriate license header to files that don't have it
|
||||
- Preserve shebangs at the top of Python files
|
||||
- Add appropriate spacing after headers
|
||||
- Show vTypeScript files
|
||||
uv run python scripts/license_header.py web/src/components/ --check
|
||||
|
||||
# Check a single file (works for both .py and .ts/.tsx)
|
||||
uv run python scripts/license_header.py src/workflow.py --check
|
||||
uv run python scripts/license_header.py web/src/core/api/chat.ts --check
|
||||
```
|
||||
|
||||
### Script Options
|
||||
|
||||
- `--check`: Check mode - verify headers without modifying files
|
||||
- `--verbose` / `-v`: Show detailed output for each file processed
|
||||
- `paths`: One or more paths (files or directories) to process
|
||||
|
||||
### Supported File Types
|
||||
|
||||
The script automatically detects and processes:
|
||||
- Python files (`.py`)
|
||||
- TypeScript files (`.ts`)
|
||||
- TypeScript React files (`.tsx`)
|
||||
|
||||
## Pre-commit Hook
|
||||
|
||||
The license header check is integrated into the pre-commit hook. Before allowing a commit, it will:
|
||||
|
||||
1. Run linting (`make lint`)
|
||||
2. Run formatting (`make format`)
|
||||
|
||||
This ensures all merged code has proper license headers for both Python and TypeScript fileill be blocked. Run `make add-license-all` to fix.
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
For continuous integration, add the license check to your workflow:
|
||||
|
||||
```bash
|
||||
# In your CI script or GitHub Actions
|
||||
- make check-license `.next` (Next.js build directory)
|
||||
```
|
||||
|
||||
This ensures all merged code has proper license headers.
|
||||
|
||||
## Files Excluded
|
||||
|
||||
The license header tool automatically skips:
|
||||
- `__pycache__` directories
|
||||
- `.pytest_cache`, `.ruff_cache`, `.mypy_cache`
|
||||
- `node_modules`
|
||||
- Virtual environment directories (`.venv`, `venv`, `.tox`)
|
||||
- Build artifacts (`build`, `dist`)
|
||||
- `.git` directory
|
||||
|
||||
## Customization
|
||||
|
||||
### Changing the License Header
|
||||
S` dictionary in `scripts/license_header.py`:
|
||||
|
||||
```python
|
||||
LICENSE_HEADERS: Dict[str, str] = {
|
||||
"python": """# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
""",
|
||||
"typescript": """// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
// SPDX-License-Identifier: MIT
|
||||
""",
|
||||
}
|
||||
```
|
||||
|
||||
### Adding Licenserce header to all files:
|
||||
@uv run python scripts/license_header.py src/ tests/ scripts/ web/src/ web/test
|
||||
1. Add the extension to `FILE_TYPE_MAP` in `scripts/license_header.py`
|
||||
2. Add the corresponding header format to `LICENSE_HEADERS`
|
||||
|
||||
```python
|
||||
FILE_TYPE_MAP = {
|
||||
".py": "python",
|
||||
".ts": "typescript",
|
||||
".tsx": "typescript",
|
||||
".js": "javascript", # Example: adding JavaScript support
|
||||
}
|
||||
|
||||
LICENSE_HEADERS = {
|
||||
# ... existing headers ...
|
||||
"javascript": """// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
// SPDX-License-Identifier: MIT
|
||||
""",
|
||||
}PDX-License-Identifier: MIT
|
||||
"""
|
||||
```
|
||||
-all
|
||||
Checking license headers in all source files...
|
||||
✅ All 289 source file(s) have license headers.
|
||||
```
|
||||
|
||||
### Example 2: Check Only Python and TypeScript Files
|
||||
```bash
|
||||
$ make check-license-all
|
||||
Checking license headers in Python and TypeScript files...
|
||||
❌ 3 file(s) missing license header:
|
||||
- web/src/components/new-component.tsx
|
||||
- web/src/core/api/new-api.ts
|
||||
- web/tests/new-test.test.ts
|
||||
|
||||
Run 'make add-license-all' to add headers.
|
||||
```
|
||||
|
||||
### Example 3: Add Headers to New Module
|
||||
```bash
|
||||
$ make add-license-all
|
||||
Adding license headers to all source files...
|
||||
✅ Added license header to 11 file(s).
|
||||
```
|
||||
|
||||
### Example 4: Check Specific Directory
|
||||
```bash
|
||||
$ uv run python scripts/license_header.py web/src/components/ --check --verbose
|
||||
Header already present: web/src/components/deer-flow/logo.tsx
|
||||
Header already present: web/src/components/deer-flow/markdown.tsx
|
||||
Header already present: web/src/components/editor/index.tsx
|
||||
✅ All 24 sourceooks for exact matches (ignoring leading/trailing whitespace)
|
||||
|
||||
### "Pre-commit hook blocks my commit"
|
||||
- Run `make add-license-all` to add headers to all files
|
||||
- Or disable the check temporarily by editing the `pre-commit` file
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Check All Files
|
||||
```bash
|
||||
$ make check-license-all
|
||||
Checking license headers in Python files...
|
||||
✅ All 156 Python file(s) have license headers.
|
||||
```
|
||||
|
||||
### Example 2: Add Headers to New Module
|
||||
```bash
|
||||
$ make add-license-all
|
||||
Adding license headers to Python files...
|
||||
✅ Added license header to 11 file(s).
|
||||
```
|
||||
|
||||
### Example 3: Check Specific Directory
|
||||
```bash
|
||||
$ uv run python scripts/license_header.py src/agents/ --check --verbose
|
||||
Header already present: src/agents/base.py
|
||||
Header already present: src/agents/coordinator.py
|
||||
✅ All 8 Python file(s) have license headers.
|
||||
```
|
||||
@@ -1,430 +0,0 @@
|
||||
# Configuration Guide
|
||||
|
||||
## Quick Settings
|
||||
|
||||
Copy the `conf.yaml.example` file to `conf.yaml` and modify the configurations to match your specific settings and requirements.
|
||||
|
||||
```bash
|
||||
cd deer-flow
|
||||
cp conf.yaml.example conf.yaml
|
||||
```
|
||||
|
||||
## Which models does DeerFlow support?
|
||||
|
||||
In DeerFlow, we currently only support non-reasoning models. This means models like OpenAI's o1/o3 or DeepSeek's R1 are not supported yet, but we plan to add support for them in the future. Additionally, all Gemma-3 models are currently unsupported due to the lack of tool usage capabilities.
|
||||
|
||||
### Supported Models
|
||||
|
||||
`doubao-1.5-pro-32k-250115`, `gpt-4o`, `qwen-max-latest`,`qwen3-235b-a22b`,`qwen3-coder`, `gemini-2.0-flash`, `deepseek-v3`, and theoretically any other non-reasoning chat models that implement the OpenAI API specification.
|
||||
|
||||
### Local Model Support
|
||||
|
||||
DeerFlow supports local models through OpenAI-compatible APIs:
|
||||
|
||||
- **Ollama**: `http://localhost:11434/v1` (tested and supported for local development)
|
||||
|
||||
See the `conf.yaml.example` file for detailed configuration examples.
|
||||
|
||||
> [!NOTE]
|
||||
> The Deep Research process requires the model to have a **longer context window**, which is not supported by all models.
|
||||
> A work-around is to set the `Max steps of a research plan` to `2` in the settings dialog located on the top right corner of the web page,
|
||||
> or set `max_step_num` to `2` when invoking the API.
|
||||
|
||||
### How to switch models?
|
||||
You can switch the model in use by modifying the `conf.yaml` file in the root directory of the project, using the configuration in the [litellm format](https://docs.litellm.ai/docs/providers/openai_compatible).
|
||||
|
||||
---
|
||||
|
||||
### How to use OpenAI-Compatible models?
|
||||
|
||||
DeerFlow supports integration with OpenAI-Compatible models, which are models that implement the OpenAI API specification. This includes various open-source and commercial models that provide API endpoints compatible with the OpenAI format. You can refer to [litellm OpenAI-Compatible](https://docs.litellm.ai/docs/providers/openai_compatible) for detailed documentation.
|
||||
The following is a configuration example of `conf.yaml` for using OpenAI-Compatible models:
|
||||
|
||||
```yaml
|
||||
# An example of Doubao models served by VolcEngine
|
||||
BASIC_MODEL:
|
||||
base_url: "https://ark.cn-beijing.volces.com/api/v3"
|
||||
model: "doubao-1.5-pro-32k-250115"
|
||||
api_key: YOUR_API_KEY
|
||||
|
||||
# An example of Aliyun models
|
||||
BASIC_MODEL:
|
||||
base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
model: "qwen-max-latest"
|
||||
api_key: YOUR_API_KEY
|
||||
|
||||
# An example of deepseek official models
|
||||
BASIC_MODEL:
|
||||
base_url: "https://api.deepseek.com"
|
||||
model: "deepseek-chat"
|
||||
api_key: YOUR_API_KEY
|
||||
|
||||
# An example of Google Gemini models using OpenAI-Compatible interface
|
||||
BASIC_MODEL:
|
||||
base_url: "https://generativelanguage.googleapis.com/v1beta/openai/"
|
||||
model: "gemini-2.0-flash"
|
||||
api_key: YOUR_API_KEY
|
||||
```
|
||||
The following is a configuration example of `conf.yaml` for using best opensource OpenAI-Compatible models:
|
||||
```yaml
|
||||
# Use latest deepseek-v3 to handle basic tasks, the open source SOTA model for basic tasks
|
||||
BASIC_MODEL:
|
||||
base_url: https://api.deepseek.com
|
||||
model: "deepseek-v3"
|
||||
api_key: YOUR_API_KEY
|
||||
temperature: 0.6
|
||||
top_p: 0.90
|
||||
# Use qwen3-235b-a22b to handle reasoning tasks, the open source SOTA model for reasoning
|
||||
REASONING_MODEL:
|
||||
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
model: "qwen3-235b-a22b-thinking-2507"
|
||||
api_key: YOUR_API_KEY
|
||||
temperature: 0.6
|
||||
top_p: 0.90
|
||||
# Use qwen3-coder-480b-a35b-instruct to handle coding tasks, the open source SOTA model for coding
|
||||
CODE_MODEL:
|
||||
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
model: "qwen3-coder-480b-a35b-instruct"
|
||||
api_key: YOUR_API_KEY
|
||||
temperature: 0.6
|
||||
top_p: 0.90
|
||||
```
|
||||
In addition, you need to set the `AGENT_LLM_MAP` in `src/config/agents.py` to use the correct model for each agent. For example:
|
||||
|
||||
```python
|
||||
# Define agent-LLM mapping
|
||||
AGENT_LLM_MAP: dict[str, LLMType] = {
|
||||
"coordinator": "reasoning",
|
||||
"planner": "reasoning",
|
||||
"researcher": "reasoning",
|
||||
"coder": "basic",
|
||||
"reporter": "basic",
|
||||
"podcast_script_writer": "basic",
|
||||
"ppt_composer": "basic",
|
||||
"prose_writer": "basic",
|
||||
"prompt_enhancer": "basic",
|
||||
}
|
||||
|
||||
|
||||
### How to use Google AI Studio models?
|
||||
|
||||
DeerFlow supports native integration with Google AI Studio (formerly Google Generative AI) API. This provides direct access to Google's Gemini models with their full feature set and optimized performance.
|
||||
|
||||
To use Google AI Studio models, you need to:
|
||||
1. Get your API key from [Google AI Studio](https://aistudio.google.com/app/apikey)
|
||||
2. Set the `platform` field to `"google_aistudio"` in your configuration
|
||||
3. Configure your model and API key
|
||||
|
||||
The following is a configuration example for using Google AI Studio models:
|
||||
|
||||
```yaml
|
||||
# Google AI Studio native API (recommended for Google models)
|
||||
BASIC_MODEL:
|
||||
platform: "google_aistudio"
|
||||
model: "gemini-2.5-flash" # or "gemini-1.5-pro" ,...
|
||||
api_key: YOUR_GOOGLE_API_KEY # Get from https://aistudio.google.com/app/apikey
|
||||
|
||||
```
|
||||
|
||||
**Note:** The `platform: "google_aistudio"` field is required to distinguish from other providers that may offer Gemini models through OpenAI-compatible APIs.
|
||||
```
|
||||
|
||||
### How to use models with self-signed SSL certificates?
|
||||
|
||||
If your LLM server uses self-signed SSL certificates, you can disable SSL certificate verification by adding the `verify_ssl: false` parameter to your model configuration:
|
||||
|
||||
```yaml
|
||||
BASIC_MODEL:
|
||||
base_url: "https://your-llm-server.com/api/v1"
|
||||
model: "your-model-name"
|
||||
api_key: YOUR_API_KEY
|
||||
verify_ssl: false # Disable SSL certificate verification for self-signed certificates
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Disabling SSL certificate verification reduces security and should only be used in development environments or when you trust the LLM server. In production environments, it's recommended to use properly signed SSL certificates.
|
||||
|
||||
### How to use Ollama models?
|
||||
|
||||
DeerFlow supports the integration of Ollama models. You can refer to [litellm Ollama](https://docs.litellm.ai/docs/providers/ollama). <br>
|
||||
The following is a configuration example of `conf.yaml` for using Ollama models(you might need to run the 'ollama serve' first):
|
||||
|
||||
```yaml
|
||||
BASIC_MODEL:
|
||||
model: "model-name" # Model name, which supports the completions API(important), such as: qwen3:8b, mistral-small3.1:24b, qwen2.5:3b
|
||||
base_url: "http://localhost:11434/v1" # Local service address of Ollama, which can be started/viewed via ollama serve
|
||||
api_key: "whatever" # Mandatory, fake api_key with a random string you like :-)
|
||||
```
|
||||
|
||||
### How to use OpenRouter models?
|
||||
|
||||
DeerFlow supports the integration of OpenRouter models. You can refer to [litellm OpenRouter](https://docs.litellm.ai/docs/providers/openrouter). To use OpenRouter models, you need to:
|
||||
1. Obtain the OPENROUTER_API_KEY from OpenRouter (https://openrouter.ai/) and set it in the environment variable.
|
||||
2. Add the `openrouter/` prefix before the model name.
|
||||
3. Configure the correct OpenRouter base URL.
|
||||
|
||||
The following is a configuration example for using OpenRouter models:
|
||||
1. Configure OPENROUTER_API_KEY in the environment variable (such as the `.env` file)
|
||||
```ini
|
||||
OPENROUTER_API_KEY=""
|
||||
```
|
||||
2. Set the model name in `conf.yaml`
|
||||
```yaml
|
||||
BASIC_MODEL:
|
||||
model: "openrouter/google/palm-2-chat-bison"
|
||||
```
|
||||
|
||||
Note: The available models and their exact names may change over time. Please verify the currently available models and their correct identifiers in [OpenRouter's official documentation](https://openrouter.ai/docs).
|
||||
|
||||
|
||||
### How to use Azure OpenAI chat models?
|
||||
|
||||
DeerFlow supports the integration of Azure OpenAI chat models. You can refer to [AzureChatOpenAI](https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.azure.AzureChatOpenAI.html). Configuration example of `conf.yaml`:
|
||||
```yaml
|
||||
BASIC_MODEL:
|
||||
model: "azure/gpt-4o-2024-08-06"
|
||||
azure_endpoint: $AZURE_OPENAI_ENDPOINT
|
||||
api_version: $OPENAI_API_VERSION
|
||||
api_key: $AZURE_OPENAI_API_KEY
|
||||
```
|
||||
|
||||
### How to configure context length for different models
|
||||
|
||||
Different models have different context length limitations. DeerFlow provides a method to control the context length between different models. You can configure the context length between different models in the `conf.yaml` file. For example:
|
||||
```yaml
|
||||
BASIC_MODEL:
|
||||
base_url: https://ark.cn-beijing.volces.com/api/v3
|
||||
model: "doubao-1-5-pro-32k-250115"
|
||||
api_key: ""
|
||||
token_limit: 128000
|
||||
```
|
||||
This means that the context length limit using this model is 128k.
|
||||
|
||||
The context management doesn't work if the token_limit is not set.
|
||||
|
||||
## About Search Engine
|
||||
|
||||
### Supported Search Engines
|
||||
DeerFlow supports the following search engines:
|
||||
- Tavily
|
||||
- InfoQuest
|
||||
- DuckDuckGo
|
||||
- Brave Search
|
||||
- Arxiv
|
||||
- Searx
|
||||
- Serper
|
||||
- Wikipedia
|
||||
|
||||
### How to use Serper Search?
|
||||
|
||||
To use Serper as your search engine, you need to:
|
||||
1. Get your API key from [Serper](https://serper.dev/)
|
||||
2. Set `SEARCH_API=serper` in your `.env` file
|
||||
3. Set `SERPER_API_KEY=your_api_key` in your `.env` file
|
||||
|
||||
### How to control search domains for Tavily?
|
||||
|
||||
DeerFlow allows you to control which domains are included or excluded in Tavily search results through the configuration file. This helps improve search result quality and reduce hallucinations by focusing on trusted sources.
|
||||
|
||||
`Tips`: it only supports Tavily currently.
|
||||
|
||||
You can configure domain filtering and search results in your `conf.yaml` file as follows:
|
||||
|
||||
```yaml
|
||||
SEARCH_ENGINE:
|
||||
engine: tavily
|
||||
# Only include results from these domains (whitelist)
|
||||
include_domains:
|
||||
- trusted-news.com
|
||||
- gov.org
|
||||
- reliable-source.edu
|
||||
# Exclude results from these domains (blacklist)
|
||||
exclude_domains:
|
||||
- unreliable-site.com
|
||||
- spam-domain.net
|
||||
# Include images in search results, default: true
|
||||
include_images: false
|
||||
# Include image descriptions in search results, default: true
|
||||
include_image_descriptions: false
|
||||
# Include raw content in search results, default: true
|
||||
include_raw_content: false
|
||||
```
|
||||
|
||||
### How to post-process Tavily search results
|
||||
|
||||
DeerFlow can post-process Tavily search results:
|
||||
* Remove duplicate content
|
||||
* Filter low-quality content: Filter out results with low relevance scores
|
||||
* Clear base64 encoded images
|
||||
* Length truncation: Truncate each search result according to the user-configured length
|
||||
|
||||
The filtering of low-quality content and length truncation depend on user configuration, providing two configurable parameters:
|
||||
* min_score_threshold: Minimum relevance score threshold, search results below this threshold will be filtered. If not set, no filtering will be performed;
|
||||
* max_content_length_per_page: Maximum length limit for each search result content, parts exceeding this length will be truncated. If not set, no truncation will be performed;
|
||||
|
||||
These two parameters can be configured in `conf.yaml` as shown below:
|
||||
```yaml
|
||||
SEARCH_ENGINE:
|
||||
engine: tavily
|
||||
include_images: true
|
||||
min_score_threshold: 0.4
|
||||
max_content_length_per_page: 5000
|
||||
```
|
||||
That's meaning that the search results will be filtered based on the minimum relevance score threshold and truncated to the maximum length limit for each search result content.
|
||||
|
||||
## Web Search Toggle
|
||||
|
||||
DeerFlow allows you to disable web search functionality, which is useful for environments without internet access or when you want to use only local RAG knowledge bases.
|
||||
|
||||
### Configuration
|
||||
|
||||
You can disable web search in your `conf.yaml` file:
|
||||
|
||||
```yaml
|
||||
# Disable web search (use only local RAG)
|
||||
ENABLE_WEB_SEARCH: false
|
||||
```
|
||||
|
||||
Or via API request parameter:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Research topic"}],
|
||||
"enable_web_search": false
|
||||
}
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> If you disable web search, make sure to configure local RAG resources; otherwise, the researcher will operate in pure LLM reasoning mode without external data sources.
|
||||
|
||||
### Behavior When Web Search is Disabled
|
||||
|
||||
- **Background investigation**: Skipped entirely (relies on web search)
|
||||
- **Researcher node**: Will use only RAG retriever tools if configured
|
||||
- **Pure reasoning mode**: If no RAG resources are available, the researcher will rely solely on LLM reasoning
|
||||
|
||||
---
|
||||
|
||||
## Recursion Fallback Configuration
|
||||
|
||||
When agents hit the recursion limit, DeerFlow can gracefully generate a summary of accumulated findings instead of failing (enabled by default).
|
||||
|
||||
### Configuration
|
||||
|
||||
In `conf.yaml`:
|
||||
```yaml
|
||||
ENABLE_RECURSION_FALLBACK: true
|
||||
```
|
||||
|
||||
### Recursion Limit
|
||||
|
||||
Set the maximum recursion limit via environment variable:
|
||||
```bash
|
||||
export AGENT_RECURSION_LIMIT=50 # default: 25
|
||||
```
|
||||
|
||||
Or in `.env`:
|
||||
```ini
|
||||
AGENT_RECURSION_LIMIT=50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RAG (Retrieval-Augmented Generation) Configuration
|
||||
|
||||
DeerFlow supports multiple RAG providers for document retrieval. Configure the RAG provider by setting environment variables.
|
||||
|
||||
### Supported RAG Providers
|
||||
|
||||
- **RAGFlow**: Document retrieval using RAGFlow API
|
||||
- **VikingDB Knowledge Base**: ByteDance's VikingDB knowledge base service
|
||||
- **Milvus**: Open-source vector database for similarity search
|
||||
- **Qdrant**: Open-source vector search engine with cloud and self-hosted options
|
||||
- **MOI**: Hybrid database for enterprise users
|
||||
- **Dify**: AI application platform with RAG capabilities
|
||||
|
||||
### Qdrant Configuration
|
||||
|
||||
To use Qdrant as your RAG provider, set the following environment variables:
|
||||
|
||||
```bash
|
||||
# RAG_PROVIDER: qdrant (using Qdrant Cloud or self-hosted)
|
||||
RAG_PROVIDER=qdrant
|
||||
QDRANT_LOCATION=https://xyz-example.eu-central.aws.cloud.qdrant.io:6333
|
||||
QDRANT_API_KEY=<your_qdrant_api_key>
|
||||
QDRANT_COLLECTION=documents
|
||||
QDRANT_EMBEDDING_PROVIDER=openai # support openai, dashscope
|
||||
QDRANT_EMBEDDING_BASE_URL=
|
||||
QDRANT_EMBEDDING_MODEL=text-embedding-ada-002
|
||||
QDRANT_EMBEDDING_API_KEY=<your_embedding_api_key>
|
||||
QDRANT_AUTO_LOAD_EXAMPLES=true # automatically load example markdown files
|
||||
```
|
||||
|
||||
### Milvus Configuration
|
||||
|
||||
To use Milvus as your RAG provider, set the following environment variables:
|
||||
|
||||
```bash
|
||||
# RAG_PROVIDER: milvus (using free milvus instance on zilliz cloud: https://docs.zilliz.com/docs/quick-start )
|
||||
RAG_PROVIDER=milvus
|
||||
MILVUS_URI=<endpoint_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
MILVUS_USER=<username_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
MILVUS_PASSWORD=<password_of_self_hosted_milvus_or_zilliz_cloud>
|
||||
MILVUS_COLLECTION=documents
|
||||
MILVUS_EMBEDDING_PROVIDER=openai
|
||||
MILVUS_EMBEDDING_BASE_URL=
|
||||
MILVUS_EMBEDDING_MODEL=
|
||||
MILVUS_EMBEDDING_API_KEY=
|
||||
|
||||
# RAG_PROVIDER: milvus (using milvus lite on Mac or Linux)
|
||||
RAG_PROVIDER=milvus
|
||||
MILVUS_URI=./milvus_demo.db
|
||||
MILVUS_COLLECTION=documents
|
||||
MILVUS_EMBEDDING_PROVIDER=openai
|
||||
MILVUS_EMBEDDING_BASE_URL=
|
||||
MILVUS_EMBEDDING_MODEL=
|
||||
MILVUS_EMBEDDING_API_KEY=
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multi-Turn Clarification (Optional)
|
||||
|
||||
An optional feature that helps clarify vague research questions through conversation. **Disabled by default.**
|
||||
|
||||
### Enable via Command Line
|
||||
|
||||
```bash
|
||||
# Enable clarification for vague questions
|
||||
uv run main.py "Research AI" --enable-clarification
|
||||
|
||||
# Set custom maximum clarification rounds
|
||||
uv run main.py "Research AI" --enable-clarification --max-clarification-rounds 3
|
||||
|
||||
# Interactive mode with clarification
|
||||
uv run main.py --interactive --enable-clarification --max-clarification-rounds 3
|
||||
```
|
||||
|
||||
### Enable via API
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Research AI"}],
|
||||
"enable_clarification": true,
|
||||
"max_clarification_rounds": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Enable via UI Settings
|
||||
|
||||
1. Open DeerFlow web interface
|
||||
2. Navigate to **Settings** → **General** tab
|
||||
3. Find **"Enable Clarification"** toggle
|
||||
4. Turn it **ON** to enable multi-turn clarification. Clarification is **disabled** by default. You need to manually enable it through any of the above methods. When clarification is enabled, you'll see **"Max Clarification Rounds"** field appear below the toggle
|
||||
6. Set the maximum number of clarification rounds (default: 3, minimum: 1)
|
||||
7. Click **Save** to apply changes
|
||||
|
||||
**When enabled**, the Coordinator will ask up to the specified number of clarifying questions for vague topics before starting research, improving report relevance and depth. The `max_clarification_rounds` parameter controls how many rounds of clarification are allowed.
|
||||
|
||||
|
||||
**Note**: The `max_clarification_rounds` parameter only takes effect when `enable_clarification` is set to `true`. If clarification is disabled, this parameter is ignored.
|
||||
@@ -1,298 +0,0 @@
|
||||
# MCP Integrations (Beta)
|
||||
|
||||
This feature is disabled by default. You can enable it by setting the environment variable `ENABLE_MCP_SERVER_CONFIGURATION=true`.
|
||||
|
||||
> [!WARNING]
|
||||
> Please enable this feature only after securing your front-end and back-end in a managed environment.
|
||||
> Otherwise, your system could be compromised.
|
||||
|
||||
## Enabling MCP
|
||||
|
||||
Set the following environment variable in your `.env` file:
|
||||
|
||||
```bash
|
||||
ENABLE_MCP_SERVER_CONFIGURATION=true
|
||||
```
|
||||
|
||||
Then restart the DeerFlow server.
|
||||
|
||||
---
|
||||
|
||||
## MCP Server Examples
|
||||
|
||||
### 1. GitHub Trending
|
||||
|
||||
Fetches trending repositories from GitHub.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"mcp-github-trending": {
|
||||
"transport": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["mcp-github-trending"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available Tools:**
|
||||
- `get_github_trending_repositories` - Get trending repositories by language and time range
|
||||
|
||||
---
|
||||
|
||||
### 2. Filesystem Access
|
||||
|
||||
Provides secure file system access with configurable allowed directories.
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"transport": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"/path/to/allowed/directory"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available Tools:**
|
||||
- `read_text_file` - Read contents of a text file
|
||||
- `read_multiple_files` - Read multiple files at once
|
||||
- `write_file` - Write content to a file
|
||||
- `edit_file` - Edit a file with line-based replacements
|
||||
- `create_directory` - Create a new directory
|
||||
- `list_directory` - List files and directories
|
||||
- `directory_tree` - Get a recursive tree view
|
||||
- `move_file` - Move or rename files
|
||||
- `search_files` - Search for files by pattern
|
||||
- `get_file_info` - Get file metadata
|
||||
|
||||
---
|
||||
|
||||
### 3. Brave Search
|
||||
|
||||
Web search using Brave Search API.
|
||||
|
||||
**Prerequisites:** Get API key from [Brave Search API](https://brave.com/search/api/)
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"brave-search": {
|
||||
"transport": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
|
||||
"env": {
|
||||
"BRAVE_API_KEY": "your-brave-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available Tools:**
|
||||
- `brave_web_search` - Perform web searches
|
||||
- `brave_local_search` - Search for local businesses and places
|
||||
|
||||
---
|
||||
|
||||
### 4. Tavily Search
|
||||
|
||||
AI-optimized search engine for research tasks.
|
||||
|
||||
**Prerequisites:** Get API key from [Tavily](https://tavily.com/)
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"tavily": {
|
||||
"transport": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "tavily-mcp"],
|
||||
"env": {
|
||||
"TAVILY_API_KEY": "tvly-your-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Available Tools:**
|
||||
- `tavily-search` - Perform AI-optimized web search
|
||||
- `tavily-extract` - Extract content from web pages
|
||||
|
||||
---
|
||||
|
||||
## Adding MCP Tools to Agents
|
||||
|
||||
When using MCP tools in DeerFlow, you need to specify:
|
||||
|
||||
1. **`enabled_tools`** - Which tools from the MCP server to enable
|
||||
2. **`add_to_agents`** - Which DeerFlow agents can use these tools (`researcher`, `coder`, or both)
|
||||
|
||||
### Example: Full Configuration for Chat API
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Research the top GitHub trends"}],
|
||||
"mcp_settings": {
|
||||
"servers": {
|
||||
"github-trending": {
|
||||
"transport": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["mcp-github-trending"],
|
||||
"enabled_tools": ["get_github_trending_repositories"],
|
||||
"add_to_agents": ["researcher"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## APIs
|
||||
|
||||
### Get MCP Server Metadata
|
||||
|
||||
**POST /api/mcp/server/metadata**
|
||||
|
||||
Use this endpoint to discover available tools from an MCP server.
|
||||
|
||||
For `stdio` type:
|
||||
```json
|
||||
{
|
||||
"transport": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
|
||||
}
|
||||
```
|
||||
|
||||
For `sse` type:
|
||||
```json
|
||||
{
|
||||
"transport": "sse",
|
||||
"url": "http://localhost:3000/sse",
|
||||
"headers": {
|
||||
"Authorization": "Bearer your-token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For `streamable_http` type:
|
||||
```json
|
||||
{
|
||||
"transport": "streamable_http",
|
||||
"url": "http://localhost:3000/mcp",
|
||||
"headers": {
|
||||
"API_KEY": "your-api-key"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Chat Stream with MCP
|
||||
|
||||
**POST /api/chat/stream**
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Your research query"}],
|
||||
"thread_id": "unique-thread-id",
|
||||
"mcp_settings": {
|
||||
"servers": {
|
||||
"your-mcp-server": {
|
||||
"transport": "stdio",
|
||||
"command": "uvx",
|
||||
"args": ["your-mcp-package"],
|
||||
"env": {
|
||||
"API_KEY": "your-api-key"
|
||||
},
|
||||
"enabled_tools": ["tool1", "tool2"],
|
||||
"add_to_agents": ["researcher"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timeout Configuration
|
||||
|
||||
DeerFlow provides configurable timeout settings for MCP server connections to handle various network conditions and server responsiveness scenarios.
|
||||
|
||||
### Global Default Timeout
|
||||
|
||||
Set the default timeout for all MCP server connections via environment variable:
|
||||
|
||||
```bash
|
||||
# .env file
|
||||
MCP_DEFAULT_TIMEOUT_SECONDS=60
|
||||
```
|
||||
|
||||
**Default value:** 60 seconds
|
||||
|
||||
### Per-Request Timeout Override
|
||||
|
||||
When querying the MCP server metadata API, you can override the default timeout for a specific request:
|
||||
|
||||
**Example: Get MCP Server Metadata with Custom Timeout**
|
||||
|
||||
```json
|
||||
{
|
||||
"transport": "sse",
|
||||
"url": "http://localhost:3000/sse",
|
||||
"headers": {
|
||||
"Authorization": "Bearer your-token"
|
||||
},
|
||||
"timeout_seconds": 45,
|
||||
"sse_read_timeout": 20
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `timeout_seconds` (optional, integer): Overall timeout in seconds for the MCP server connection. Overrides `MCP_DEFAULT_TIMEOUT_SECONDS` environment variable.
|
||||
- `sse_read_timeout` (optional, integer): Timeout in seconds for SSE (Server-Sent Events) streaming read operations. Only applicable for `sse` transport type. When provided, allows fine-grained control over streaming timeouts.
|
||||
|
||||
### Timeout Recommendations
|
||||
|
||||
- **Fast, Local MCP Servers**: 10-15 seconds
|
||||
- **Standard Production Servers**: 30-60 seconds
|
||||
- **Slow or High-Latency Servers**: 60+ seconds (use with caution)
|
||||
|
||||
> [!NOTE]
|
||||
> The `timeout_seconds` parameter is recommended for most use cases. The `sse_read_timeout` parameter should only be used when you need separate control over SSE streaming read operations.
|
||||
|
||||
### Example: Chat API with Custom Timeouts
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Research query"}],
|
||||
"mcp_settings": {
|
||||
"servers": {
|
||||
"my-mcp-server": {
|
||||
"transport": "sse",
|
||||
"url": "http://localhost:3000/sse",
|
||||
"timeout_seconds": 45,
|
||||
"sse_read_timeout": 20,
|
||||
"enabled_tools": ["tool1", "tool2"],
|
||||
"add_to_agents": ["researcher"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [MCP Official Documentation](https://modelcontextprotocol.io/)
|
||||
- [MCP Server Registry](https://github.com/modelcontextprotocol/servers)
|
||||
@@ -1,740 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "DeerFlow API",
|
||||
"description": "API for Deer - Advanced research and content generation system",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "http://localhost:8000",
|
||||
"description": "Local development server"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/api/chat/stream": {
|
||||
"post": {
|
||||
"summary": "Stream chat responses",
|
||||
"description": "Initiates a streaming chat session with the research agent. Returns server-sent events with message chunks, tool calls, and intermediate results.",
|
||||
"tags": ["Chat"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ChatRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful streaming response",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"description": "Server-sent events with various message types"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "MCP server configuration is disabled"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error during graph execution"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/tts": {
|
||||
"post": {
|
||||
"summary": "Convert text to speech",
|
||||
"description": "Converts text to speech using Volcengine TTS API. Requires VOLCENGINE_TTS_APPID and VOLCENGINE_TTS_ACCESS_TOKEN environment variables.",
|
||||
"tags": ["Text-to-Speech"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TTSRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Audio file in requested format",
|
||||
"content": {
|
||||
"audio/mp3": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
},
|
||||
"audio/wav": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Missing required environment variables"
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error during TTS processing"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/podcast/generate": {
|
||||
"post": {
|
||||
"summary": "Generate podcast from content",
|
||||
"description": "Generates an audio podcast from the provided text content",
|
||||
"tags": ["Content Generation"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GeneratePodcastRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Generated podcast audio file",
|
||||
"content": {
|
||||
"audio/mp3": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Error during podcast generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/ppt/generate": {
|
||||
"post": {
|
||||
"summary": "Generate PowerPoint presentation",
|
||||
"description": "Generates a PowerPoint presentation from the provided content",
|
||||
"tags": ["Content Generation"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GeneratePPTRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Generated PowerPoint file",
|
||||
"content": {
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Error during PPT generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/prose/generate": {
|
||||
"post": {
|
||||
"summary": "Generate prose content",
|
||||
"description": "Generates prose content with streaming output based on the provided prompt and option",
|
||||
"tags": ["Content Generation"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GenerateProseRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Streaming prose content",
|
||||
"content": {
|
||||
"text/event-stream": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"description": "Server-sent events with prose content chunks"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Error during prose generation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/prompt/enhance": {
|
||||
"post": {
|
||||
"summary": "Enhance user prompts",
|
||||
"description": "Enhances and refines user prompts with specified report style and context",
|
||||
"tags": ["Prompt"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/EnhancePromptRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Enhanced prompt result",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "string",
|
||||
"description": "The enhanced prompt"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Error during prompt enhancement"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/mcp/server/metadata": {
|
||||
"post": {
|
||||
"summary": "Get MCP server metadata",
|
||||
"description": "Retrieves metadata and available tools from a Model Context Protocol (MCP) server. Requires ENABLE_MCP_SERVER_CONFIGURATION=true.",
|
||||
"tags": ["MCP"],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MCPServerMetadataRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "MCP server metadata and available tools",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MCPServerMetadataResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "MCP server configuration is disabled"
|
||||
},
|
||||
"500": {
|
||||
"description": "Error retrieving MCP server metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/rag/config": {
|
||||
"get": {
|
||||
"summary": "Get RAG configuration",
|
||||
"description": "Returns the current RAG (Retrieval-Augmented Generation) provider configuration",
|
||||
"tags": ["RAG"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "RAG configuration",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RAGConfigResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/rag/resources": {
|
||||
"get": {
|
||||
"summary": "Get RAG resources",
|
||||
"description": "Retrieves available resources from the RAG system based on optional query parameter",
|
||||
"tags": ["RAG"],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"in": "query",
|
||||
"description": "Search query for resources",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "List of RAG resources",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RAGResourcesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/config": {
|
||||
"get": {
|
||||
"summary": "Get server configuration",
|
||||
"description": "Returns the complete server configuration including RAG settings and available models",
|
||||
"tags": ["Configuration"],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Server configuration",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ConfigResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ChatRequest": {
|
||||
"type": "object",
|
||||
"description": "Request for chat streaming endpoint",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ChatMessage"
|
||||
},
|
||||
"description": "History of messages between the user and assistant"
|
||||
},
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Resource"
|
||||
},
|
||||
"description": "Resources to be used for the research"
|
||||
},
|
||||
"debug": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to enable debug logging"
|
||||
},
|
||||
"thread_id": {
|
||||
"type": "string",
|
||||
"default": "__default__",
|
||||
"description": "A specific conversation identifier"
|
||||
},
|
||||
"max_plan_iterations": {
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "The maximum number of plan iterations"
|
||||
},
|
||||
"max_step_num": {
|
||||
"type": "integer",
|
||||
"default": 3,
|
||||
"description": "The maximum number of steps in a plan"
|
||||
},
|
||||
"max_search_results": {
|
||||
"type": "integer",
|
||||
"default": 3,
|
||||
"description": "The maximum number of search results"
|
||||
},
|
||||
"auto_accepted_plan": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to automatically accept the plan"
|
||||
},
|
||||
"interrupt_feedback": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "Interrupt feedback from the user on the plan"
|
||||
},
|
||||
"mcp_settings": {
|
||||
"type": "object",
|
||||
"nullable": true,
|
||||
"description": "MCP settings for the chat request"
|
||||
},
|
||||
"enable_background_investigation": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Whether to get background investigation before plan"
|
||||
},
|
||||
"report_style": {
|
||||
"type": "string",
|
||||
"enum": ["ACADEMIC", "POPULAR_SCIENCE", "NEWS", "SOCIAL_MEDIA", "STRATEGIC_INVESTMENT"],
|
||||
"default": "ACADEMIC",
|
||||
"description": "The style of the report"
|
||||
},
|
||||
"enable_deep_thinking": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Whether to enable deep thinking"
|
||||
},
|
||||
"enable_clarification": {
|
||||
"type": "boolean",
|
||||
"nullable": true,
|
||||
"description": "Whether to enable multi-turn clarification"
|
||||
},
|
||||
"max_clarification_rounds": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "Maximum number of clarification rounds"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChatMessage": {
|
||||
"type": "object",
|
||||
"required": ["role", "content"],
|
||||
"properties": {
|
||||
"role": {
|
||||
"type": "string",
|
||||
"enum": ["user", "assistant"],
|
||||
"description": "The role of the message sender"
|
||||
},
|
||||
"content": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Text content"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ContentItem"
|
||||
},
|
||||
"description": "Multiple content items"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentItem": {
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "The type of content (text, image, etc.)"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The text content if type is 'text'"
|
||||
},
|
||||
"image_url": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The image URL if type is 'image'"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Resource": {
|
||||
"type": "object",
|
||||
"description": "A resource for RAG queries"
|
||||
},
|
||||
"TTSRequest": {
|
||||
"type": "object",
|
||||
"required": ["text"],
|
||||
"properties": {
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "The text to convert to speech (max 1024 characters)"
|
||||
},
|
||||
"voice_type": {
|
||||
"type": "string",
|
||||
"default": "BV700_V2_streaming",
|
||||
"description": "The voice type to use"
|
||||
},
|
||||
"encoding": {
|
||||
"type": "string",
|
||||
"default": "mp3",
|
||||
"enum": ["mp3", "wav"],
|
||||
"description": "The audio encoding format"
|
||||
},
|
||||
"speed_ratio": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": 1.0,
|
||||
"description": "Speech speed ratio"
|
||||
},
|
||||
"volume_ratio": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": 1.0,
|
||||
"description": "Speech volume ratio"
|
||||
},
|
||||
"pitch_ratio": {
|
||||
"type": "number",
|
||||
"format": "float",
|
||||
"default": 1.0,
|
||||
"description": "Speech pitch ratio"
|
||||
},
|
||||
"text_type": {
|
||||
"type": "string",
|
||||
"default": "plain",
|
||||
"enum": ["plain", "ssml"],
|
||||
"description": "Text type"
|
||||
},
|
||||
"with_frontend": {
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
"description": "Whether to use frontend processing"
|
||||
},
|
||||
"frontend_type": {
|
||||
"type": "string",
|
||||
"default": "unitTson",
|
||||
"description": "Frontend type"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GeneratePodcastRequest": {
|
||||
"type": "object",
|
||||
"required": ["content"],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The content of the podcast"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GeneratePPTRequest": {
|
||||
"type": "object",
|
||||
"required": ["content"],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "The content of the PowerPoint presentation"
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenerateProseRequest": {
|
||||
"type": "object",
|
||||
"required": ["prompt", "option"],
|
||||
"properties": {
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The content/prompt of the prose"
|
||||
},
|
||||
"option": {
|
||||
"type": "string",
|
||||
"description": "The option of the prose writer"
|
||||
},
|
||||
"command": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "The user custom command of the prose writer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"EnhancePromptRequest": {
|
||||
"type": "object",
|
||||
"required": ["prompt"],
|
||||
"properties": {
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The original prompt to enhance"
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "Additional context about the intended use"
|
||||
},
|
||||
"report_style": {
|
||||
"type": "string",
|
||||
"default": "academic",
|
||||
"enum": ["academic", "ACADEMIC", "popular_science", "POPULAR_SCIENCE", "news", "NEWS", "social_media", "SOCIAL_MEDIA", "strategic_investment", "STRATEGIC_INVESTMENT"],
|
||||
"description": "The style of the report"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MCPServerMetadataRequest": {
|
||||
"type": "object",
|
||||
"required": ["transport"],
|
||||
"properties": {
|
||||
"transport": {
|
||||
"type": "string",
|
||||
"enum": ["stdio", "sse", "streamable_http"],
|
||||
"description": "The type of MCP server connection"
|
||||
},
|
||||
"command": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The command to execute (for stdio type)"
|
||||
},
|
||||
"args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "Command arguments (for stdio type)"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The URL of the SSE server (for sse type)"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "Environment variables (for stdio type)"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "HTTP headers (for sse/streamable_http type)"
|
||||
},
|
||||
"timeout_seconds": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "Optional custom timeout in seconds"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MCPServerMetadataResponse": {
|
||||
"type": "object",
|
||||
"required": ["transport"],
|
||||
"properties": {
|
||||
"transport": {
|
||||
"type": "string",
|
||||
"description": "The type of MCP server connection"
|
||||
},
|
||||
"command": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The command to execute (for stdio type)"
|
||||
},
|
||||
"args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "Command arguments (for stdio type)"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The URL of the SSE server (for sse type)"
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "Environment variables (for stdio type)"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"nullable": true,
|
||||
"description": "HTTP headers (for sse/streamable_http type)"
|
||||
},
|
||||
"tools": {
|
||||
"type": "array",
|
||||
"description": "Available tools from the MCP server"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RAGConfigResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The provider of the RAG (default: ragflow)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RAGResourceRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The query of the resource to be searched"
|
||||
}
|
||||
}
|
||||
},
|
||||
"RAGResourcesResponse": {
|
||||
"type": "object",
|
||||
"required": ["resources"],
|
||||
"properties": {
|
||||
"resources": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Resource"
|
||||
},
|
||||
"description": "The resources of the RAG"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConfigResponse": {
|
||||
"type": "object",
|
||||
"required": ["rag", "models"],
|
||||
"properties": {
|
||||
"rag": {
|
||||
"$ref": "#/components/schemas/RAGConfigResponse",
|
||||
"description": "The config of the RAG"
|
||||
},
|
||||
"models": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "The configured models"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
# AI Adoption in Healthcare: Influencing Factors
|
||||
|
||||
## Key Points
|
||||
|
||||
- AI technologies like machine learning, deep learning, and NLP are rapidly changing healthcare, offering enhanced accuracy and efficiency.
|
||||
- Data quality, including volume, type, bias, security, and privacy, significantly impacts the reliability and ethical implications of AI applications in healthcare.
|
||||
- Ethical considerations, such as data privacy, algorithmic bias, and transparency, are critical for ensuring fair and equitable AI outcomes in healthcare.
|
||||
- Economic evaluations of AI in healthcare need to be comprehensive, considering initial investments, running costs, and comparisons with traditional methods.
|
||||
- Organizational readiness, including digital skills, structural adaptations, and addressing ethical concerns, is essential for successful AI integration in healthcare.
|
||||
- Healthcare lags behind other industries in AI adoption, necessitating enhanced digital infrastructure and a shift in how healthcare is delivered and accessed.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Artificial Intelligence (AI) is poised to revolutionize healthcare through machine learning, deep learning, and natural language processing. The successful integration of AI in healthcare depends on several factors, including technological maturity, data quality, ethical considerations, economic feasibility, organizational readiness, and digital infrastructure. Addressing these elements is essential for creating trustworthy and effective AI solutions that improve patient outcomes and optimize healthcare delivery.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Technical Maturity and Validation
|
||||
|
||||
AI technologies, particularly machine learning (ML), deep learning (DL), and natural language processing (NLP), are increasingly prevalent in healthcare. Large Language Models (LLMs) leverage deep learning and large datasets to process text-based content. However, the accuracy, reliability, and performance of AI algorithms must be comprehensively tested using diverse datasets to avoid overfitting and ensure proper validation [https://pmc.ncbi.nlm.nih.gov/articles/PMC11047988/].
|
||||
|
||||
### Data Availability and Quality
|
||||
|
||||
Data quality is crucial for the trustworthiness of AI in healthcare [https://www.nature.com/articles/s41746-024-01196-4]. Key considerations include:
|
||||
|
||||
* **Data Volume:** AI applications require large datasets to train effectively.
|
||||
* **Data Type:** AI must handle both structured and unstructured data, including text, images, and sensor readings.
|
||||
* **Data Bias:** Biases in training data can lead to unfair or inaccurate outcomes, raising ethical concerns [https://pmc.ncbi.nlm.nih.gov/articles/PMC10718098/].
|
||||
* **Data Security and Privacy:** Protecting patient data is paramount, especially with increased data volumes. De-identification may not completely eliminate the risk of data linkage [https://pmc.ncbi.nlm.nih.gov/articles/PMC10718098/].
|
||||
|
||||
Sharing inclusive AI algorithms and retraining existing algorithms with local data can address the lack of diversity in openly shared datasets, while preserving patient privacy [https://pmc.ncbi.nlm.nih.gov/articles/PMC8515002/].
|
||||
|
||||
### Ethical Considerations
|
||||
|
||||
Ethical considerations are paramount in the use of AI in healthcare [https://pmc.ncbi.nlm.nih.gov/articles/PMC11249277/]. Key issues include:
|
||||
|
||||
* **Privacy and Data Security:** Ensuring the confidentiality and security of patient data.
|
||||
* **Algorithmic Bias:** Mitigating biases in algorithms to ensure equitable outcomes.
|
||||
* **Transparency:** Making AI decision-making processes understandable.
|
||||
* **Clinical Validation:** Ensuring AI tools are rigorously tested and validated for clinical use.
|
||||
* **Professional Responsibility:** Defining the roles and responsibilities of healthcare professionals when using AI.
|
||||
|
||||
### Economic Costs and Benefits
|
||||
|
||||
Comprehensive cost-benefit analyses of AI in healthcare are needed [https://www.jmir.org/2020/2/e16866/]. These analyses should include:
|
||||
|
||||
* **Initial Investment:** Costs associated with AI technology, infrastructure and software.
|
||||
* **Running Costs:** Ongoing expenses for maintenance, updates, and training.
|
||||
* **Comparison with Alternatives:** Evaluating AI against traditional methods to determine cost-effectiveness [https://pmc.ncbi.nlm.nih.gov/articles/PMC9777836/].
|
||||
* **Potential Savings:** AI can automate administrative tasks and improve diagnostic accuracy, leading to potential cost savings [https://itrexgroup.com/blog/assessing-the-costs-of-implementing-ai-in-healthcare/].
|
||||
|
||||
### Organizational Impact
|
||||
|
||||
AI integration impacts healthcare organizations by:
|
||||
|
||||
* **Assisting Physicians:** AI supports diagnosis and treatment planning [https://pmc.ncbi.nlm.nih.gov/articles/PMC10804900/].
|
||||
* **Improving Efficiency:** AI can expedite patient waiting times and reduce paperwork [https://pmc.ncbi.nlm.nih.gov/articles/PMC10804900/].
|
||||
* **Requiring New Skills:** Organizations need to embed digital and AI skills within their workforce [https://www.mckinsey.com/industries/healthcare/our-insights/transforming-healthcare-with-ai].
|
||||
* **Demanding Cultural Change:** A shift towards innovation, continuous learning, and multidisciplinary working is necessary [https://www.mckinsey.com/industries/healthcare/our-insights/transforming-healthcare-with-ai].
|
||||
|
||||
The AI application management model (AIAMA) can help manage AI implementation from an organizational perspective [https://www.sciencedirect.com/science/article/pii/S0268401223001093].
|
||||
|
||||
### Digital Readiness
|
||||
|
||||
Healthcare's digital transformation through AI depends on:
|
||||
|
||||
* **Data Infrastructure:** Ability to manage and analyze large volumes of patient data [https://www.sciencedirect.com/science/article/abs/pii/B9780443215988000142].
|
||||
* **Technology Adoption:** Addressing challenges through efficiency, accuracy, and patient-centric services [https://optasy.com/blog/revolutionizing-patient-care-rise-ai-and-digital-healthcare].
|
||||
* **Industry Lag:** Healthcare is "below average" in AI adoption compared to other sectors [https://www.weforum.org/stories/2025/03/ai-transforming-global-health/].
|
||||
* **Rethinking Healthcare Delivery:** AI transformation requires rethinking how healthcare is delivered and accessed [https://www.weforum.org/stories/2025/03/ai-transforming-global-health/].
|
||||
|
||||
---
|
||||
|
||||
## Key Citations
|
||||
|
||||
- [AI Technologies in Healthcare](https://bmcmededuc.biomedcentral.com/articles/10.1186/s12909-023-04698-z)
|
||||
|
||||
- [NLP in Healthcare](https://pmc.ncbi.nlm.nih.gov/articles/PMC6616181/)
|
||||
|
||||
- [AI Algorithm Validation](https://pmc.ncbi.nlm.nih.gov/articles/PMC11047988/)
|
||||
|
||||
- [Data Quality for Trustworthy AI](https://www.nature.com/articles/s41746-024-01196-4)
|
||||
|
||||
- [Data Privacy in the Era of AI](https://pmc.ncbi.nlm.nih.gov/articles/PMC10718098/)
|
||||
|
||||
- [Addressing Bias in Big Data and AI](https://pmc.ncbi.nlm.nih.gov/articles/PMC8515002/)
|
||||
|
||||
- [Ethical Considerations in the Use of Artificial Intelligence and ...](https://pmc.ncbi.nlm.nih.gov/articles/PMC11249277/)
|
||||
|
||||
- [The Economic Impact of Artificial Intelligence in Health Care](https://www.jmir.org/2020/2/e16866/)
|
||||
|
||||
- [Economics of Artificial Intelligence in Healthcare: Diagnosis vs ...](https://pmc.ncbi.nlm.nih.gov/articles/PMC9777836/)
|
||||
|
||||
- [Assessing the Cost of Implementing AI in Healthcare - ITRex Group](https://itrexgroup.com/blog/assessing-the-costs-of-implementing-ai-in-healthcare/)
|
||||
|
||||
- [Impact of Artificial Intelligence (AI) Technology in Healthcare Sector](https://pmc.ncbi.nlm.nih.gov/articles/PMC10804900/)
|
||||
|
||||
- [Transforming healthcare with AI: The impact on the workforce and ...](https://www.mckinsey.com/industries/healthcare/our-insights/transforming-healthcare-with-ai)
|
||||
|
||||
- [Managing artificial intelligence applications in healthcare: Promoting ...](https://www.sciencedirect.com/science/article/pii/S0268401223001093)
|
||||
|
||||
- [Healthcare digital transformation through the adoption of artificial ...](https://www.sciencedirect.com/science/article/abs/pii/B9780443215988000142)
|
||||
|
||||
- [Revolutionize Patient Care: The Rise of AI and Digital Healthcare](https://optasy.com/blog/revolutionizing-patient-care-rise-ai-and-digital-healthcare)
|
||||
|
||||
- [6 ways AI is transforming healthcare - The World Economic Forum](https://www.weforum.org/stories/2025/03/ai-transforming-global-health/)
|
||||
@@ -1,146 +0,0 @@
|
||||
# Cristiano Ronaldo's Performance Highlights
|
||||
|
||||
## Key Points
|
||||
|
||||
- Cristiano Ronaldo is Portugal's all-time top scorer with **136 goals** and record appearance maker with **219 caps** as of March 23, 2025.
|
||||
- He holds the record for most goals in the UEFA Champions League (140), most international goals (136), and most appearances for a national team (219).
|
||||
- Ronaldo has won the UEFA European Championship (2016) and the UEFA Nations League (2019) with Portugal.
|
||||
- He has scored a record 924 senior career goals for club and country and has made over 1,250 professional career appearances.
|
||||
- Ronaldo has won 5 Ballon d'Or awards, the most for a European player.
|
||||
- He is the current captain of Portugal and has the most caps as captain for the team.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Cristiano Ronaldo dos Santos Aveiro is widely regarded as one of the greatest football players of all time. Throughout his illustrious career, he has achieved remarkable success at both the club and international levels. This report highlights Ronaldo's performance milestones, records, and achievements, showcasing his impact on the sport.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Sporting CP
|
||||
|
||||
Ronaldo began his professional career at Sporting CP, where he quickly gained attention for his skill and potential.
|
||||
|
||||
| Achievement | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------------------------ |
|
||||
| Debut | Made his debut for Sporting CP's first team. |
|
||||
| Manchester United Friendly | Played in a friendly match against Manchester United in 2003, impressing the English side. |
|
||||
| Goals | Scored 5 goals for the club. |
|
||||
|
||||
### Manchester United
|
||||
|
||||
Ronaldo's move to Manchester United marked the beginning of his international stardom.
|
||||
|
||||
| Achievement | Description |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| Debut | Made his debut for Manchester United. |
|
||||
| First Goal | Scored his first goal for the club. |
|
||||
| Best Moments | Showcased exceptional performances and won multiple titles. |
|
||||
|
||||
### Real Madrid
|
||||
|
||||
At Real Madrid, Ronaldo reached new heights, becoming the club's all-time leading goalscorer.
|
||||
|
||||
| Achievement | Description |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Goals Scored | Scored 451 goals in 438 appearances. |
|
||||
| Trophies Won | Won four Champions League titles, three Club World Cups, and two LaLiga titles. |
|
||||
| All-Time Leading Goalscorer | Became the club's all-time leading goalscorer. |
|
||||
|
||||
### Juventus
|
||||
|
||||
Ronaldo continued his success at Juventus, winning Serie A titles and scoring consistently.
|
||||
|
||||
| Achievement | Description |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| Goals Scored | Scored 101 goals in 134 appearances. |
|
||||
| Key Performances | Notable performances include a double against Parma and a hat-trick against Atletico Madrid. |
|
||||
|
||||
### Al Nassr
|
||||
|
||||
Currently playing for Al Nassr, Ronaldo continues to add to his legacy.
|
||||
|
||||
| Achievement | Description |
|
||||
| ---------------- | --------------------------------------------------------------------------- |
|
||||
| AFC Champions League | Featured in matches in the AFC Champions League Elite. |
|
||||
| Other Games | Played in various other games, contributing to the team's performance. |
|
||||
|
||||
### Portugal National Team
|
||||
|
||||
Ronaldo's international career is filled with records and achievements.
|
||||
|
||||
| Achievement | Description |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Total Goals and Appearances | Portugal's record appearance maker with 219 caps and all-time top scorer with 136 goals as of March 23, 2025. |
|
||||
| Goals in Competitive Matches | Scored 114 goals in competitive matches. |
|
||||
| Trophies Won | Won the UEFA European Championship (2016) and the UEFA Nations League (2019). |
|
||||
| Individual Awards | Named the Best Portuguese Player of All Time in 2015. |
|
||||
| Major Tournament Statistics | Most games in the European Championship (30) and all-time leading scorer (14 goals). The only player to score at five different Euros. |
|
||||
| Significant Match Performances | Scored twice against the Republic of Ireland on September 1, 2021, surpassing Ali Daei's record. |
|
||||
| Records Broken | Holds the record for most goals scored in the history of international football, with 136 goals. |
|
||||
| Captaincy and Leadership | Current captain of Portugal with the most caps as captain. |
|
||||
| Other Records and Achievements | Won 5 Ballon d'Or awards, 3 UEFA Men's Player of the Year Awards, and 4 European Golden Shoes; Has won 33 trophies, including 7 league titles and 5 UEFA Champions Leagues |
|
||||
|
||||
---
|
||||
|
||||
## Key Citations
|
||||
|
||||
- [Cristiano Ronaldo Sporting CP Highlights | TikTok](https://www.tiktok.com/@sporting_cp/video/7339169970695228705?lang=en)
|
||||
|
||||
- [Cristiano Ronaldo Sporting Lisbon friendly highlights](https://www.manutd.com/en/videos/detail/cristiano-ronaldo-sporting-lisbon-friendly-highlights)
|
||||
|
||||
- [All 5 Goals For Sporting Lisbon - Cristiano Ronaldo - YouTube](https://www.youtube.com/watch?v=OBV57bqFvrw)
|
||||
|
||||
- [The Best of CR7 at Manchester United: 45 Minutes of Pure Magic ...](https://www.youtube.com/watch?v=Q9_NhdNLyBQ)
|
||||
|
||||
- [Cristiano Ronaldo's performance at Manchester United: 5 best moments at ...](https://www.elfutbolero.us/premier-league/cristiano-ronaldos-performance-at-manchester-united-5-best-moments-at-the-club-20241230-47146.html)
|
||||
|
||||
- [Manchester United Guide: Ronaldo's Career Highlights](https://unitedingratitude.aon.com/manchester-united-guide-ronaldos-career-highlights)
|
||||
|
||||
- [Cristiano Ronaldo's Real Madrid Highlights Gallery - Getty Images](https://www.gettyimages.com/sets/Un9jMk8A2kyZVnrlal2KXg/cristiano-ronaldo's-real-madrid-highlights)
|
||||
|
||||
- [Cristiano Ronaldo | Official Website | Real Madrid C.F.](https://www.realmadrid.com/en-US/the-club/history/football-legends/cristiano-ronaldo-dos-santos-aveiro)
|
||||
|
||||
- [Real Madrid 6 x 0 Espanyol (C. Ronaldo Hat-Trick) La Liga 15/16 ...](https://www.youtube.com/watch?v=RunxuA6wtHk&vl=en)
|
||||
|
||||
- [HIGHLIGHTS: Juventus vs Parma - 2-1 - Cristiano Ronaldo at the double ...](https://www.juventus.com/en/video/highlights-juventus-vs-parma-2-1-cristiano-ronaldo-at-the-double)
|
||||
|
||||
- [HIGHLIGHTS | Juventus 3-0 Atletico Madrid | Ronaldo greatest ... - YouTube](https://www.youtube.com/watch?v=cLfSpFg6Pxg)
|
||||
|
||||
- [REWIND | Cristiano Ronaldo's First for Juve - Juventus.com](https://www.juventus.com/en/news/articles/rewind-cristiano-ronaldo-s-first-for-juve)
|
||||
|
||||
- [Al Nassr (KSA) - Esteghlal FC (IRN) | Highlights ACL Elite™ - YouTube](https://www.youtube.com/watch?v=CUbYX4s-n8A)
|
||||
|
||||
- [Cristiano Ronaldo FINISHED? Al Orobah 2-1 Al Nassr HIGHLIGHTS](https://www.youtube.com/watch?v=FRhpTh0Eauk)
|
||||
|
||||
- [Ronaldo Brace! | Al Nassr (KSA) - Al Wasl FC (UAE) | Highlights](https://www.youtube.com/watch?v=Lyss81RSvBg)
|
||||
|
||||
- [Portugal national football team records and statistics - Wikipedia](https://en.wikipedia.org/wiki/Portugal_national_football_team_records_and_statistics)
|
||||
|
||||
- [The Records Cristiano Ronaldo Holds in Portugal National Team](https://setantasports.com/uncategorized/the-records-cristiano-ronaldo-holds-in-portugal-national-team/)
|
||||
|
||||
- [Cristiano Ronaldo - National team - Transfermarkt](https://www.transfermarkt.us/cristiano-ronaldo/nationalmannschaft/spieler/8198)
|
||||
|
||||
- [Cristiano Ronaldo's 136 international goals: Opposition, when they ...](https://www.uefa.com/uefanationsleague/news/0257-0e001aafb4e9-7c6ad3889ce0-7c6ad3889ce0)
|
||||
|
||||
- [Cristiano Ronaldo: All-time leading scorer in men's international ...](https://www.uefa.com/uefanationsleague/news/026a-1297500e1b34-a17bbbcad258-1000--cristiano-ronaldo-all-time-leading-scorer-in-men-s-interna/)
|
||||
|
||||
- [International Goals and Stats - Messi vs Ronaldo All Time ...](https://www.messivsronaldo.app/international-stats/)
|
||||
|
||||
- [List of career achievements by Cristiano Ronaldo - Wikipedia](https://en.wikipedia.org/wiki/List_of_career_achievements_by_Cristiano_Ronaldo)
|
||||
|
||||
- [Cristiano Ronaldo - Wikipedia](https://en.wikipedia.org/wiki/Cristiano_Ronaldo)
|
||||
|
||||
- [Trophies won by Cristiano Ronaldo 2024 - Statista](https://www.statista.com/statistics/1008294/cristiano-ronaldo-trophy-titles/)
|
||||
|
||||
- [[Statmuse] Cristiano Ronaldo major tournament career: 21 knockout ...](https://www.reddit.com/r/soccer/comments/1dw9r96/statmuse_cristiano_ronaldo_major_tournament/)
|
||||
|
||||
- [Cristiano Ronaldo | Stats | Portugal | UEFA EURO 2024](https://www.uefa.com/euro2024/teams/players/63706--cristiano-ronaldo/statistics/)
|
||||
|
||||
- [List of Portugal national football team captains - Wikipedia](https://en.wikipedia.org/wiki/List_of_Portugal_national_football_team_captains)
|
||||
|
||||
- [Ronaldo retains Portugal captaincy after Euro 2024](https://punchng.com/ronaldo-retains-portugal-captaincy-after-euro-2024/)
|
||||
|
||||
- [Euro 2024: Cristiano Ronaldo captains Portugal to become the first ...](https://www.marca.com/en/football/uefa-euro/2024/06/18/6671f709e2704ee6288b45b4.html)
|
||||
@@ -1,177 +0,0 @@
|
||||
## Quantum Computing Impact on Cryptography
|
||||
|
||||
### Key Points
|
||||
|
||||
- Quantum computers threaten classical cryptographic algorithms like RSA and ECC due to Shor's algorithm.
|
||||
- AES is vulnerable to Grover's algorithm, albeit to a lesser extent than RSA and ECC. AES-256 is more resistant than AES-128.
|
||||
- Post-quantum cryptography (PQC) aims to develop algorithms resistant to quantum computer attacks.
|
||||
- Quantum Key Distribution (QKD) offers secure key exchange based on quantum mechanics, but faces practical challenges.
|
||||
- NIST is standardizing PQC algorithms, and organizations are exploring hybrid QKD/PQC solutions.
|
||||
- The timeline for significant quantum attacks is uncertain, but proactive measures are necessary.
|
||||
|
||||
---
|
||||
|
||||
### Overview
|
||||
|
||||
Quantum computing poses a significant threat to modern cryptography. Quantum algorithms like Shor's and Grover's can break or weaken widely used encryption methods. This necessitates the development and adoption of quantum-resistant cryptographic solutions. This report provides an overview of the impact of quantum computing on existing cryptographic algorithms and explores potential solutions like post-quantum cryptography (PQC) and Quantum Key Distribution (QKD).
|
||||
|
||||
---
|
||||
|
||||
### Detailed Analysis
|
||||
|
||||
#### Vulnerabilities of Classical Cryptography
|
||||
|
||||
Classical cryptographic algorithms rely on mathematical problems that are difficult for classical computers to solve but are vulnerable to quantum algorithms.
|
||||
|
||||
| Algorithm | Vulnerability | Quantum Algorithm | Impact |
|
||||
| :-------- | :------------ | :---------------- | :------------------------------------------------------------------ |
|
||||
| RSA | Factoring | Shor's Algorithm | Efficient factorization of large numbers, breaking RSA encryption |
|
||||
| ECC | Discrete Log | Shor's Algorithm | Efficiently solves discrete logarithm problems, breaking ECC encryption |
|
||||
| AES | Brute Force | Grover's Algorithm| Reduces the search space, weakening AES security; AES-256 is stronger |
|
||||
|
||||
Shor's algorithm can efficiently factor large numbers, rendering RSA and ECC useless if a sufficiently powerful quantum computer is developed [http://greekcrisis.net/shors-algorithm-quantum-computers/]. Breaking RSA-2048 requires approximately 4000 qubits, and ECC-256 requires about 2500 qubits [https://ej-compute.org/index.php/compute/article/view/146].
|
||||
|
||||
Grover's algorithm reduces the brute-force search space for AES, weakening its security [https://ej-compute.org/index.php/compute/article/view/146]. AES-256 is more secure against quantum attacks than AES-128 or AES-192 [https://crypto.stackexchange.com/questions/6712/is-aes-256-a-post-quantum-secure-cipher-or-not].
|
||||
|
||||
#### Quantum Computational Resources
|
||||
|
||||
Breaking RSA-2048 requires around 4000 qubits and millions of gate operations, potentially achievable within the next decade [https://ej-compute.org/index.php/compute/article/view/146]. A quantum computer breaking RSA-2048 in hours could be built by 2030 for around a billion dollars [https://crypto.stackexchange.com/questions/102671/is-aes-128-quantum-safe]. IBM has a 1121-qubit 'Condor' processor, with leading platforms aiming for two-qubit gate fidelity in the range of 99.9% to 99.99% [https://methodologists.net/Exploring-the-Transformative-Advancements-in-Quantum-Computing-and-Their-Global-Impact-in-2024].
|
||||
|
||||
#### Post-Quantum Cryptography (PQC)
|
||||
|
||||
Post-quantum cryptography (PQC) involves developing cryptographic algorithms that are secure against attacks by both classical and quantum computers [https://en.wikipedia.org/wiki/Post-quantum_cryptography].
|
||||
|
||||
**PQC Algorithm Types**
|
||||
|
||||
| Algorithm Type | Examples | Characteristics |
|
||||
| :------------------- | :---------------------------------------- | :--------------------------------------------------------------------------- |
|
||||
| Lattice-based | CRYSTALS-Kyber, CRYSTALS-Dilithium, NTRU | Based on the hardness of lattice problems |
|
||||
| Multivariate | Rainbow | Based on the difficulty of solving systems of multivariate polynomial equations |
|
||||
| Hash-based | SPHINCS+ | Based on the security of cryptographic hash functions |
|
||||
| Code-based | Classic McEliece | Based on the difficulty of decoding general linear codes |
|
||||
| Isogeny-based | CSIDH | Based on isogenies between supersingular elliptic curves |
|
||||
| Symmetric Key Quantum Resistance | AES and SNOW 3G | Post quantum resistance to known Symmetric Key Quantum resistance attacks |
|
||||
|
||||
PQC algorithms often require larger key sizes compared to pre-quantum algorithms [https://en.wikipedia.org/wiki/Post-quantum_cryptography].
|
||||
|
||||
**NIST Standardization**
|
||||
|
||||
NIST is conducting a Post-Quantum Cryptography Standardization Process to select PQC algorithms [https://en.wikipedia.org/wiki/Post-quantum_cryptography]. NIST has released the first three finalized post-quantum encryption standards: CRYSTALS-Kyber (ML-KEM), CRYSTALS-Dilithium (ML-DSA), and SPHINCS+ [https://www.nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards].
|
||||
|
||||
#### Quantum Key Distribution (QKD)
|
||||
|
||||
QKD offers a method for secure key exchange leveraging the principles of quantum mechanics [https://www.iosrjournals.org/iosr-jce/papers/Vol16-issue2/Version-11/A0162110109.pdf]. Eavesdropping introduces detectable anomalies due to the disturbance of the quantum system [https://en.wikipedia.org/wiki/Quantum_key_distribution].
|
||||
|
||||
**QKD Protocols**
|
||||
|
||||
| Protocol | Description |
|
||||
| :------- | :---------- |
|
||||
| BB84 | First QKD protocol |
|
||||
| E91 | Uses entangled photons |
|
||||
| COW | Coherent One Way |
|
||||
|
||||
Practical challenges include secret key rate, distance, size, cost, and practical security [https://arxiv.org/abs/1606.05853]. The NSA views quantum-resistant cryptography (PQC) as a more cost-effective and easily maintained solution than QKD for securing data in National Security Systems [https://www.nsa.gov/Cybersecurity/Quantum-Key-Distribution-QKD-and-Quantum-Cryptography-QC/].
|
||||
|
||||
#### Hybrid Approaches
|
||||
|
||||
Hybrid security systems integrating PQC and QKD are being explored [https://www.gsma.com/newsroom/wp-content/uploads//IG.18-Hybrid-QKD-and-PQC-security-scenarios-and-use-cases-Whitepaper-v1.0-002.pdf]. Network operators are expected to spend over $6 billion on QKD development and implementation between 2025 and 2030 [https://smartinfrastructuremagazine.com/news/quantum-key-distribution-network-operators-to-spend-6-3-billion-over-next-six-years].
|
||||
|
||||
#### Risk Assessment and Timelines
|
||||
|
||||
Quantum computing advancements are progressing, creating an urgent need to transition to quantum-safe alternatives [https://ej-compute.org/index.php/compute/article/view/146]. Cryptographic vulnerabilities may emerge within the next 5–10 years [https://ej-compute.org/index.php/compute/article/view/146].
|
||||
|
||||
---
|
||||
|
||||
### Key Citations
|
||||
|
||||
- [Implementation of Shor's Algorithm and Its Demonstrated Quantum ... - JSR](https://www.jsr.org/hs/index.php/path/article/view/6348)
|
||||
|
||||
- [Implementation and Analysis of Shor's Algorithm to Break RSA ...](https://www.researchgate.net/publication/377245624_Implementation_and_Analysis_of_Shor's_Algorithm_to_Break_RSA_Cryptosystem_Security)
|
||||
|
||||
- [Quantum AI: Shor's Algorithm - How Quantum Computers Break Cryptography ...](http://greekcrisis.net/shors-algorithm-quantum-computers/)
|
||||
|
||||
- [vulnerability of RSA/ECC to QC : r/cryptography - Reddit](https://www.reddit.com/r/cryptography/comments/1ajubq8/vulnerability_of_rsaecc_to_qc/)
|
||||
|
||||
- [Cyber Security Implications of Quantum Computing: Shor's ...](https://www.academia.edu/127333737/Cyber_Security_Implications_of_Quantum_Computing_Shors_Algorithm_and_Beyond)
|
||||
|
||||
- [The Impact of Quantum Computing on Cryptographic Systems: Urgency of ...](https://ej-compute.org/index.php/compute/article/view/146)
|
||||
|
||||
- [Exploring AES Encryption Implementation Through Quantum Computing ...](https://sciencepublishinggroup.com/article/10.11648/j.ajcst.20240704.12)
|
||||
|
||||
- [CSRC Presentations | CSRC - NIST Computer Security Resource Center](https://csrc.nist.gov/Presentations/2024/practical-cost-of-grover-for-aes-key-recovery)
|
||||
|
||||
- [Is AES-256 a post-quantum secure cipher or not?](https://crypto.stackexchange.com/questions/6712/is-aes-256-a-post-quantum-secure-cipher-or-not)
|
||||
|
||||
- [RSA's demise from quantum attacks is very much ... - Ars Technica](https://arstechnica.com/information-technology/2023/01/fear-not-rsa-encryption-wont-fall-to-quantum-computing-anytime-soon/)
|
||||
|
||||
- [Chinese researchers break RSA encryption with a quantum computer](https://www.csoonline.com/article/3562701/chinese-researchers-break-rsa-encryption-with-a-quantum-computer.html)
|
||||
|
||||
- [Quantum Computing and the Risks to the RSA Algorithm](https://robharrisoneu.substack.com/p/quantum-computing-and-the-risks-to)
|
||||
|
||||
- [Quantum Computing Breakthrough Could Crack ECC Cryptography ...](https://quantumzeitgeist.com/quantum-computing-breakthrough-could-crack-ecc-cryptography-exposing-internet-secrets-claims-psiquantum-researcher/)
|
||||
|
||||
- [Quantum vs. regular computing time to break ECC?](https://crypto.stackexchange.com/questions/35384/quantum-vs-regular-computing-time-to-break-ecc)
|
||||
|
||||
- [Is AES-128 quantum safe? - Cryptography Stack Exchange](https://crypto.stackexchange.com/questions/102671/is-aes-128-quantum-safe)
|
||||
|
||||
- [How many decades AES-128 will last? : r/cryptography - Reddit](https://www.reddit.com/r/cryptography/13lp9nf/how_many_decades_aes128_will_last/)
|
||||
|
||||
- [AES-256 joins the quantum resistance - Fierce Electronics](https://www.fierceelectronics.com/electronics/aes-256-joins-quantum-resistance)
|
||||
|
||||
- [The State of Quantum Computing in 2024: Innovations, Challenges, and ...](https://methodologists.net/Exploring-the-Transformative-Advancements-in-Quantum-Computing-and-Their-Global-Impact-in-2024)
|
||||
|
||||
- [The Current State of Quantum Computing - IEEE Computer Society](https://www.computer.org/publications/tech-news/research/current-state-of-quantum-computing)
|
||||
|
||||
- [The Quantum Hardware Landscape: Competing Architectures](https://quantumzeitgeist.com/quantum-hardware/)
|
||||
|
||||
- [Practical Impacts of Quantum Computing - National Institute of ...](https://www.nist.gov/document/post-quantum-cryptography-and-cybersecurity)
|
||||
|
||||
- [Quantum Threat Timeline Report 2024 - Global Risk Institute](https://globalriskinstitute.org/publication/2024-quantum-threat-timeline-report/)
|
||||
|
||||
- [The quantum threat to blockchain: summary and timeline analysis](https://link.springer.com/article/10.1007/s42484-023-00105-4)
|
||||
|
||||
- [Post-quantum cryptography - Wikipedia](https://en.wikipedia.org/wiki/Post-quantum_cryptography)
|
||||
|
||||
- [First Four Quantum-Resistant Cryptographic Algorithms - Embedded](https://www.embedded.com/first-four-quantum-resistant-cryptographic-algorithms/)
|
||||
|
||||
- [Microsoft's quantum-resistant cryptography is here](https://techcommunity.microsoft.com/blog/microsoft-security-blog/microsofts-quantum-resistant-cryptography-is-here/4238780)
|
||||
|
||||
- [Exploring Elliptic Curve vs. Lattice-Based Cryptography for Future ...](https://medium.com/@RocketMeUpCybersecurity/exploring-elliptic-curve-vs-lattice-based-cryptography-for-future-security-0c8426c97deb)
|
||||
|
||||
- [[PDF] Performance Comparisons and Migration Analyses of Lattice-based ...](https://eprint.iacr.org/2020/990.pdf)
|
||||
|
||||
- [[PDF] A Survey on Code-based Cryptography - arXiv](https://arxiv.org/pdf/2201.07119)
|
||||
|
||||
- [Understanding Lattice-Based Cryptography - Blue Goat Cyber](https://bluegoatcyber.com/blog/understanding-lattice-based-cryptography/)
|
||||
|
||||
- [A Survey of Code-Based Cryptography - Clemson University](https://open.clemson.edu/cgi/viewcontent.cgi?article=5227&context=all_theses)
|
||||
|
||||
- [NIST Releases First 3 Finalized Post-Quantum Encryption Standards](https://www.nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards)
|
||||
|
||||
- [Post-Quantum Cryptography Is a Must to Protect Your Systems | Gartner](https://www.gartner.com/en/articles/post-quantum-cryptography)
|
||||
|
||||
- [Secure Data Infrastructure in a Post-Quantum Cryptographic World](https://futurumgroup.com/research-reports/secure-data-infrastructure-in-a-post-quantum-cryptographic-world/)
|
||||
|
||||
- [NCSC Sets 2035 Deadline for Post-Quantum Cryptography Migration](https://www.infosecurity-magazine.com/news/ncsc-post-quantum-cryptography/)
|
||||
|
||||
- [PQC (Post-Quantum Cryptography): The New Network Security Threat](https://hackhunting.com/2025/01/11/post-quantum-cryptography-the-new-network-security-threat/)
|
||||
|
||||
- [Exploring Post-Quantum Cryptography: Review and Directions for the ...](https://www.mdpi.com/2227-7080/12/12/241)
|
||||
|
||||
- [[PDF] a performance comparison of some hash functions in hash-based ...](https://jomardpublishing.com/UploadFiles/Files/journals/JTME/V5N3/KaratayM_et_al.pdf)
|
||||
|
||||
- [[PDF] Comparative Analysis of Different Cryptographic Hash Functions](http://www.diva-portal.org/smash/get/diva2:1885074/FULLTEXT01.pdf)
|
||||
|
||||
- [Multivariate Cryptography - SpringerLink](https://link.springer.com/referenceworkentry/10.1007/978-3-642-27739-9_421-2)
|
||||
|
||||
- [Quantum Key Distribution Protocols: A Review](https://www.iosrjournals.org/iosr-jce/papers/Vol16-issue2/Version-11/A0162110109.pdf)
|
||||
|
||||
- [Quantum key distribution - Wikipedia](https://en.wikipedia.org/wiki/Quantum_key_distribution)
|
||||
|
||||
- [Practical challenges in quantum key distribution - arXiv.org](https://arxiv.org/abs/1606.05853)
|
||||
|
||||
- [Quantum Key Distribution (QKD) and Quantum Cryptography QC](https://www.nsa.gov/Cybersecurity/Quantum-Key-Distribution-QKD-and-Quantum-Cryptography-QC/)
|
||||
|
||||
- [Hybrid QKD and PQC security scenarios and use cases Whitepaper](https://www.gsma.com/newsroom/wp-content/uploads//IG.18-Hybrid-QKD-and-PQC-security-scenarios-and-use-cases-Whitepaper-v1.0-002.pdf)
|
||||
|
||||
- [Quantum key distribution: Network… | Smart Infrastructure Magazine](https://smartinfrastructuremagazine.com/news/quantum-key-distribution-network-operators-to-spend-6-3-billion-over-next-six-years)
|
||||
@@ -1,45 +0,0 @@
|
||||
## Bitcoin Price Fluctuations in the Recent 3 Months
|
||||
|
||||
### Executive Summary
|
||||
|
||||
This report analyzes Bitcoin price fluctuations over the past three months, based on available search results. The analysis considers market sentiment, regulatory influences, economic factors, and technical analysis indicators. Due to limitations in accessing and processing raw data, the report relies on summarized findings from various sources.
|
||||
|
||||
### Key Findings
|
||||
|
||||
* **Trump Administration Policies:** Tariffs imposed in April 2025 impacted Bitcoin, causing it to fall from $109K to $84K.
|
||||
* **Economic Uncertainty:** General economic uncertainty contributed to Bitcoin falling below $90,000.
|
||||
* **Market Sentiment:** The Crypto Fear and Greed Index reflects the overall market sentiment, which fluctuates based on news and events.
|
||||
* **Technical Analysis:** Key support levels around $80,400 and $74,000, with resistance levels near $98,500 and $106,000.
|
||||
|
||||
### Detailed Analysis
|
||||
|
||||
**Influencing Factors:**
|
||||
|
||||
* **Regulatory Environment:** The Trump administration's approach to crypto regulation and SEC actions appear to have influenced Bitcoin's price.
|
||||
* **Market Sentiment:** The Crypto Fear and Greed Index is a key indicator of market sentiment.
|
||||
* **Trading Volume:** Historical data from Yahoo Finance and Investing.com shows Bitcoin trading volume over the past 3 months.
|
||||
* **Social Media Sentiment:** Sentiment analysis from platforms like the r/cryptocurrency subreddit and Twitter (X) can provide insights into market perceptions.
|
||||
* **GBTC Holdings:** Grayscale Bitcoin Trust (GBTC) historical prices and data reflect its holdings.
|
||||
* **Bitcoin Futures:** Historical data for Bitcoin Futures (BTC=F) is available on Yahoo Finance.
|
||||
* **Google Trends:** Google Trends data indicates the search popularity of "bitcoin" over time. Recent articles suggest a decline in interest in "bitcoin" and "bitcoin price" searches.
|
||||
|
||||
**Price Movements:**
|
||||
|
||||
* Bitcoin experienced a drop from $109K to $84K following Trump's tariffs on April 2, 2025.
|
||||
* Bitcoin fell below $90,000 due to economic uncertainty.
|
||||
* Key support levels to watch are around $80,400 and $74,000, with resistance levels near $98,500 and $106,000.
|
||||
|
||||
### Conclusions and Recommendations
|
||||
|
||||
Based on the available information, Bitcoin's price fluctuations in the last three months have been influenced by a combination of regulatory actions, economic conditions, and market sentiment.
|
||||
|
||||
**Recommendations:**
|
||||
|
||||
* Monitor regulatory developments and their potential impact on the cryptocurrency market.
|
||||
* Track economic indicators and assess their influence on investor behavior.
|
||||
* Analyze market sentiment using tools like the Crypto Fear and Greed Index and social media analysis.
|
||||
* Consider technical analysis indicators to identify potential support and resistance levels.
|
||||
|
||||
**Limitations:**
|
||||
|
||||
This report is based on summarized search results and lacks access to raw data for comprehensive analysis. Further investigation with detailed data analysis is recommended for more accurate conclusions.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Deep Research with Claude: Workflows and Best Practices
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This report outlines optimal workflows and best practices for integrating Claude into deep research processes, covering data collection, preprocessing, analysis, and synthesis. It also addresses integration with other tools, validation methods, cost management, collaboration strategies, documentation practices, and relevant case studies. Claude can assist in academic writing and research and should be used to support, not replace, original thought.
|
||||
|
||||
## Key Findings
|
||||
|
||||
* Claude can assist in academic writing and research, but should be used to support, not replace, original thought.
|
||||
* Claude's Project feature allows uploading relevant documents to reduce repetitive context-setting.
|
||||
* The AI has a data analysis tool that can write and run JavaScript code to process data and offer insights.
|
||||
* Claude offers citation tools for verifying sources and ensuring proper formatting.
|
||||
* Haiku is the fastest and most cost-effective model in its intelligence category.
|
||||
* Claude can serve as a virtual teammate to advance work.
|
||||
* Sharing work products created with Claude can improve innovation in product development and research.
|
||||
* Claude can create technical documentation faster while maintaining consistency.
|
||||
* Claude integrates with note-taking, writing, and reference management tools.
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Workflows and Best Practices
|
||||
|
||||
* **Define Research Questions:** Clearly define research questions and areas of focus in initial prompts.
|
||||
* **Structured Data:** Provide relevant data in a structured message.
|
||||
* **Project Feature:** Use Claude's Project feature to upload relevant documents, reducing the need for repetitive context-setting.
|
||||
* [Source: [https://support.anthropic.com/en/articles/9797557-usage-limit-best-practices](https://support.anthropic.com/en/articles/9797557-usage-limit-best-practices)]
|
||||
* **Prompt Engineering:** Employ prompt engineering techniques, such as including "Think step by step," to improve performance.
|
||||
* [Source: [https://aws.amazon.com/blogs/machine-learning/prompt-engineering-techniques-and-best-practices-learn-by-doing-with-anthropics-claude-3-on-amazon-bedrock/](https://aws.amazon.com/blogs/machine-learning/prompt-engineering-techniques-and-best-practices-learn-by-doing-with-anthropics-claude-3-on-amazon-bedrock/)]
|
||||
|
||||
### Data Analysis
|
||||
|
||||
* **Data Analysis Tool:** Utilize Claude’s built-in data analysis tool, which writes and runs JavaScript code to process data and provide insights.
|
||||
* [Source: [https://www.anthropic.com/news/analysis-tool](https://www.anthropic.com/news/analysis-tool), [https://support.anthropic.com/en/articles/10008684-enabling-and-using-the-analysis-tool](https://support.anthropic.com/en/articles/10008684-enabling-and-using-the-analysis-tool)]
|
||||
* **CSV Analysis:** Use the data analysis tool to analyze and visualize data from uploaded CSV files.
|
||||
* [Source: [https://support.anthropic.com/en/articles/10008684-enabling-and-using-the-analysis-tool](https://support.anthropic.com/en/articles/10008684-enabling-and-using-the-analysis-tool)]
|
||||
|
||||
### Validation
|
||||
|
||||
* **Citation Tools:** Utilize Claude's citation tools to verify sources and ensure correct formatting for academic rigor.
|
||||
* [Source: [https://www.yomu.ai/blog/claude-ai-in-academic-writing-and-research-essential-tips-for-optimal-results](https://www.yomu.ai/blog/claude-ai-in-academic-writing-and-research-essential-tips-for-optimal-results)]
|
||||
* **Prompt Sanitization:** Note that the Anthropic API performs basic prompt sanitization and validation.
|
||||
* [Source: [https://docs.anthropic.com/en/api/prompt-validation](https://docs.anthropic.com/en/api/prompt-validation)]
|
||||
|
||||
### Cost Management
|
||||
|
||||
* **Model Selection:** Consider using the Haiku model for cost-effective performance in its intelligence category.
|
||||
* [Source: [https://www.anthropic.com/news/claude-3-family](https://www.anthropic.com/news/claude-3-family)]
|
||||
|
||||
### Collaboration
|
||||
|
||||
* **Virtual Teammate:** Leverage Claude as a virtual teammate to move work forward.
|
||||
* [Source: [https://www.anthropic.com/team](https://www.anthropic.com/team)]
|
||||
* **Shared Work Products:** Share work products co-created with Claude to foster innovation, particularly in product development and research.
|
||||
* [Source: [https://www.anthropic.com/news/projects](https://www.anthropic.com/news/projects)]
|
||||
|
||||
### Documentation
|
||||
|
||||
* **Technical Documentation:** Use Claude to create technical documentation more efficiently and maintain consistency.
|
||||
* [Source: [https://beginswithai.com/how-to-use-claude-ai-to-create-technical-documentation/](https://beginswithai.com/how-to-use-claude-ai-to-create-technical-documentation/)]
|
||||
|
||||
### Integration with Other Tools
|
||||
|
||||
* **Note-Taking and Writing Tools:** Integrate Claude with note-taking and writing tools such as Evernote, OneNote, or Google Docs.
|
||||
* [Source: [https://beginswithai.com/using-claude-for-research/](https://beginswithai.com/using-claude-for-research/)]
|
||||
* **Reference Management Tools:** Work with reference management tools like Zotero, Mendeley, and EndNote.
|
||||
* [Source: [https://beginswithai.com/using-claude-for-research/](https://beginswithai.com/using-claude-for-research/)]
|
||||
* **Platform Integration:** Ensure smooth integration with platforms like Anthropic API and Google Cloud's Vertex AI.
|
||||
* [Source: [https://www.yomu.ai/blog/claude-ai-in-academic-writing-and-research-essential-tips-for-optimal-results](https://www.yomu.ai/blog/claude-ai-in-academic-writing-and-research-essential-tips-for-optimal-results)]
|
||||
|
||||
### Case Studies
|
||||
|
||||
* **Diverse Applications:** Explore case studies that demonstrate the successful use of Claude in various domains, including whale conservation, brand management, cybersecurity, hiring, insurance, code review, customer service, and sales.
|
||||
* [Source: [https://www.anthropic.com/customers](https://www.anthropic.com/customers)]
|
||||
|
||||
## Conclusions and Recommendations
|
||||
|
||||
Claude is a valuable tool for deep research if used strategically. By defining clear research questions, providing structured data, and utilizing Claude's project features, researchers can maximize its potential. The AI's data analysis capabilities, especially with CSV files, offer real-time insights. Validating Claude's outputs through citation tools and careful prompt engineering is essential for accuracy. Collaboration features enhance teamwork, and integrations with other research tools streamline workflows. The case studies show the broad applicability of Claude across different fields, highlighting its versatility and potential impact.
|
||||
@@ -1,72 +0,0 @@
|
||||
# Nanjing Tangbao: A Culinary Specialty
|
||||
|
||||
## Key Points
|
||||
|
||||
- Nanjing Tangbao are soup-filled dumplings with a rich history and cultural significance in Nanjing, dating back to the Ming and Qing Dynasties.
|
||||
- Key characteristics include a thin, almost translucent skin, a generous amount of rich broth, and a savory filling, often featuring minced pork and flavorful seasonings.
|
||||
- Modern adaptations of Tangbao include variations in size, skin thickness, and filling, with some preferring the Nanjing style over the Shanghai Xiaolongbao.
|
||||
- Numerous restaurants and street food stalls specialize in Tangbao, with Yinshi Jishi Tangbao and Fuzimiao being frequently recommended establishments.
|
||||
- Tourism in Nanjing significantly impacts the popularity and availability of Nanjing Tangbao, contributing to the protection of cultural heritage and the growth of related industries.
|
||||
- Preparation involves making a gelatinous pork stock (aspic) that melts into soup when steamed, and high-quality pork is crucial for authentic taste and texture.
|
||||
|
||||
## Overview
|
||||
|
||||
Nanjing Tangbao, also known as soup dumplings, are a traditional delicacy in Nanjing. They have a long history and are culturally significant to the region. These soup-filled buns are known for their flavorful broth and savory filling encased in a delicate wrapper. The meticulous preparation and unique characteristics contribute to their cultural significance in Nanjing.
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Historical and Cultural Significance
|
||||
|
||||
Nanjing Tangbao has a high reputation dating back to the Ming and Qing Dynasties. Nanjing, as a city with a rich history, features Tangbao as one of its culinary specialties. The rise of tourism in Nanjing promotes the protection of tangible and intangible cultural heritage.
|
||||
|
||||
### Characteristics and Ingredients
|
||||
|
||||
Nanjing Tangbao are characterized by a thin skin and lots of soup. The Nanjing style Tangbao is smaller, with an almost translucent skin and less meat, which has become a preferred style. Key ingredients include minced pork, soy sauce, ginger, garlic, sesame oil, Shaoxing wine, and a gelatinous pork stock (aspic) that creates the soup.
|
||||
|
||||
### Preparation
|
||||
|
||||
The preparation of Nanjing Tangbao involves several key steps: marinating the pork, making the aspic, preparing the dough for the wrappers, filling the dumplings, and steaming them. The sourcing of high-quality pork and the careful preparation of the aspic are crucial for achieving the authentic taste and texture.
|
||||
|
||||
### Modern Adaptations and Variations
|
||||
|
||||
Modern adaptations of Xiao Long Bao are considered by some to be Nanjing Tangbao. Nanjing's Tangbao is slightly different from Shanghai’s Xiaolongbao, being larger with a generous amount of rich broth inside, often served with a straw.
|
||||
|
||||
### Notable Establishments
|
||||
|
||||
Several establishments in Nanjing are renowned for their Tangbao offerings. Yinshi Jishi Tangbao at No. 398 Mochou Road is a recommended place to try Tangbao. Fuzimiao (Confucius Temple) and Hunan Road are also known for their Tangbao. Other famous restaurants include Zhiwei Guan and Zhu Yansheng Tang Bao. Liu Changxing restaurant is recommended for those who prefer more savory dumplings.
|
||||
|
||||
### Impact of Tourism
|
||||
|
||||
The development of Nanjing tourism directly promotes the growth of local transportation, hotels, and retail stores. Tourism significantly impacts the popularity and availability of Nanjing Tangbao, contributing to the protection of cultural heritage and the growth of related industries.
|
||||
|
||||
### Images of Nanjing Tangbao
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Key Citations
|
||||
|
||||
- [What to eat in Nanjing? — 11+ best Nanjing street food & Nanjing famous ...](https://livingnomads.com/2023/10/nanjing-street-food/)
|
||||
|
||||
- [Tang Bao 汤包 - Chinese Food | Study in China](https://www.istudy-china.com/tang-bao-%E6%B1%A4%E5%8C%85-chinese-food/)
|
||||
|
||||
- [Jia Jia Tang Bao, Shanghai - Best xiao long bao in Shanghai? - Foodnut.com](https://www.foodnut.com/590/jia-jia-tang-bao-restaurant-review-shanghai/)
|
||||
|
||||
- [Jia Jia Tang Bao : Shanghai | Xtreme Foodies](https://www.xtremefoodies.com/food-category/Steamed/review/Jia-Jia-Tang-Bao/Xiaolongbao/7186_4288)
|
||||
|
||||
- [Culinary Delights of the South Capital: Top 10 Foods to Eat in Nanjing](https://chinatraveltales.com/article/culinary-delights-of-the-south-capital-top-10-foods-to-eat-in-nanjing)
|
||||
|
||||
- [Top 10 Nanjing Food You Must Try - Trip.com](https://www.trip.com/guide/food/nanjing-food.html)
|
||||
|
||||
- [8 Must-Eats In Nanjing - hiredchina.com](https://www.hiredchina.com/articles/8-must-eats-in-nanjing/)
|
||||
|
||||
- [PDF] On Study of “Macro-tourism” Industry Theory](https://ccsenet.org/journal/index.php/ijbm/article/download/3779/3389)
|
||||
|
||||
- [Easy Tangbao Recipe for Juicy Soup Dumplings - altadiscus.com](https://altadiscus.com/tangbao-recipe/)
|
||||
|
||||
- [Tangbao Authentic Recipe - TasteAtlas](https://www.tasteatlas.com/tangbao/recipe)
|
||||
|
||||
- [Tangbao - Wikipedia](https://en.wikipedia.org/wiki/Tangbao)
|
||||
|
||||
- [Xiaolongbao - ArcGIS StoryMaps](https://storymaps.arcgis.com/stories/088f5531b41547b7b2e022142ad74953)
|
||||
@@ -1,128 +0,0 @@
|
||||
# OpenAI Sora Usage Report
|
||||
|
||||
## Key Points
|
||||
|
||||
* Sora is OpenAI's text-to-video model that generates videos from text prompts and can extend existing short videos. It was released publicly for ChatGPT Plus and ChatGPT Pro users in December 2024.
|
||||
* Currently, access to Sora is limited, primarily granted to selected developers, visual artists, designers, and filmmakers for testing and feedback purposes. The API is not yet publicly available.
|
||||
* Sora allows users to generate videos with customizable resolutions up to 1080p and lengths up to 20 seconds, supporting various aspect ratios and the incorporation of user-provided assets.
|
||||
* Sora is capable of generating videos in diverse styles, applying camera angles, motion, and lighting effects, and mimicking realistic or imaginative scenarios based on text prompts.
|
||||
* Limitations include potential inaccuracies in simulating physics, biases, and ethical concerns related to deepfakes and misinformation, which OpenAI is addressing with content moderation and community-driven guidelines.
|
||||
* Geographically, Sora is available in over 150 countries but remains inaccessible in the European Union and the UK due to regulatory challenges and a prioritized rollout to US users.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
OpenAI's Sora is a text-to-video model designed to generate short video clips based on user-provided text prompts. Launched in December 2024, Sora represents a significant advancement in AI-driven content creation, allowing users to bring imaginative scenarios to life through video. However, its release is accompanied by both excitement and concerns regarding its capabilities, limitations, and ethical implications.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Functionalities and Capabilities
|
||||
|
||||
Sora offers a range of functionalities, including:
|
||||
|
||||
* **Text-to-Video Generation**: Creating realistic and imaginative videos from text prompts.
|
||||
* **Video Editing**: Options for remixing, re-cutting, looping, blending, and storyboarding video content.
|
||||
* **Prompt Interpretation**: Generating videos that mimic real-world scenes or bring to life imaginative scenarios.
|
||||
* **Video Styles and Content**: Generating videos in various styles, from realistic to artistic.
|
||||
|
||||
Sora is capable of applying various camera angles, motion, and lighting effects to the generated videos. Specific camera movements like pan, tilt, dolly, zoom, and more can be directed using detailed prompts.
|
||||
|
||||
### Access and Availability
|
||||
|
||||
Currently, access to Sora is limited. It is primarily available to selected developers, visual artists, designers, and filmmakers for the purpose of testing, gathering feedback, and assessing potential weaknesses and risks. The API is not yet publicly available, and OpenAI has not specified a concrete timeline for broader access. It was released publicly for ChatGPT Plus and ChatGPT Pro users in December 2024.
|
||||
|
||||
Geographical availability is also restricted. While Sora is available in more than 150 countries, it is currently inaccessible in the European Union and the UK due to specific EU regulations regarding AI use and an initial focus on US users.
|
||||
|
||||
### Content Limitations and Restrictions
|
||||
|
||||
Sora has several limitations and restrictions:
|
||||
|
||||
* **Resolution and Length**: Videos can be generated up to 1080p resolution, with lengths up to 20 seconds for ChatGPT Pro users and 10 seconds for ChatGPT Plus users. Lower resolutions, such as 480p, are also available.
|
||||
* **Complexity**: The model sometimes struggles with realistic physics and complex actions over long durations.
|
||||
* **Content Restrictions**: There are age restrictions, allowing only adults (above 18 years) to use the tool, and visual content depicting minors is prohibited. There are limitations in depicting humans; for now, only a small group of selected testers can create human-like videos.
|
||||
* **Biases and Inaccuracies**: Sora may not always understand the entire context of a prompt, leading to inaccurate or irrelevant outputs and potential biases perpetuating stereotypes.
|
||||
|
||||
### Ethical Considerations and Policies
|
||||
|
||||
Sora raises ethical concerns related to the creation of deepfakes and the potential spread of misinformation. OpenAI is aware of these concerns and is implementing policies and safeguards to address them:
|
||||
|
||||
* **Content Moderation**: Features to promote responsible use and prohibit harmful content.
|
||||
* **Community Guidelines**: Community-driven guidelines to ensure Sora responds to cultural diversity.
|
||||
* **Limited Initial Access**: Limiting initial access to a carefully chosen group to understand and address concerns before wider release.
|
||||
|
||||
### Potential Applications and Impact
|
||||
|
||||
Sora has potential applications in filmmaking, advertising, education, and gaming. It can revolutionize content creation by enabling the creation of realistic and personalized video content and transform educational materials and marketing campaigns.
|
||||
|
||||
However, Sora also has the potential to cause job displacement across various industries, raising concerns about fair compensation for intellectual property rights holders and artists.
|
||||
|
||||
---
|
||||
|
||||
## Key Citations
|
||||
|
||||
- [OpenAI Sora: Text to Video generation - ElevenLabs](https://elevenlabs.io/blog/openai-sora)
|
||||
|
||||
- [Sora (text-to-video model) - Wikipedia](https://en.wikipedia.org/wiki/Sora_(text-to-video_model))
|
||||
|
||||
- [Introducing OpenAI's Sora: Revolutionizing Text-to-Video Conversion](https://pcsocial.medium.com/introducing-openais-sora-revolutionizing-text-to-video-conversion-b99b37a71e55)
|
||||
|
||||
- [OpenAI Sora Is Here! How to Access It and Feature Overview](https://blog.vive.com/us/openai-sora-is-here-how-to-access-it-and-feature-overview/)
|
||||
|
||||
- [What Is OpenAI's Sora? How It Works, Examples, Features](https://www.datacamp.com/blog/openai-announces-sora-text-to-video-generative-ai-is-about-to-go-mainstream)
|
||||
|
||||
- [Six Top Features of Sora, OpenAI's New AI Video Creation Platform](https://www.maginative.com/article/six-top-features-of-sora-openais-new-ai-video-creation-platform/)
|
||||
|
||||
- [The Ultimate Guide to Sora AI + Prompts and Examples - SaaS Genius](https://www.saasgenius.com/blog-business/the-ultimate-guide-to-sora/)
|
||||
|
||||
- [17 Best OpenAI Sora AI Video Examples (2025) - SEO.AI](https://seo.ai/blog/openai-sora-examples)
|
||||
|
||||
- [OpenAI's Sora Video Generator Is Now Available...But Not to All](https://tech.co/news/openai-sora-video-generator-launch)
|
||||
|
||||
- [Generating videos on Sora | OpenAI Help Center](https://help.openai.com/en/articles/9957612-generating-videos-on-sora)
|
||||
|
||||
- [OpenAI Limits Sora Access After Higher-Than-Expected Demand](https://www.pcmag.com/news/openai-releases-sora-video-generator-will-it-simplify-or-destroy-filmmaking)
|
||||
|
||||
- [My OpenAI's Sora video generator review: Is it worth the hype?](https://techpoint.africa/guide/openai-sora-video-generator-review/)
|
||||
|
||||
- [OpenAI disables video gen for certain Sora users as capacity ...](https://techcrunch.com/2025/03/31/openai-disables-video-gen-for-certain-sora-users-as-capacity-challenges-continue/)
|
||||
|
||||
- [Feedback on Sora Text-to-Video Generator. Disappointed - ChatGPT](https://community.openai.com/t/feedback-on-sora-text-to-video-generator-disappointed/1079553)
|
||||
|
||||
- [10 Best Sora AI Prompts For Viral Videos - AI Tools](https://www.godofprompt.ai/blog/10-best-sora-ai-prompts-for-viral-videos?srsltid=AfmBOopcJdIgojxyXbT5TbGxgnr5ijJJAn0dp3wn8net2BmqUzKzCSzS)
|
||||
|
||||
- [Crafting Cinematic Sora Video Prompts: A complete guide · GitHub](https://gist.github.com/ruvnet/e20537eb50866b2d837d4d13b066bd88)
|
||||
|
||||
- [How to Use OpenAI Sora: A Step-by-Step Guide - Alicia Lyttle](https://alicialyttle.com/how-to-use-openai-sora-ai-video-generator/)
|
||||
|
||||
- [Sora: Creating video from text - OpenAI](https://openai.com/index/sora/)
|
||||
|
||||
- [Is OpenAI Sora API Available? And How to Use it? - Apidog](https://apidog.com/blog/openai-sora-api/)
|
||||
|
||||
- [Sora is here - OpenAI](https://openai.com/index/sora-is-here/)
|
||||
|
||||
- [Understanding OpenAI Sora: Features, Uses, and Limitations](https://digitalguider.com/blog/openai-sora/)
|
||||
|
||||
- [Sora's Limitations, Hidden Features and Capabilities (2025)](https://618media.com/en/blog/soras-limitations-and-its-capabilities/)
|
||||
|
||||
- [OpenAI Unveils AI Video Generator Sora, But Limits Human Depictions](https://vocal.media/futurism/open-ai-unveils-ai-video-generator-sora-but-limits-human-depictions)
|
||||
|
||||
- [How to Access Sora in Europe? - Swiftask](https://www.swiftask.ai/blog/comment-acceder-a-sora-en-europe)
|
||||
|
||||
- [How to access Sora AI in UK and EU 2025 - VPNpro](https://vpnpro.com/guides-and-tutorials/how-to-access-sora/)
|
||||
|
||||
- [The Rise of Sora: OpenAI's Frontier in Generative Video Innovation](https://www.launchconsulting.com/posts/the-rise-of-sora-openais-frontier-in-generative-video-innovation)
|
||||
|
||||
- [Meet Sora— OpenAI's Latest Innovation to Bridge the Gap Between Text and Visuals](https://www.practicallogix.com/meet-sora-openais-latest-innovation-to-bridge-the-gap-between-text-and-visuals/)
|
||||
|
||||
- [Do you think the potential use of OpenAI's Sora for creating AI ...](https://www.quora.com/Do-you-think-the-potential-use-of-OpenAIs-Sora-for-creating-AI-deepfakes-are-outweighed-by-the-risks)
|
||||
|
||||
- [What We Know About OpenAI's Sora So Far](https://www.unite.ai/what-we-know-about-openais-sora-so-far/)
|
||||
|
||||
- [What is OpenAI's Sora? and How to Use it? - Great Learning](https://www.mygreatlearning.com/blog/what-is-sora-and-how-to-use-it/)
|
||||
|
||||
- [SORA By Open AI To Kill Off Jobs: Bane or Boon? - Be10X](https://be10x.in/blog/sora-by-open-ai-to-kill-off-jobs-bane-or-boon/)
|
||||
|
||||
- [OpenAI Is Ready for Hollywood to Accept Its Vision](https://www.hollywoodreporter.com/business/business-news/openai-hollywood-sora-1236170402/)
|
||||
@@ -1,54 +0,0 @@
|
||||
# Google's Agent to Agent Protocol Report
|
||||
|
||||
## Key Points
|
||||
|
||||
- Google's Agent2Agent (A2A) protocol standardizes communication between AI agents, promoting collaboration across diverse systems.
|
||||
- A2A facilitates message exchange for sharing context, instructions, and artifacts between agents.
|
||||
- The protocol complements Anthropic's Model Context Protocol (MCP) by providing a networking layer for agents.
|
||||
- A2A allows agents to negotiate content formats, supporting diverse media types such as iframes and video.
|
||||
- Google intends A2A to be an open, community-driven project to foster innovation and adoption.
|
||||
- Industry experts anticipate A2A will accelerate AI adoption by simplifying integrations and data exchange.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Google's Agent2Agent (A2A) protocol is designed to establish a standardized method for AI agents to communicate, irrespective of their origin, framework, or location. This initiative seeks to foster seamless collaboration and collective intelligence among AI agents, thereby enhancing the effectiveness of agentic solutions. A2A operates as a networking layer that complements other protocols such as Anthropic's Model Context Protocol (MCP), contributing to a more unified AI agent ecosystem.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Purpose and Design
|
||||
|
||||
A2A addresses the challenge of integrating disparate AI systems by providing a common language for AI agents. It enables these agents to share context, replies, artifacts, and user instructions, facilitating collaborative problem-solving. The design of A2A supports flexible user experiences by allowing agents to negotiate content formats like iframes, videos, and web forms.
|
||||
|
||||
### Technical Aspects
|
||||
|
||||
The protocol utilizes "parts" within messages, which are fully formed pieces of content with specified content types, enabling negotiation of the correct format needed between agents. A2A builds upon existing standards including HTTP, SSE, and JSON-RPC.
|
||||
|
||||
### Community and Industry Impact
|
||||
|
||||
Google's vision for A2A is to create an open, community-driven project that encourages contributions and updates from the open-source community. Industry experts from companies like Deloitte, Accenture, EPAM, and New Relic believe A2A will accelerate AI adoption by simplifying integrations, facilitating data exchange, and fostering a more unified AI agent ecosystem. LangChain has also expressed interest in collaborating with Google Cloud on this shared protocol.
|
||||
|
||||
### Relationship with MCP
|
||||
|
||||
A2A complements Anthropic's Model Context Protocol (MCP). While A2A provides a networking layer for agents to communicate, MCP functions as a plugin system, granting agents access to tools, context, and data.
|
||||
|
||||
---
|
||||
|
||||
## Key Citations
|
||||
|
||||
- [Announcing the Agent2Agent Protocol (A2A)](https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/)
|
||||
|
||||
- [Build and manage multi-system agents with Vertex AI - Google Cloud](https://cloud.google.com/blog/products/ai-machine-learning/build-and-manage-multi-system-agents-with-vertex-ai)
|
||||
|
||||
- [Google just Launched Agent2Agent, an Open Protocol for AI agents ...](https://www.maginative.com/article/google-just-launched-agent2agent-an-open-protocol-for-ai-agents-to-work-directly-with-each-other/)
|
||||
|
||||
- [Protocols for Agentic AI: Google's New A2A Joins Viral MCP](https://virtualizationreview.com/articles/2025/04/09/protocols-for-agentic-ai-googles-new-a2a-joins-viral-mcp.aspx)
|
||||
|
||||
- [Google's Agent2Agent interoperability protocol aims to standardize ...](https://venturebeat.com/ai/googles-agent2agent-interoperability-protocol-aims-to-standardize-agentic-communication/)
|
||||
|
||||
- [Meet Google A2A: The Protocol That will Revolutionize Multi-Agent ...](https://medium.com/@the_manoj_desai/meet-google-a2a-the-protocol-that-will-revolutionize-multi-agent-ai-systems-80d55a4583ed)
|
||||
|
||||
- [Google's Agent2Agent Protocol Helps AI Agents Talk to Each Other](https://thenewstack.io/googles-agent2agent-protocol-helps-ai-agents-talk-to-each-other/)
|
||||
@@ -1,106 +0,0 @@
|
||||
## Report on Large Language Models (LLMs)
|
||||
|
||||
This report provides a comprehensive overview of Large Language Models (LLMs), covering their definition, architecture, training, applications, limitations, biases, ethical considerations, and mitigation strategies, based on the provided search results.
|
||||
|
||||
### Executive Summary
|
||||
|
||||
LLMs are deep learning models that use transformer architecture and are trained on massive datasets. They excel at various Natural Language Processing (NLP) tasks, including text generation, translation, and question answering. However, they also present limitations, biases, and ethical challenges that need to be addressed for responsible development and deployment.
|
||||
|
||||
### Key Findings
|
||||
|
||||
* **Definition and Architecture**: LLMs are deep learning algorithms that perform NLP tasks using transformer models and are trained on massive datasets. They consist of encoders, decoders, and attention mechanisms, with key components like embedding layers and attention mechanisms.
|
||||
* **Training Data and Methodologies**: LLMs are trained on datasets like Common Crawl (5.4 trillion tokens) and The Pile (800 GB). Training methodologies include unsupervised pre-training, supervised fine-tuning, and transfer learning.
|
||||
* **Applications**: LLMs are used in text generation, machine translation, question answering, code generation, text summarization, and sentiment analysis.
|
||||
* **Performance Benchmarks**: LLM performance is evaluated using metrics like accuracy, precision, recall, F1 score, BLEU, ROUGE, perplexity, and HumanEval (pass@k).
|
||||
* **Limitations**: LLMs have computational constraints, struggle with complex linguistic elements, lack long-term memory, and can perpetuate biases.
|
||||
* **Biases**: LLMs exhibit gender, racial, cultural, and socio-economic stereotypes due to biases in their training data.
|
||||
* **Ethical Considerations**: LLMs raise ethical concerns about misuse, privacy, and accountability.
|
||||
* **Mitigation Strategies**: Mitigation strategies include data curation, model adjustments, and post-processing techniques.
|
||||
|
||||
### Detailed Analysis
|
||||
|
||||
#### Definition and Architecture
|
||||
|
||||
LLMs are a specific type of generative AI designed for text-based content generation. They leverage deep learning algorithms and transformer models to perform various NLP tasks. A typical LLM architecture includes:
|
||||
|
||||
* **Embedding Layer**: Converts input text into numerical embeddings, capturing semantic and syntactic meaning.
|
||||
* **Attention Mechanism**: Allows the model to focus on relevant parts of the input text.
|
||||
* **Transformer Models**: A tokenizer converts text into numerical values (tokens), and encoders create meaningful embeddings.
|
||||
|
||||
LLMs typically have at least one billion or more parameters.
|
||||
|
||||
#### Training Data and Methodologies
|
||||
|
||||
LLMs require vast amounts of data for effective training. Some key datasets include:
|
||||
|
||||
* **Common Crawl**: 5.4 trillion tokens
|
||||
* **Cosmopedia**: 25 billion tokens
|
||||
* **The Pile**: 800 GB
|
||||
|
||||
Training methodologies include:
|
||||
|
||||
* **Unsupervised Pre-training**: Learning general language representations.
|
||||
* **Supervised Fine-tuning**: Adapting models to specific tasks.
|
||||
* **Transfer Learning**: Leveraging knowledge gained from one task to improve performance on another.
|
||||
|
||||
#### Applications
|
||||
|
||||
LLMs have a wide array of applications across various domains:
|
||||
|
||||
* **Text Generation**: Creating coherent and contextually relevant text.
|
||||
* **Machine Translation**: Converting text from one language to another.
|
||||
* **Question Answering**: Providing answers to questions posed in natural language.
|
||||
* **Code Generation**: Generating code snippets or complete programs.
|
||||
* **Text Summarization**: Condensing large amounts of text into shorter summaries.
|
||||
* **Sentiment Analysis**: Determining the emotional tone or attitude expressed in text.
|
||||
|
||||
#### Performance Benchmarks and Evaluation Metrics
|
||||
|
||||
Evaluating LLM performance involves using standardized benchmarks and metrics. Key metrics include:
|
||||
|
||||
* **Accuracy**: Measures the correctness of the model's outputs.
|
||||
* **Precision and Recall**: Assess the relevance and completeness of the results.
|
||||
* **F1 Score**: Provides a balanced measure of precision and recall.
|
||||
* **BLEU and ROUGE**: Evaluate the quality of machine-translated or summarized text.
|
||||
* **Perplexity**: Measures the uncertainty of the model in predicting the next word in a sequence.
|
||||
* **HumanEval (pass@k)**: Assesses code generation performance.
|
||||
|
||||
#### Limitations, Biases, and Ethical Considerations
|
||||
|
||||
LLMs face several limitations:
|
||||
|
||||
* **Computational Constraints**: Limited by fixed token limits.
|
||||
* **Complex Linguistic Elements**: Struggle with nuanced language.
|
||||
* **Lack of Long-Term Memory**: Difficulty retaining information over extended contexts.
|
||||
* **Perpetuation of Biases**: Reinforce stereotypes from training data.
|
||||
|
||||
Biases in LLMs can manifest as:
|
||||
|
||||
* **Gender Stereotypes**: Skewed outputs based on gender.
|
||||
* **Racial Stereotypes**: Unfair representations of different racial groups.
|
||||
* **Cultural Stereotypes**: Biased outputs related to specific cultures.
|
||||
|
||||
Ethical considerations include:
|
||||
|
||||
* **Potential Misuse**: Disinformation and manipulation.
|
||||
* **Privacy Issues**: Data usage and potential exposure of personal information.
|
||||
* **Accountability Challenges**: Difficulty in tracing the reasoning processes of LLMs.
|
||||
|
||||
#### Mitigation Strategies
|
||||
|
||||
Various strategies can be employed to mitigate limitations and biases:
|
||||
|
||||
* **Data Curation**: Refining training data to reduce biases.
|
||||
* **Model Adjustments**: Implementing fairness constraints during training.
|
||||
* **Post-processing Corrections**: Fine-tuning outputs to reduce biases.
|
||||
* **Resampling and Augmentation**: Balancing and expanding the training dataset.
|
||||
|
||||
### Conclusions and Recommendations
|
||||
|
||||
LLMs are powerful tools with a wide range of applications, but they are not without limitations and risks. Addressing these challenges requires:
|
||||
|
||||
* **Ongoing Research**: Continued investigation into biases, limitations, and mitigation strategies.
|
||||
* **Ethical Frameworks**: Development of updated ethical guidelines for responsible development and deployment.
|
||||
* **Collaboration**: Interdisciplinary efforts involving researchers, developers, and policymakers.
|
||||
* **Data Transparency**: Increased transparency about training data and model development processes.
|
||||
* **Careful Implementation**: Strategic application of mitigation techniques to avoid unintended performance trade-offs.
|
||||
@@ -1,51 +0,0 @@
|
||||
# Anthropic Model Context Protocol (MCP) Report
|
||||
|
||||
## Key Points
|
||||
|
||||
* Anthropic's Model Context Protocol (MCP) is an open standard introduced in late November 2024, designed to standardize how AI models interact with external data and tools.
|
||||
* MCP acts as a universal interface, similar to a "USB port," facilitating easier integration of AI models with various data sources and services without custom integrations.
|
||||
* Anthropic focuses on developer experience with MCP, aiming to simplify integration and enhance the utility of AI models in real-world scenarios.
|
||||
* MCP faces scalability challenges, particularly in distributed cloud environments, which Anthropic addresses through remote server support with robust security measures.
|
||||
* User testimonials and case studies from Anthropic highlight improvements in talent acquisition, knowledge worker productivity, developer productivity, search, productivity, and investment analysis.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Anthropic's Model Context Protocol (MCP) is an open standard introduced in late November 2024, designed to standardize how AI models, especially Large Language Models (LLMs), interact with external data sources and tools. It addresses the challenge of integrating AI systems by providing a universal interface that allows models to access relevant context and perform actions on other systems. The protocol aims to break AI systems out of isolation by making them easily integrable with various data sources and services, promoting a more scalable and efficient approach to AI application development.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Definition and Purpose
|
||||
|
||||
Anthropic's Model Context Protocol (MCP) functions as a universal interface, akin to a "USB port," enabling AI models to interact seamlessly with external data sources and tools. This standardization simplifies integration processes and enables AI systems to access relevant context and execute actions on other systems more efficiently. The protocol facilitates two-way communication, empowering models to fetch data and trigger actions via standardized messages.
|
||||
|
||||
### Performance
|
||||
|
||||
Anthropic's strategic focus with MCP centers on enhancing the developer experience rather than solely optimizing raw model performance. This approach differentiates them from companies prioritizing larger, more powerful models. MCP is geared towards streamlining the integration and utility of existing models within practical, real-world workflows. Key quantitative metrics for evaluating LLM performance include F1 score, BLEU score, perplexity, accuracy, precision, and recall.
|
||||
|
||||
### Scalability
|
||||
|
||||
MCP encounters scalability challenges, particularly within distributed cloud environments. Anthropic is actively addressing these issues by developing remote server support, which includes robust authentication, encryption, and potentially brokered connections to accommodate enterprise-scale deployments. MCP offers a more scalable methodology for managing context and instructions for intricate AI applications by delivering specific "policy" context precisely when required.
|
||||
|
||||
### User Testimonials and Case Studies
|
||||
|
||||
Anthropic provides case studies demonstrating how customers utilize Claude, showcasing improvements in talent acquisition, knowledge worker productivity, developer productivity, search and productivity, and investment analysis. These examples illustrate the practical benefits and versatility of Anthropic's AI solutions.
|
||||
|
||||
---
|
||||
|
||||
## Key Citations
|
||||
|
||||
- [Create strong empirical evaluations - Anthropic API](https://docs.anthropic.com/en/docs/build-with-claude/develop-tests)
|
||||
|
||||
- [Define your success criteria - Anthropic API](https://docs.anthropic.com/en/docs/build-with-claude/define-success)
|
||||
|
||||
- [The Model Context Protocol (MCP) by Anthropic: Origins ... - Wandb](https://wandb.ai/onlineinference/mcp/reports/The-Model-Context-Protocol-MCP-by-Anthropic-Origins-functionality-and-impact--VmlldzoxMTY5NDI4MQ)
|
||||
|
||||
- [Anthropic introduces open source Model Context Protocol to boost ...](https://www.techmonitor.ai/digital-economy/ai-and-automation/anthropic-introduces-open-source-mcp-to-simplify-ai-system-integrations)
|
||||
|
||||
- [Anthropic's Model Context Protocol: Building an 'ODBC for AI' in an ...](https://salesforcedevops.net/index.php/2024/11/29/anthropics-model-context-protocol/)
|
||||
|
||||
- [Customers - Anthropic](https://www.anthropic.com/customers)
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"dockerfile_lines": [],
|
||||
"graphs": {
|
||||
"deep_research": "./src/workflow.py:graph",
|
||||
"podcast_generation": "./src/podcast/graph/builder.py:workflow",
|
||||
"ppt_generation": "./src/ppt/graph/builder.py:workflow"
|
||||
},
|
||||
"python_version": "3.12",
|
||||
"env": "./.env",
|
||||
"dependencies": ["."]
|
||||
}
|
||||
191
main.py
191
main.py
@@ -1,191 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Entry point script for the DeerFlow project.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
from InquirerPy import inquirer
|
||||
|
||||
from src.config.questions import BUILT_IN_QUESTIONS, BUILT_IN_QUESTIONS_ZH_CN
|
||||
from src.workflow import run_agent_workflow_async
|
||||
|
||||
|
||||
def ask(
|
||||
question,
|
||||
debug=False,
|
||||
max_plan_iterations=1,
|
||||
max_step_num=3,
|
||||
enable_background_investigation=True,
|
||||
enable_clarification=False,
|
||||
max_clarification_rounds=None,
|
||||
locale=None,
|
||||
):
|
||||
"""Run the agent workflow with the given question.
|
||||
|
||||
Args:
|
||||
question: The user's query or request
|
||||
debug: If True, enables debug level logging
|
||||
max_plan_iterations: Maximum number of plan iterations
|
||||
max_step_num: Maximum number of steps in a plan
|
||||
enable_background_investigation: If True, performs web search before planning to enhance context
|
||||
enable_clarification: If False (default), skip clarification; if True, enable multi-turn clarification
|
||||
max_clarification_rounds: Maximum number of clarification rounds (default: None, uses State default=3)
|
||||
locale: The locale setting (e.g., 'en-US', 'zh-CN')
|
||||
"""
|
||||
asyncio.run(
|
||||
run_agent_workflow_async(
|
||||
user_input=question,
|
||||
debug=debug,
|
||||
max_plan_iterations=max_plan_iterations,
|
||||
max_step_num=max_step_num,
|
||||
enable_background_investigation=enable_background_investigation,
|
||||
enable_clarification=enable_clarification,
|
||||
max_clarification_rounds=max_clarification_rounds,
|
||||
locale=locale,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main(
|
||||
debug=False,
|
||||
max_plan_iterations=1,
|
||||
max_step_num=3,
|
||||
enable_background_investigation=True,
|
||||
enable_clarification=False,
|
||||
max_clarification_rounds=None,
|
||||
):
|
||||
"""Interactive mode with built-in questions.
|
||||
|
||||
Args:
|
||||
enable_background_investigation: If True, performs web search before planning to enhance context
|
||||
debug: If True, enables debug level logging
|
||||
max_plan_iterations: Maximum number of plan iterations
|
||||
max_step_num: Maximum number of steps in a plan
|
||||
enable_clarification: If False (default), skip clarification; if True, enable multi-turn clarification
|
||||
max_clarification_rounds: Maximum number of clarification rounds (default: None, uses State default=3)
|
||||
"""
|
||||
# First select language
|
||||
language = inquirer.select(
|
||||
message="Select language / 选择语言:",
|
||||
choices=["English", "中文"],
|
||||
).execute()
|
||||
|
||||
# Set locale based on language
|
||||
locale = "en-US" if language == "English" else "zh-CN"
|
||||
|
||||
# Choose questions based on language
|
||||
questions = (
|
||||
BUILT_IN_QUESTIONS if language == "English" else BUILT_IN_QUESTIONS_ZH_CN
|
||||
)
|
||||
ask_own_option = (
|
||||
"[Ask my own question]" if language == "English" else "[自定义问题]"
|
||||
)
|
||||
|
||||
# Select a question
|
||||
initial_question = inquirer.select(
|
||||
message=(
|
||||
"What do you want to know?" if language == "English" else "您想了解什么?"
|
||||
),
|
||||
choices=[ask_own_option] + questions,
|
||||
).execute()
|
||||
|
||||
if initial_question == ask_own_option:
|
||||
initial_question = inquirer.text(
|
||||
message=(
|
||||
"What do you want to know?"
|
||||
if language == "English"
|
||||
else "您想了解什么?"
|
||||
),
|
||||
).execute()
|
||||
|
||||
# Pass all parameters to ask function
|
||||
ask(
|
||||
question=initial_question,
|
||||
debug=debug,
|
||||
max_plan_iterations=max_plan_iterations,
|
||||
max_step_num=max_step_num,
|
||||
enable_background_investigation=enable_background_investigation,
|
||||
enable_clarification=enable_clarification,
|
||||
max_clarification_rounds=max_clarification_rounds,
|
||||
locale=locale,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Set up argument parser
|
||||
parser = argparse.ArgumentParser(description="Run the Deer")
|
||||
parser.add_argument("query", nargs="*", help="The query to process")
|
||||
parser.add_argument(
|
||||
"--interactive",
|
||||
action="store_true",
|
||||
help="Run in interactive mode with built-in questions",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_plan_iterations",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Maximum number of plan iterations (default: 1)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max_step_num",
|
||||
type=int,
|
||||
default=3,
|
||||
help="Maximum number of steps in a plan (default: 3)",
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
||||
parser.add_argument(
|
||||
"--no-background-investigation",
|
||||
action="store_false",
|
||||
dest="enable_background_investigation",
|
||||
help="Disable background investigation before planning",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-clarification",
|
||||
action="store_true",
|
||||
dest="enable_clarification",
|
||||
help="Enable multi-turn clarification for vague questions (default: disabled)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-clarification-rounds",
|
||||
type=int,
|
||||
dest="max_clarification_rounds",
|
||||
help="Maximum number of clarification rounds (default: 3)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.interactive:
|
||||
# Pass command line arguments to main function
|
||||
main(
|
||||
debug=args.debug,
|
||||
max_plan_iterations=args.max_plan_iterations,
|
||||
max_step_num=args.max_step_num,
|
||||
enable_background_investigation=args.enable_background_investigation,
|
||||
enable_clarification=args.enable_clarification,
|
||||
max_clarification_rounds=args.max_clarification_rounds,
|
||||
)
|
||||
else:
|
||||
# Parse user input from command line arguments or user input
|
||||
if args.query:
|
||||
user_query = " ".join(args.query)
|
||||
else:
|
||||
# Loop until user provides non-empty input
|
||||
while True:
|
||||
user_query = input("Enter your query: ")
|
||||
if user_query is not None and user_query != "":
|
||||
break
|
||||
|
||||
# Run the agent workflow with the provided parameters
|
||||
ask(
|
||||
question=user_query,
|
||||
debug=args.debug,
|
||||
max_plan_iterations=args.max_plan_iterations,
|
||||
max_step_num=args.max_step_num,
|
||||
enable_background_investigation=args.enable_background_investigation,
|
||||
enable_clarification=args.enable_clarification,
|
||||
max_clarification_rounds=args.max_clarification_rounds,
|
||||
)
|
||||
38
pre-commit
38
pre-commit
@@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Run make lint
|
||||
echo "Running linting..."
|
||||
make lint
|
||||
LINT_RESULT=$?
|
||||
|
||||
if [ $LINT_RESULT -ne 0 ]; then
|
||||
echo "❌ Linting failed. Please fix the issues and try committing again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run make format
|
||||
echo "Running formatting..."
|
||||
make format
|
||||
FORMAT_RESULT=$?
|
||||
|
||||
if [ $FORMAT_RESULT -ne 0 ]; then
|
||||
echo "❌ Formatting failed. Please fix the issues and try committing again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check license headers
|
||||
echo "Checking license headers..."
|
||||
make check-license-all
|
||||
LICENSE_RESULT=$?
|
||||
|
||||
if [ $LICENSE_RESULT -ne 0 ]; then
|
||||
echo "❌ Some files are missing license headers."
|
||||
echo "Run 'make add-license-all' to add them automatically."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If any files were reformatted, add them back to staging
|
||||
git diff --name-only | xargs -I {} git add "{}"
|
||||
|
||||
echo "✅ Pre-commit checks passed!"
|
||||
exit 0
|
||||
@@ -1,99 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "deer-flow"
|
||||
version = "0.1.0"
|
||||
description = "DeerFlow project"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"httpx>=0.28.1",
|
||||
# LangChain 1.x core packages
|
||||
"langchain>=1.0.0",
|
||||
"langchain-core>=1.2.5",
|
||||
"langchain-community>=0.3.19",
|
||||
"langchain-experimental>=0.3.4",
|
||||
"langchain-openai>=0.3.8",
|
||||
"langchain-text-splitters>=0.3.6",
|
||||
# LangGraph
|
||||
"langgraph>=0.3.5",
|
||||
# Other dependencies
|
||||
"readabilipy>=0.3.0",
|
||||
"python-dotenv>=1.0.1",
|
||||
"socksio>=1.0.0",
|
||||
"markdownify>=1.1.0",
|
||||
"fastapi>=0.110.0",
|
||||
"uvicorn>=0.27.1",
|
||||
"sse-starlette>=1.6.5",
|
||||
"pandas>=2.2.3",
|
||||
"numpy>=2.2.3",
|
||||
"yfinance>=0.2.54",
|
||||
"litellm>=1.63.11",
|
||||
"json-repair>=0.7.0",
|
||||
"jinja2>=3.1.3",
|
||||
"duckduckgo-search>=8.0.0",
|
||||
"ddgs>=9.0.0",
|
||||
"inquirerpy>=0.3.4",
|
||||
"arxiv>=2.2.0",
|
||||
"mcp>=1.11.0",
|
||||
"langchain-mcp-adapters>=0.0.9",
|
||||
"langchain-deepseek>=0.1.3",
|
||||
"langchain-google-genai>=2.0.6",
|
||||
"wikipedia>=1.4.0",
|
||||
"langchain-tavily>=0.2.0",
|
||||
"langgraph-checkpoint-mongodb>=0.1.4",
|
||||
"langgraph-checkpoint-postgres==2.0.21",
|
||||
"pymilvus>=2.3.0",
|
||||
"langchain-milvus>=0.2.1",
|
||||
"psycopg[binary]>=3.2.9",
|
||||
"qdrant-client>=1.15.1",
|
||||
"langchain-qdrant>=0.2.0",
|
||||
"orjson>=3.11.5",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"ruff",
|
||||
"langgraph-cli[inmem]>=0.2.10",
|
||||
]
|
||||
test = [
|
||||
"pytest>=7.4.0",
|
||||
"pytest-cov>=4.1.0",
|
||||
"pytest-asyncio>=1.0.0",
|
||||
"pytest-cov>=6.0.0",
|
||||
"asyncpg-stubs>=0.30.2",
|
||||
"mongomock>=4.3.0",
|
||||
"pytest-postgresql>=7.0.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
required-version = ">=0.6.15"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
addopts = "-v --cov=src --cov-report=term-missing"
|
||||
filterwarnings = [
|
||||
"ignore::DeprecationWarning",
|
||||
"ignore::UserWarning",
|
||||
]
|
||||
|
||||
[tool.coverage.report]
|
||||
fail_under = 25
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
target-version = "py312"
|
||||
extend-include = ["*.pyi"]
|
||||
|
||||
[tool.ruff.format]
|
||||
indent-style = "space"
|
||||
line-ending = "auto"
|
||||
exclude = ['^/build/']
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""Script to add or check license headers in Python and TypeScript files."""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
# License headers for different file types
|
||||
LICENSE_HEADERS: Dict[str, str] = {
|
||||
"python": """# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
""",
|
||||
"typescript": """// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
// SPDX-License-Identifier: MIT
|
||||
""",
|
||||
}
|
||||
|
||||
# File extensions mapping
|
||||
FILE_TYPE_MAP = {
|
||||
".py": "python",
|
||||
".ts": "typescript",
|
||||
".tsx": "typescript",
|
||||
}
|
||||
|
||||
# Patterns to skip
|
||||
SKIP_PATTERNS = [
|
||||
"__pycache__",
|
||||
".pytest_cache",
|
||||
".ruff_cache",
|
||||
"node_modules",
|
||||
".next",
|
||||
".venv",
|
||||
"venv",
|
||||
".tox",
|
||||
"build",
|
||||
"dist",
|
||||
".git",
|
||||
".mypy_cache",
|
||||
]
|
||||
|
||||
|
||||
def should_skip(path: Path) -> bool:
|
||||
"""Check if a path should be skipped."""
|
||||
return any(pattern in str(path) for pattern in SKIP_PATTERNS)
|
||||
|
||||
|
||||
def get_file_type(file_path: Path) -> str | None:
|
||||
"""Get the file type based on extension."""
|
||||
return FILE_TYPE_MAP.get(file_path.suffix)
|
||||
|
||||
|
||||
def has_license_header(content: str, file_type: str) -> bool:
|
||||
"""Check if content already has the license header."""
|
||||
lines = content.split("\n")
|
||||
license_header = LICENSE_HEADERS[file_type]
|
||||
|
||||
# Skip shebang if present (Python files)
|
||||
start_idx = 0
|
||||
if lines and lines[0].startswith("#!"):
|
||||
start_idx = 1
|
||||
# Skip empty lines after shebang
|
||||
while start_idx < len(lines) and not lines[start_idx].strip():
|
||||
start_idx += 1
|
||||
|
||||
# Check if license header is present
|
||||
header_lines = license_header.strip().split("\n")
|
||||
if len(lines) < start_idx + len(header_lines):
|
||||
return False
|
||||
|
||||
for i, header_line in enumerate(header_lines):
|
||||
if lines[start_idx + i].strip() != header_line.strip():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def add_license_header(file_path: Path, dry_run: bool = False) -> bool:
|
||||
"""Add license header to a file if not present.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file
|
||||
dry_run: If True, only check without modifying
|
||||
|
||||
Returns:
|
||||
True if header was added (or would be added in dry-run), False if already present
|
||||
"""
|
||||
file_type = get_file_type(file_path)
|
||||
if not file_type:
|
||||
return False
|
||||
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(f"Error reading {file_path}: {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
if has_license_header(content, file_type):
|
||||
return False
|
||||
|
||||
if dry_run:
|
||||
return True
|
||||
|
||||
# Prepare new content with license header
|
||||
license_header = LICENSE_HEADERS[file_type]
|
||||
lines = content.split("\n")
|
||||
new_lines = []
|
||||
|
||||
# Preserve shebang at the top if present (Python files)
|
||||
start_idx = 0
|
||||
if lines and lines[0].startswith("#!"):
|
||||
new_lines.append(lines[0])
|
||||
start_idx = 1
|
||||
# Skip empty lines after shebang
|
||||
while start_idx < len(lines) and not lines[start_idx].strip():
|
||||
start_idx += 1
|
||||
new_lines.append("") # Empty line after shebang
|
||||
|
||||
# Add license header
|
||||
new_lines.extend(license_header.strip().split("\n"))
|
||||
new_lines.append("") # Empty line after header
|
||||
|
||||
# Add the rest of the file
|
||||
new_lines.extend(lines[start_idx:])
|
||||
|
||||
# Write back to file
|
||||
try:
|
||||
file_path.write_text("\n".join(new_lines), encoding="utf-8")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error writing {file_path}: {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def find_source_files(root: Path) -> List[Path]:
|
||||
"""Find all Python and TypeScript files in the given directory tree."""
|
||||
source_files = []
|
||||
|
||||
for extension in FILE_TYPE_MAP.keys():
|
||||
for path in root.rglob(f"*{extension}"):
|
||||
if should_skip(path):
|
||||
continue
|
||||
source_files.append(path)
|
||||
|
||||
return sorted(source_files)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Add or check license headers in Python and TypeScript files"
|
||||
)
|
||||
parser.add_argument(
|
||||
"paths",
|
||||
nargs="*",
|
||||
default=["."],
|
||||
help="Paths to check (files or directories)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--check",
|
||||
action="store_true",
|
||||
help="Check if headers are present without modifying files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose",
|
||||
"-v",
|
||||
action="store_true",
|
||||
help="Verbose output",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Collect all source files
|
||||
all_files = []
|
||||
for path_str in args.paths:
|
||||
path = Path(path_str)
|
||||
if not path.exists():
|
||||
print(f"Error: Path does not exist: {path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if path.is_file():
|
||||
if path.suffix in FILE_TYPE_MAP and not should_skip(path):
|
||||
all_files.append(path)
|
||||
else:
|
||||
all_files.extend(find_source_files(path))
|
||||
|
||||
if not all_files:
|
||||
print("No source files found.")
|
||||
return 0
|
||||
|
||||
# Process files
|
||||
missing_header = []
|
||||
modified = []
|
||||
|
||||
for file_path in all_files:
|
||||
if add_license_header(file_path, dry_run=args.check):
|
||||
missing_header.append(file_path)
|
||||
if not args.check:
|
||||
modified.append(file_path)
|
||||
if args.verbose:
|
||||
print(f"Added header to: {file_path}")
|
||||
elif args.verbose:
|
||||
print(f"Header already present: {file_path}")
|
||||
|
||||
# Report results
|
||||
if args.check:
|
||||
if missing_header:
|
||||
print(f"\n❌ {len(missing_header)} file(s) missing license header:")
|
||||
for path in missing_header:
|
||||
print(f" - {path}")
|
||||
print("\nRun 'make add-license-all' to add headers.")
|
||||
return 1
|
||||
else:
|
||||
print(f"✅ All {len(all_files)} source file(s) have license headers.")
|
||||
return 0
|
||||
else:
|
||||
if modified:
|
||||
print(f"✅ Added license header to {len(modified)} file(s).")
|
||||
else:
|
||||
print(f"✅ All {len(all_files)} source file(s) already have license headers.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
108
server.py
108
server.py
@@ -1,108 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Server script for running the DeerFlow API.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
import uvicorn
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# To ensure compatibility with Windows event loop issues when using Uvicorn and Asyncio Checkpointer,
|
||||
# This is necessary because some libraries expect a selector-based event loop.
|
||||
# This is a workaround for issues with Uvicorn and Watchdog on Windows.
|
||||
# See:
|
||||
# Since Python 3.8 the default on Windows is the Proactor event loop,
|
||||
# which lacks add_reader/add_writer and can break libraries that expect selector-based I/O (e.g., some Uvicorn/Watchdog/stdio integrations).
|
||||
# For compatibility, this forces the selector loop.
|
||||
if os.name == "nt":
|
||||
logger.info("Setting Windows event loop policy for asyncio")
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
|
||||
def handle_shutdown(signum, frame):
|
||||
"""Handle graceful shutdown on SIGTERM/SIGINT"""
|
||||
logger.info("Received shutdown signal. Starting graceful shutdown...")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# Register signal handlers
|
||||
signal.signal(signal.SIGTERM, handle_shutdown)
|
||||
signal.signal(signal.SIGINT, handle_shutdown)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description="Run the DeerFlow API server")
|
||||
parser.add_argument(
|
||||
"--reload",
|
||||
action="store_true",
|
||||
help="Enable auto-reload (default: True except on Windows)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--host",
|
||||
type=str,
|
||||
default="localhost",
|
||||
help="Host to bind the server to (default: localhost)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port",
|
||||
type=int,
|
||||
default=8000,
|
||||
help="Port to bind the server to (default: 8000)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
type=str,
|
||||
default="info",
|
||||
choices=["debug", "info", "warning", "error", "critical"],
|
||||
help="Log level (default: info)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Determine reload setting
|
||||
reload = False
|
||||
if args.reload:
|
||||
reload = True
|
||||
|
||||
# Check for DEBUG environment variable to override log level
|
||||
if os.getenv("DEBUG", "").lower() in ("true", "1", "yes"):
|
||||
log_level = "debug"
|
||||
else:
|
||||
log_level = args.log_level
|
||||
|
||||
try:
|
||||
logger.info(f"Starting DeerFlow API server on {args.host}:{args.port}")
|
||||
logger.info(f"Log level: {log_level.upper()}")
|
||||
|
||||
# Set the appropriate logging level for the src package if debug is enabled
|
||||
if log_level.lower() == "debug":
|
||||
logging.getLogger("src").setLevel(logging.DEBUG)
|
||||
logging.getLogger("langchain").setLevel(logging.DEBUG)
|
||||
logging.getLogger("langgraph").setLevel(logging.DEBUG)
|
||||
logger.info("DEBUG logging enabled for src, langchain, and langgraph packages - detailed diagnostic information will be logged")
|
||||
|
||||
uvicorn.run(
|
||||
"src.server:app",
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
reload=reload,
|
||||
log_level=log_level,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start server: {str(e)}")
|
||||
sys.exit(1)
|
||||
@@ -1,11 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
|
||||
# Configure Windows event loop policy for PostgreSQL compatibility
|
||||
# On Windows, psycopg requires a selector-based event loop, not the default ProactorEventLoop
|
||||
# This must be set at the earliest possible point before any event loop is created
|
||||
if os.name == "nt":
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
@@ -1,6 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from .agents import create_agent
|
||||
|
||||
__all__ = ["create_agent"]
|
||||
@@ -1,173 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
import logging
|
||||
from typing import Any, Callable, List, Optional
|
||||
|
||||
from langchain.agents import create_agent as langchain_create_agent
|
||||
from langchain.agents.middleware import AgentMiddleware
|
||||
from langgraph.runtime import Runtime
|
||||
|
||||
from src.agents.tool_interceptor import wrap_tools_with_interceptor
|
||||
from src.config.agents import AGENT_LLM_MAP
|
||||
from src.llms.llm import get_llm_by_type
|
||||
from src.prompts import apply_prompt_template
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DynamicPromptMiddleware(AgentMiddleware):
|
||||
"""Middleware to apply dynamic prompt template before model invocation.
|
||||
|
||||
This middleware prepends a system message with the rendered prompt template
|
||||
to the messages list before the model is called.
|
||||
"""
|
||||
|
||||
def __init__(self, prompt_template: str, locale: str = "en-US"):
|
||||
self.prompt_template = prompt_template
|
||||
self.locale = locale
|
||||
|
||||
def before_model(self, state: Any, runtime: Runtime) -> dict[str, Any] | None:
|
||||
"""Apply prompt template and prepend system message to messages."""
|
||||
try:
|
||||
# Get the rendered messages including system prompt from template
|
||||
rendered_messages = apply_prompt_template(
|
||||
self.prompt_template, state, locale=self.locale
|
||||
)
|
||||
# The first message is the system prompt, extract it
|
||||
if rendered_messages and len(rendered_messages) > 0:
|
||||
system_message = rendered_messages[0]
|
||||
# Prepend system message to existing messages
|
||||
return {"messages": [system_message]}
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to apply prompt template in before_model: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
return None
|
||||
|
||||
async def abefore_model(self, state: Any, runtime: Runtime) -> dict[str, Any] | None:
|
||||
"""Async version of before_model."""
|
||||
return self.before_model(state, runtime)
|
||||
|
||||
|
||||
class PreModelHookMiddleware(AgentMiddleware):
|
||||
"""Middleware to execute a pre-model hook before model invocation.
|
||||
|
||||
This middleware wraps the legacy pre_model_hook callable and executes it
|
||||
as part of the middleware chain.
|
||||
"""
|
||||
|
||||
def __init__(self, pre_model_hook: Callable):
|
||||
self._pre_model_hook = pre_model_hook
|
||||
|
||||
def before_model(self, state: Any, runtime: Runtime) -> dict[str, Any] | None:
|
||||
"""Execute the pre-model hook."""
|
||||
if not self._pre_model_hook:
|
||||
return None
|
||||
|
||||
try:
|
||||
result = self._pre_model_hook(state, runtime)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Pre-model hook execution failed in before_model: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
return None
|
||||
|
||||
async def abefore_model(self, state: Any, runtime: Runtime) -> dict[str, Any] | None:
|
||||
"""Async version of before_model."""
|
||||
if not self._pre_model_hook:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Check if the hook is async
|
||||
if inspect.iscoroutinefunction(self._pre_model_hook):
|
||||
result = await self._pre_model_hook(state, runtime)
|
||||
else:
|
||||
# Run synchronous hook in thread pool to avoid blocking event loop
|
||||
result = await asyncio.to_thread(self._pre_model_hook, state, runtime)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Pre-model hook execution failed in abefore_model: {e}",
|
||||
exc_info=True
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
# Create agents using configured LLM types
|
||||
def create_agent(
|
||||
agent_name: str,
|
||||
agent_type: str,
|
||||
tools: list,
|
||||
prompt_template: str,
|
||||
pre_model_hook: callable = None,
|
||||
interrupt_before_tools: Optional[List[str]] = None,
|
||||
locale: str = "en-US",
|
||||
):
|
||||
"""Factory function to create agents with consistent configuration.
|
||||
|
||||
Args:
|
||||
agent_name: Name of the agent
|
||||
agent_type: Type of agent (researcher, coder, etc.)
|
||||
tools: List of tools available to the agent
|
||||
prompt_template: Name of the prompt template to use
|
||||
pre_model_hook: Optional hook to preprocess state before model invocation
|
||||
interrupt_before_tools: Optional list of tool names to interrupt before execution
|
||||
locale: Language locale for prompt template selection (e.g., en-US, zh-CN)
|
||||
|
||||
Returns:
|
||||
A configured agent graph
|
||||
"""
|
||||
logger.debug(
|
||||
f"Creating agent '{agent_name}' of type '{agent_type}' "
|
||||
f"with {len(tools)} tools and template '{prompt_template}'"
|
||||
)
|
||||
|
||||
# Wrap tools with interrupt logic if specified
|
||||
processed_tools = tools
|
||||
if interrupt_before_tools:
|
||||
logger.info(
|
||||
f"Creating agent '{agent_name}' with tool-specific interrupts: {interrupt_before_tools}"
|
||||
)
|
||||
logger.debug(f"Wrapping {len(tools)} tools for agent '{agent_name}'")
|
||||
processed_tools = wrap_tools_with_interceptor(tools, interrupt_before_tools)
|
||||
logger.debug(f"Agent '{agent_name}' tool wrapping completed")
|
||||
else:
|
||||
logger.debug(f"Agent '{agent_name}' has no interrupt-before-tools configured")
|
||||
|
||||
if agent_type not in AGENT_LLM_MAP:
|
||||
logger.warning(
|
||||
f"Agent type '{agent_type}' not found in AGENT_LLM_MAP. "
|
||||
f"Falling back to default LLM type 'basic' for agent '{agent_name}'. "
|
||||
"This may indicate a configuration issue."
|
||||
)
|
||||
llm_type = AGENT_LLM_MAP.get(agent_type, "basic")
|
||||
logger.debug(f"Agent '{agent_name}' using LLM type: {llm_type}")
|
||||
|
||||
logger.debug(f"Creating agent '{agent_name}' with locale: {locale}")
|
||||
|
||||
# Build middleware list
|
||||
# Use closure to capture locale from the workflow state instead of relying on
|
||||
# agent state.get("locale"), which doesn't have the locale field
|
||||
# See: https://github.com/bytedance/deer-flow/issues/743
|
||||
middleware = [DynamicPromptMiddleware(prompt_template, locale)]
|
||||
|
||||
# Add pre-model hook middleware if provided
|
||||
if pre_model_hook:
|
||||
middleware.append(PreModelHookMiddleware(pre_model_hook))
|
||||
|
||||
agent = langchain_create_agent(
|
||||
name=agent_name,
|
||||
model=get_llm_by_type(llm_type),
|
||||
tools=processed_tools,
|
||||
middleware=middleware,
|
||||
)
|
||||
logger.info(f"Agent '{agent_name}' created successfully")
|
||||
|
||||
return agent
|
||||
@@ -1,245 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Callable, List, Optional
|
||||
|
||||
from langchain_core.tools import BaseTool
|
||||
from langgraph.types import interrupt
|
||||
|
||||
from src.utils.log_sanitizer import (
|
||||
sanitize_feedback,
|
||||
sanitize_log_input,
|
||||
sanitize_tool_name,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ToolInterceptor:
|
||||
"""Intercepts tool calls and triggers interrupts for specified tools."""
|
||||
|
||||
def __init__(self, interrupt_before_tools: Optional[List[str]] = None):
|
||||
"""Initialize the interceptor with list of tools to interrupt before.
|
||||
|
||||
Args:
|
||||
interrupt_before_tools: List of tool names to interrupt before execution.
|
||||
If None or empty, no interrupts are triggered.
|
||||
"""
|
||||
self.interrupt_before_tools = interrupt_before_tools or []
|
||||
logger.info(
|
||||
f"ToolInterceptor initialized with interrupt_before_tools: {self.interrupt_before_tools}"
|
||||
)
|
||||
|
||||
def should_interrupt(self, tool_name: str) -> bool:
|
||||
"""Check if execution should be interrupted before this tool.
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool being called
|
||||
|
||||
Returns:
|
||||
bool: True if tool should trigger an interrupt, False otherwise
|
||||
"""
|
||||
should_interrupt = tool_name in self.interrupt_before_tools
|
||||
if should_interrupt:
|
||||
logger.info(f"Tool '{tool_name}' marked for interrupt")
|
||||
return should_interrupt
|
||||
|
||||
@staticmethod
|
||||
def _format_tool_input(tool_input: Any) -> str:
|
||||
"""Format tool input for display in interrupt messages.
|
||||
|
||||
Attempts to format as JSON for better readability, with fallback to string representation.
|
||||
|
||||
Args:
|
||||
tool_input: The tool input to format
|
||||
|
||||
Returns:
|
||||
str: Formatted representation of the tool input
|
||||
"""
|
||||
if tool_input is None:
|
||||
return "No input"
|
||||
|
||||
# Try to serialize as JSON first for better readability
|
||||
try:
|
||||
# Handle dictionaries and other JSON-serializable objects
|
||||
if isinstance(tool_input, (dict, list, tuple)):
|
||||
return json.dumps(tool_input, indent=2, default=str)
|
||||
elif isinstance(tool_input, str):
|
||||
return tool_input
|
||||
else:
|
||||
# For other types, try to convert to dict if it has __dict__
|
||||
# Otherwise fall back to string representation
|
||||
return str(tool_input)
|
||||
except (TypeError, ValueError):
|
||||
# JSON serialization failed, use string representation
|
||||
return str(tool_input)
|
||||
|
||||
@staticmethod
|
||||
def wrap_tool(
|
||||
tool: BaseTool, interceptor: "ToolInterceptor"
|
||||
) -> BaseTool:
|
||||
"""Wrap a tool to add interrupt logic by creating a wrapper.
|
||||
|
||||
Args:
|
||||
tool: The tool to wrap
|
||||
interceptor: The ToolInterceptor instance
|
||||
|
||||
Returns:
|
||||
BaseTool: The wrapped tool with interrupt capability
|
||||
"""
|
||||
original_func = tool.func
|
||||
safe_tool_name = sanitize_tool_name(tool.name)
|
||||
logger.debug(f"Wrapping tool '{safe_tool_name}' with interrupt capability")
|
||||
|
||||
def intercepted_func(*args: Any, **kwargs: Any) -> Any:
|
||||
"""Execute the tool with interrupt check."""
|
||||
tool_name = tool.name
|
||||
safe_tool_name_local = sanitize_tool_name(tool_name)
|
||||
logger.debug(f"[ToolInterceptor] Executing tool: {safe_tool_name_local}")
|
||||
|
||||
# Format tool input for display
|
||||
tool_input = args[0] if args else kwargs
|
||||
tool_input_repr = ToolInterceptor._format_tool_input(tool_input)
|
||||
safe_tool_input = sanitize_log_input(tool_input_repr, max_length=100)
|
||||
logger.debug(f"[ToolInterceptor] Tool input: {safe_tool_input}")
|
||||
|
||||
should_interrupt = interceptor.should_interrupt(tool_name)
|
||||
logger.debug(f"[ToolInterceptor] should_interrupt={should_interrupt} for tool '{safe_tool_name_local}'")
|
||||
|
||||
if should_interrupt:
|
||||
logger.info(
|
||||
f"[ToolInterceptor] Interrupting before tool '{safe_tool_name_local}'"
|
||||
)
|
||||
logger.debug(
|
||||
f"[ToolInterceptor] Interrupt message: About to execute tool '{safe_tool_name_local}' with input: {safe_tool_input}..."
|
||||
)
|
||||
|
||||
# Trigger interrupt and wait for user feedback
|
||||
try:
|
||||
feedback = interrupt(
|
||||
f"About to execute tool: '{tool_name}'\n\nInput:\n{tool_input_repr}\n\nApprove execution?"
|
||||
)
|
||||
safe_feedback = sanitize_feedback(feedback)
|
||||
logger.debug(f"[ToolInterceptor] Interrupt returned with feedback: {f'{safe_feedback[:100]}...' if safe_feedback and len(safe_feedback) > 100 else safe_feedback if safe_feedback else 'None'}")
|
||||
except Exception as e:
|
||||
logger.error(f"[ToolInterceptor] Error during interrupt: {str(e)}")
|
||||
raise
|
||||
|
||||
logger.debug(f"[ToolInterceptor] Processing feedback approval for '{safe_tool_name_local}'")
|
||||
|
||||
# Check if user approved
|
||||
is_approved = ToolInterceptor._parse_approval(feedback)
|
||||
logger.info(f"[ToolInterceptor] Tool '{safe_tool_name_local}' approval decision: {is_approved}")
|
||||
|
||||
if not is_approved:
|
||||
logger.warning(f"[ToolInterceptor] User rejected execution of tool '{safe_tool_name_local}'")
|
||||
return {
|
||||
"error": f"Tool execution rejected by user",
|
||||
"tool": tool_name,
|
||||
"status": "rejected",
|
||||
}
|
||||
|
||||
logger.info(f"[ToolInterceptor] User approved execution of tool '{safe_tool_name_local}', proceeding")
|
||||
|
||||
# Execute the original tool
|
||||
try:
|
||||
logger.debug(f"[ToolInterceptor] Calling original function for tool '{safe_tool_name_local}'")
|
||||
result = original_func(*args, **kwargs)
|
||||
logger.info(f"[ToolInterceptor] Tool '{safe_tool_name_local}' execution completed successfully")
|
||||
result_len = len(str(result))
|
||||
logger.debug(f"[ToolInterceptor] Tool result length: {result_len}")
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"[ToolInterceptor] Error executing tool '{safe_tool_name_local}': {str(e)}")
|
||||
raise
|
||||
|
||||
# Replace the function and update the tool
|
||||
# Use object.__setattr__ to bypass Pydantic validation
|
||||
logger.debug(f"Attaching intercepted function to tool '{safe_tool_name}'")
|
||||
object.__setattr__(tool, "func", intercepted_func)
|
||||
|
||||
# Also ensure the tool's _run method is updated if it exists
|
||||
if hasattr(tool, '_run'):
|
||||
logger.debug(f"Also wrapping _run method for tool '{safe_tool_name}'")
|
||||
# Wrap _run to ensure interception is applied regardless of invocation method
|
||||
object.__setattr__(tool, "_run", intercepted_func)
|
||||
|
||||
return tool
|
||||
|
||||
@staticmethod
|
||||
def _parse_approval(feedback: str) -> bool:
|
||||
"""Parse user feedback to determine if tool execution was approved.
|
||||
|
||||
Args:
|
||||
feedback: The feedback string from the user
|
||||
|
||||
Returns:
|
||||
bool: True if feedback indicates approval, False otherwise
|
||||
"""
|
||||
if not feedback:
|
||||
logger.warning("Empty feedback received, treating as rejection")
|
||||
return False
|
||||
|
||||
feedback_lower = feedback.lower().strip()
|
||||
|
||||
# Check for approval keywords
|
||||
approval_keywords = [
|
||||
"approved",
|
||||
"approve",
|
||||
"yes",
|
||||
"proceed",
|
||||
"continue",
|
||||
"ok",
|
||||
"okay",
|
||||
"accepted",
|
||||
"accept",
|
||||
"[approved]",
|
||||
]
|
||||
|
||||
for keyword in approval_keywords:
|
||||
if keyword in feedback_lower:
|
||||
return True
|
||||
|
||||
# Default to rejection if no approval keywords found
|
||||
logger.warning(
|
||||
f"No approval keywords found in feedback: {feedback}. Treating as rejection."
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def wrap_tools_with_interceptor(
|
||||
tools: List[BaseTool], interrupt_before_tools: Optional[List[str]] = None
|
||||
) -> List[BaseTool]:
|
||||
"""Wrap multiple tools with interrupt logic.
|
||||
|
||||
Args:
|
||||
tools: List of tools to wrap
|
||||
interrupt_before_tools: List of tool names to interrupt before
|
||||
|
||||
Returns:
|
||||
List[BaseTool]: List of wrapped tools
|
||||
"""
|
||||
if not interrupt_before_tools:
|
||||
logger.debug("No tool interrupts configured, returning tools as-is")
|
||||
return tools
|
||||
|
||||
logger.info(
|
||||
f"Wrapping {len(tools)} tools with interrupt logic for: {interrupt_before_tools}"
|
||||
)
|
||||
interceptor = ToolInterceptor(interrupt_before_tools)
|
||||
|
||||
wrapped_tools = []
|
||||
for tool in tools:
|
||||
try:
|
||||
wrapped_tool = ToolInterceptor.wrap_tool(tool, interceptor)
|
||||
wrapped_tools.append(wrapped_tool)
|
||||
logger.debug(f"Wrapped tool: {tool.name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to wrap tool {tool.name}: {str(e)}")
|
||||
# Add original tool if wrapping fails
|
||||
wrapped_tools.append(tool)
|
||||
|
||||
logger.info(f"Successfully wrapped {len(wrapped_tools)} tools")
|
||||
return wrapped_tools
|
||||
@@ -1,28 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Citation management module for DeerFlow.
|
||||
|
||||
This module provides structured citation/source metadata handling
|
||||
for research reports, enabling proper attribution and inline citations.
|
||||
"""
|
||||
|
||||
from .collector import CitationCollector
|
||||
from .extractor import (
|
||||
citations_to_markdown_references,
|
||||
extract_citations_from_messages,
|
||||
merge_citations,
|
||||
)
|
||||
from .formatter import CitationFormatter
|
||||
from .models import Citation, CitationMetadata
|
||||
|
||||
__all__ = [
|
||||
"Citation",
|
||||
"CitationMetadata",
|
||||
"CitationCollector",
|
||||
"CitationFormatter",
|
||||
"extract_citations_from_messages",
|
||||
"merge_citations",
|
||||
"citations_to_markdown_references",
|
||||
]
|
||||
@@ -1,285 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Citation collector for gathering and managing citations during research.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from .models import Citation, CitationMetadata
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CitationCollector:
|
||||
"""
|
||||
Collects and manages citations during the research process.
|
||||
|
||||
This class handles:
|
||||
- Collecting citations from search results and crawled pages
|
||||
- Deduplicating citations by URL
|
||||
- Assigning citation numbers
|
||||
- Tracking which citations are actually used in the report
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._citations: Dict[str, CitationMetadata] = {} # url -> metadata
|
||||
self._citation_order: List[str] = [] # ordered list of URLs
|
||||
self._used_citations: set[str] = set() # URLs that are actually cited
|
||||
self._url_to_index: Dict[str, int] = {} # url -> index of _citation_order (O(1) lookup)
|
||||
|
||||
def add_from_search_results(
|
||||
self, results: List[Dict[str, Any]], query: str = ""
|
||||
) -> List[CitationMetadata]:
|
||||
"""
|
||||
Add citations from search results.
|
||||
|
||||
Args:
|
||||
results: List of search result dictionaries
|
||||
query: The search query that produced these results
|
||||
|
||||
Returns:
|
||||
List of CitationMetadata objects that were added
|
||||
"""
|
||||
added = []
|
||||
for result in results:
|
||||
# Skip image results
|
||||
if result.get("type") == "image_url":
|
||||
continue
|
||||
|
||||
url = result.get("url")
|
||||
if not url:
|
||||
continue
|
||||
|
||||
# Create or update citation metadata
|
||||
metadata = CitationMetadata.from_search_result(result, query)
|
||||
|
||||
if url not in self._citations:
|
||||
self._citations[url] = metadata
|
||||
self._citation_order.append(url)
|
||||
self._url_to_index[url] = len(self._citation_order) - 1
|
||||
added.append(metadata)
|
||||
logger.debug(f"Added citation: {metadata.title} ({url})")
|
||||
else:
|
||||
# Update with potentially better metadata
|
||||
existing = self._citations[url]
|
||||
if metadata.relevance_score > existing.relevance_score:
|
||||
self._citations[url] = metadata
|
||||
logger.debug(f"Updated citation: {metadata.title} ({url})")
|
||||
|
||||
return added
|
||||
|
||||
def add_from_crawl_result(
|
||||
self, url: str, title: str, content: Optional[str] = None, **extra_metadata
|
||||
) -> CitationMetadata:
|
||||
"""
|
||||
Add or update a citation from a crawled page.
|
||||
|
||||
Args:
|
||||
url: The URL of the crawled page
|
||||
title: The page title
|
||||
content: The page content
|
||||
**extra_metadata: Additional metadata fields
|
||||
|
||||
Returns:
|
||||
The CitationMetadata object
|
||||
"""
|
||||
if url in self._citations:
|
||||
# Update existing citation with crawled content
|
||||
metadata = self._citations[url]
|
||||
if title and title != "Untitled":
|
||||
metadata.title = title
|
||||
if content:
|
||||
metadata.raw_content = content
|
||||
if not metadata.content_snippet:
|
||||
metadata.content_snippet = content[:500]
|
||||
else:
|
||||
# Create new citation
|
||||
metadata = CitationMetadata(
|
||||
url=url,
|
||||
title=title or "Untitled",
|
||||
content_snippet=content[:500] if content else None,
|
||||
raw_content=content,
|
||||
**extra_metadata,
|
||||
)
|
||||
self._citations[url] = metadata
|
||||
self._citation_order.append(url)
|
||||
self._url_to_index[url] = len(self._citation_order) - 1
|
||||
|
||||
return metadata
|
||||
|
||||
def mark_used(self, url: str) -> Optional[int]:
|
||||
"""
|
||||
Mark a citation as used and return its number.
|
||||
|
||||
Args:
|
||||
url: The URL of the citation
|
||||
|
||||
Returns:
|
||||
The citation number (1-indexed) or None if not found
|
||||
"""
|
||||
if url in self._citations:
|
||||
self._used_citations.add(url)
|
||||
return self.get_number(url)
|
||||
return None
|
||||
|
||||
def get_number(self, url: str) -> Optional[int]:
|
||||
"""
|
||||
Get the citation number for a URL (O(1) time complexity).
|
||||
|
||||
Args:
|
||||
url: The URL to look up
|
||||
|
||||
Returns:
|
||||
The citation number (1-indexed) or None if not found
|
||||
"""
|
||||
index = self._url_to_index.get(url)
|
||||
return index + 1 if index is not None else None
|
||||
|
||||
def get_metadata(self, url: str) -> Optional[CitationMetadata]:
|
||||
"""
|
||||
Get the metadata for a URL.
|
||||
|
||||
Args:
|
||||
url: The URL to look up
|
||||
|
||||
Returns:
|
||||
The CitationMetadata or None if not found
|
||||
"""
|
||||
return self._citations.get(url)
|
||||
|
||||
def get_all_citations(self) -> List[Citation]:
|
||||
"""
|
||||
Get all collected citations in order.
|
||||
|
||||
Returns:
|
||||
List of Citation objects
|
||||
"""
|
||||
citations = []
|
||||
for i, url in enumerate(self._citation_order):
|
||||
metadata = self._citations[url]
|
||||
citations.append(
|
||||
Citation(
|
||||
number=i + 1,
|
||||
metadata=metadata,
|
||||
)
|
||||
)
|
||||
return citations
|
||||
|
||||
def get_used_citations(self) -> List[Citation]:
|
||||
"""
|
||||
Get only the citations that have been marked as used.
|
||||
|
||||
Returns:
|
||||
List of Citation objects that are actually used
|
||||
"""
|
||||
citations = []
|
||||
number = 1
|
||||
for url in self._citation_order:
|
||||
if url in self._used_citations:
|
||||
metadata = self._citations[url]
|
||||
citations.append(
|
||||
Citation(
|
||||
number=number,
|
||||
metadata=metadata,
|
||||
)
|
||||
)
|
||||
number += 1
|
||||
return citations
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Serialize the collector state to a dictionary.
|
||||
|
||||
Returns:
|
||||
Dictionary representation of the collector
|
||||
"""
|
||||
return {
|
||||
"citations": [c.to_dict() for c in self.get_all_citations()],
|
||||
"used_urls": list(self._used_citations),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "CitationCollector":
|
||||
"""
|
||||
Deserialize a collector from a dictionary.
|
||||
|
||||
Args:
|
||||
data: Dictionary representation
|
||||
|
||||
Returns:
|
||||
CitationCollector instance
|
||||
"""
|
||||
collector = cls()
|
||||
for citation_data in data.get("citations", []):
|
||||
citation = Citation.from_dict(citation_data)
|
||||
collector._citations[citation.url] = citation.metadata
|
||||
index = len(collector._citation_order)
|
||||
collector._citation_order.append(citation.url)
|
||||
collector._url_to_index[citation.url] = index
|
||||
collector._used_citations = set(data.get("used_urls", []))
|
||||
return collector
|
||||
|
||||
def merge_with(self, other: "CitationCollector") -> None:
|
||||
"""
|
||||
Merge another collector's citations into this one.
|
||||
|
||||
Args:
|
||||
other: Another CitationCollector to merge
|
||||
"""
|
||||
for url in other._citation_order:
|
||||
if url not in self._citations:
|
||||
self._citations[url] = other._citations[url]
|
||||
self._citation_order.append(url)
|
||||
self._url_to_index[url] = len(self._citation_order) - 1
|
||||
self._used_citations.update(other._used_citations)
|
||||
|
||||
@property
|
||||
def count(self) -> int:
|
||||
"""Return the total number of citations."""
|
||||
return len(self._citations)
|
||||
|
||||
@property
|
||||
def used_count(self) -> int:
|
||||
"""Return the number of used citations."""
|
||||
return len(self._used_citations)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear all citations."""
|
||||
self._citations.clear()
|
||||
self._citation_order.clear()
|
||||
self._used_citations.clear()
|
||||
self._url_to_index.clear()
|
||||
|
||||
|
||||
def extract_urls_from_text(text: str) -> List[str]:
|
||||
"""
|
||||
Extract URLs from markdown text.
|
||||
|
||||
Args:
|
||||
text: Markdown text that may contain URLs
|
||||
|
||||
Returns:
|
||||
List of URLs found in the text
|
||||
"""
|
||||
import re
|
||||
|
||||
urls = []
|
||||
|
||||
# Match markdown links: [text](url)
|
||||
markdown_pattern = r"\[([^\]]+)\]\(([^)]+)\)"
|
||||
for match in re.finditer(markdown_pattern, text):
|
||||
url = match.group(2)
|
||||
if url.startswith(("http://", "https://")):
|
||||
urls.append(url)
|
||||
|
||||
# Match bare URLs
|
||||
bare_url_pattern = r"(?<![\(\[])(https?://[^\s\)>\]]+)"
|
||||
for match in re.finditer(bare_url_pattern, text):
|
||||
url = match.group(1)
|
||||
if url not in urls:
|
||||
urls.append(url)
|
||||
|
||||
return urls
|
||||
@@ -1,445 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Citation extraction utilities for extracting citations from tool results.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langchain_core.messages import AIMessage, ToolMessage
|
||||
|
||||
from .models import CitationMetadata
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_citations_from_messages(messages: List[Any]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract citation metadata from agent messages (tool calls/results).
|
||||
|
||||
Args:
|
||||
messages: List of messages from agent execution
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
seen_urls = set()
|
||||
|
||||
logger.info(f"[Citations] Starting extraction from {len(messages)} messages")
|
||||
|
||||
for message in messages:
|
||||
# Extract from ToolMessage results (web_search, crawl)
|
||||
if isinstance(message, ToolMessage):
|
||||
logger.info(
|
||||
f"[Citations] Found ToolMessage: name={getattr(message, 'name', 'unknown')}"
|
||||
)
|
||||
tool_citations = _extract_from_tool_message(message)
|
||||
for citation in tool_citations:
|
||||
url = citation.get("url", "")
|
||||
if url and url not in seen_urls:
|
||||
seen_urls.add(url)
|
||||
citations.append(citation)
|
||||
|
||||
# Also check AIMessage tool_calls for any embedded results
|
||||
if isinstance(message, AIMessage) and hasattr(message, "tool_calls"):
|
||||
for tool_call in message.tool_calls or []:
|
||||
if tool_call.get("name") == "web_search":
|
||||
# The query is in the args
|
||||
query = tool_call.get("args", {}).get("query", "")
|
||||
logger.info(
|
||||
"[Citations] Found web_search tool call with query=%r", query
|
||||
)
|
||||
# Note: results come in subsequent ToolMessage
|
||||
|
||||
logger.info(
|
||||
f"[Citations] Extracted {len(citations)} unique citations from {len(messages)} messages"
|
||||
)
|
||||
return citations
|
||||
|
||||
|
||||
def _extract_from_tool_message(message: ToolMessage) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract citations from a tool message result.
|
||||
|
||||
Args:
|
||||
message: ToolMessage with tool execution result
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
tool_name = getattr(message, "name", "") or ""
|
||||
content = getattr(message, "content", "")
|
||||
|
||||
logger.info(
|
||||
f"Processing tool message: tool_name='{tool_name}', content_len={len(str(content)) if content else 0}"
|
||||
)
|
||||
|
||||
if not content:
|
||||
return citations
|
||||
|
||||
# Parse JSON content
|
||||
try:
|
||||
if isinstance(content, str):
|
||||
data = json.loads(content)
|
||||
else:
|
||||
data = content
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
logger.debug(
|
||||
f"Could not parse tool message content as JSON: {str(content)[:100]}..."
|
||||
)
|
||||
return citations
|
||||
|
||||
logger.debug(f"Parsed tool message data type: {type(data).__name__}")
|
||||
|
||||
# Try to detect content type by structure rather than just tool name
|
||||
tool_name_lower = tool_name.lower() if tool_name else ""
|
||||
|
||||
# Handle web_search results (by name or by structure)
|
||||
if tool_name_lower in (
|
||||
"web_search",
|
||||
"tavily_search",
|
||||
"duckduckgo_search",
|
||||
"brave_search",
|
||||
"searx_search",
|
||||
):
|
||||
citations.extend(_extract_from_search_results(data))
|
||||
logger.debug(
|
||||
f"Extracted {len(citations)} citations from search tool '{tool_name}'"
|
||||
)
|
||||
|
||||
# Handle crawl results (by name or by structure)
|
||||
elif tool_name_lower in ("crawl_tool", "crawl", "jina_crawl"):
|
||||
citation = _extract_from_crawl_result(data)
|
||||
if citation:
|
||||
citations.append(citation)
|
||||
logger.debug(f"Extracted 1 citation from crawl tool '{tool_name}'")
|
||||
|
||||
# Fallback: Try to detect by data structure
|
||||
else:
|
||||
# Check if it looks like search results (list of items with url)
|
||||
if isinstance(data, list) and len(data) > 0:
|
||||
first_item = data[0]
|
||||
if isinstance(first_item, dict) and "url" in first_item:
|
||||
logger.debug(
|
||||
f"Auto-detected search results format for tool '{tool_name}'"
|
||||
)
|
||||
citations.extend(_extract_from_search_results(data))
|
||||
# Check if it looks like crawl result (dict with url and crawled_content)
|
||||
elif (
|
||||
isinstance(data, dict)
|
||||
and "url" in data
|
||||
and ("crawled_content" in data or "content" in data)
|
||||
):
|
||||
logger.debug(f"Auto-detected crawl result format for tool '{tool_name}'")
|
||||
citation = _extract_from_crawl_result(data)
|
||||
if citation:
|
||||
citations.append(citation)
|
||||
|
||||
return citations
|
||||
|
||||
|
||||
def _extract_from_search_results(data: Any) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Extract citations from web search results.
|
||||
|
||||
Args:
|
||||
data: Parsed JSON data from search tool
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
|
||||
# Handle list of results
|
||||
if isinstance(data, list):
|
||||
for result in data:
|
||||
if isinstance(result, dict) and result.get("type") != "image_url":
|
||||
citation = _result_to_citation(result)
|
||||
if citation:
|
||||
citations.append(citation)
|
||||
|
||||
# Handle dict with results key
|
||||
elif isinstance(data, dict):
|
||||
if "error" in data:
|
||||
logger.warning(f"Search error: {data.get('error')}")
|
||||
return citations
|
||||
|
||||
results = data.get("results", [])
|
||||
for result in results:
|
||||
if isinstance(result, dict) and result.get("type") != "image_url":
|
||||
citation = _result_to_citation(result)
|
||||
if citation:
|
||||
citations.append(citation)
|
||||
|
||||
return citations
|
||||
|
||||
|
||||
def _result_to_citation(result: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Convert a search result to a citation dictionary.
|
||||
|
||||
Args:
|
||||
result: Search result dictionary
|
||||
|
||||
Returns:
|
||||
Citation dictionary or None
|
||||
"""
|
||||
url = result.get("url", "")
|
||||
if not url:
|
||||
return None
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"title": result.get("title", "Untitled"),
|
||||
"description": result.get("content", ""),
|
||||
"content_snippet": (result.get("content", "") or "")[:500],
|
||||
"relevance_score": result.get("score", 0.0),
|
||||
"domain": _extract_domain(url),
|
||||
"accessed_at": None, # Will be filled by CitationMetadata
|
||||
"source_type": "web_search",
|
||||
}
|
||||
|
||||
|
||||
def extract_title_from_content(content: Optional[str], max_length: int = 200) -> str:
|
||||
"""
|
||||
Intelligent title extraction supporting multiple formats.
|
||||
|
||||
Priority:
|
||||
1. HTML <title> tag
|
||||
2. Markdown h1 (# Title)
|
||||
3. Markdown h2-h6 (## Title, etc.)
|
||||
4. JSON/YAML title field
|
||||
5. First substantial non-empty line
|
||||
6. "Untitled" as fallback
|
||||
|
||||
Args:
|
||||
content: The content to extract title from (can be None)
|
||||
max_length: Maximum title length (default: 200)
|
||||
|
||||
Returns:
|
||||
Extracted title or "Untitled"
|
||||
"""
|
||||
if not content:
|
||||
return "Untitled"
|
||||
|
||||
# 1. Try HTML title tag
|
||||
html_title_match = re.search(
|
||||
r'<title[^>]*>([^<]+)</title>',
|
||||
content,
|
||||
re.IGNORECASE | re.DOTALL
|
||||
)
|
||||
if html_title_match:
|
||||
title = html_title_match.group(1).strip()
|
||||
if title:
|
||||
return title[:max_length]
|
||||
|
||||
# 2. Try Markdown h1 (exact match of only one #)
|
||||
md_h1_match = re.search(
|
||||
r'^#{1}\s+(.+?)$',
|
||||
content,
|
||||
re.MULTILINE
|
||||
)
|
||||
if md_h1_match:
|
||||
title = md_h1_match.group(1).strip()
|
||||
if title:
|
||||
return title[:max_length]
|
||||
|
||||
# 3. Try any Markdown heading (h2-h6)
|
||||
md_heading_match = re.search(
|
||||
r'^#{2,6}\s+(.+?)$',
|
||||
content,
|
||||
re.MULTILINE
|
||||
)
|
||||
if md_heading_match:
|
||||
title = md_heading_match.group(1).strip()
|
||||
if title:
|
||||
return title[:max_length]
|
||||
|
||||
# 4. Try JSON/YAML title field
|
||||
json_title_match = re.search(
|
||||
r'"?title"?\s*:\s*["\']?([^"\'\n]+)["\']?',
|
||||
content,
|
||||
re.IGNORECASE
|
||||
)
|
||||
if json_title_match:
|
||||
title = json_title_match.group(1).strip()
|
||||
if title and len(title) > 3:
|
||||
return title[:max_length]
|
||||
|
||||
# 5. First substantial non-empty line
|
||||
for line in content.split('\n'):
|
||||
line = line.strip()
|
||||
# Skip short lines, code blocks, list items, and separators
|
||||
if (line and
|
||||
len(line) > 10 and
|
||||
not line.startswith(('```', '---', '***', '- ', '* ', '+ ', '#'))):
|
||||
return line[:max_length]
|
||||
|
||||
return "Untitled"
|
||||
|
||||
|
||||
def _extract_from_crawl_result(data: Any) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Extract citation from crawl tool result.
|
||||
|
||||
Args:
|
||||
data: Parsed JSON data from crawl tool
|
||||
|
||||
Returns:
|
||||
Citation dictionary or None
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
return None
|
||||
|
||||
url = data.get("url", "")
|
||||
if not url:
|
||||
return None
|
||||
|
||||
content = data.get("crawled_content", "")
|
||||
|
||||
# Extract title using intelligent extraction function
|
||||
title = extract_title_from_content(content)
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"title": title,
|
||||
"description": content[:300] if content else "",
|
||||
"content_snippet": content[:500] if content else "",
|
||||
"raw_content": content,
|
||||
"domain": _extract_domain(url),
|
||||
"source_type": "crawl",
|
||||
}
|
||||
|
||||
|
||||
def _extract_domain(url: Optional[str]) -> str:
|
||||
"""
|
||||
Extract domain from URL using urllib with regex fallback.
|
||||
|
||||
Handles:
|
||||
- Standard URLs: https://www.example.com/path
|
||||
- Short URLs: example.com
|
||||
- Invalid URLs: graceful fallback
|
||||
|
||||
Args:
|
||||
url: The URL string to extract domain from (can be None)
|
||||
|
||||
Returns:
|
||||
The domain netloc (including port if present), or empty string if extraction fails
|
||||
"""
|
||||
if not url:
|
||||
return ""
|
||||
|
||||
# Approach 1: Try urllib first (fast path for standard URLs)
|
||||
try:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
parsed = urlparse(url)
|
||||
if parsed.netloc:
|
||||
return parsed.netloc
|
||||
except Exception as e:
|
||||
logger.debug(f"URL parsing failed for {url}: {e}")
|
||||
|
||||
# Approach 2: Regex fallback (for non-standard or bare URLs without scheme)
|
||||
# Matches: domain[:port] where domain is a valid hostname
|
||||
# Pattern breakdown:
|
||||
# ([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)
|
||||
# - domain labels separated by dots, each 1-63 chars, starting/ending with alphanumeric
|
||||
# (?::\d+)? - optional port
|
||||
pattern = r'^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*(?::\d+)?)(?:[/?#]|$)'
|
||||
|
||||
match = re.match(pattern, url)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
logger.warning(f"Could not extract domain from URL: {url}")
|
||||
return ""
|
||||
|
||||
|
||||
def merge_citations(
|
||||
existing: List[Dict[str, Any]], new: List[Dict[str, Any]]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Merge new citations into existing list, avoiding duplicates.
|
||||
|
||||
Args:
|
||||
existing: Existing citations list
|
||||
new: New citations to add
|
||||
|
||||
Returns:
|
||||
Merged list of citations
|
||||
"""
|
||||
seen_urls = {c.get("url") for c in existing if c.get("url")}
|
||||
result = list(existing)
|
||||
|
||||
for citation in new:
|
||||
url = citation.get("url", "")
|
||||
if url and url not in seen_urls:
|
||||
seen_urls.add(url)
|
||||
result.append(citation)
|
||||
elif url in seen_urls:
|
||||
# Update existing citation with potentially better data
|
||||
for i, existing_citation in enumerate(result):
|
||||
if existing_citation.get("url") == url:
|
||||
# Prefer higher relevance score
|
||||
if citation.get("relevance_score", 0) > existing_citation.get(
|
||||
"relevance_score", 0
|
||||
):
|
||||
# Update selectively instead of blindly merging all fields.
|
||||
updated = existing_citation.copy()
|
||||
# Always update relevance_score
|
||||
if "relevance_score" in citation:
|
||||
updated["relevance_score"] = citation["relevance_score"]
|
||||
# Merge other metadata only if improved (here assuming non-empty is 'better')
|
||||
for key in ("title", "description", "snippet"):
|
||||
new_value = citation.get(key)
|
||||
if new_value:
|
||||
updated[key] = new_value
|
||||
result[i] = updated
|
||||
break
|
||||
break
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def citations_to_markdown_references(citations: List[Dict[str, Any]]) -> str:
|
||||
"""
|
||||
Convert citations list to markdown references section.
|
||||
|
||||
Args:
|
||||
citations: List of citation dictionaries
|
||||
|
||||
Returns:
|
||||
Markdown formatted references section
|
||||
"""
|
||||
if not citations:
|
||||
return ""
|
||||
|
||||
lines = ["## Key Citations", ""]
|
||||
|
||||
for i, citation in enumerate(citations, 1):
|
||||
title = citation.get("title", "Untitled")
|
||||
url = citation.get("url", "")
|
||||
domain = citation.get("domain", "")
|
||||
|
||||
# Main reference link
|
||||
lines.append(f"- [{title}]({url})")
|
||||
|
||||
# Add metadata as comment for parsing
|
||||
metadata_parts = []
|
||||
if domain:
|
||||
metadata_parts.append(f"domain: {domain}")
|
||||
if citation.get("relevance_score"):
|
||||
metadata_parts.append(f"score: {citation['relevance_score']:.2f}")
|
||||
|
||||
if metadata_parts:
|
||||
lines.append(f" <!-- {', '.join(metadata_parts)} -->")
|
||||
|
||||
lines.append("") # Empty line between citations
|
||||
|
||||
return "\n".join(lines)
|
||||
@@ -1,397 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Citation formatter for generating citation sections and inline references.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from .models import Citation
|
||||
|
||||
|
||||
class CitationFormatter:
|
||||
"""
|
||||
Formats citations for display in reports.
|
||||
|
||||
Supports multiple citation styles:
|
||||
- numbered: [1], [2], etc.
|
||||
- superscript: ¹, ², etc.
|
||||
- footnote: [^1], [^2], etc.
|
||||
- inline: (Author, Year) or (Source)
|
||||
"""
|
||||
|
||||
SUPERSCRIPT_MAP = {
|
||||
"0": "⁰",
|
||||
"1": "¹",
|
||||
"2": "²",
|
||||
"3": "³",
|
||||
"4": "⁴",
|
||||
"5": "⁵",
|
||||
"6": "⁶",
|
||||
"7": "⁷",
|
||||
"8": "⁸",
|
||||
"9": "⁹",
|
||||
}
|
||||
|
||||
def __init__(self, style: str = "numbered"):
|
||||
"""
|
||||
Initialize the formatter.
|
||||
|
||||
Args:
|
||||
style: Citation style ('numbered', 'superscript', 'footnote', 'inline')
|
||||
"""
|
||||
self.style = style
|
||||
|
||||
def format_inline_marker(self, number: int) -> str:
|
||||
"""
|
||||
Format an inline citation marker.
|
||||
|
||||
Args:
|
||||
number: The citation number
|
||||
|
||||
Returns:
|
||||
Formatted marker string
|
||||
"""
|
||||
if self.style == "superscript":
|
||||
return "".join(self.SUPERSCRIPT_MAP.get(c, c) for c in str(number))
|
||||
elif self.style == "footnote":
|
||||
return f"[^{number}]"
|
||||
else: # numbered
|
||||
return f"[{number}]"
|
||||
|
||||
def format_reference(self, citation: Citation) -> str:
|
||||
"""
|
||||
Format a single reference for the citations section.
|
||||
|
||||
Args:
|
||||
citation: The citation to format
|
||||
|
||||
Returns:
|
||||
Formatted reference string
|
||||
"""
|
||||
metadata = citation.metadata
|
||||
|
||||
# Build reference with available metadata
|
||||
parts = []
|
||||
|
||||
# Number and title
|
||||
parts.append(f"[{citation.number}] **{metadata.title}**")
|
||||
|
||||
# Author if available
|
||||
if metadata.author:
|
||||
parts.append(f" *{metadata.author}*")
|
||||
|
||||
# Domain/source
|
||||
if metadata.domain:
|
||||
parts.append(f" Source: {metadata.domain}")
|
||||
|
||||
# Published date if available
|
||||
if metadata.published_date:
|
||||
parts.append(f" Published: {metadata.published_date}")
|
||||
|
||||
# URL
|
||||
parts.append(f" URL: {metadata.url}")
|
||||
|
||||
# Description/snippet
|
||||
if metadata.description:
|
||||
snippet = metadata.description[:200]
|
||||
if len(metadata.description) > 200:
|
||||
snippet += "..."
|
||||
parts.append(f" > {snippet}")
|
||||
|
||||
return "\n".join(parts)
|
||||
|
||||
def format_simple_reference(self, citation: Citation) -> str:
|
||||
"""
|
||||
Format a simple reference (title + URL).
|
||||
|
||||
Args:
|
||||
citation: The citation to format
|
||||
|
||||
Returns:
|
||||
Simple reference string
|
||||
"""
|
||||
return f"- [{citation.metadata.title}]({citation.metadata.url})"
|
||||
|
||||
def format_rich_reference(self, citation: Citation) -> str:
|
||||
"""
|
||||
Format a rich reference with metadata as JSON-like annotation.
|
||||
|
||||
Args:
|
||||
citation: The citation to format
|
||||
|
||||
Returns:
|
||||
Rich reference string with metadata
|
||||
"""
|
||||
metadata = citation.metadata
|
||||
parts = [f"- [{metadata.title}]({metadata.url})"]
|
||||
|
||||
annotations = []
|
||||
if metadata.domain:
|
||||
annotations.append(f"domain: {metadata.domain}")
|
||||
if metadata.relevance_score > 0:
|
||||
annotations.append(f"relevance: {metadata.relevance_score:.2f}")
|
||||
if metadata.accessed_at:
|
||||
annotations.append(f"accessed: {metadata.accessed_at[:10]}")
|
||||
|
||||
if annotations:
|
||||
parts.append(f" <!-- {', '.join(annotations)} -->")
|
||||
|
||||
return "\n".join(parts)
|
||||
|
||||
def format_citations_section(
|
||||
self, citations: List[Citation], include_metadata: bool = True
|
||||
) -> str:
|
||||
"""
|
||||
Format the full citations section for a report.
|
||||
|
||||
Args:
|
||||
citations: List of citations to include
|
||||
include_metadata: Whether to include rich metadata
|
||||
|
||||
Returns:
|
||||
Formatted citations section markdown
|
||||
"""
|
||||
if not citations:
|
||||
return ""
|
||||
|
||||
lines = ["## Key Citations", ""]
|
||||
|
||||
for citation in citations:
|
||||
if include_metadata:
|
||||
lines.append(self.format_rich_reference(citation))
|
||||
else:
|
||||
lines.append(self.format_simple_reference(citation))
|
||||
lines.append("") # Empty line between citations
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def format_footnotes_section(self, citations: List[Citation]) -> str:
|
||||
"""
|
||||
Format citations as footnotes (for footnote style).
|
||||
|
||||
Args:
|
||||
citations: List of citations
|
||||
|
||||
Returns:
|
||||
Footnotes section markdown
|
||||
"""
|
||||
if not citations:
|
||||
return ""
|
||||
|
||||
lines = ["", "---", ""]
|
||||
for citation in citations:
|
||||
lines.append(
|
||||
f"[^{citation.number}]: {citation.metadata.title} - {citation.metadata.url}"
|
||||
)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def add_citation_markers_to_text(
|
||||
self, text: str, citations: List[Citation], url_to_number: Dict[str, int]
|
||||
) -> str:
|
||||
"""
|
||||
Add citation markers to text where URLs are referenced.
|
||||
|
||||
Args:
|
||||
text: The text to process
|
||||
citations: Available citations
|
||||
url_to_number: Mapping from URL to citation number
|
||||
|
||||
Returns:
|
||||
Text with citation markers added
|
||||
"""
|
||||
|
||||
# Find all markdown links and add citation numbers
|
||||
def replace_link(match):
|
||||
full_match = match.group(0)
|
||||
url = match.group(2)
|
||||
|
||||
if url in url_to_number:
|
||||
number = url_to_number[url]
|
||||
marker = self.format_inline_marker(number)
|
||||
return f"{full_match}{marker}"
|
||||
return full_match
|
||||
|
||||
pattern = r"\[([^\]]+)\]\(([^)]+)\)"
|
||||
return re.sub(pattern, replace_link, text)
|
||||
|
||||
@staticmethod
|
||||
def build_citation_data_json(citations: List[Citation]) -> str:
|
||||
"""
|
||||
Build a JSON block containing citation data for frontend use.
|
||||
|
||||
Args:
|
||||
citations: List of citations
|
||||
|
||||
Returns:
|
||||
JSON string with citation data
|
||||
"""
|
||||
import json
|
||||
|
||||
data = {
|
||||
"citations": [c.to_dict() for c in citations],
|
||||
"count": len(citations),
|
||||
}
|
||||
|
||||
return json.dumps(data, ensure_ascii=False)
|
||||
|
||||
|
||||
def parse_citations_from_report(
|
||||
report: str, section_patterns: List[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Extract citation information from report, supporting multiple formats.
|
||||
|
||||
Supports various citation formats:
|
||||
- Markdown: [Title](URL)
|
||||
- Numbered: [1] Title - URL
|
||||
- Footnote: [^1]: Title - URL
|
||||
- HTML: <a href="URL">Title</a>
|
||||
|
||||
Args:
|
||||
report: The report markdown text
|
||||
section_patterns: Custom section header patterns (optional)
|
||||
|
||||
Returns:
|
||||
Dictionary with 'citations' list and 'count' of unique citations
|
||||
"""
|
||||
if section_patterns is None:
|
||||
section_patterns = [
|
||||
r"(?:##\s*Key Citations|##\s*References|##\s*Sources|##\s*Bibliography)",
|
||||
]
|
||||
|
||||
citations = []
|
||||
|
||||
# 1. Find citation section and extract citations
|
||||
for pattern in section_patterns:
|
||||
# Use a more efficient pattern that matches line-by-line content
|
||||
# instead of relying on dotall with greedy matching for large reports
|
||||
section_matches = re.finditer(
|
||||
pattern + r"\s*\n((?:(?!\n##).*\n?)*)",
|
||||
report,
|
||||
re.IGNORECASE | re.MULTILINE,
|
||||
)
|
||||
|
||||
for section_match in section_matches:
|
||||
section = section_match.group(1)
|
||||
|
||||
# 2. Extract citations in various formats
|
||||
citations.extend(_extract_markdown_links(section))
|
||||
citations.extend(_extract_numbered_citations(section))
|
||||
citations.extend(_extract_footnote_citations(section))
|
||||
citations.extend(_extract_html_links(section))
|
||||
|
||||
# 3. Deduplicate by URL
|
||||
unique_citations = {}
|
||||
for citation in citations:
|
||||
url = citation.get("url", "")
|
||||
if url and url not in unique_citations:
|
||||
unique_citations[url] = citation
|
||||
|
||||
return {
|
||||
"citations": list(unique_citations.values()),
|
||||
"count": len(unique_citations),
|
||||
}
|
||||
|
||||
|
||||
def _extract_markdown_links(text: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Extract Markdown links [title](url).
|
||||
|
||||
Args:
|
||||
text: Text to extract from
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries with title, url, and format
|
||||
"""
|
||||
citations = []
|
||||
pattern = r"\[([^\]]+)\]\(([^)]+)\)"
|
||||
|
||||
for match in re.finditer(pattern, text):
|
||||
title, url = match.groups()
|
||||
if url.startswith(("http://", "https://")):
|
||||
citations.append({
|
||||
"title": title.strip(),
|
||||
"url": url.strip(),
|
||||
"format": "markdown",
|
||||
})
|
||||
|
||||
return citations
|
||||
|
||||
|
||||
def _extract_numbered_citations(text: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Extract numbered citations [1] Title - URL.
|
||||
|
||||
Args:
|
||||
text: Text to extract from
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
# Match: [number] title - URL
|
||||
pattern = r"\[\d+\]\s+([^-\n]+?)\s*-\s*(https?://[^\s\n]+)"
|
||||
|
||||
for match in re.finditer(pattern, text):
|
||||
title, url = match.groups()
|
||||
citations.append({
|
||||
"title": title.strip(),
|
||||
"url": url.strip(),
|
||||
"format": "numbered",
|
||||
})
|
||||
|
||||
return citations
|
||||
|
||||
|
||||
def _extract_footnote_citations(text: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Extract footnote citations [^1]: Title - URL.
|
||||
|
||||
Args:
|
||||
text: Text to extract from
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
# Match: [^number]: title - URL
|
||||
pattern = r"\[\^(\d+)\]:\s+([^-\n]+?)\s*-\s*(https?://[^\s\n]+)"
|
||||
|
||||
for match in re.finditer(pattern, text):
|
||||
_, title, url = match.groups()
|
||||
citations.append({
|
||||
"title": title.strip(),
|
||||
"url": url.strip(),
|
||||
"format": "footnote",
|
||||
})
|
||||
|
||||
return citations
|
||||
|
||||
|
||||
def _extract_html_links(text: str) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Extract HTML links <a href="url">title</a>.
|
||||
|
||||
Args:
|
||||
text: Text to extract from
|
||||
|
||||
Returns:
|
||||
List of citation dictionaries
|
||||
"""
|
||||
citations = []
|
||||
pattern = r'<a\s+(?:[^>]*?\s)?href=(["\'])([^"\']+)\1[^>]*>([^<]+)</a>'
|
||||
|
||||
for match in re.finditer(pattern, text, re.IGNORECASE):
|
||||
_, url, title = match.groups()
|
||||
if url.startswith(("http://", "https://")):
|
||||
citations.append({
|
||||
"title": title.strip(),
|
||||
"url": url.strip(),
|
||||
"format": "html",
|
||||
})
|
||||
|
||||
return citations
|
||||
@@ -1,185 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Citation data models for structured source metadata.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class CitationMetadata(BaseModel):
|
||||
"""Metadata extracted from a source."""
|
||||
|
||||
# Core identifiers
|
||||
url: str
|
||||
title: str
|
||||
|
||||
# Content information
|
||||
description: Optional[str] = None
|
||||
content_snippet: Optional[str] = None
|
||||
raw_content: Optional[str] = None
|
||||
|
||||
# Source metadata
|
||||
domain: Optional[str] = None
|
||||
author: Optional[str] = None
|
||||
published_date: Optional[str] = None
|
||||
language: Optional[str] = None
|
||||
|
||||
# Media
|
||||
images: List[str] = Field(default_factory=list)
|
||||
favicon: Optional[str] = None
|
||||
|
||||
# Quality indicators
|
||||
relevance_score: float = 0.0
|
||||
credibility_score: float = 0.0
|
||||
|
||||
# Timestamps
|
||||
accessed_at: str = Field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
# Additional metadata
|
||||
extra: Dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
def __init__(self, **data):
|
||||
"""Initialize and extract domain from URL if not provided."""
|
||||
super().__init__(**data)
|
||||
if not self.domain and self.url:
|
||||
try:
|
||||
parsed = urlparse(self.url)
|
||||
self.domain = parsed.netloc
|
||||
except Exception:
|
||||
# If URL parsing fails for any reason, leave `domain` as None.
|
||||
# This is a non-critical convenience field and failures here
|
||||
# should not prevent citation metadata creation.
|
||||
pass
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
"""Generate a unique ID for this citation based on URL."""
|
||||
return hashlib.sha256(self.url.encode("utf-8")).hexdigest()[:12]
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary for JSON serialization."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"url": self.url,
|
||||
"title": self.title,
|
||||
"description": self.description,
|
||||
"content_snippet": self.content_snippet,
|
||||
"domain": self.domain,
|
||||
"author": self.author,
|
||||
"published_date": self.published_date,
|
||||
"language": self.language,
|
||||
"images": self.images,
|
||||
"favicon": self.favicon,
|
||||
"relevance_score": self.relevance_score,
|
||||
"credibility_score": self.credibility_score,
|
||||
"accessed_at": self.accessed_at,
|
||||
"extra": self.extra,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "CitationMetadata":
|
||||
"""Create from dictionary."""
|
||||
# Remove 'id' as it's computed from url
|
||||
data = {k: v for k, v in data.items() if k != "id"}
|
||||
return cls.model_validate(data)
|
||||
|
||||
@classmethod
|
||||
def from_search_result(
|
||||
cls, result: Dict[str, Any], query: str = ""
|
||||
) -> "CitationMetadata":
|
||||
"""Create citation metadata from a search result."""
|
||||
return cls(
|
||||
url=result.get("url", ""),
|
||||
title=result.get("title", "Untitled"),
|
||||
description=result.get("content", result.get("description", "")),
|
||||
content_snippet=result.get("content", "")[:500]
|
||||
if result.get("content")
|
||||
else None,
|
||||
raw_content=result.get("raw_content"),
|
||||
relevance_score=result.get("score", 0.0),
|
||||
extra={"query": query, "result_type": result.get("type", "page")},
|
||||
)
|
||||
|
||||
|
||||
|
||||
class Citation(BaseModel):
|
||||
"""
|
||||
A citation reference that can be used in reports.
|
||||
|
||||
This represents a numbered citation that links to source metadata.
|
||||
"""
|
||||
|
||||
# Citation number (1-indexed for display)
|
||||
number: int
|
||||
|
||||
# Reference to the source metadata
|
||||
metadata: CitationMetadata
|
||||
|
||||
# Context where this citation is used
|
||||
context: Optional[str] = None
|
||||
|
||||
# Specific quote or fact being cited
|
||||
cited_text: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
"""Get the citation ID from metadata."""
|
||||
return self.metadata.id
|
||||
|
||||
@property
|
||||
def url(self) -> str:
|
||||
"""Get the URL from metadata."""
|
||||
return self.metadata.url
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
"""Get the title from metadata."""
|
||||
return self.metadata.title
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary for JSON serialization."""
|
||||
return {
|
||||
"number": self.number,
|
||||
"metadata": self.metadata.to_dict(),
|
||||
"context": self.context,
|
||||
"cited_text": self.cited_text,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> "Citation":
|
||||
"""Create from dictionary."""
|
||||
return cls.model_validate({
|
||||
"number": data["number"],
|
||||
"metadata": CitationMetadata.from_dict(data["metadata"])
|
||||
if isinstance(data.get("metadata"), dict)
|
||||
else data["metadata"],
|
||||
"context": data.get("context"),
|
||||
"cited_text": data.get("cited_text"),
|
||||
})
|
||||
|
||||
def to_markdown_reference(self) -> str:
|
||||
"""Generate markdown reference format: [Title](URL)"""
|
||||
return f"[{self.title}]({self.url})"
|
||||
|
||||
def to_numbered_reference(self) -> str:
|
||||
"""Generate numbered reference format: [1] Title - URL"""
|
||||
return f"[{self.number}] {self.title} - {self.url}"
|
||||
|
||||
def to_inline_marker(self) -> str:
|
||||
"""Generate inline citation marker: [^1]"""
|
||||
return f"[^{self.number}]"
|
||||
|
||||
def to_footnote(self) -> str:
|
||||
"""Generate footnote definition: [^1]: Title - URL"""
|
||||
return f"[^{self.number}]: {self.title} - {self.url}"
|
||||
@@ -1,50 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from .loader import load_yaml_config
|
||||
from .questions import BUILT_IN_QUESTIONS, BUILT_IN_QUESTIONS_ZH_CN
|
||||
from .tools import SELECTED_SEARCH_ENGINE, SearchEngine
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Team configuration
|
||||
TEAM_MEMBER_CONFIGURATIONS = {
|
||||
"researcher": {
|
||||
"name": "researcher",
|
||||
"desc": (
|
||||
"Responsible for searching and collecting relevant information, understanding user needs and conducting research analysis"
|
||||
),
|
||||
"desc_for_llm": (
|
||||
"Uses search engines and web crawlers to gather information from the internet. "
|
||||
"Outputs a Markdown report summarizing findings. Researcher can not do math or programming."
|
||||
),
|
||||
"is_optional": False,
|
||||
},
|
||||
"coder": {
|
||||
"name": "coder",
|
||||
"desc": (
|
||||
"Responsible for code implementation, debugging and optimization, handling technical programming tasks"
|
||||
),
|
||||
"desc_for_llm": (
|
||||
"Executes Python or Bash commands, performs mathematical calculations, and outputs a Markdown report. "
|
||||
"Must be used for all mathematical computations."
|
||||
),
|
||||
"is_optional": True,
|
||||
},
|
||||
}
|
||||
|
||||
TEAM_MEMBERS = list(TEAM_MEMBER_CONFIGURATIONS.keys())
|
||||
|
||||
__all__ = [
|
||||
# Other configurations
|
||||
"TEAM_MEMBERS",
|
||||
"TEAM_MEMBER_CONFIGURATIONS",
|
||||
"SELECTED_SEARCH_ENGINE",
|
||||
"SearchEngine",
|
||||
"BUILT_IN_QUESTIONS",
|
||||
"BUILT_IN_QUESTIONS_ZH_CN",
|
||||
load_yaml_config,
|
||||
]
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from typing import Literal
|
||||
|
||||
# Define available LLM types
|
||||
LLMType = Literal["basic", "reasoning", "vision", "code"]
|
||||
|
||||
# Define agent-LLM mapping
|
||||
AGENT_LLM_MAP: dict[str, LLMType] = {
|
||||
"coordinator": "basic",
|
||||
"planner": "basic",
|
||||
"researcher": "basic",
|
||||
"analyst": "basic",
|
||||
"coder": "basic",
|
||||
"reporter": "basic",
|
||||
"podcast_script_writer": "basic",
|
||||
"ppt_composer": "basic",
|
||||
"prose_writer": "basic",
|
||||
"prompt_enhancer": "basic",
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import dataclass, field, fields
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from src.config.loader import get_bool_env, get_int_env, get_str_env
|
||||
from src.config.report_style import ReportStyle
|
||||
from src.rag.retriever import Resource
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_recursion_limit(default: int = 25) -> int:
|
||||
"""Get the recursion limit from environment variable or use default.
|
||||
|
||||
Args:
|
||||
default: Default recursion limit if environment variable is not set or invalid
|
||||
|
||||
Returns:
|
||||
int: The recursion limit to use
|
||||
"""
|
||||
env_value_str = get_str_env("AGENT_RECURSION_LIMIT", str(default))
|
||||
parsed_limit = get_int_env("AGENT_RECURSION_LIMIT", default)
|
||||
|
||||
if parsed_limit > 0:
|
||||
logger.info(f"Recursion limit set to: {parsed_limit}")
|
||||
return parsed_limit
|
||||
else:
|
||||
logger.warning(
|
||||
f"AGENT_RECURSION_LIMIT value '{env_value_str}' (parsed as {parsed_limit}) is not positive. "
|
||||
f"Using default value {default}."
|
||||
)
|
||||
return default
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class Configuration:
|
||||
"""The configurable fields."""
|
||||
|
||||
resources: list[Resource] = field(
|
||||
default_factory=list
|
||||
) # Resources to be used for the research
|
||||
max_plan_iterations: int = 1 # Maximum number of plan iterations
|
||||
max_step_num: int = 3 # Maximum number of steps in a plan
|
||||
max_search_results: int = 3 # Maximum number of search results
|
||||
mcp_settings: dict = None # MCP settings, including dynamic loaded tools
|
||||
report_style: str = ReportStyle.ACADEMIC.value # Report style
|
||||
enable_deep_thinking: bool = False # Whether to enable deep thinking
|
||||
enforce_web_search: bool = (
|
||||
False # Enforce at least one web search step in every plan
|
||||
)
|
||||
enforce_researcher_search: bool = (
|
||||
True # Enforce that researcher must use web search tool at least once
|
||||
)
|
||||
enable_web_search: bool = (
|
||||
True # Whether to enable web search, set to False to use only local RAG
|
||||
)
|
||||
interrupt_before_tools: list[str] = field(
|
||||
default_factory=list
|
||||
) # List of tool names to interrupt before execution
|
||||
enable_recursion_fallback: bool = (
|
||||
True # Enable graceful fallback when recursion limit is reached
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_runnable_config(
|
||||
cls, config: Optional[RunnableConfig] = None
|
||||
) -> "Configuration":
|
||||
"""Create a Configuration instance from a RunnableConfig."""
|
||||
configurable = (
|
||||
config["configurable"] if config and "configurable" in config else {}
|
||||
)
|
||||
values: dict[str, Any] = {
|
||||
f.name: os.environ.get(f.name.upper(), configurable.get(f.name))
|
||||
for f in fields(cls)
|
||||
if f.init
|
||||
}
|
||||
return cls(**{k: v for k, v in values.items() if v is not None})
|
||||
@@ -1,78 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os
|
||||
from typing import Any, Dict
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
def get_bool_env(name: str, default: bool = False) -> bool:
|
||||
val = os.getenv(name)
|
||||
if val is None:
|
||||
return default
|
||||
return str(val).strip().lower() in {"1", "true", "yes", "y", "on"}
|
||||
|
||||
|
||||
def get_str_env(name: str, default: str = "") -> str:
|
||||
val = os.getenv(name)
|
||||
return default if val is None else str(val).strip()
|
||||
|
||||
|
||||
def get_int_env(name: str, default: int = 0) -> int:
|
||||
val = os.getenv(name)
|
||||
if val is None:
|
||||
return default
|
||||
try:
|
||||
return int(val.strip())
|
||||
except ValueError:
|
||||
print(f"Invalid integer value for {name}: {val}. Using default {default}.")
|
||||
return default
|
||||
|
||||
|
||||
def replace_env_vars(value: str) -> str:
|
||||
"""Replace environment variables in string values."""
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
if value.startswith("$"):
|
||||
env_var = value[1:]
|
||||
return os.getenv(env_var, env_var)
|
||||
return value
|
||||
|
||||
|
||||
def process_dict(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Recursively process dictionary to replace environment variables."""
|
||||
if not config:
|
||||
return {}
|
||||
result = {}
|
||||
for key, value in config.items():
|
||||
if isinstance(value, dict):
|
||||
result[key] = process_dict(value)
|
||||
elif isinstance(value, str):
|
||||
result[key] = replace_env_vars(value)
|
||||
else:
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
_config_cache: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
|
||||
def load_yaml_config(file_path: str) -> Dict[str, Any]:
|
||||
"""Load and process YAML configuration file."""
|
||||
# 如果文件不存在,返回{}
|
||||
if not os.path.exists(file_path):
|
||||
return {}
|
||||
|
||||
# 检查缓存中是否已存在配置
|
||||
if file_path in _config_cache:
|
||||
return _config_cache[file_path]
|
||||
|
||||
# 如果缓存中不存在,则加载并处理配置
|
||||
with open(file_path, "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
processed_config = process_dict(config)
|
||||
|
||||
# 将处理后的配置存入缓存
|
||||
_config_cache[file_path] = processed_config
|
||||
return processed_config
|
||||
@@ -1,34 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Built-in questions for Deer.
|
||||
"""
|
||||
|
||||
# English built-in questions
|
||||
BUILT_IN_QUESTIONS = [
|
||||
"What factors are influencing AI adoption in healthcare?",
|
||||
"How does quantum computing impact cryptography?",
|
||||
"What are the latest developments in renewable energy technology?",
|
||||
"How is climate change affecting global agriculture?",
|
||||
"What are the ethical implications of artificial intelligence?",
|
||||
"What are the current trends in cybersecurity?",
|
||||
"How is blockchain technology being used outside of cryptocurrency?",
|
||||
"What advances have been made in natural language processing?",
|
||||
"How is machine learning transforming the financial industry?",
|
||||
"What are the environmental impacts of electric vehicles?",
|
||||
]
|
||||
|
||||
# Chinese built-in questions
|
||||
BUILT_IN_QUESTIONS_ZH_CN = [
|
||||
"人工智能在医疗保健领域的应用有哪些因素影响?",
|
||||
"量子计算如何影响密码学?",
|
||||
"可再生能源技术的最新发展是什么?",
|
||||
"气候变化如何影响全球农业?",
|
||||
"人工智能的伦理影响是什么?",
|
||||
"网络安全的当前趋势是什么?",
|
||||
"区块链技术在加密货币之外如何应用?",
|
||||
"自然语言处理领域有哪些进展?",
|
||||
"机器学习如何改变金融行业?",
|
||||
"电动汽车对环境有什么影响?",
|
||||
]
|
||||
@@ -1,9 +0,0 @@
|
||||
import enum
|
||||
|
||||
|
||||
class ReportStyle(enum.Enum):
|
||||
ACADEMIC = "academic"
|
||||
POPULAR_SCIENCE = "popular_science"
|
||||
NEWS = "news"
|
||||
SOCIAL_MEDIA = "social_media"
|
||||
STRATEGIC_INVESTMENT = "strategic_investment"
|
||||
@@ -1,40 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import enum
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class SearchEngine(enum.Enum):
|
||||
TAVILY = "tavily"
|
||||
INFOQUEST = "infoquest"
|
||||
DUCKDUCKGO = "duckduckgo"
|
||||
BRAVE_SEARCH = "brave_search"
|
||||
ARXIV = "arxiv"
|
||||
SEARX = "searx"
|
||||
WIKIPEDIA = "wikipedia"
|
||||
SERPER = "serper"
|
||||
|
||||
|
||||
class CrawlerEngine(enum.Enum):
|
||||
JINA = "jina"
|
||||
INFOQUEST = "infoquest"
|
||||
|
||||
|
||||
# Tool configuration
|
||||
SELECTED_SEARCH_ENGINE = os.getenv("SEARCH_API", SearchEngine.TAVILY.value)
|
||||
|
||||
class RAGProvider(enum.Enum):
|
||||
DIFY = "dify"
|
||||
RAGFLOW = "ragflow"
|
||||
VIKINGDB_KNOWLEDGE_BASE = "vikingdb_knowledge_base"
|
||||
MOI = "moi"
|
||||
MILVUS = "milvus"
|
||||
QDRANT = "qdrant"
|
||||
|
||||
|
||||
SELECTED_RAG_PROVIDER = os.getenv("RAG_PROVIDER")
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from .article import Article
|
||||
from .crawler import Crawler
|
||||
from .jina_client import JinaClient
|
||||
from .readability_extractor import ReadabilityExtractor
|
||||
|
||||
__all__ = ["Article", "Crawler", "JinaClient", "ReadabilityExtractor"]
|
||||
@@ -1,53 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import re
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from markdownify import markdownify as md
|
||||
|
||||
|
||||
class Article:
|
||||
url: str
|
||||
|
||||
def __init__(self, title: str, html_content: str):
|
||||
self.title = title
|
||||
self.html_content = html_content
|
||||
|
||||
def to_markdown(self, including_title: bool = True) -> str:
|
||||
markdown = ""
|
||||
if including_title:
|
||||
markdown += f"# {self.title}\n\n"
|
||||
|
||||
if self.html_content is None or not str(self.html_content).strip():
|
||||
markdown += "*No content available*\n"
|
||||
else:
|
||||
markdown += md(self.html_content)
|
||||
|
||||
return markdown
|
||||
|
||||
def to_message(self) -> list[dict]:
|
||||
image_pattern = r"!\[.*?\]\((.*?)\)"
|
||||
|
||||
content: list[dict[str, str]] = []
|
||||
markdown = self.to_markdown()
|
||||
|
||||
if not markdown or not markdown.strip():
|
||||
return [{"type": "text", "text": "No content available"}]
|
||||
|
||||
parts = re.split(image_pattern, markdown)
|
||||
|
||||
for i, part in enumerate(parts):
|
||||
if i % 2 == 1:
|
||||
image_url = urljoin(self.url, part.strip())
|
||||
content.append({"type": "image_url", "image_url": {"url": image_url}})
|
||||
else:
|
||||
text_part = part.strip()
|
||||
if text_part:
|
||||
content.append({"type": "text", "text": text_part})
|
||||
|
||||
# If after processing all parts, content is still empty, provide a fallback message.
|
||||
if not content:
|
||||
content = [{"type": "text", "text": "No content available"}]
|
||||
|
||||
return content
|
||||
@@ -1,236 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
||||
from src.config.tools import CrawlerEngine
|
||||
from src.config import load_yaml_config
|
||||
from src.crawler.article import Article
|
||||
from src.crawler.infoquest_client import InfoQuestClient
|
||||
from src.crawler.jina_client import JinaClient
|
||||
from src.crawler.readability_extractor import ReadabilityExtractor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def safe_truncate(text: str, max_length: int = 500) -> str:
|
||||
"""
|
||||
Safely truncate text to a maximum length without breaking multi-byte characters.
|
||||
|
||||
Args:
|
||||
text: The text to truncate
|
||||
max_length: Maximum number of characters to keep
|
||||
|
||||
Returns:
|
||||
Truncated text that is safe to use without encoding issues
|
||||
"""
|
||||
if text is None:
|
||||
return None
|
||||
|
||||
if len(text) <= max_length:
|
||||
return text
|
||||
|
||||
# Ensure max_length is at least 3 to accommodate the placeholder
|
||||
if max_length < 3:
|
||||
return "..."[:max_length]
|
||||
|
||||
# Use Python's built-in textwrap.shorten which handles unicode safely
|
||||
try:
|
||||
import textwrap
|
||||
return textwrap.shorten(text, width=max_length, placeholder="...")
|
||||
except (ImportError, TypeError):
|
||||
# Fallback for older Python versions or if textwrap.shorten has issues
|
||||
# Truncate to max_length - 3 to make room for "..."
|
||||
truncated = text[:max_length - 3]
|
||||
# Remove any incomplete Unicode surrogate pair
|
||||
while truncated and ord(truncated[-1]) >= 0xD800 and ord(truncated[-1]) <= 0xDFFF:
|
||||
truncated = truncated[:-1]
|
||||
return truncated + "..."
|
||||
|
||||
|
||||
def is_html_content(content: str) -> bool:
|
||||
"""
|
||||
Check if the provided content is HTML.
|
||||
|
||||
Uses a more robust detection method that checks for common HTML patterns
|
||||
including DOCTYPE declarations, HTML tags, and other HTML markers.
|
||||
"""
|
||||
if not content or not content.strip():
|
||||
return False
|
||||
|
||||
content = content.strip()
|
||||
|
||||
# Check for HTML comments
|
||||
if content.startswith('<!--') and '-->' in content:
|
||||
return True
|
||||
|
||||
# Check for DOCTYPE declarations (case insensitive)
|
||||
if re.match(r'^<!DOCTYPE\s+html', content, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
# Check for XML declarations followed by HTML
|
||||
if content.startswith('<?xml') and '<html' in content:
|
||||
return True
|
||||
|
||||
# Check for common HTML tags at the beginning
|
||||
html_start_patterns = [
|
||||
r'^<html',
|
||||
r'^<head',
|
||||
r'^<body',
|
||||
r'^<title',
|
||||
r'^<meta',
|
||||
r'^<link',
|
||||
r'^<script',
|
||||
r'^<style',
|
||||
r'^<div',
|
||||
r'^<p>',
|
||||
r'^<p\s',
|
||||
r'^<span',
|
||||
r'^<h[1-6]',
|
||||
r'^<!DOCTYPE',
|
||||
r'^<\!DOCTYPE', # Some variations
|
||||
]
|
||||
|
||||
for pattern in html_start_patterns:
|
||||
if re.match(pattern, content, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
# Check for any HTML-like tags in the content (more permissive)
|
||||
if re.search(r'<[^>]+>', content):
|
||||
# Additional check: ensure it's not just XML or other markup
|
||||
# Look for common HTML attributes or elements
|
||||
html_indicators = [
|
||||
r'href\s*=',
|
||||
r'src\s*=',
|
||||
r'class\s*=',
|
||||
r'id\s*=',
|
||||
r'<img\s',
|
||||
r'<a\s',
|
||||
r'<div',
|
||||
r'<p>',
|
||||
r'<p\s',
|
||||
r'<!DOCTYPE',
|
||||
]
|
||||
|
||||
for indicator in html_indicators:
|
||||
if re.search(indicator, content, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
# Also check for self-closing HTML tags
|
||||
self_closing_tags = [
|
||||
r'<img\s+[^>]*?/>',
|
||||
r'<br\s*/?>',
|
||||
r'<hr\s*/?>',
|
||||
r'<input\s+[^>]*?/>',
|
||||
r'<meta\s+[^>]*?/>',
|
||||
r'<link\s+[^>]*?/>',
|
||||
]
|
||||
|
||||
for tag in self_closing_tags:
|
||||
if re.search(tag, content, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Crawler:
|
||||
def crawl(self, url: str) -> Article:
|
||||
# To help LLMs better understand content, we extract clean
|
||||
# articles from HTML, convert them to markdown, and split
|
||||
# them into text and image blocks for one single and unified
|
||||
# LLM message.
|
||||
#
|
||||
# The system supports multiple crawler engines:
|
||||
# - Jina: An accessible solution, though with some limitations in readability extraction
|
||||
# - InfoQuest: A BytePlus product offering advanced capabilities with configurable parameters
|
||||
# like fetch_time, timeout, and navi_timeout.
|
||||
#
|
||||
# Instead of using Jina's own markdown converter, we'll use
|
||||
# our own solution to get better readability results.
|
||||
|
||||
# Get crawler configuration
|
||||
config = load_yaml_config("conf.yaml")
|
||||
crawler_config = config.get("CRAWLER_ENGINE", {})
|
||||
|
||||
# Get the selected crawler tool based on configuration
|
||||
crawler_client = self._select_crawler_tool(crawler_config)
|
||||
html = self._crawl_with_tool(crawler_client, url)
|
||||
|
||||
# Check if we got valid HTML content
|
||||
if not html or not html.strip():
|
||||
logger.warning(f"Empty content received from URL {url}")
|
||||
article = Article(
|
||||
title="Empty Content",
|
||||
html_content="<p>No content could be extracted from this page</p>"
|
||||
)
|
||||
article.url = url
|
||||
return article
|
||||
|
||||
# Check if content is actually HTML using more robust detection
|
||||
if not is_html_content(html):
|
||||
logger.warning(f"Non-HTML content received from URL {url}, creating fallback article")
|
||||
# Return a simple article with the raw content (safely truncated)
|
||||
article = Article(
|
||||
title="Non-HTML Content",
|
||||
html_content=f"<p>This URL returned content that cannot be parsed as HTML. Raw content: {safe_truncate(html, 500)}</p>"
|
||||
)
|
||||
article.url = url
|
||||
return article
|
||||
|
||||
try:
|
||||
extractor = ReadabilityExtractor()
|
||||
article = extractor.extract_article(html)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to extract article from {url}: {repr(e)}")
|
||||
# Fall back to a simple article with the raw HTML (safely truncated)
|
||||
article = Article(
|
||||
title="Content Extraction Failed",
|
||||
html_content=f"<p>Content extraction failed. Raw content: {safe_truncate(html, 500)}</p>"
|
||||
)
|
||||
article.url = url
|
||||
return article
|
||||
|
||||
article.url = url
|
||||
return article
|
||||
|
||||
def _select_crawler_tool(self, crawler_config: dict):
|
||||
# Only check engine from configuration file
|
||||
engine = crawler_config.get("engine", CrawlerEngine.JINA.value)
|
||||
|
||||
if engine == CrawlerEngine.JINA.value:
|
||||
logger.info(f"Selecting Jina crawler engine")
|
||||
return JinaClient()
|
||||
elif engine == CrawlerEngine.INFOQUEST.value:
|
||||
logger.info(f"Selecting InfoQuest crawler engine")
|
||||
# Read timeout parameters directly from crawler_config root level
|
||||
# These parameters are only effective when engine is set to "infoquest"
|
||||
fetch_time = crawler_config.get("fetch_time", -1)
|
||||
timeout = crawler_config.get("timeout", -1)
|
||||
navi_timeout = crawler_config.get("navi_timeout", -1)
|
||||
|
||||
# Log the configuration being used
|
||||
if fetch_time > 0 or timeout > 0 or navi_timeout > 0:
|
||||
logger.debug(
|
||||
f"Initializing InfoQuestCrawler with parameters: "
|
||||
f"fetch_time={fetch_time}, "
|
||||
f"timeout={timeout}, "
|
||||
f"navi_timeout={navi_timeout}"
|
||||
)
|
||||
|
||||
# Initialize InfoQuestClient with the parameters from configuration
|
||||
return InfoQuestClient(
|
||||
fetch_time=fetch_time,
|
||||
timeout=timeout,
|
||||
navi_timeout=navi_timeout
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unsupported crawler engine: {engine}")
|
||||
|
||||
def _crawl_with_tool(self, crawler_client, url: str) -> str:
|
||||
logger.info(f"Crawling URL: {url} using {crawler_client.__class__.__name__}")
|
||||
try:
|
||||
return crawler_client.crawl(url, return_format="html")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch URL {url} using {crawler_client.__class__.__name__}: {repr(e)}")
|
||||
raise
|
||||
@@ -1,153 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""Util that calls InfoQuest Crawler API.
|
||||
|
||||
In order to set this up, follow instructions at:
|
||||
https://docs.byteplus.com/en/docs/InfoQuest/What_is_Info_Quest
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict, Any
|
||||
|
||||
import requests
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class InfoQuestClient:
|
||||
"""Client for interacting with the InfoQuest web crawling API."""
|
||||
|
||||
def __init__(self, fetch_time: int = -1, timeout: int = -1, navi_timeout: int = -1):
|
||||
logger.info(
|
||||
"\n============================================\n"
|
||||
"🚀 BytePlus InfoQuest Crawler Initialization 🚀\n"
|
||||
"============================================"
|
||||
)
|
||||
|
||||
self.fetch_time = fetch_time
|
||||
self.timeout = timeout
|
||||
self.navi_timeout = navi_timeout
|
||||
self.api_key_set = bool(os.getenv("INFOQUEST_API_KEY"))
|
||||
|
||||
config_details = (
|
||||
f"\n📋 Configuration Details:\n"
|
||||
f"├── Fetch Timeout: {fetch_time} {'(Default: No timeout)' if fetch_time == -1 else '(Custom)'}\n"
|
||||
f"├── Timeout: {timeout} {'(Default: No timeout)' if timeout == -1 else '(Custom)'}\n"
|
||||
f"├── Navigation Timeout: {navi_timeout} {'(Default: No timeout)' if navi_timeout == -1 else '(Custom)'}\n"
|
||||
f"└── API Key: {'✅ Configured' if self.api_key_set else '❌ Not set'}"
|
||||
)
|
||||
|
||||
logger.info(config_details)
|
||||
logger.info("\n" + "*" * 70 + "\n")
|
||||
|
||||
def crawl(self, url: str, return_format: str = "html") -> str:
|
||||
logger.debug("Preparing request for URL: %s", url)
|
||||
|
||||
# Prepare headers
|
||||
headers = self._prepare_headers()
|
||||
|
||||
# Prepare request data
|
||||
data = self._prepare_request_data(url, return_format)
|
||||
|
||||
# Log request details
|
||||
logger.debug(
|
||||
"InfoQuest Crawler request prepared: endpoint=https://reader.infoquest.bytepluses.com, "
|
||||
"format=%s",
|
||||
data.get("format")
|
||||
)
|
||||
|
||||
logger.debug("Sending crawl request to InfoQuest API")
|
||||
try:
|
||||
response = requests.post(
|
||||
"https://reader.infoquest.bytepluses.com",
|
||||
headers=headers,
|
||||
json=data
|
||||
)
|
||||
|
||||
# Check if status code is not 200
|
||||
if response.status_code != 200:
|
||||
error_message = f"InfoQuest API returned status {response.status_code}: {response.text}"
|
||||
logger.error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
# Check for empty response
|
||||
if not response.text or not response.text.strip():
|
||||
error_message = "InfoQuest Crawler API returned empty response"
|
||||
logger.error("BytePlus InfoQuest Crawler returned empty response for URL: %s", url)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
# Try to parse response as JSON and extract reader_result
|
||||
try:
|
||||
response_data = json.loads(response.text)
|
||||
# Extract reader_result if it exists
|
||||
if "reader_result" in response_data:
|
||||
logger.debug("Successfully extracted reader_result from JSON response")
|
||||
return response_data["reader_result"]
|
||||
elif "content" in response_data:
|
||||
# Fallback to content field if reader_result is not available
|
||||
logger.debug("Using content field as fallback")
|
||||
return response_data["content"]
|
||||
else:
|
||||
# If neither field exists, return the original response
|
||||
logger.warning("Neither reader_result nor content field found in JSON response")
|
||||
except json.JSONDecodeError:
|
||||
# If response is not JSON, return the original text
|
||||
logger.debug("Response is not in JSON format, returning as-is")
|
||||
|
||||
# Print partial response for debugging
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
response_sample = response.text[:200] + ("..." if len(response.text) > 200 else "")
|
||||
logger.debug(
|
||||
"Successfully received response, content length: %d bytes, first 200 chars: %s",
|
||||
len(response.text), response_sample
|
||||
)
|
||||
return response.text
|
||||
except Exception as e:
|
||||
error_message = f"Request to InfoQuest API failed: {str(e)}"
|
||||
logger.error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
def _prepare_headers(self) -> Dict[str, str]:
|
||||
"""Prepare request headers."""
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
# Add API key if available
|
||||
if os.getenv("INFOQUEST_API_KEY"):
|
||||
headers["Authorization"] = f"Bearer {os.getenv('INFOQUEST_API_KEY')}"
|
||||
logger.debug("API key added to request headers")
|
||||
else:
|
||||
logger.warning(
|
||||
"InfoQuest API key is not set. Provide your own key for authentication."
|
||||
)
|
||||
|
||||
return headers
|
||||
|
||||
def _prepare_request_data(self, url: str, return_format: str) -> Dict[str, Any]:
|
||||
"""Prepare request data with formatted parameters."""
|
||||
# Normalize return_format
|
||||
if return_format and return_format.lower() == "html":
|
||||
normalized_format = "HTML"
|
||||
else:
|
||||
normalized_format = return_format
|
||||
|
||||
data = {"url": url, "format": normalized_format}
|
||||
|
||||
# Add timeout parameters if set to positive values
|
||||
timeout_params = {}
|
||||
if self.fetch_time > 0:
|
||||
timeout_params["fetch_time"] = self.fetch_time
|
||||
if self.timeout > 0:
|
||||
timeout_params["timeout"] = self.timeout
|
||||
if self.navi_timeout > 0:
|
||||
timeout_params["navi_timeout"] = self.navi_timeout
|
||||
|
||||
# Log applied timeout parameters
|
||||
if timeout_params:
|
||||
logger.debug("Applying timeout parameters: %s", timeout_params)
|
||||
data.update(timeout_params)
|
||||
|
||||
return data
|
||||
@@ -1,42 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import requests
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JinaClient:
|
||||
def crawl(self, url: str, return_format: str = "html") -> str:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Return-Format": return_format,
|
||||
}
|
||||
if os.getenv("JINA_API_KEY"):
|
||||
headers["Authorization"] = f"Bearer {os.getenv('JINA_API_KEY')}"
|
||||
else:
|
||||
logger.warning(
|
||||
"Jina API key is not set. Provide your own key to access a higher rate limit. See https://jina.ai/reader for more information."
|
||||
)
|
||||
data = {"url": url}
|
||||
try:
|
||||
response = requests.post("https://r.jina.ai/", headers=headers, json=data)
|
||||
|
||||
if response.status_code != 200:
|
||||
error_message = f"Jina API returned status {response.status_code}: {response.text}"
|
||||
logger.error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
if not response.text or not response.text.strip():
|
||||
error_message = "Jina API returned empty response"
|
||||
logger.error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
|
||||
return response.text
|
||||
except Exception as e:
|
||||
error_message = f"Request to Jina API failed: {str(e)}"
|
||||
logger.error(error_message)
|
||||
return f"Error: {error_message}"
|
||||
@@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
|
||||
from readabilipy import simple_json_from_html_string
|
||||
|
||||
from .article import Article
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ReadabilityExtractor:
|
||||
def extract_article(self, html: str) -> Article:
|
||||
article = simple_json_from_html_string(html, use_readability=True)
|
||||
|
||||
content = article.get("content")
|
||||
if not content or not str(content).strip():
|
||||
logger.warning("Readability extraction returned empty content")
|
||||
content = "<p>No content could be extracted from this page</p>"
|
||||
|
||||
title = article.get("title")
|
||||
if not title or not str(title).strip():
|
||||
title = "Untitled"
|
||||
|
||||
return Article(
|
||||
title=title,
|
||||
html_content=content,
|
||||
)
|
||||
@@ -1,21 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Report Quality Evaluation Module for DeerFlow.
|
||||
|
||||
This module provides objective methods to evaluate generated report quality,
|
||||
including automated metrics and LLM-based evaluation.
|
||||
"""
|
||||
|
||||
from .evaluator import ReportEvaluator
|
||||
from .metrics import ReportMetrics, compute_metrics
|
||||
from .llm_judge import LLMJudge, evaluate_with_llm
|
||||
|
||||
__all__ = [
|
||||
"ReportEvaluator",
|
||||
"ReportMetrics",
|
||||
"compute_metrics",
|
||||
"LLMJudge",
|
||||
"evaluate_with_llm",
|
||||
]
|
||||
@@ -1,249 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Combined report evaluator orchestrating both automated metrics and LLM evaluation.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from .llm_judge import EvaluationResult, LLMJudge
|
||||
from .metrics import ReportMetrics, compute_metrics, get_word_count_target
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CombinedEvaluation:
|
||||
"""Combined evaluation results from metrics and LLM judge."""
|
||||
|
||||
metrics: ReportMetrics
|
||||
llm_evaluation: Optional[EvaluationResult]
|
||||
final_score: float
|
||||
grade: str
|
||||
summary: str
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert to dictionary format."""
|
||||
return {
|
||||
"metrics": self.metrics.to_dict(),
|
||||
"llm_evaluation": (
|
||||
self.llm_evaluation.to_dict() if self.llm_evaluation else None
|
||||
),
|
||||
"final_score": self.final_score,
|
||||
"grade": self.grade,
|
||||
"summary": self.summary,
|
||||
}
|
||||
|
||||
|
||||
def score_to_grade(score: float) -> str:
|
||||
"""Convert numeric score to letter grade."""
|
||||
if score >= 9.0:
|
||||
return "A+"
|
||||
elif score >= 8.5:
|
||||
return "A"
|
||||
elif score >= 8.0:
|
||||
return "A-"
|
||||
elif score >= 7.5:
|
||||
return "B+"
|
||||
elif score >= 7.0:
|
||||
return "B"
|
||||
elif score >= 6.5:
|
||||
return "B-"
|
||||
elif score >= 6.0:
|
||||
return "C+"
|
||||
elif score >= 5.5:
|
||||
return "C"
|
||||
elif score >= 5.0:
|
||||
return "C-"
|
||||
elif score >= 4.0:
|
||||
return "D"
|
||||
else:
|
||||
return "F"
|
||||
|
||||
|
||||
class ReportEvaluator:
|
||||
"""
|
||||
Combined report evaluator using both automated metrics and LLM-as-Judge.
|
||||
|
||||
This evaluator provides comprehensive report quality assessment by:
|
||||
1. Computing automated metrics (fast, deterministic)
|
||||
2. Running LLM-based evaluation (nuanced, contextual)
|
||||
3. Combining both for a final score and grade
|
||||
"""
|
||||
|
||||
def __init__(self, llm: Any = None, use_llm: bool = True):
|
||||
"""
|
||||
Initialize the evaluator.
|
||||
|
||||
Args:
|
||||
llm: Optional LLM instance for LLM-as-Judge evaluation
|
||||
use_llm: Whether to use LLM evaluation (can be disabled for speed)
|
||||
"""
|
||||
self.use_llm = use_llm
|
||||
self.llm_judge = LLMJudge(llm=llm) if use_llm else None
|
||||
|
||||
def _compute_metrics_score(
|
||||
self, metrics: ReportMetrics, report_style: str
|
||||
) -> float:
|
||||
"""
|
||||
Convert automated metrics to a 0-10 score.
|
||||
|
||||
Scoring breakdown:
|
||||
- Section coverage: 30%
|
||||
- Citation quality: 25%
|
||||
- Word count compliance: 20%
|
||||
- Source diversity: 15%
|
||||
- Image inclusion: 10%
|
||||
"""
|
||||
score = 0.0
|
||||
|
||||
section_score = metrics.section_coverage_score * 10
|
||||
score += section_score * 0.30
|
||||
|
||||
citation_score = min(metrics.citation_count / 10, 1.0) * 10
|
||||
score += citation_score * 0.25
|
||||
|
||||
target = get_word_count_target(report_style)
|
||||
if target:
|
||||
if target["min"] <= metrics.word_count <= target["max"]:
|
||||
word_score = 10.0
|
||||
elif metrics.word_count < target["min"]:
|
||||
word_score = (metrics.word_count / target["min"]) * 8
|
||||
else:
|
||||
excess_ratio = metrics.word_count / target["max"]
|
||||
word_score = max(10 - (excess_ratio - 1) * 5, 5)
|
||||
score += word_score * 0.20
|
||||
|
||||
diversity_score = min(metrics.unique_sources / 5, 1.0) * 10
|
||||
score += diversity_score * 0.15
|
||||
|
||||
image_score = min(metrics.image_count / 3, 1.0) * 10
|
||||
score += image_score * 0.10
|
||||
|
||||
return round(score, 2)
|
||||
|
||||
def _generate_summary(
|
||||
self,
|
||||
metrics: ReportMetrics,
|
||||
llm_eval: Optional[EvaluationResult],
|
||||
final_score: float,
|
||||
grade: str,
|
||||
) -> str:
|
||||
"""Generate a human-readable evaluation summary."""
|
||||
lines = [f"Report Grade: {grade} ({final_score}/10)", ""]
|
||||
|
||||
lines.append("**Automated Metrics:**")
|
||||
lines.append(f"- Word Count: {metrics.word_count}")
|
||||
lines.append(f"- Citations: {metrics.citation_count}")
|
||||
lines.append(f"- Unique Sources: {metrics.unique_sources}")
|
||||
lines.append(f"- Images: {metrics.image_count}")
|
||||
lines.append(
|
||||
f"- Section Coverage: {metrics.section_coverage_score * 100:.0f}%"
|
||||
)
|
||||
|
||||
if metrics.sections_missing:
|
||||
lines.append(f"- Missing Sections: {', '.join(metrics.sections_missing)}")
|
||||
|
||||
if llm_eval:
|
||||
lines.append("")
|
||||
lines.append("**LLM Evaluation:**")
|
||||
for criterion, score in llm_eval.scores.items():
|
||||
lines.append(f"- {criterion.replace('_', ' ').title()}: {score}/10")
|
||||
|
||||
if llm_eval.strengths:
|
||||
lines.append("")
|
||||
lines.append("**Strengths:**")
|
||||
for strength in llm_eval.strengths[:3]:
|
||||
lines.append(f"- {strength}")
|
||||
|
||||
if llm_eval.weaknesses:
|
||||
lines.append("")
|
||||
lines.append("**Areas for Improvement:**")
|
||||
for weakness in llm_eval.weaknesses[:3]:
|
||||
lines.append(f"- {weakness}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
async def evaluate(
|
||||
self,
|
||||
report: str,
|
||||
query: str,
|
||||
report_style: str = "default",
|
||||
) -> CombinedEvaluation:
|
||||
"""
|
||||
Evaluate a report using both metrics and LLM.
|
||||
|
||||
Args:
|
||||
report: The report text to evaluate
|
||||
query: The original research query
|
||||
report_style: The style of report
|
||||
|
||||
Returns:
|
||||
CombinedEvaluation with full results
|
||||
"""
|
||||
metrics = compute_metrics(report, report_style)
|
||||
metrics_score = self._compute_metrics_score(metrics, report_style)
|
||||
|
||||
llm_eval = None
|
||||
if self.use_llm and self.llm_judge:
|
||||
try:
|
||||
llm_eval = await self.llm_judge.evaluate(report, query, report_style)
|
||||
except Exception as e:
|
||||
logger.warning(f"LLM evaluation failed, using metrics only: {e}")
|
||||
|
||||
if llm_eval and llm_eval.overall_score > 0:
|
||||
final_score = (metrics_score * 0.4) + (llm_eval.weighted_score * 0.6)
|
||||
else:
|
||||
final_score = metrics_score
|
||||
|
||||
final_score = round(final_score, 2)
|
||||
grade = score_to_grade(final_score)
|
||||
|
||||
summary = self._generate_summary(metrics, llm_eval, final_score, grade)
|
||||
|
||||
return CombinedEvaluation(
|
||||
metrics=metrics,
|
||||
llm_evaluation=llm_eval,
|
||||
final_score=final_score,
|
||||
grade=grade,
|
||||
summary=summary,
|
||||
)
|
||||
|
||||
def evaluate_sync(
|
||||
self,
|
||||
report: str,
|
||||
query: str,
|
||||
report_style: str = "default",
|
||||
) -> CombinedEvaluation:
|
||||
"""Synchronous version of evaluate."""
|
||||
import asyncio
|
||||
|
||||
return asyncio.run(self.evaluate(report, query, report_style))
|
||||
|
||||
def evaluate_metrics_only(
|
||||
self,
|
||||
report: str,
|
||||
report_style: str = "default",
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Quick evaluation using only automated metrics (no LLM).
|
||||
|
||||
Args:
|
||||
report: The report text to evaluate
|
||||
report_style: The style of report
|
||||
|
||||
Returns:
|
||||
Dictionary with metrics and score
|
||||
"""
|
||||
metrics = compute_metrics(report, report_style)
|
||||
metrics_score = self._compute_metrics_score(metrics, report_style)
|
||||
grade = score_to_grade(metrics_score)
|
||||
|
||||
return {
|
||||
"metrics": metrics.to_dict(),
|
||||
"score": metrics_score,
|
||||
"grade": grade,
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
LLM-as-Judge evaluation for report quality.
|
||||
|
||||
Uses an LLM to evaluate reports on multiple quality dimensions,
|
||||
providing more nuanced assessment than automated metrics alone.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Maximum characters of report content to send to the LLM for evaluation.
|
||||
# This limit prevents exceeding LLM context windows and controls token usage.
|
||||
MAX_REPORT_LENGTH = 15000
|
||||
|
||||
EVALUATION_CRITERIA = {
|
||||
"factual_accuracy": {
|
||||
"description": "Are claims supported by cited sources? Is information accurate and verifiable?",
|
||||
"weight": 0.25,
|
||||
},
|
||||
"completeness": {
|
||||
"description": "Does the report comprehensively cover all aspects of the topic?",
|
||||
"weight": 0.20,
|
||||
},
|
||||
"coherence": {
|
||||
"description": "Is the report logically structured, well-organized, and easy to follow?",
|
||||
"weight": 0.20,
|
||||
},
|
||||
"relevance": {
|
||||
"description": "Does the content directly address the research question without unnecessary tangents?",
|
||||
"weight": 0.15,
|
||||
},
|
||||
"citation_quality": {
|
||||
"description": "Are sources credible, diverse, and properly cited?",
|
||||
"weight": 0.10,
|
||||
},
|
||||
"writing_quality": {
|
||||
"description": "Is the writing clear, professional, and appropriate for the target audience?",
|
||||
"weight": 0.10,
|
||||
},
|
||||
}
|
||||
|
||||
JUDGE_SYSTEM_PROMPT = """You are an expert report quality evaluator. Your task is to objectively assess the quality of research reports.
|
||||
|
||||
Evaluate the report on the following criteria, scoring each from 1-10:
|
||||
|
||||
1. **Factual Accuracy** (1-10): Are claims supported by cited sources? Is information accurate?
|
||||
2. **Completeness** (1-10): Does the report cover all aspects of the topic comprehensively?
|
||||
3. **Coherence** (1-10): Is the report logically structured and easy to follow?
|
||||
4. **Relevance** (1-10): Does content directly address the research question?
|
||||
5. **Citation Quality** (1-10): Are sources credible, diverse, and properly cited?
|
||||
6. **Writing Quality** (1-10): Is the writing clear and appropriate for the audience?
|
||||
|
||||
Respond ONLY with a valid JSON object in this exact format:
|
||||
{
|
||||
"scores": {
|
||||
"factual_accuracy": <1-10>,
|
||||
"completeness": <1-10>,
|
||||
"coherence": <1-10>,
|
||||
"relevance": <1-10>,
|
||||
"citation_quality": <1-10>,
|
||||
"writing_quality": <1-10>
|
||||
},
|
||||
"overall_score": <1-10>,
|
||||
"strengths": ["strength1", "strength2"],
|
||||
"weaknesses": ["weakness1", "weakness2"],
|
||||
"suggestions": ["suggestion1", "suggestion2"]
|
||||
}
|
||||
|
||||
Be objective and thorough in your evaluation."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvaluationResult:
|
||||
"""Container for LLM evaluation results."""
|
||||
|
||||
scores: Dict[str, int]
|
||||
overall_score: float
|
||||
weighted_score: float
|
||||
strengths: List[str]
|
||||
weaknesses: List[str]
|
||||
suggestions: List[str]
|
||||
raw_response: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convert evaluation result to dictionary."""
|
||||
return {
|
||||
"scores": self.scores,
|
||||
"overall_score": self.overall_score,
|
||||
"weighted_score": self.weighted_score,
|
||||
"strengths": self.strengths,
|
||||
"weaknesses": self.weaknesses,
|
||||
"suggestions": self.suggestions,
|
||||
}
|
||||
|
||||
|
||||
class LLMJudge:
|
||||
"""LLM-based report quality evaluator."""
|
||||
|
||||
def __init__(self, llm: Any = None):
|
||||
"""
|
||||
Initialize the LLM Judge.
|
||||
|
||||
Args:
|
||||
llm: LangChain-compatible LLM instance. If None, will be created on demand.
|
||||
"""
|
||||
self._llm = llm
|
||||
|
||||
def _get_llm(self):
|
||||
"""Get or create the LLM instance."""
|
||||
if self._llm is None:
|
||||
from src.llms.llm import get_llm_by_type
|
||||
|
||||
self._llm = get_llm_by_type("basic")
|
||||
return self._llm
|
||||
|
||||
def _calculate_weighted_score(self, scores: Dict[str, int]) -> float:
|
||||
"""Calculate weighted average score based on criteria weights."""
|
||||
total_weight = 0
|
||||
weighted_sum = 0
|
||||
|
||||
for criterion, score in scores.items():
|
||||
if criterion in EVALUATION_CRITERIA:
|
||||
weight = EVALUATION_CRITERIA[criterion]["weight"]
|
||||
weighted_sum += score * weight
|
||||
total_weight += weight
|
||||
|
||||
if total_weight > 0:
|
||||
return round(weighted_sum / total_weight, 2)
|
||||
return 0.0
|
||||
|
||||
def _parse_response(self, response: str) -> Dict[str, Any]:
|
||||
"""Parse LLM response into structured format."""
|
||||
try:
|
||||
json_match = response
|
||||
if "```json" in response:
|
||||
json_match = response.split("```json")[1].split("```")[0]
|
||||
elif "```" in response:
|
||||
json_match = response.split("```")[1].split("```")[0]
|
||||
|
||||
return json.loads(json_match.strip())
|
||||
except (json.JSONDecodeError, IndexError) as e:
|
||||
logger.warning(f"Failed to parse LLM response: {e}")
|
||||
return {
|
||||
"scores": {
|
||||
"factual_accuracy": 5,
|
||||
"completeness": 5,
|
||||
"coherence": 5,
|
||||
"relevance": 5,
|
||||
"citation_quality": 5,
|
||||
"writing_quality": 5,
|
||||
},
|
||||
"overall_score": 5,
|
||||
"strengths": ["Unable to parse evaluation"],
|
||||
"weaknesses": ["Evaluation parsing failed"],
|
||||
"suggestions": ["Please re-run evaluation"],
|
||||
}
|
||||
|
||||
async def evaluate(
|
||||
self,
|
||||
report: str,
|
||||
query: str,
|
||||
report_style: str = "default",
|
||||
) -> EvaluationResult:
|
||||
"""
|
||||
Evaluate a report using LLM-as-Judge.
|
||||
|
||||
Args:
|
||||
report: The report text to evaluate
|
||||
query: The original research query
|
||||
report_style: The style of report for context
|
||||
|
||||
Returns:
|
||||
EvaluationResult with scores and feedback
|
||||
"""
|
||||
llm = self._get_llm()
|
||||
|
||||
user_prompt = f"""Please evaluate the following research report.
|
||||
|
||||
**Original Research Query:** {query}
|
||||
|
||||
**Report Style:** {report_style}
|
||||
|
||||
**Report to Evaluate:**
|
||||
{report[:MAX_REPORT_LENGTH]}
|
||||
|
||||
Provide your evaluation in the specified JSON format."""
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=JUDGE_SYSTEM_PROMPT),
|
||||
HumanMessage(content=user_prompt),
|
||||
]
|
||||
|
||||
try:
|
||||
response = await llm.ainvoke(messages)
|
||||
response_text = (
|
||||
response.content if hasattr(response, "content") else str(response)
|
||||
)
|
||||
|
||||
parsed = self._parse_response(response_text)
|
||||
|
||||
scores = parsed.get("scores", {})
|
||||
weighted_score = self._calculate_weighted_score(scores)
|
||||
|
||||
return EvaluationResult(
|
||||
scores=scores,
|
||||
overall_score=parsed.get("overall_score", 5),
|
||||
weighted_score=weighted_score,
|
||||
strengths=parsed.get("strengths", []),
|
||||
weaknesses=parsed.get("weaknesses", []),
|
||||
suggestions=parsed.get("suggestions", []),
|
||||
raw_response=response_text,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"LLM evaluation failed: {e}")
|
||||
return EvaluationResult(
|
||||
scores={
|
||||
"factual_accuracy": 0,
|
||||
"completeness": 0,
|
||||
"coherence": 0,
|
||||
"relevance": 0,
|
||||
"citation_quality": 0,
|
||||
"writing_quality": 0,
|
||||
},
|
||||
overall_score=0,
|
||||
weighted_score=0,
|
||||
strengths=[],
|
||||
weaknesses=[f"Evaluation failed: {str(e)}"],
|
||||
suggestions=["Please retry evaluation"],
|
||||
)
|
||||
|
||||
def evaluate_sync(
|
||||
self,
|
||||
report: str,
|
||||
query: str,
|
||||
report_style: str = "default",
|
||||
) -> EvaluationResult:
|
||||
"""
|
||||
Synchronous version of evaluate.
|
||||
|
||||
Args:
|
||||
report: The report text to evaluate
|
||||
query: The original research query
|
||||
report_style: The style of report for context
|
||||
|
||||
Returns:
|
||||
EvaluationResult with scores and feedback
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
return asyncio.run(self.evaluate(report, query, report_style))
|
||||
|
||||
|
||||
async def evaluate_with_llm(
|
||||
report: str,
|
||||
query: str,
|
||||
report_style: str = "default",
|
||||
llm: Any = None,
|
||||
) -> EvaluationResult:
|
||||
"""
|
||||
Convenience function to evaluate a report with LLM.
|
||||
|
||||
Args:
|
||||
report: The report text to evaluate
|
||||
query: The original research query
|
||||
report_style: The style of report for context
|
||||
llm: Optional LLM instance to use
|
||||
|
||||
Returns:
|
||||
EvaluationResult with scores and feedback
|
||||
"""
|
||||
judge = LLMJudge(llm=llm)
|
||||
return await judge.evaluate(report, query, report_style)
|
||||
@@ -1,229 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Automated metrics for report quality evaluation.
|
||||
|
||||
These metrics can be computed without LLM calls, providing fast and
|
||||
deterministic quality assessment.
|
||||
"""
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReportMetrics:
|
||||
"""Container for computed report metrics."""
|
||||
|
||||
word_count: int = 0
|
||||
citation_count: int = 0
|
||||
unique_sources: int = 0
|
||||
image_count: int = 0
|
||||
section_count: int = 0
|
||||
sections_found: List[str] = field(default_factory=list)
|
||||
sections_missing: List[str] = field(default_factory=list)
|
||||
section_coverage_score: float = 0.0
|
||||
has_title: bool = False
|
||||
has_key_points: bool = False
|
||||
has_overview: bool = False
|
||||
has_citations_section: bool = False
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Convert metrics to dictionary."""
|
||||
return {
|
||||
"word_count": self.word_count,
|
||||
"citation_count": self.citation_count,
|
||||
"unique_sources": self.unique_sources,
|
||||
"image_count": self.image_count,
|
||||
"section_count": self.section_count,
|
||||
"sections_found": self.sections_found,
|
||||
"sections_missing": self.sections_missing,
|
||||
"section_coverage_score": self.section_coverage_score,
|
||||
"has_title": self.has_title,
|
||||
"has_key_points": self.has_key_points,
|
||||
"has_overview": self.has_overview,
|
||||
"has_citations_section": self.has_citations_section,
|
||||
}
|
||||
|
||||
|
||||
# Required sections for different report styles
|
||||
REPORT_STYLE_SECTIONS = {
|
||||
"default": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"detailed_analysis",
|
||||
"key_citations",
|
||||
],
|
||||
"academic": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"detailed_analysis",
|
||||
"literature_review",
|
||||
"methodology",
|
||||
"key_citations",
|
||||
],
|
||||
"news": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"detailed_analysis",
|
||||
"key_citations",
|
||||
],
|
||||
"popular_science": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"detailed_analysis",
|
||||
"key_citations",
|
||||
],
|
||||
"social_media": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"key_citations",
|
||||
],
|
||||
"strategic_investment": [
|
||||
"title",
|
||||
"key_points",
|
||||
"overview",
|
||||
"detailed_analysis",
|
||||
"executive_summary",
|
||||
"market_analysis",
|
||||
"technology_analysis",
|
||||
"investment_recommendations",
|
||||
"key_citations",
|
||||
],
|
||||
}
|
||||
|
||||
# Section name patterns for detection (supports both English and Chinese)
|
||||
SECTION_PATTERNS = {
|
||||
"title": r"^#\s+.+",
|
||||
"key_points": r"(?:key\s*points|要点|关键发现|核心观点)",
|
||||
"overview": r"(?:overview|概述|简介|背景)",
|
||||
"detailed_analysis": r"(?:detailed\s*analysis|详细分析|深度分析|分析)",
|
||||
"key_citations": r"(?:key\s*citations|references|参考文献|引用|来源)",
|
||||
"literature_review": r"(?:literature\s*review|文献综述|研究回顾)",
|
||||
"methodology": r"(?:methodology|方法论|研究方法)",
|
||||
"executive_summary": r"(?:executive\s*summary|执行摘要|投资建议)",
|
||||
"market_analysis": r"(?:market\s*analysis|市场分析|产业分析)",
|
||||
"technology_analysis": r"(?:technology|技术.*(?:分析|解析|深度))",
|
||||
"investment_recommendations": r"(?:investment.*recommend|投资建议|投资评级)",
|
||||
}
|
||||
|
||||
|
||||
def count_words(text: str) -> int:
|
||||
"""Count words in text, handling both English and Chinese."""
|
||||
english_words = len(re.findall(r"\b[a-zA-Z]+\b", text))
|
||||
chinese_chars = len(re.findall(r"[\u4e00-\u9fff]", text))
|
||||
return english_words + chinese_chars
|
||||
|
||||
|
||||
def count_citations(text: str) -> int:
|
||||
"""Count markdown-style citations [text](url)."""
|
||||
pattern = r"\[[^\]]*\]\(https?://[^\s\)]+\)"
|
||||
return len(re.findall(pattern, text))
|
||||
|
||||
|
||||
def extract_domains(text: str) -> List[str]:
|
||||
"""Extract unique domains from URLs in the text."""
|
||||
url_pattern = r"https?://([^\s\)\]]+)"
|
||||
urls = re.findall(url_pattern, text)
|
||||
domains = set()
|
||||
for url in urls:
|
||||
try:
|
||||
parsed = urlparse(f"http://{url}")
|
||||
domain = parsed.netloc or url.split("/")[0]
|
||||
domain = domain.lower().replace("www.", "")
|
||||
if domain:
|
||||
domains.add(domain)
|
||||
except Exception:
|
||||
continue
|
||||
return list(domains)
|
||||
|
||||
|
||||
def count_images(text: str) -> int:
|
||||
"""Count markdown images ."""
|
||||
pattern = r"!\[[^\]]*\]\([^)]+\)"
|
||||
return len(re.findall(pattern, text))
|
||||
|
||||
|
||||
def detect_sections(text: str, report_style: str = "default") -> Dict[str, bool]:
|
||||
"""Detect which sections are present in the report."""
|
||||
required_sections = REPORT_STYLE_SECTIONS.get(
|
||||
report_style, REPORT_STYLE_SECTIONS["default"]
|
||||
)
|
||||
detected = {}
|
||||
|
||||
text_lower = text.lower()
|
||||
|
||||
for section in required_sections:
|
||||
pattern = SECTION_PATTERNS.get(section, section.replace("_", r"\s*"))
|
||||
if section == "title":
|
||||
detected[section] = bool(re.search(pattern, text, re.MULTILINE))
|
||||
else:
|
||||
detected[section] = bool(
|
||||
re.search(pattern, text_lower, re.IGNORECASE | re.MULTILINE)
|
||||
)
|
||||
|
||||
return detected
|
||||
|
||||
|
||||
def compute_metrics(
|
||||
report: str, report_style: str = "default", target_word_count: Optional[int] = None
|
||||
) -> ReportMetrics:
|
||||
"""
|
||||
Compute automated metrics for a report.
|
||||
|
||||
Args:
|
||||
report: The report text in markdown format
|
||||
report_style: The style of report (academic, news, etc.)
|
||||
target_word_count: Optional target word count for compliance check
|
||||
|
||||
Returns:
|
||||
ReportMetrics object with computed values
|
||||
"""
|
||||
metrics = ReportMetrics()
|
||||
|
||||
metrics.word_count = count_words(report)
|
||||
metrics.citation_count = count_citations(report)
|
||||
|
||||
domains = extract_domains(report)
|
||||
metrics.unique_sources = len(domains)
|
||||
|
||||
metrics.image_count = count_images(report)
|
||||
|
||||
sections_detected = detect_sections(report, report_style)
|
||||
metrics.sections_found = [s for s, found in sections_detected.items() if found]
|
||||
metrics.sections_missing = [
|
||||
s for s, found in sections_detected.items() if not found
|
||||
]
|
||||
metrics.section_count = len(metrics.sections_found)
|
||||
|
||||
total_sections = len(sections_detected)
|
||||
if total_sections > 0:
|
||||
metrics.section_coverage_score = len(metrics.sections_found) / total_sections
|
||||
|
||||
metrics.has_title = sections_detected.get("title", False)
|
||||
metrics.has_key_points = sections_detected.get("key_points", False)
|
||||
metrics.has_overview = sections_detected.get("overview", False)
|
||||
metrics.has_citations_section = sections_detected.get("key_citations", False)
|
||||
|
||||
return metrics
|
||||
|
||||
|
||||
def get_word_count_target(report_style: str) -> Dict[str, int]:
|
||||
"""Get target word count range for a report style."""
|
||||
targets = {
|
||||
"strategic_investment": {"min": 10000, "max": 15000},
|
||||
"academic": {"min": 3000, "max": 8000},
|
||||
"news": {"min": 800, "max": 2000},
|
||||
"popular_science": {"min": 1500, "max": 4000},
|
||||
"social_media": {"min": 500, "max": 1500},
|
||||
"default": {"min": 1000, "max": 5000},
|
||||
}
|
||||
return targets.get(report_style, targets["default"])
|
||||
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from .builder import build_graph, build_graph_with_memory
|
||||
|
||||
__all__ = [
|
||||
"build_graph_with_memory",
|
||||
"build_graph",
|
||||
]
|
||||
@@ -1,91 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
|
||||
from src.prompts.planner_model import StepType
|
||||
|
||||
from .nodes import (
|
||||
analyst_node,
|
||||
background_investigation_node,
|
||||
coder_node,
|
||||
coordinator_node,
|
||||
human_feedback_node,
|
||||
planner_node,
|
||||
reporter_node,
|
||||
research_team_node,
|
||||
researcher_node,
|
||||
)
|
||||
from .types import State
|
||||
|
||||
|
||||
def continue_to_running_research_team(state: State):
|
||||
current_plan = state.get("current_plan")
|
||||
if not current_plan or not current_plan.steps:
|
||||
return "planner"
|
||||
|
||||
if all(step.execution_res for step in current_plan.steps):
|
||||
return "planner"
|
||||
|
||||
# Find first incomplete step
|
||||
incomplete_step = None
|
||||
for step in current_plan.steps:
|
||||
if not step.execution_res:
|
||||
incomplete_step = step
|
||||
break
|
||||
|
||||
if not incomplete_step:
|
||||
return "planner"
|
||||
|
||||
if incomplete_step.step_type == StepType.RESEARCH:
|
||||
return "researcher"
|
||||
if incomplete_step.step_type == StepType.ANALYSIS:
|
||||
return "analyst"
|
||||
if incomplete_step.step_type == StepType.PROCESSING:
|
||||
return "coder"
|
||||
return "planner"
|
||||
|
||||
|
||||
def _build_base_graph():
|
||||
"""Build and return the base state graph with all nodes and edges."""
|
||||
builder = StateGraph(State)
|
||||
builder.add_edge(START, "coordinator")
|
||||
builder.add_node("coordinator", coordinator_node)
|
||||
builder.add_node("background_investigator", background_investigation_node)
|
||||
builder.add_node("planner", planner_node)
|
||||
builder.add_node("reporter", reporter_node)
|
||||
builder.add_node("research_team", research_team_node)
|
||||
builder.add_node("researcher", researcher_node)
|
||||
builder.add_node("analyst", analyst_node)
|
||||
builder.add_node("coder", coder_node)
|
||||
builder.add_node("human_feedback", human_feedback_node)
|
||||
builder.add_edge("background_investigator", "planner")
|
||||
builder.add_conditional_edges(
|
||||
"research_team",
|
||||
continue_to_running_research_team,
|
||||
["planner", "researcher", "analyst", "coder"],
|
||||
)
|
||||
builder.add_edge("reporter", END)
|
||||
return builder
|
||||
|
||||
|
||||
def build_graph_with_memory():
|
||||
"""Build and return the agent workflow graph with memory."""
|
||||
# use persistent memory to save conversation history
|
||||
# TODO: be compatible with SQLite / PostgreSQL
|
||||
memory = MemorySaver()
|
||||
|
||||
# build state graph
|
||||
builder = _build_base_graph()
|
||||
return builder.compile(checkpointer=memory)
|
||||
|
||||
|
||||
def build_graph():
|
||||
"""Build and return the agent workflow graph without memory."""
|
||||
# build state graph
|
||||
builder = _build_base_graph()
|
||||
return builder.compile()
|
||||
|
||||
|
||||
graph = build_graph()
|
||||
@@ -1,393 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import json
|
||||
import logging
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import psycopg
|
||||
from langgraph.store.memory import InMemoryStore
|
||||
from psycopg.rows import dict_row
|
||||
from pymongo import MongoClient
|
||||
|
||||
from src.config.loader import get_bool_env, get_str_env
|
||||
|
||||
|
||||
class ChatStreamManager:
|
||||
"""
|
||||
Manages chat stream messages with persistent storage and in-memory caching.
|
||||
|
||||
This class handles the storage and retrieval of chat messages using both
|
||||
an in-memory store for temporary data and MongoDB or PostgreSQL for persistent storage.
|
||||
It tracks message chunks and consolidates them when a conversation finishes.
|
||||
|
||||
Attributes:
|
||||
store (InMemoryStore): In-memory storage for temporary message chunks
|
||||
mongo_client (MongoClient): MongoDB client connection
|
||||
mongo_db (Database): MongoDB database instance
|
||||
postgres_conn (psycopg.Connection): PostgreSQL connection
|
||||
logger (logging.Logger): Logger instance for this class
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, checkpoint_saver: bool = False, db_uri: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the ChatStreamManager with database connections.
|
||||
|
||||
Args:
|
||||
db_uri: Database connection URI. Supports MongoDB (mongodb://) and PostgreSQL (postgresql://)
|
||||
If None, uses LANGGRAPH_CHECKPOINT_DB_URL env var or defaults to localhost
|
||||
"""
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.store = InMemoryStore()
|
||||
self.checkpoint_saver = checkpoint_saver
|
||||
# Use provided URI or fall back to environment variable or default
|
||||
self.db_uri = db_uri
|
||||
|
||||
# Initialize database connections
|
||||
self.mongo_client = None
|
||||
self.mongo_db = None
|
||||
self.postgres_conn = None
|
||||
|
||||
if self.checkpoint_saver:
|
||||
if self.db_uri is None:
|
||||
self.logger.warning(
|
||||
"Checkpoint saver is enabled but db_uri is None. "
|
||||
"Please provide a valid database URI or disable checkpoint saver."
|
||||
)
|
||||
elif self.db_uri.startswith("mongodb://"):
|
||||
self._init_mongodb()
|
||||
elif self.db_uri.startswith("postgresql://") or self.db_uri.startswith(
|
||||
"postgres://"
|
||||
):
|
||||
self._init_postgresql()
|
||||
else:
|
||||
self.logger.warning(
|
||||
f"Unsupported database URI scheme: {self.db_uri}. "
|
||||
"Supported schemes: mongodb://, postgresql://, postgres://"
|
||||
)
|
||||
else:
|
||||
self.logger.warning("Checkpoint saver is disabled")
|
||||
|
||||
def _init_mongodb(self) -> None:
|
||||
"""Initialize MongoDB connection."""
|
||||
|
||||
try:
|
||||
self.mongo_client = MongoClient(self.db_uri)
|
||||
self.mongo_db = self.mongo_client.checkpointing_db
|
||||
# Test connection
|
||||
self.mongo_client.admin.command("ping")
|
||||
self.logger.info("Successfully connected to MongoDB")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to connect to MongoDB: {e}")
|
||||
|
||||
def _init_postgresql(self) -> None:
|
||||
"""Initialize PostgreSQL connection and create table if needed."""
|
||||
|
||||
try:
|
||||
self.postgres_conn = psycopg.connect(self.db_uri, row_factory=dict_row)
|
||||
self.logger.info("Successfully connected to PostgreSQL")
|
||||
self._create_chat_streams_table()
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to connect to PostgreSQL: {e}")
|
||||
|
||||
def _create_chat_streams_table(self) -> None:
|
||||
"""Create the chat_streams table if it doesn't exist."""
|
||||
try:
|
||||
with self.postgres_conn.cursor() as cursor:
|
||||
create_table_sql = """
|
||||
CREATE TABLE IF NOT EXISTS chat_streams (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
thread_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
messages JSONB NOT NULL,
|
||||
ts TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_chat_streams_thread_id ON chat_streams(thread_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_chat_streams_ts ON chat_streams(ts);
|
||||
"""
|
||||
cursor.execute(create_table_sql)
|
||||
self.postgres_conn.commit()
|
||||
self.logger.info("Chat streams table created/verified successfully")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to create chat_streams table: {e}")
|
||||
if self.postgres_conn:
|
||||
self.postgres_conn.rollback()
|
||||
|
||||
def process_stream_message(
|
||||
self, thread_id: str, message: str, finish_reason: str
|
||||
) -> bool:
|
||||
"""
|
||||
Process and store a chat stream message chunk.
|
||||
|
||||
This method handles individual message chunks during streaming and consolidates
|
||||
them into a complete message when the stream finishes. Messages are stored
|
||||
temporarily in memory and permanently in MongoDB when complete.
|
||||
|
||||
Args:
|
||||
thread_id: Unique identifier for the conversation thread
|
||||
message: The message content or chunk to store
|
||||
finish_reason: Reason for message completion ("stop", "interrupt", or partial)
|
||||
|
||||
Returns:
|
||||
bool: True if message was processed successfully, False otherwise
|
||||
"""
|
||||
if not thread_id or not isinstance(thread_id, str):
|
||||
self.logger.warning("Invalid thread_id provided")
|
||||
return False
|
||||
|
||||
if not message:
|
||||
self.logger.warning("Empty message provided")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Create namespace for this thread's messages
|
||||
store_namespace: Tuple[str, str] = ("messages", thread_id)
|
||||
|
||||
# Get or initialize message cursor for tracking chunks
|
||||
cursor = self.store.get(store_namespace, "cursor")
|
||||
current_index = 0
|
||||
|
||||
if cursor is None:
|
||||
# Initialize cursor for new conversation
|
||||
self.store.put(store_namespace, "cursor", {"index": 0})
|
||||
else:
|
||||
# Increment index for next chunk
|
||||
current_index = int(cursor.value.get("index", 0)) + 1
|
||||
self.store.put(store_namespace, "cursor", {"index": current_index})
|
||||
|
||||
# Store the current message chunk
|
||||
self.store.put(store_namespace, f"chunk_{current_index}", message)
|
||||
|
||||
# Check if conversation is complete and should be persisted
|
||||
if finish_reason in ("stop", "interrupt"):
|
||||
return self._persist_complete_conversation(
|
||||
thread_id, store_namespace, current_index
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"Error processing stream message for thread {thread_id}: {e}"
|
||||
)
|
||||
return False
|
||||
|
||||
def _persist_complete_conversation(
|
||||
self, thread_id: str, store_namespace: Tuple[str, str], final_index: int
|
||||
) -> bool:
|
||||
"""
|
||||
Persist completed conversation to database (MongoDB or PostgreSQL).
|
||||
|
||||
Retrieves all message chunks from memory store and saves the complete
|
||||
conversation to the configured database for permanent storage.
|
||||
|
||||
Args:
|
||||
thread_id: Unique identifier for the conversation thread
|
||||
store_namespace: Namespace tuple for accessing stored messages
|
||||
final_index: The final chunk index for this conversation
|
||||
|
||||
Returns:
|
||||
bool: True if persistence was successful, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Retrieve all message chunks from memory store
|
||||
# Get all messages up to the final index including cursor metadata
|
||||
memories = self.store.search(store_namespace, limit=final_index + 2)
|
||||
|
||||
# Extract message content, filtering out cursor metadata
|
||||
messages: List[str] = []
|
||||
for item in memories:
|
||||
value = item.dict().get("value", "")
|
||||
# Skip cursor metadata, only include actual message chunks
|
||||
if value and not isinstance(value, dict):
|
||||
messages.append(str(value))
|
||||
|
||||
if not messages:
|
||||
self.logger.warning(f"No messages found for thread {thread_id}")
|
||||
return False
|
||||
|
||||
if not self.checkpoint_saver:
|
||||
self.logger.warning("Checkpoint saver is disabled")
|
||||
return False
|
||||
|
||||
# Choose persistence method based on available connection
|
||||
success = False
|
||||
if self.mongo_db is not None:
|
||||
success = self._persist_to_mongodb(thread_id, messages)
|
||||
elif self.postgres_conn is not None:
|
||||
success = self._persist_to_postgresql(thread_id, messages)
|
||||
else:
|
||||
self.logger.warning("No database connection available")
|
||||
return False
|
||||
|
||||
if success:
|
||||
try:
|
||||
for item in memories:
|
||||
self.store.delete(store_namespace, item.key)
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"Error cleaning up memory store for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(
|
||||
f"Error persisting conversation for thread {thread_id}: {e}"
|
||||
)
|
||||
return False
|
||||
|
||||
def _persist_to_mongodb(self, thread_id: str, messages: List[str]) -> bool:
|
||||
"""Persist conversation to MongoDB."""
|
||||
try:
|
||||
# Get MongoDB collection for chat streams
|
||||
collection = self.mongo_db.chat_streams
|
||||
|
||||
# Check if conversation already exists in database
|
||||
existing_document = collection.find_one({"thread_id": thread_id})
|
||||
|
||||
current_timestamp = datetime.now()
|
||||
|
||||
if existing_document:
|
||||
# Append new messages to existing conversation
|
||||
update_result = collection.update_one(
|
||||
{"thread_id": thread_id},
|
||||
{
|
||||
"$push": {"messages": {"$each": messages}},
|
||||
"$set": {"ts": current_timestamp}
|
||||
},
|
||||
)
|
||||
self.logger.info(
|
||||
f"Updated conversation for thread {thread_id}: "
|
||||
f"{update_result.modified_count} documents modified"
|
||||
)
|
||||
return update_result.modified_count > 0
|
||||
else:
|
||||
# Create new conversation document
|
||||
new_document = {
|
||||
"thread_id": thread_id,
|
||||
"messages": messages,
|
||||
"ts": current_timestamp,
|
||||
"id": uuid.uuid4().hex,
|
||||
}
|
||||
insert_result = collection.insert_one(new_document)
|
||||
self.logger.info(
|
||||
f"Created new conversation: {insert_result.inserted_id}"
|
||||
)
|
||||
return insert_result.inserted_id is not None
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error persisting to MongoDB: {e}")
|
||||
return False
|
||||
|
||||
def _persist_to_postgresql(self, thread_id: str, messages: List[str]) -> bool:
|
||||
"""Persist conversation to PostgreSQL."""
|
||||
try:
|
||||
with self.postgres_conn.cursor() as cursor:
|
||||
# Check if conversation already exists
|
||||
cursor.execute(
|
||||
"SELECT id FROM chat_streams WHERE thread_id = %s", (thread_id,)
|
||||
)
|
||||
existing_record = cursor.fetchone()
|
||||
|
||||
current_timestamp = datetime.now()
|
||||
messages_json = json.dumps(messages)
|
||||
|
||||
if existing_record:
|
||||
# Append new messages to existing conversation
|
||||
cursor.execute(
|
||||
"""
|
||||
UPDATE chat_streams
|
||||
SET messages = messages || %s::jsonb, ts = %s
|
||||
WHERE thread_id = %s
|
||||
""",
|
||||
(messages_json, current_timestamp, thread_id),
|
||||
)
|
||||
affected_rows = cursor.rowcount
|
||||
self.postgres_conn.commit()
|
||||
|
||||
self.logger.info(
|
||||
f"Updated conversation for thread {thread_id}: "
|
||||
f"{affected_rows} rows modified"
|
||||
)
|
||||
return affected_rows > 0
|
||||
else:
|
||||
# Create new conversation record
|
||||
conversation_id = uuid.uuid4()
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO chat_streams (id, thread_id, messages, ts)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
""",
|
||||
(conversation_id, thread_id, messages_json, current_timestamp),
|
||||
)
|
||||
affected_rows = cursor.rowcount
|
||||
self.postgres_conn.commit()
|
||||
|
||||
self.logger.info(
|
||||
f"Created new conversation with ID: {conversation_id}"
|
||||
)
|
||||
return affected_rows > 0
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error persisting to PostgreSQL: {e}")
|
||||
if self.postgres_conn:
|
||||
self.postgres_conn.rollback()
|
||||
return False
|
||||
|
||||
def close(self) -> None:
|
||||
"""Close database connections."""
|
||||
try:
|
||||
if self.mongo_client is not None:
|
||||
self.mongo_client.close()
|
||||
self.logger.info("MongoDB connection closed")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error closing MongoDB connection: {e}")
|
||||
|
||||
try:
|
||||
if self.postgres_conn is not None:
|
||||
self.postgres_conn.close()
|
||||
self.logger.info("PostgreSQL connection closed")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error closing PostgreSQL connection: {e}")
|
||||
|
||||
def __enter__(self):
|
||||
"""Context manager entry."""
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Context manager exit - close connections."""
|
||||
self.close()
|
||||
|
||||
|
||||
# Global instance for backward compatibility
|
||||
# TODO: Consider using dependency injection instead of global instance
|
||||
_default_manager = ChatStreamManager(
|
||||
checkpoint_saver=get_bool_env("LANGGRAPH_CHECKPOINT_SAVER", False),
|
||||
db_uri=get_str_env("LANGGRAPH_CHECKPOINT_DB_URL", "mongodb://localhost:27017"),
|
||||
)
|
||||
|
||||
|
||||
def chat_stream_message(thread_id: str, message: str, finish_reason: str) -> bool:
|
||||
"""
|
||||
Legacy function wrapper for backward compatibility.
|
||||
|
||||
Args:
|
||||
thread_id: Unique identifier for the conversation thread
|
||||
message: The message content to store
|
||||
finish_reason: Reason for message completion
|
||||
|
||||
Returns:
|
||||
bool: True if message was processed successfully
|
||||
"""
|
||||
checkpoint_saver = get_bool_env("LANGGRAPH_CHECKPOINT_SAVER", False)
|
||||
if checkpoint_saver:
|
||||
return _default_manager.process_stream_message(
|
||||
thread_id, message, finish_reason
|
||||
)
|
||||
else:
|
||||
return False
|
||||
1459
src/graph/nodes.py
1459
src/graph/nodes.py
File diff suppressed because it is too large
Load Diff
@@ -1,48 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
from dataclasses import field
|
||||
from typing import Any
|
||||
|
||||
from langgraph.graph import MessagesState
|
||||
|
||||
from src.prompts.planner_model import Plan
|
||||
from src.rag import Resource
|
||||
|
||||
|
||||
class State(MessagesState):
|
||||
"""State for the agent system, extends MessagesState with next field."""
|
||||
|
||||
# Runtime Variables
|
||||
locale: str = "en-US"
|
||||
research_topic: str = ""
|
||||
clarified_research_topic: str = (
|
||||
"" # Complete/final clarified topic with all clarification rounds
|
||||
)
|
||||
observations: list[str] = []
|
||||
resources: list[Resource] = []
|
||||
plan_iterations: int = 0
|
||||
current_plan: Plan | str = None
|
||||
final_report: str = ""
|
||||
auto_accepted_plan: bool = False
|
||||
enable_background_investigation: bool = True
|
||||
background_investigation_results: str = None
|
||||
|
||||
# Citation metadata collected during research
|
||||
# Format: List of citation dictionaries with url, title, description, etc.
|
||||
citations: list[dict[str, Any]] = field(default_factory=list)
|
||||
|
||||
# Clarification state tracking (disabled by default)
|
||||
enable_clarification: bool = (
|
||||
False # Enable/disable clarification feature (default: False)
|
||||
)
|
||||
clarification_rounds: int = 0
|
||||
clarification_history: list[str] = field(default_factory=list)
|
||||
is_clarification_complete: bool = False
|
||||
max_clarification_rounds: int = (
|
||||
3 # Default: 3 rounds (only used when enable_clarification=True)
|
||||
)
|
||||
|
||||
# Workflow control
|
||||
goto: str = "planner" # Default next node
|
||||
@@ -1,113 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from typing import Any
|
||||
|
||||
ASSISTANT_SPEAKER_NAMES = {
|
||||
"coordinator",
|
||||
"planner",
|
||||
"researcher",
|
||||
"coder",
|
||||
"reporter",
|
||||
"background_investigator",
|
||||
}
|
||||
|
||||
|
||||
def get_message_content(message: Any) -> str:
|
||||
"""Extract message content from dict or LangChain message."""
|
||||
if isinstance(message, dict):
|
||||
return message.get("content", "")
|
||||
return getattr(message, "content", "")
|
||||
|
||||
|
||||
def is_user_message(message: Any) -> bool:
|
||||
"""Return True if the message originated from the end user."""
|
||||
if isinstance(message, dict):
|
||||
role = (message.get("role") or "").lower()
|
||||
if role in {"user", "human"}:
|
||||
return True
|
||||
if role in {"assistant", "system"}:
|
||||
return False
|
||||
name = (message.get("name") or "").lower()
|
||||
if name and name in ASSISTANT_SPEAKER_NAMES:
|
||||
return False
|
||||
return role == "" and name not in ASSISTANT_SPEAKER_NAMES
|
||||
|
||||
message_type = (getattr(message, "type", "") or "").lower()
|
||||
name = (getattr(message, "name", "") or "").lower()
|
||||
if message_type == "human":
|
||||
return not (name and name in ASSISTANT_SPEAKER_NAMES)
|
||||
|
||||
role_attr = getattr(message, "role", None)
|
||||
if isinstance(role_attr, str) and role_attr.lower() in {"user", "human"}:
|
||||
return True
|
||||
|
||||
additional_role = getattr(message, "additional_kwargs", {}).get("role")
|
||||
if isinstance(additional_role, str) and additional_role.lower() in {
|
||||
"user",
|
||||
"human",
|
||||
}:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_latest_user_message(messages: list[Any]) -> tuple[Any, str]:
|
||||
"""Return the latest user-authored message and its content."""
|
||||
for message in reversed(messages or []):
|
||||
if is_user_message(message):
|
||||
content = get_message_content(message)
|
||||
if content:
|
||||
return message, content
|
||||
return None, ""
|
||||
|
||||
|
||||
def build_clarified_topic_from_history(
|
||||
clarification_history: list[str],
|
||||
) -> tuple[str, list[str]]:
|
||||
"""Construct clarified topic string from an ordered clarification history."""
|
||||
sequence = [item for item in clarification_history if item]
|
||||
if not sequence:
|
||||
return "", []
|
||||
if len(sequence) == 1:
|
||||
return sequence[0], sequence
|
||||
head, *tail = sequence
|
||||
clarified_string = f"{head} - {', '.join(tail)}"
|
||||
return clarified_string, sequence
|
||||
|
||||
|
||||
def reconstruct_clarification_history(
|
||||
messages: list[Any],
|
||||
fallback_history: list[str] | None = None,
|
||||
base_topic: str = "",
|
||||
) -> list[str]:
|
||||
"""Rebuild clarification history from user-authored messages, with fallback.
|
||||
|
||||
Args:
|
||||
messages: Conversation messages in chronological order.
|
||||
fallback_history: Optional existing history to use if no user messages found.
|
||||
base_topic: Optional topic to use when no user messages are available.
|
||||
|
||||
Returns:
|
||||
A cleaned clarification history containing unique consecutive user contents.
|
||||
"""
|
||||
sequence: list[str] = []
|
||||
for message in messages or []:
|
||||
if not is_user_message(message):
|
||||
continue
|
||||
content = get_message_content(message)
|
||||
if not content:
|
||||
continue
|
||||
if sequence and sequence[-1] == content:
|
||||
continue
|
||||
sequence.append(content)
|
||||
|
||||
if sequence:
|
||||
return sequence
|
||||
|
||||
fallback = [item for item in (fallback_history or []) if item]
|
||||
if fallback:
|
||||
return fallback
|
||||
|
||||
base_topic = (base_topic or "").strip()
|
||||
return [base_topic] if base_topic else []
|
||||
@@ -1,2 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
341
src/llms/llm.py
341
src/llms/llm.py
@@ -1,341 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, get_args
|
||||
|
||||
import httpx
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_deepseek import ChatDeepSeek
|
||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||
from langchain_openai import AzureChatOpenAI, ChatOpenAI
|
||||
|
||||
from src.config import load_yaml_config
|
||||
from src.config.agents import LLMType
|
||||
from src.llms.providers.dashscope import ChatDashscope
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Cache for LLM instances
|
||||
_llm_cache: dict[LLMType, BaseChatModel] = {}
|
||||
|
||||
# Allowed LLM configuration keys to prevent unexpected parameters from being passed
|
||||
# to LLM constructors (Issue #411 - SEARCH_ENGINE warning fix)
|
||||
ALLOWED_LLM_CONFIG_KEYS = {
|
||||
# Common LLM configuration keys
|
||||
"model",
|
||||
"api_key",
|
||||
"base_url",
|
||||
"api_base",
|
||||
"max_retries",
|
||||
"timeout",
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"frequency_penalty",
|
||||
"presence_penalty",
|
||||
"stop",
|
||||
"n",
|
||||
"stream",
|
||||
"logprobs",
|
||||
"echo",
|
||||
"best_of",
|
||||
"logit_bias",
|
||||
"user",
|
||||
"seed",
|
||||
# SSL and HTTP client settings
|
||||
"verify_ssl",
|
||||
"http_client",
|
||||
"http_async_client",
|
||||
# Platform-specific keys
|
||||
"platform",
|
||||
"google_api_key",
|
||||
# Azure-specific keys
|
||||
"azure_endpoint",
|
||||
"azure_deployment",
|
||||
"api_version",
|
||||
"azure_ad_token",
|
||||
"azure_ad_token_provider",
|
||||
# Dashscope/Doubao specific keys
|
||||
"extra_body",
|
||||
# Token limit for context compression (removed before passing to LLM)
|
||||
"token_limit",
|
||||
# Default headers
|
||||
"default_headers",
|
||||
"default_query",
|
||||
}
|
||||
|
||||
|
||||
def _get_config_file_path() -> str:
|
||||
"""Get the path to the configuration file."""
|
||||
return str((Path(__file__).parent.parent.parent / "conf.yaml").resolve())
|
||||
|
||||
|
||||
def _get_llm_type_config_keys() -> dict[str, str]:
|
||||
"""Get mapping of LLM types to their configuration keys."""
|
||||
return {
|
||||
"reasoning": "REASONING_MODEL",
|
||||
"basic": "BASIC_MODEL",
|
||||
"vision": "VISION_MODEL",
|
||||
"code": "CODE_MODEL",
|
||||
}
|
||||
|
||||
|
||||
def _get_env_llm_conf(llm_type: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Get LLM configuration from environment variables.
|
||||
Environment variables should follow the format: {LLM_TYPE}__{KEY}
|
||||
e.g., BASIC_MODEL__api_key, BASIC_MODEL__base_url
|
||||
"""
|
||||
prefix = f"{llm_type.upper()}_MODEL__"
|
||||
conf = {}
|
||||
for key, value in os.environ.items():
|
||||
if key.startswith(prefix):
|
||||
conf_key = key[len(prefix) :].lower()
|
||||
conf[conf_key] = value
|
||||
return conf
|
||||
|
||||
|
||||
def _create_llm_use_conf(llm_type: LLMType, conf: Dict[str, Any]) -> BaseChatModel:
|
||||
"""Create LLM instance using configuration."""
|
||||
llm_type_config_keys = _get_llm_type_config_keys()
|
||||
config_key = llm_type_config_keys.get(llm_type)
|
||||
|
||||
if not config_key:
|
||||
raise ValueError(f"Unknown LLM type: {llm_type}")
|
||||
|
||||
llm_conf = conf.get(config_key, {})
|
||||
if not isinstance(llm_conf, dict):
|
||||
raise ValueError(f"Invalid LLM configuration for {llm_type}: {llm_conf}")
|
||||
|
||||
# Get configuration from environment variables
|
||||
env_conf = _get_env_llm_conf(llm_type)
|
||||
|
||||
# Merge configurations, with environment variables taking precedence
|
||||
merged_conf = {**llm_conf, **env_conf}
|
||||
|
||||
# Filter out unexpected parameters to prevent LangChain warnings (Issue #411)
|
||||
# This prevents configuration keys like SEARCH_ENGINE from being passed to LLM constructors
|
||||
allowed_keys_lower = {k.lower() for k in ALLOWED_LLM_CONFIG_KEYS}
|
||||
unexpected_keys = [key for key in merged_conf.keys() if key.lower() not in allowed_keys_lower]
|
||||
for key in unexpected_keys:
|
||||
removed_value = merged_conf.pop(key)
|
||||
logger.warning(
|
||||
f"Removed unexpected LLM configuration key '{key}'. "
|
||||
f"This key is not a valid LLM parameter and may have been placed in the wrong section of conf.yaml. "
|
||||
f"Valid LLM config keys include: model, api_key, base_url, max_retries, temperature, etc."
|
||||
)
|
||||
|
||||
# Remove unnecessary parameters when initializing the client
|
||||
if "token_limit" in merged_conf:
|
||||
merged_conf.pop("token_limit")
|
||||
|
||||
if not merged_conf:
|
||||
raise ValueError(f"No configuration found for LLM type: {llm_type}")
|
||||
|
||||
# Add max_retries to handle rate limit errors
|
||||
if "max_retries" not in merged_conf:
|
||||
merged_conf["max_retries"] = 3
|
||||
|
||||
# Handle SSL verification settings
|
||||
verify_ssl = merged_conf.pop("verify_ssl", True)
|
||||
|
||||
# Create custom HTTP client if SSL verification is disabled
|
||||
if not verify_ssl:
|
||||
http_client = httpx.Client(verify=False)
|
||||
http_async_client = httpx.AsyncClient(verify=False)
|
||||
merged_conf["http_client"] = http_client
|
||||
merged_conf["http_async_client"] = http_async_client
|
||||
|
||||
# Check if it's Google AI Studio platform based on configuration
|
||||
platform = merged_conf.get("platform", "").lower()
|
||||
is_google_aistudio = platform == "google_aistudio" or platform == "google-aistudio"
|
||||
|
||||
if is_google_aistudio:
|
||||
# Handle Google AI Studio specific configuration
|
||||
gemini_conf = merged_conf.copy()
|
||||
|
||||
# Map common keys to Google AI Studio specific keys
|
||||
if "api_key" in gemini_conf:
|
||||
gemini_conf["google_api_key"] = gemini_conf.pop("api_key")
|
||||
|
||||
# Remove base_url and platform since Google AI Studio doesn't use them
|
||||
gemini_conf.pop("base_url", None)
|
||||
gemini_conf.pop("platform", None)
|
||||
|
||||
# Remove unsupported parameters for Google AI Studio
|
||||
gemini_conf.pop("http_client", None)
|
||||
gemini_conf.pop("http_async_client", None)
|
||||
|
||||
return ChatGoogleGenerativeAI(**gemini_conf)
|
||||
|
||||
if "azure_endpoint" in merged_conf or os.getenv("AZURE_OPENAI_ENDPOINT"):
|
||||
return AzureChatOpenAI(**merged_conf)
|
||||
|
||||
# Check if base_url is dashscope endpoint
|
||||
if "base_url" in merged_conf and "dashscope." in merged_conf["base_url"]:
|
||||
if llm_type == "reasoning":
|
||||
merged_conf["extra_body"] = {"enable_thinking": True}
|
||||
else:
|
||||
merged_conf["extra_body"] = {"enable_thinking": False}
|
||||
return ChatDashscope(**merged_conf)
|
||||
|
||||
if llm_type == "reasoning":
|
||||
merged_conf["api_base"] = merged_conf.pop("base_url", None)
|
||||
return ChatDeepSeek(**merged_conf)
|
||||
else:
|
||||
return ChatOpenAI(**merged_conf)
|
||||
|
||||
|
||||
def get_llm_by_type(llm_type: LLMType) -> BaseChatModel:
|
||||
"""
|
||||
Get LLM instance by type. Returns cached instance if available.
|
||||
"""
|
||||
if llm_type in _llm_cache:
|
||||
return _llm_cache[llm_type]
|
||||
|
||||
conf = load_yaml_config(_get_config_file_path())
|
||||
llm = _create_llm_use_conf(llm_type, conf)
|
||||
_llm_cache[llm_type] = llm
|
||||
return llm
|
||||
|
||||
|
||||
def get_configured_llm_models() -> dict[str, list[str]]:
|
||||
"""
|
||||
Get all configured LLM models grouped by type.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping LLM type to list of configured model names.
|
||||
"""
|
||||
try:
|
||||
conf = load_yaml_config(_get_config_file_path())
|
||||
llm_type_config_keys = _get_llm_type_config_keys()
|
||||
|
||||
configured_models: dict[str, list[str]] = {}
|
||||
|
||||
for llm_type in get_args(LLMType):
|
||||
# Get configuration from YAML file
|
||||
config_key = llm_type_config_keys.get(llm_type, "")
|
||||
yaml_conf = conf.get(config_key, {}) if config_key else {}
|
||||
|
||||
# Get configuration from environment variables
|
||||
env_conf = _get_env_llm_conf(llm_type)
|
||||
|
||||
# Merge configurations, with environment variables taking precedence
|
||||
merged_conf = {**yaml_conf, **env_conf}
|
||||
|
||||
# Check if model is configured
|
||||
model_name = merged_conf.get("model")
|
||||
if model_name:
|
||||
configured_models.setdefault(llm_type, []).append(model_name)
|
||||
|
||||
return configured_models
|
||||
|
||||
except Exception as e:
|
||||
# Log error and return empty dict to avoid breaking the application
|
||||
print(f"Warning: Failed to load LLM configuration: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
def _get_model_token_limit_defaults() -> dict[str, int]:
|
||||
"""
|
||||
Get default token limits for common LLM models.
|
||||
These are conservative limits to prevent token overflow errors (Issue #721).
|
||||
Users can override by setting token_limit in their config.
|
||||
"""
|
||||
return {
|
||||
# OpenAI models
|
||||
"gpt-4o": 120000,
|
||||
"gpt-4-turbo": 120000,
|
||||
"gpt-4": 8000,
|
||||
"gpt-3.5-turbo": 4000,
|
||||
# Anthropic Claude
|
||||
"claude-3": 180000,
|
||||
"claude-2": 100000,
|
||||
# Google Gemini
|
||||
"gemini-2": 180000,
|
||||
"gemini-1.5-pro": 180000,
|
||||
"gemini-1.5-flash": 180000,
|
||||
"gemini-pro": 30000,
|
||||
# Bytedance Doubao
|
||||
"doubao": 200000,
|
||||
# DeepSeek
|
||||
"deepseek": 100000,
|
||||
# Ollama/local
|
||||
"qwen": 30000,
|
||||
"llama": 4000,
|
||||
# Default fallback for unknown models
|
||||
"default": 100000,
|
||||
}
|
||||
|
||||
|
||||
def _infer_token_limit_from_model(model_name: str) -> int:
|
||||
"""
|
||||
Infer a reasonable token limit from the model name.
|
||||
This helps protect against token overflow errors when token_limit is not explicitly configured.
|
||||
|
||||
Args:
|
||||
model_name: The model name from configuration
|
||||
|
||||
Returns:
|
||||
A conservative token limit based on known model capabilities
|
||||
"""
|
||||
if not model_name:
|
||||
return 100000 # Safe default
|
||||
|
||||
model_name_lower = model_name.lower()
|
||||
defaults = _get_model_token_limit_defaults()
|
||||
|
||||
# Try exact or prefix matches
|
||||
for key, limit in defaults.items():
|
||||
if key in model_name_lower:
|
||||
return limit
|
||||
|
||||
# Return safe default if no match found
|
||||
return defaults["default"]
|
||||
|
||||
|
||||
def get_llm_token_limit_by_type(llm_type: str) -> int:
|
||||
"""
|
||||
Get the maximum token limit for a given LLM type.
|
||||
|
||||
Priority order:
|
||||
1. Explicitly configured token_limit in conf.yaml
|
||||
2. Inferred from model name based on known model capabilities
|
||||
3. Safe default (100,000 tokens)
|
||||
|
||||
This helps prevent token overflow errors (Issue #721) even when token_limit is not configured.
|
||||
|
||||
Args:
|
||||
llm_type (str): The type of LLM (e.g., 'basic', 'reasoning', 'vision', 'code').
|
||||
|
||||
Returns:
|
||||
int: The maximum token limit for the specified LLM type (conservative estimate).
|
||||
"""
|
||||
llm_type_config_keys = _get_llm_type_config_keys()
|
||||
config_key = llm_type_config_keys.get(llm_type)
|
||||
|
||||
conf = load_yaml_config(_get_config_file_path())
|
||||
model_config = conf.get(config_key, {})
|
||||
|
||||
# First priority: explicitly configured token_limit
|
||||
if "token_limit" in model_config:
|
||||
configured_limit = model_config["token_limit"]
|
||||
if configured_limit is not None:
|
||||
return configured_limit
|
||||
|
||||
# Second priority: infer from model name
|
||||
model_name = model_config.get("model")
|
||||
if model_name:
|
||||
inferred_limit = _infer_token_limit_from_model(model_name)
|
||||
return inferred_limit
|
||||
|
||||
# Fallback: safe default
|
||||
return _get_model_token_limit_defaults()["default"]
|
||||
|
||||
|
||||
# In the future, we will use reasoning_llm and vl_llm for different purposes
|
||||
# reasoning_llm = get_llm_by_type("reasoning")
|
||||
# vl_llm = get_llm_by_type("vision")
|
||||
@@ -1,320 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# Standard library imports
|
||||
from typing import Any, Dict, Iterator, List, Mapping, Optional, Type, Union, cast
|
||||
|
||||
# Third-party imports
|
||||
import openai
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.messages import (
|
||||
AIMessageChunk,
|
||||
BaseMessage,
|
||||
BaseMessageChunk,
|
||||
ChatMessageChunk,
|
||||
FunctionMessageChunk,
|
||||
HumanMessageChunk,
|
||||
SystemMessageChunk,
|
||||
ToolMessageChunk,
|
||||
)
|
||||
from langchain_core.messages.ai import UsageMetadata
|
||||
from langchain_core.messages.tool import tool_call_chunk
|
||||
from langchain_core.outputs import ChatGenerationChunk, ChatResult
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_openai.chat_models.base import (
|
||||
_create_usage_metadata,
|
||||
_handle_openai_bad_request,
|
||||
warnings,
|
||||
)
|
||||
|
||||
|
||||
def _convert_delta_to_message_chunk(
|
||||
delta_dict: Mapping[str, Any], default_class: Type[BaseMessageChunk]
|
||||
) -> BaseMessageChunk:
|
||||
"""Convert a delta dictionary to a message chunk.
|
||||
|
||||
Args:
|
||||
delta_dict: Dictionary containing delta information from OpenAI response
|
||||
default_class: Default message chunk class to use if role is not specified
|
||||
|
||||
Returns:
|
||||
BaseMessageChunk: Appropriate message chunk based on role and content
|
||||
|
||||
Raises:
|
||||
KeyError: If required keys are missing from the delta dictionary
|
||||
"""
|
||||
message_id = delta_dict.get("id")
|
||||
role = cast(str, delta_dict.get("role", ""))
|
||||
content = cast(str, delta_dict.get("content") or "")
|
||||
additional_kwargs: Dict[str, Any] = {}
|
||||
|
||||
# Handle function calls
|
||||
if function_call_data := delta_dict.get("function_call"):
|
||||
function_call = dict(function_call_data)
|
||||
if "name" in function_call and function_call["name"] is None:
|
||||
function_call["name"] = ""
|
||||
additional_kwargs["function_call"] = function_call
|
||||
|
||||
# Handle tool calls
|
||||
tool_call_chunks = []
|
||||
if raw_tool_calls := delta_dict.get("tool_calls"):
|
||||
additional_kwargs["tool_calls"] = raw_tool_calls
|
||||
try:
|
||||
tool_call_chunks = [
|
||||
tool_call_chunk(
|
||||
name=rtc.get("function", {}).get("name"),
|
||||
args=rtc.get("function", {}).get("arguments"),
|
||||
id=rtc.get("id"),
|
||||
index=rtc.get("index", 0),
|
||||
)
|
||||
for rtc in raw_tool_calls
|
||||
if rtc.get("function") # Ensure function key exists
|
||||
]
|
||||
except (KeyError, TypeError):
|
||||
# Log the error but continue processing
|
||||
pass
|
||||
|
||||
# Return appropriate message chunk based on role
|
||||
if role == "user" or default_class == HumanMessageChunk:
|
||||
return HumanMessageChunk(content=content, id=message_id)
|
||||
elif role == "assistant" or default_class == AIMessageChunk:
|
||||
# Handle reasoning content for OpenAI reasoning models
|
||||
if reasoning_content := delta_dict.get("reasoning_content"):
|
||||
additional_kwargs["reasoning_content"] = reasoning_content
|
||||
return AIMessageChunk(
|
||||
content=content,
|
||||
additional_kwargs=additional_kwargs,
|
||||
id=message_id,
|
||||
tool_call_chunks=tool_call_chunks, # type: ignore[arg-type]
|
||||
)
|
||||
elif role in ("system", "developer") or default_class == SystemMessageChunk:
|
||||
if role == "developer":
|
||||
additional_kwargs = {"__openai_role__": "developer"}
|
||||
return SystemMessageChunk(
|
||||
content=content, id=message_id, additional_kwargs=additional_kwargs
|
||||
)
|
||||
elif role == "function" or default_class == FunctionMessageChunk:
|
||||
function_name = delta_dict.get("name", "")
|
||||
return FunctionMessageChunk(content=content, name=function_name, id=message_id)
|
||||
elif role == "tool" or default_class == ToolMessageChunk:
|
||||
tool_call_id = delta_dict.get("tool_call_id", "")
|
||||
return ToolMessageChunk(
|
||||
content=content, tool_call_id=tool_call_id, id=message_id
|
||||
)
|
||||
elif role or default_class == ChatMessageChunk:
|
||||
return ChatMessageChunk(content=content, role=role, id=message_id)
|
||||
else:
|
||||
return default_class(content=content, id=message_id) # type: ignore
|
||||
|
||||
|
||||
def _convert_chunk_to_generation_chunk(
|
||||
chunk: Dict[str, Any],
|
||||
default_chunk_class: Type[BaseMessageChunk],
|
||||
base_generation_info: Optional[Dict[str, Any]],
|
||||
) -> Optional[ChatGenerationChunk]:
|
||||
"""Convert a streaming chunk to a generation chunk.
|
||||
|
||||
Args:
|
||||
chunk: Raw chunk data from OpenAI streaming response
|
||||
default_chunk_class: Default message chunk class to use
|
||||
base_generation_info: Base generation information to include
|
||||
|
||||
Returns:
|
||||
Optional[ChatGenerationChunk]: Generated chunk or None if chunk should be skipped
|
||||
"""
|
||||
# Skip content.delta type chunks from beta.chat.completions.stream
|
||||
if chunk.get("type") == "content.delta":
|
||||
return None
|
||||
|
||||
token_usage = chunk.get("usage")
|
||||
choices = (
|
||||
chunk.get("choices", [])
|
||||
# Handle chunks from beta.chat.completions.stream format
|
||||
or chunk.get("chunk", {}).get("choices", [])
|
||||
)
|
||||
|
||||
usage_metadata: Optional[UsageMetadata] = (
|
||||
_create_usage_metadata(token_usage) if token_usage else None
|
||||
)
|
||||
|
||||
# Handle empty choices
|
||||
if not choices:
|
||||
generation_chunk = ChatGenerationChunk(
|
||||
message=default_chunk_class(content="", usage_metadata=usage_metadata)
|
||||
)
|
||||
return generation_chunk
|
||||
|
||||
choice = choices[0]
|
||||
if choice.get("delta") is None:
|
||||
return None
|
||||
|
||||
message_chunk = _convert_delta_to_message_chunk(
|
||||
choice["delta"], default_chunk_class
|
||||
)
|
||||
generation_info = dict(base_generation_info) if base_generation_info else {}
|
||||
|
||||
# Add finish reason and model info if available
|
||||
if finish_reason := choice.get("finish_reason"):
|
||||
generation_info["finish_reason"] = finish_reason
|
||||
if model_name := chunk.get("model"):
|
||||
generation_info["model_name"] = model_name
|
||||
if system_fingerprint := chunk.get("system_fingerprint"):
|
||||
generation_info["system_fingerprint"] = system_fingerprint
|
||||
|
||||
# Add log probabilities if available
|
||||
if logprobs := choice.get("logprobs"):
|
||||
generation_info["logprobs"] = logprobs
|
||||
|
||||
# Attach usage metadata to AI message chunks
|
||||
if usage_metadata and isinstance(message_chunk, AIMessageChunk):
|
||||
message_chunk.usage_metadata = usage_metadata
|
||||
|
||||
generation_chunk = ChatGenerationChunk(
|
||||
message=message_chunk, generation_info=generation_info or None
|
||||
)
|
||||
return generation_chunk
|
||||
|
||||
|
||||
class ChatDashscope(ChatOpenAI):
|
||||
"""Extended ChatOpenAI model with reasoning capabilities.
|
||||
|
||||
This class extends the base ChatOpenAI model to support OpenAI's reasoning models
|
||||
that include reasoning_content in their responses. It handles the extraction and
|
||||
preservation of reasoning content during both streaming and non-streaming operations.
|
||||
"""
|
||||
|
||||
def _create_chat_result(
|
||||
self,
|
||||
response: Union[Dict[str, Any], openai.BaseModel],
|
||||
generation_info: Optional[Dict[str, Any]] = None,
|
||||
) -> ChatResult:
|
||||
"""Create a chat result from the OpenAI response.
|
||||
|
||||
Args:
|
||||
response: The response from OpenAI API
|
||||
generation_info: Additional generation information
|
||||
|
||||
Returns:
|
||||
ChatResult: The formatted chat result with reasoning content if available
|
||||
"""
|
||||
chat_result = super()._create_chat_result(response, generation_info)
|
||||
|
||||
# Only process BaseModel responses (not raw dict responses)
|
||||
if not isinstance(response, openai.BaseModel):
|
||||
return chat_result
|
||||
|
||||
# Extract reasoning content if available
|
||||
try:
|
||||
if (
|
||||
hasattr(response, "choices")
|
||||
and response.choices
|
||||
and hasattr(response.choices[0], "message")
|
||||
and hasattr(response.choices[0].message, "reasoning_content")
|
||||
):
|
||||
reasoning_content = response.choices[0].message.reasoning_content
|
||||
if reasoning_content and chat_result.generations:
|
||||
chat_result.generations[0].message.additional_kwargs[
|
||||
"reasoning_content"
|
||||
] = reasoning_content
|
||||
except (IndexError, AttributeError):
|
||||
# If reasoning content extraction fails, continue without it
|
||||
pass
|
||||
|
||||
return chat_result
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
"""Create a streaming generator for chat completions.
|
||||
|
||||
Args:
|
||||
messages: List of messages to send to the model
|
||||
stop: Optional list of stop sequences
|
||||
run_manager: Optional callback manager for LLM runs
|
||||
**kwargs: Additional keyword arguments for the API call
|
||||
|
||||
Yields:
|
||||
ChatGenerationChunk: Individual chunks from the streaming response
|
||||
|
||||
Raises:
|
||||
openai.BadRequestError: If the API request is invalid
|
||||
"""
|
||||
kwargs["stream"] = True
|
||||
payload = self._get_request_payload(messages, stop=stop, **kwargs)
|
||||
default_chunk_class: Type[BaseMessageChunk] = AIMessageChunk
|
||||
base_generation_info: Dict[str, Any] = {}
|
||||
|
||||
# Handle response format for beta completions
|
||||
if "response_format" in payload:
|
||||
if self.include_response_headers:
|
||||
warnings.warn(
|
||||
"Cannot currently include response headers when response_format is "
|
||||
"specified."
|
||||
)
|
||||
payload.pop("stream")
|
||||
response_stream = self.root_client.beta.chat.completions.stream(**payload)
|
||||
context_manager = response_stream
|
||||
else:
|
||||
# Handle regular streaming with optional response headers
|
||||
if self.include_response_headers:
|
||||
raw_response = self.client.with_raw_response.create(**payload)
|
||||
response = raw_response.parse()
|
||||
base_generation_info = {"headers": dict(raw_response.headers)}
|
||||
else:
|
||||
response = self.client.create(**payload)
|
||||
context_manager = response
|
||||
|
||||
try:
|
||||
with context_manager as response:
|
||||
is_first_chunk = True
|
||||
for chunk in response:
|
||||
# Convert chunk to dict if it's a model object
|
||||
if not isinstance(chunk, dict):
|
||||
chunk = chunk.model_dump()
|
||||
|
||||
generation_chunk = _convert_chunk_to_generation_chunk(
|
||||
chunk,
|
||||
default_chunk_class,
|
||||
base_generation_info if is_first_chunk else {},
|
||||
)
|
||||
|
||||
if generation_chunk is None:
|
||||
continue
|
||||
|
||||
# Update default chunk class for subsequent chunks
|
||||
default_chunk_class = generation_chunk.message.__class__
|
||||
|
||||
# Handle log probabilities for callback
|
||||
logprobs = (generation_chunk.generation_info or {}).get("logprobs")
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(
|
||||
generation_chunk.text,
|
||||
chunk=generation_chunk,
|
||||
logprobs=logprobs,
|
||||
)
|
||||
|
||||
is_first_chunk = False
|
||||
yield generation_chunk
|
||||
|
||||
except openai.BadRequestError as e:
|
||||
_handle_openai_bad_request(e)
|
||||
|
||||
# Handle final completion for response_format requests
|
||||
if hasattr(response, "get_final_completion") and "response_format" in payload:
|
||||
try:
|
||||
final_completion = response.get_final_completion()
|
||||
generation_chunk = self._get_generation_chunk_from_completion(
|
||||
final_completion
|
||||
)
|
||||
if run_manager:
|
||||
run_manager.on_llm_new_token(
|
||||
generation_chunk.text, chunk=generation_chunk
|
||||
)
|
||||
yield generation_chunk
|
||||
except AttributeError:
|
||||
# If get_final_completion method doesn't exist, continue without it
|
||||
pass
|
||||
@@ -1,16 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
|
||||
from src.podcast.graph.state import PodcastState
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def audio_mixer_node(state: PodcastState):
|
||||
logger.info("Mixing audio chunks for podcast...")
|
||||
audio_chunks = state["audio_chunks"]
|
||||
combined_audio = b"".join(audio_chunks)
|
||||
logger.info("The podcast audio is now ready.")
|
||||
return {"output": combined_audio}
|
||||
@@ -1,39 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
|
||||
from src.podcast.graph.audio_mixer_node import audio_mixer_node
|
||||
from src.podcast.graph.script_writer_node import script_writer_node
|
||||
from src.podcast.graph.state import PodcastState
|
||||
from src.podcast.graph.tts_node import tts_node
|
||||
|
||||
|
||||
def build_graph():
|
||||
"""Build and return the podcast workflow graph."""
|
||||
# build state graph
|
||||
builder = StateGraph(PodcastState)
|
||||
builder.add_node("script_writer", script_writer_node)
|
||||
builder.add_node("tts", tts_node)
|
||||
builder.add_node("audio_mixer", audio_mixer_node)
|
||||
builder.add_edge(START, "script_writer")
|
||||
builder.add_edge("script_writer", "tts")
|
||||
builder.add_edge("tts", "audio_mixer")
|
||||
builder.add_edge("audio_mixer", END)
|
||||
return builder.compile()
|
||||
|
||||
|
||||
workflow = build_graph()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
report_content = open("examples/nanjing_tangbao.md").read()
|
||||
final_state = workflow.invoke({"input": report_content})
|
||||
for line in final_state["script"].lines:
|
||||
print("<M>" if line.speaker == "male" else "<F>", line.text)
|
||||
|
||||
with open("final.mp3", "wb") as f:
|
||||
f.write(final_state["output"])
|
||||
@@ -1,58 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import openai
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
from src.config.agents import AGENT_LLM_MAP
|
||||
from src.llms.llm import get_llm_by_type
|
||||
from src.prompts.template import get_prompt_template
|
||||
from src.utils.json_utils import repair_json_output
|
||||
|
||||
from ..types import Script
|
||||
from .state import PodcastState
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def script_writer_node(state: PodcastState):
|
||||
logger.info("Generating script for podcast...")
|
||||
base_model = get_llm_by_type(AGENT_LLM_MAP["podcast_script_writer"])
|
||||
|
||||
messages = [
|
||||
SystemMessage(content=get_prompt_template("podcast/podcast_script_writer")),
|
||||
HumanMessage(content=state["input"]),
|
||||
]
|
||||
|
||||
try:
|
||||
# Try structured output with json_mode first
|
||||
model = base_model.with_structured_output(Script, method="json_mode")
|
||||
script = model.invoke(messages)
|
||||
except openai.BadRequestError as e:
|
||||
# Fall back for models that don't support json_object (e.g., Kimi K2)
|
||||
if "json_object" in str(e).lower():
|
||||
logger.warning(
|
||||
f"Model doesn't support json_mode, falling back to prompting: {e}"
|
||||
)
|
||||
response = base_model.invoke(messages)
|
||||
content = response.content if hasattr(response, "content") else str(response)
|
||||
try:
|
||||
repaired = repair_json_output(content)
|
||||
script_dict = json.loads(repaired)
|
||||
except json.JSONDecodeError as json_err:
|
||||
logger.error(
|
||||
"Failed to parse JSON from podcast script writer fallback "
|
||||
"response: %s; content: %r",
|
||||
json_err,
|
||||
content,
|
||||
)
|
||||
raise
|
||||
script = Script.model_validate(script_dict)
|
||||
else:
|
||||
raise
|
||||
|
||||
logger.debug("Generated podcast script: %s", script)
|
||||
return {"script": script, "audio_chunks": []}
|
||||
@@ -1,22 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from langgraph.graph import MessagesState
|
||||
|
||||
from ..types import Script
|
||||
|
||||
|
||||
class PodcastState(MessagesState):
|
||||
"""State for the podcast generation."""
|
||||
|
||||
# Input
|
||||
input: str = ""
|
||||
|
||||
# Output
|
||||
output: Optional[bytes] = None
|
||||
|
||||
# Assets
|
||||
script: Optional[Script] = None
|
||||
audio_chunks: list[bytes] = []
|
||||
@@ -1,47 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
|
||||
from src.podcast.graph.state import PodcastState
|
||||
from src.tools.tts import VolcengineTTS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def tts_node(state: PodcastState):
|
||||
logger.info("Generating audio chunks for podcast...")
|
||||
tts_client = _create_tts_client()
|
||||
for line in state["script"].lines:
|
||||
tts_client.voice_type = (
|
||||
"BV002_streaming" if line.speaker == "male" else "BV001_streaming"
|
||||
)
|
||||
result = tts_client.text_to_speech(line.paragraph, speed_ratio=1.05)
|
||||
if result["success"]:
|
||||
audio_data = result["audio_data"]
|
||||
audio_chunk = base64.b64decode(audio_data)
|
||||
state["audio_chunks"].append(audio_chunk)
|
||||
else:
|
||||
logger.error(result["error"])
|
||||
return {
|
||||
"audio_chunks": state["audio_chunks"],
|
||||
}
|
||||
|
||||
|
||||
def _create_tts_client():
|
||||
app_id = os.getenv("VOLCENGINE_TTS_APPID", "")
|
||||
if not app_id:
|
||||
raise Exception("VOLCENGINE_TTS_APPID is not set")
|
||||
access_token = os.getenv("VOLCENGINE_TTS_ACCESS_TOKEN", "")
|
||||
if not access_token:
|
||||
raise Exception("VOLCENGINE_TTS_ACCESS_TOKEN is not set")
|
||||
cluster = os.getenv("VOLCENGINE_TTS_CLUSTER", "volcano_tts")
|
||||
voice_type = "BV001_streaming"
|
||||
return VolcengineTTS(
|
||||
appid=app_id,
|
||||
access_token=access_token,
|
||||
cluster=cluster,
|
||||
voice_type=voice_type,
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class ScriptLine(BaseModel):
|
||||
speaker: Literal["male", "female"] = Field(default="male")
|
||||
paragraph: str = Field(default="")
|
||||
|
||||
|
||||
class Script(BaseModel):
|
||||
locale: Literal["en", "zh"] = Field(default="en")
|
||||
lines: list[ScriptLine] = Field(default=[])
|
||||
@@ -1,31 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from langgraph.graph import END, START, StateGraph
|
||||
|
||||
from src.ppt.graph.ppt_composer_node import ppt_composer_node
|
||||
from src.ppt.graph.ppt_generator_node import ppt_generator_node
|
||||
from src.ppt.graph.state import PPTState
|
||||
|
||||
|
||||
def build_graph():
|
||||
"""Build and return the ppt workflow graph."""
|
||||
# build state graph
|
||||
builder = StateGraph(PPTState)
|
||||
builder.add_node("ppt_composer", ppt_composer_node)
|
||||
builder.add_node("ppt_generator", ppt_generator_node)
|
||||
builder.add_edge(START, "ppt_composer")
|
||||
builder.add_edge("ppt_composer", "ppt_generator")
|
||||
builder.add_edge("ppt_generator", END)
|
||||
return builder.compile()
|
||||
|
||||
|
||||
workflow = build_graph()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
report_content = open("examples/nanjing_tangbao.md").read()
|
||||
final_state = workflow.invoke({"input": report_content})
|
||||
@@ -1,33 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
from src.config.agents import AGENT_LLM_MAP
|
||||
from src.llms.llm import get_llm_by_type
|
||||
from src.prompts.template import get_prompt_template
|
||||
|
||||
from .state import PPTState
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def ppt_composer_node(state: PPTState):
|
||||
logger.info("Generating ppt content...")
|
||||
model = get_llm_by_type(AGENT_LLM_MAP["ppt_composer"])
|
||||
ppt_content = model.invoke(
|
||||
[
|
||||
SystemMessage(content=get_prompt_template("ppt/ppt_composer", locale=state.get("locale", "en-US"))),
|
||||
HumanMessage(content=state["input"]),
|
||||
],
|
||||
)
|
||||
logger.info(f"ppt_content: {ppt_content}")
|
||||
# save the ppt content in a temp file
|
||||
temp_ppt_file_path = os.path.join(os.getcwd(), f"ppt_content_{uuid.uuid4()}.md")
|
||||
with open(temp_ppt_file_path, "w") as f:
|
||||
f.write(ppt_content.content)
|
||||
return {"ppt_content": ppt_content, "ppt_file_path": temp_ppt_file_path}
|
||||
@@ -1,25 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import uuid
|
||||
|
||||
from src.ppt.graph.state import PPTState
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def ppt_generator_node(state: PPTState):
|
||||
logger.info("Generating ppt file...")
|
||||
# use marp cli to generate ppt file
|
||||
# https://github.com/marp-team/marp-cli?tab=readme-ov-file
|
||||
generated_file_path = os.path.join(
|
||||
os.getcwd(), f"generated_ppt_{uuid.uuid4()}.pptx"
|
||||
)
|
||||
subprocess.run(["marp", state["ppt_file_path"], "-o", generated_file_path])
|
||||
# remove the temp file
|
||||
os.remove(state["ppt_file_path"])
|
||||
logger.info(f"generated_file_path: {generated_file_path}")
|
||||
return {"generated_file_path": generated_file_path}
|
||||
@@ -1,19 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
from langgraph.graph import MessagesState
|
||||
|
||||
|
||||
class PPTState(MessagesState):
|
||||
"""State for the ppt generation."""
|
||||
|
||||
# Input
|
||||
input: str = ""
|
||||
locale: str = ""
|
||||
# Output
|
||||
generated_file_path: str = ""
|
||||
|
||||
# Assets
|
||||
ppt_content: str = ""
|
||||
ppt_file_path: str = ""
|
||||
@@ -1,4 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""Prompt enhancer module for improving user prompts."""
|
||||
@@ -1,25 +0,0 @@
|
||||
# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from langgraph.graph import StateGraph
|
||||
|
||||
from src.prompt_enhancer.graph.enhancer_node import prompt_enhancer_node
|
||||
from src.prompt_enhancer.graph.state import PromptEnhancerState
|
||||
|
||||
|
||||
def build_graph():
|
||||
"""Build and return the prompt enhancer workflow graph."""
|
||||
# Build state graph
|
||||
builder = StateGraph(PromptEnhancerState)
|
||||
|
||||
# Add the enhancer node
|
||||
builder.add_node("enhancer", prompt_enhancer_node)
|
||||
|
||||
# Set entry point
|
||||
builder.set_entry_point("enhancer")
|
||||
|
||||
# Set finish point
|
||||
builder.set_finish_point("enhancer")
|
||||
|
||||
# Compile and return the graph
|
||||
return builder.compile()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user