Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9523c5d
feat(kg): add dual-mode knowledge graph support (Zep Cloud + Graphiti)
huamingjie0815 Mar 11, 2026
5e05e7e
feat(report): change tool call format from JSON to XML
huamingjie0815 Mar 11, 2026
48ff659
fix(report): add title style constraints to PLAN_SYSTEM_PROMPT
huamingjie0815 Mar 11, 2026
dada9e6
Revert "fix(report): add title style constraints to PLAN_SYSTEM_PROMPT"
huamingjie0815 Mar 12, 2026
cdf4fcb
feat(report): add regenerate report button and title style constraints
huamingjie0815 Mar 12, 2026
4f6db3d
fix(report): simplify title style constraints in PLAN_SYSTEM_PROMPT
huamingjie0815 Mar 12, 2026
b06f0f1
feat(report): add export report button
huamingjie0815 Mar 12, 2026
fd37d45
Merge pull request #2 from huamingjie0815/feat/project-optimization
huamingjie0815 Mar 12, 2026
9e3f0cb
feat(history): add delete simulation functionality
huamingjie0815 Mar 12, 2026
c3953e4
Merge pull request #3 from huamingjie0815/feat/project-optimization
huamingjie0815 Mar 12, 2026
9dbccbb
fix(kg): add dynamic entity labels for local mode
huamingjie0815 Mar 12, 2026
e5fa052
Merge pull request #4 from huamingjie0815/fix/local-mode-entity-labels
huamingjie0815 Mar 12, 2026
0130bf5
fix(interview): add database index optimization and dynamic timeout
huamingjie0815 Mar 12, 2026
1a9314b
feat(llm): add configurable LLM_MAX_TOKENS parameter
huamingjie0815 Mar 12, 2026
7ab3169
feat(kg): 优化嵌入批处理和Graphiti适配器
huamingjie0815 Mar 14, 2026
fe8511d
refactor(kg): 清理无用代码和优化查询
huamingjie0815 Mar 14, 2026
81e3d97
feat(interview): 调整采访问题数量为3-5个
huamingjie0815 Mar 14, 2026
f4316f2
test: 修复 KNOWLEDGE_GRAPH_MODE 测试
huamingjie0815 Mar 14, 2026
2613ab3
fix(kg): 修复Cloud模式search返回格式
huamingjie0815 Mar 14, 2026
b81feb7
fix(kg): 修复Cloud模式Zep搜索返回空结果的问题
huamingjie0815 Mar 14, 2026
1d27130
docs: 完善 env.example 参数说明 + 新增双模式 README
huamingjie0815 Mar 14, 2026
0b6de73
Merge pull request #5 from huamingjie0815/fix/kg-embedding-optimization
huamingjie0815 Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,39 @@
LLM_API_KEY=your_api_key_here
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_MODEL_NAME=qwen-plus
LLM_MAX_TOKENS=4096 # LLM 最大输出 token 数

# ===== ZEP记忆图谱配置 =====
# 嵌入模型配置(用于 Graphiti local 模式,可独立配置)
# 如果不配置,则默认使用 LLM 的 API
# EMBEDDING_API_KEY=your_embedding_api_key # 可选
# EMBEDDING_BASE_URL=your_embedding_base_url # 可选
# EMBEDDING_MODEL=text-embedding-3-small
# EMBEDDING_DIM=1536
EMBEDDING_BATCH_SIZE=5 # 嵌入向量批处理大小

# ===== 知识图谱配置 =====
# 模式选择: "cloud" (Zep Cloud) 或 "local" (Graphiti + Neo4j)
KNOWLEDGE_GRAPH_MODE=cloud

# Zep Cloud 配置 (KNOWLEDGE_GRAPH_MODE=cloud 时需要)
# 每月免费额度即可支撑简单使用:https://app.getzep.com/
ZEP_API_KEY=your_zep_api_key_here

# Graphiti / Neo4j 配置 (KNOWLEDGE_GRAPH_MODE=local 时需要)
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_neo4j_password
# 嵌入向量 API Key(可选,用 LLM_API_KEY 即可,会自动使用同一 base_url)
# OPENAI_API_KEY=your_openai_key_for_embedding

# OASIS 模拟配置
OASIS_DEFAULT_MAX_ROUNDS=10 # 默认最大轮数

# Report Agent 配置
REPORT_AGENT_MAX_TOOL_CALLS=5 # 最大工具调用次数
REPORT_AGENT_MAX_REFLECTION_ROUNDS=2 # 最大反思轮数
REPORT_AGENT_TEMPERATURE=0.5 # 温度参数

# ===== 加速 LLM 配置(可选)=====
# 注意如果不使用加速配置,env文件中就不要出现下面的配置项
LLM_BOOST_API_KEY=your_api_key_here
Expand Down
28 changes: 17 additions & 11 deletions backend/app/api/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,9 @@ def build_graph():
try:
logger.info("=== 开始构建图谱 ===")

# 检查配置
# 检查配置 (cloud 模式需要 Zep Cloud)
errors = []
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
errors.append("ZEP_API_KEY未配置")
if errors:
logger.error(f"配置错误: {errors}")
Expand Down Expand Up @@ -374,7 +374,7 @@ def build_graph():
def build_task():
build_logger = get_logger('mirofish.build')
try:
build_logger.info(f"[{task_id}] 开始构建图谱...")
build_logger.debug(f"[{task_id}] 开始构建图谱...")
task_manager.update_task(
task_id,
status=TaskStatus.PROCESSING,
Expand Down Expand Up @@ -410,12 +410,15 @@ def build_task():
ProjectManager.save_project(project)

# 设置本体
build_logger.debug(f"[{task_id}] 准备设置本体...")
task_manager.update_task(
task_id,
message="设置本体定义...",
progress=15
)
build_logger.debug(f"[{task_id}] 开始设置本体...")
builder.set_ontology(graph_id, ontology)
build_logger.debug(f"[{task_id}] 本体设置完成")

# 添加文本(progress_callback 签名是 (msg, progress_ratio))
def add_progress_callback(msg, progress_ratio):
Expand All @@ -431,15 +434,18 @@ def add_progress_callback(msg, progress_ratio):
message=f"开始添加 {total_chunks} 个文本块...",
progress=15
)


build_logger.debug(f"[{task_id}] 准备添加文本,共 {total_chunks} 个块")
episode_uuids = builder.add_text_batches(
graph_id,
graph_id,
chunks,
batch_size=3,
progress_callback=add_progress_callback
)
build_logger.debug(f"[{task_id}] 文本添加完成,共 {len(episode_uuids)} 个 episode")

# 等待Zep处理完成(查询每个episode的processed状态)
build_logger.debug(f"[{task_id}] 开始等待处理,共 {len(episode_uuids)} 个 episode")
task_manager.update_task(
task_id,
message="等待Zep处理数据...",
Expand Down Expand Up @@ -567,13 +573,13 @@ def get_graph_data(graph_id: str):
获取图谱数据(节点和边)
"""
try:
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
return jsonify({
"success": False,
"error": "ZEP_API_KEY未配置"
}), 500
builder = GraphBuilderService(api_key=Config.ZEP_API_KEY)

builder = GraphBuilderService()
graph_data = builder.get_graph_data(graph_id)

return jsonify({
Expand All @@ -595,13 +601,13 @@ def delete_graph(graph_id: str):
删除Zep图谱
"""
try:
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
return jsonify({
"success": False,
"error": "ZEP_API_KEY未配置"
}), 500
builder = GraphBuilderService(api_key=Config.ZEP_API_KEY)

builder = GraphBuilderService()
builder.delete_graph(graph_id)

return jsonify({
Expand Down
58 changes: 48 additions & 10 deletions backend/app/api/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,18 @@ def get_graph_entities(graph_id: str):
enrich: 是否获取相关边信息(默认true)
"""
try:
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
return jsonify({
"success": False,
"error": "ZEP_API_KEY未配置"
}), 500

entity_types_str = request.args.get('entity_types', '')
entity_types = [t.strip() for t in entity_types_str.split(',') if t.strip()] if entity_types_str else None
enrich = request.args.get('enrich', 'true').lower() == 'true'

logger.info(f"获取图谱实体: graph_id={graph_id}, entity_types={entity_types}, enrich={enrich}")

reader = ZepEntityReader()
result = reader.filter_defined_entities(
graph_id=graph_id,
Expand All @@ -93,12 +93,12 @@ def get_graph_entities(graph_id: str):
def get_entity_detail(graph_id: str, entity_uuid: str):
"""获取单个实体的详细信息"""
try:
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
return jsonify({
"success": False,
"error": "ZEP_API_KEY未配置"
}), 500

reader = ZepEntityReader()
entity = reader.get_entity_with_context(graph_id, entity_uuid)

Expand Down Expand Up @@ -126,12 +126,12 @@ def get_entity_detail(graph_id: str, entity_uuid: str):
def get_entities_by_type(graph_id: str, entity_type: str):
"""获取指定类型的所有实体"""
try:
if not Config.ZEP_API_KEY:
if Config.KNOWLEDGE_GRAPH_MODE == 'cloud' and not Config.ZEP_API_KEY:
return jsonify({
"success": False,
"error": "ZEP_API_KEY未配置"
}), 500

enrich = request.args.get('enrich', 'true').lower() == 'true'

reader = ZepEntityReader()
Expand Down Expand Up @@ -982,6 +982,44 @@ def get_simulation_history():
}), 500


@simulation_bp.route('/<simulation_id>', methods=['DELETE'])
def delete_simulation(simulation_id: str):
"""
删除模拟及其所有相关数据

Args:
simulation_id: 模拟ID

Returns:
{
"success": true,
"message": "删除成功"
}
"""
try:
manager = SimulationManager()
success = manager.delete_simulation(simulation_id)

if success:
return jsonify({
"success": True,
"message": "删除成功"
})
else:
return jsonify({
"success": False,
"error": "删除失败,模拟可能不存在"
}), 404

except Exception as e:
logger.error(f"删除模拟失败: {str(e)}")
return jsonify({
"success": False,
"error": str(e),
"traceback": traceback.format_exc()
}), 500


@simulation_bp.route('/<simulation_id>/profiles', methods=['GET'])
def get_simulation_profiles(simulation_id: str):
"""
Expand Down Expand Up @@ -1409,7 +1447,7 @@ def generate_profiles():
"error": "没有找到符合条件的实体"
}), 400

generator = OasisProfileGenerator()
generator = OasisProfileGenerator(graph_id=graph_id)
profiles = generator.generate_profiles_from_entities(
entities=filtered.entities,
use_llm=use_llm
Expand Down Expand Up @@ -2440,7 +2478,7 @@ def interview_all_agents():
simulation_id = data.get('simulation_id')
prompt = data.get('prompt')
platform = data.get('platform') # 可选:twitter/reddit/None
timeout = data.get('timeout', 180)
timeout = data.get('timeout', 240)

if not simulation_id:
return jsonify({
Expand Down
39 changes: 35 additions & 4 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,29 @@ class Config:
LLM_API_KEY = os.environ.get('LLM_API_KEY')
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')

# Zep配置
LLM_MAX_TOKENS = int(os.environ.get('LLM_MAX_TOKENS', '4096'))

# 嵌入模型配置(用于 Graphiti local 模式,可独立配置)
EMBEDDING_API_KEY = os.environ.get('EMBEDDING_API_KEY') # 可选,默认使用 LLM_API_KEY
EMBEDDING_BASE_URL = os.environ.get('EMBEDDING_BASE_URL') # 可选,默认使用 LLM_BASE_URL
EMBEDDING_MODEL = os.environ.get('EMBEDDING_MODEL', 'text-embedding-3-small')
EMBEDDING_DIM = int(os.environ.get('EMBEDDING_DIM', '1536'))
EMBEDDING_BATCH_SIZE = int(os.environ.get('EMBEDDING_BATCH_SIZE', '5')) # 批处理大小,默认5

# 知识图谱模式配置
# cloud: 使用 Zep Cloud (默认)
# local: 使用 Graphiti + Neo4j (本地部署)
KNOWLEDGE_GRAPH_MODE = os.environ.get('KNOWLEDGE_GRAPH_MODE', 'cloud')

# Zep Cloud 配置 (KNOWLEDGE_GRAPH_MODE=cloud 时需要)
ZEP_API_KEY = os.environ.get('ZEP_API_KEY')

# Graphiti / Neo4j 配置 (KNOWLEDGE_GRAPH_MODE=local 时需要)
NEO4J_URI = os.environ.get('NEO4J_URI', 'bolt://localhost:7687')
NEO4J_USER = os.environ.get('NEO4J_USER', 'neo4j')
NEO4J_PASSWORD = os.environ.get('NEO4J_PASSWORD')
# OpenAI API 用于嵌入向量 (Graphiti 模式需要)
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')

# 文件上传配置
MAX_CONTENT_LENGTH = 50 * 1024 * 1024 # 50MB
Expand Down Expand Up @@ -69,7 +89,18 @@ def validate(cls):
errors = []
if not cls.LLM_API_KEY:
errors.append("LLM_API_KEY 未配置")
if not cls.ZEP_API_KEY:
errors.append("ZEP_API_KEY 未配置")

# 根据模式验证对应的配置
if cls.KNOWLEDGE_GRAPH_MODE == 'cloud':
if not cls.ZEP_API_KEY:
errors.append("ZEP_API_KEY 未配置 (当前模式: cloud)")
elif cls.KNOWLEDGE_GRAPH_MODE == 'local':
if not cls.NEO4J_PASSWORD:
errors.append("NEO4J_PASSWORD 未配置 (当前模式: local)")
if not cls.LLM_API_KEY and not cls.OPENAI_API_KEY:
errors.append("LLM_API_KEY 或 OPENAI_API_KEY 未配置 (当前模式: local,用于嵌入向量)")
else:
errors.append(f"未知的 KNOWLEDGE_GRAPH_MODE: {cls.KNOWLEDGE_GRAPH_MODE}")

return errors

Loading