feat(ontology): make entity/edge type limits configurable via env vars

Revert default limits back to 10 edges and 12 entities (10 specific + 2
fallback), matching Zep Cloud's maximum. Expose ONTOLOGY_MAX_ENTITY_TYPES
and ONTOLOGY_MAX_EDGE_TYPES in Config so operators can override without
touching code. Prompts now inject the live values at request time.
Also replace Chinese comments in .env.example with English equivalents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-04-25 19:56:44 +00:00
parent 0ce4d6c10e
commit ba9c8d1f42
3 changed files with 39 additions and 14 deletions

View File

@ -41,8 +41,12 @@ ZEP_API_KEY=your_zep_api_key_here
# LLM_SMALL_BASE_URL=https://<resource>.cognitiveservices.azure.com/openai/deployments/<small-model>/chat/completions?api-version=2024-05-01-preview # LLM_SMALL_BASE_URL=https://<resource>.cognitiveservices.azure.com/openai/deployments/<small-model>/chat/completions?api-version=2024-05-01-preview
# LLM_SMALL_MODEL_NAME=gpt-4o-mini # LLM_SMALL_MODEL_NAME=gpt-4o-mini
# ===== 加速 LLM 配置(可选)===== # ===== Ontology limits =====
# 注意如果不使用加速配置env文件中就不要出现下面的配置项 # ONTOLOGY_MAX_ENTITY_TYPES=12 # total entity types (specific + 2 fallback); Zep Cloud max is 10 specific + 2 = 12
# ONTOLOGY_MAX_EDGE_TYPES=10 # max relationship types; Zep Cloud max is 10
# ===== Boost LLM (optional) =====
# Secondary faster LLM — omit these lines entirely if not used
LLM_BOOST_API_KEY=your_api_key_here LLM_BOOST_API_KEY=your_api_key_here
LLM_BOOST_BASE_URL=your_base_url_here LLM_BOOST_BASE_URL=your_base_url_here
LLM_BOOST_MODEL_NAME=your_model_name_here LLM_BOOST_MODEL_NAME=your_model_name_here

View File

@ -33,6 +33,18 @@ class Config:
LLM_BASE_URL = os.environ.get('LLM_BASE_URL', 'https://api.openai.com/v1') LLM_BASE_URL = os.environ.get('LLM_BASE_URL', 'https://api.openai.com/v1')
LLM_MODEL_NAME = os.environ.get('LLM_MODEL_NAME', 'gpt-4o-mini') LLM_MODEL_NAME = os.environ.get('LLM_MODEL_NAME', 'gpt-4o-mini')
# Embedding LLM (used by Graphiti for vector indexing)
# Falls back to LLM_* values if not set
LLM_EMBED_API_KEY = os.environ.get('LLM_EMBED_API_KEY') or os.environ.get('LLM_API_KEY')
LLM_EMBED_BASE_URL = os.environ.get('LLM_EMBED_BASE_URL') or os.environ.get('LLM_BASE_URL', 'https://api.openai.com/v1')
LLM_EMBED_MODEL_NAME = os.environ.get('LLM_EMBED_MODEL_NAME', 'text-embedding-3-small')
# Small/fast LLM (used by Graphiti for lightweight tasks like reranking)
# Falls back to LLM_* values if not set
LLM_SMALL_API_KEY = os.environ.get('LLM_SMALL_API_KEY') or os.environ.get('LLM_API_KEY')
LLM_SMALL_BASE_URL = os.environ.get('LLM_SMALL_BASE_URL') or os.environ.get('LLM_BASE_URL', 'https://api.openai.com/v1')
LLM_SMALL_MODEL_NAME = os.environ.get('LLM_SMALL_MODEL_NAME') or os.environ.get('LLM_MODEL_NAME', 'gpt-4o-mini')
# Graph backend: "zep" (default, cloud) o "graphiti" (Neo4j local) # Graph backend: "zep" (default, cloud) o "graphiti" (Neo4j local)
GRAPH_BACKEND = os.environ.get('GRAPH_BACKEND', 'zep') GRAPH_BACKEND = os.environ.get('GRAPH_BACKEND', 'zep')
@ -43,6 +55,7 @@ class Config:
NEO4J_URI = os.environ.get('NEO4J_URI', 'bolt://localhost:7687') NEO4J_URI = os.environ.get('NEO4J_URI', 'bolt://localhost:7687')
NEO4J_USER = os.environ.get('NEO4J_USER', 'neo4j') NEO4J_USER = os.environ.get('NEO4J_USER', 'neo4j')
NEO4J_PASSWORD = os.environ.get('NEO4J_PASSWORD') NEO4J_PASSWORD = os.environ.get('NEO4J_PASSWORD')
GRAPHITI_BATCH_SIZE = int(os.environ.get('GRAPHITI_BATCH_SIZE', '10'))
# LLM provider ("" = OpenAI-compatible per defecte, "gemini" = Google AI Studio) # LLM provider ("" = OpenAI-compatible per defecte, "gemini" = Google AI Studio)
LLM_PROVIDER = os.environ.get('LLM_PROVIDER', '') LLM_PROVIDER = os.environ.get('LLM_PROVIDER', '')
@ -56,6 +69,10 @@ class Config:
DEFAULT_CHUNK_SIZE = 500 # default chunk size DEFAULT_CHUNK_SIZE = 500 # default chunk size
DEFAULT_CHUNK_OVERLAP = 50 # default overlap size DEFAULT_CHUNK_OVERLAP = 50 # default overlap size
# Ontology generation limits
ONTOLOGY_MAX_ENTITY_TYPES = int(os.environ.get('ONTOLOGY_MAX_ENTITY_TYPES', '12'))
ONTOLOGY_MAX_EDGE_TYPES = int(os.environ.get('ONTOLOGY_MAX_EDGE_TYPES', '10'))
# OASIS simulation settings # OASIS simulation settings
OASIS_DEFAULT_MAX_ROUNDS = int(os.environ.get('OASIS_DEFAULT_MAX_ROUNDS', '10')) OASIS_DEFAULT_MAX_ROUNDS = int(os.environ.get('OASIS_DEFAULT_MAX_ROUNDS', '10'))
OASIS_SIMULATION_DATA_DIR = os.path.join(os.path.dirname(__file__), '../uploads/simulations') OASIS_SIMULATION_DATA_DIR = os.path.join(os.path.dirname(__file__), '../uploads/simulations')

View File

@ -9,6 +9,7 @@ import re
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
from ..utils.llm_client import LLMClient from ..utils.llm_client import LLMClient
from ..utils.locale import get_language_instruction, t from ..utils.locale import get_language_instruction, t
from ..config import Config
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -92,17 +93,17 @@ Please output JSON format with the following structure:
### 1. Entity Type Design — Must Be Strictly Followed ### 1. Entity Type Design — Must Be Strictly Followed
**Quantity requirement: exactly 20 entity types** **Quantity requirement: see the mandatory rules in the user message**
**Hierarchy requirement (must include both specific types and fallback types)**: **Hierarchy requirement (must include both specific types and fallback types)**:
Your 20 entity types must include the following levels: Your entity types must include the following levels:
A. **Fallback types (required, placed as the last 2 in the list)**: A. **Fallback types (required, placed as the last 2 in the list)**:
- `Person`: Fallback type for any individual person. Use this when a person does not fit any other more specific person type. - `Person`: Fallback type for any individual person. Use this when a person does not fit any other more specific person type.
- `Organization`: Fallback type for any organization. Use this when an organization does not fit any other more specific organization type. - `Organization`: Fallback type for any organization. Use this when an organization does not fit any other more specific organization type.
B. **Specific types (18 types, designed based on text content)**: B. **Specific types (designed based on text content)**:
- Design more specific types for the main roles that appear in the text - Design more specific types for the main roles that appear in the text
- Example: if the text involves an academic event, you might have `Student`, `Professor`, `University`, `ResearchGroup`, `Alumni`, etc. - Example: if the text involves an academic event, you might have `Student`, `Professor`, `University`, `ResearchGroup`, `Alumni`, etc.
- Example: if the text involves a business event, you might have `Company`, `CEO`, `Employee`, `Investor`, `Regulator`, etc. - Example: if the text involves a business event, you might have `Company`, `CEO`, `Employee`, `Investor`, `Regulator`, etc.
@ -120,7 +121,7 @@ B. **Specific types (18 types, designed based on text content)**:
### 2. Relationship Type Design ### 2. Relationship Type Design
- Quantity: 12-20 - Quantity: see the mandatory rules in the user message
- Relationships should reflect real connections in social media interactions - Relationships should reflect real connections in social media interactions
- Ensure the source_targets in relationships cover the entity types you have defined - Ensure the source_targets in relationships cover the entity types you have defined
- Aim for rich coverage: include hierarchical, collaborative, adversarial, and informational relationships - Aim for rich coverage: include hierarchical, collaborative, adversarial, and informational relationships
@ -193,7 +194,7 @@ class OntologyGenerator:
{"role": "user", "content": user_message} {"role": "user", "content": user_message}
] ]
# Call LLM — 20 entity types + 20 edge types need more tokens than the old 10+10 # Call LLM — token budget scales with ONTOLOGY_MAX_ENTITY_TYPES / ONTOLOGY_MAX_EDGE_TYPES
result = self.llm_client.chat_json( result = self.llm_client.chat_json(
messages=messages, messages=messages,
temperature=0.3, temperature=0.3,
@ -242,16 +243,21 @@ class OntologyGenerator:
{additional_context} {additional_context}
""" """
max_entities = Config.ONTOLOGY_MAX_ENTITY_TYPES
max_edges = Config.ONTOLOGY_MAX_EDGE_TYPES
specific_entities = max_entities - 2
edge_min = max(1, max_edges - 2)
message += f""" message += f"""
Based on the content above, design entity types and relationship types suitable for social opinion simulation. Based on the content above, design entity types and relationship types suitable for social opinion simulation.
**Mandatory rules**: **Mandatory rules**:
1. Output exactly 20 entity types 1. Output exactly {max_entities} entity types
2. The last 2 must be fallback types: Person (individual fallback) and Organization (organization fallback) 2. The last 2 must be fallback types: Person (individual fallback) and Organization (organization fallback)
3. The first 18 are specific types designed from the document content 3. The first {specific_entities} are specific types designed from the document content
4. All entity types must be real-world subjects capable of speaking out, not abstract concepts 4. All entity types must be real-world subjects capable of speaking out, not abstract concepts
5. Attribute names must not use reserved words: name, uuid, group_id use full_name, org_name, etc. instead 5. Attribute names must not use reserved words: name, uuid, group_id use full_name, org_name, etc. instead
6. Output 12-20 relationship types covering hierarchical, collaborative, adversarial, and informational relationships 6. Output {edge_min}-{max_edges} relationship types covering hierarchical, collaborative, adversarial, and informational relationships
{lang_instruction} {lang_instruction}
""" """
@ -309,10 +315,8 @@ Based on the content above, design entity types and relationship types suitable
if len(edge.get("description", "")) > 100: if len(edge.get("description", "")) > 100:
edge["description"] = edge["description"][:97] + "..." edge["description"] = edge["description"][:97] + "..."
# Limits: Graphiti/Neo4j has no hard cap; Zep Cloud allows max 10 of each. MAX_ENTITY_TYPES = Config.ONTOLOGY_MAX_ENTITY_TYPES
# We keep a generous cap for Graphiti and enforce Zep's limit at build time via config. MAX_EDGE_TYPES = Config.ONTOLOGY_MAX_EDGE_TYPES
MAX_ENTITY_TYPES = 20
MAX_EDGE_TYPES = 20
# Deduplicate: keep first occurrence by name # Deduplicate: keep first occurrence by name
seen_names = set() seen_names = set()