Compare commits

...

4 Commits

Author SHA1 Message Date
朱潮
c751c7997e sigle agent mode 2026-02-26 08:58:32 +08:00
朱潮
7cb2c9c5ca feat: 单智能体模式下加载用户所有知识库
- get_bot_settings 接口中,单智能体模式时加载用户的所有知识库
- 不再从 settings_json 读取 dataset_ids

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-26 08:29:10 +08:00
朱潮
1d82ca9ba8 feat: 添加启动时自动执行数据库迁移
添加 migrate_single_agent_mode 函数,在系统启动时自动添加
single_agent_bot_id 字段到 agent_user 表

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-26 08:13:36 +08:00
朱潮
2e928cbc9c feat: 添加单智能体模式 (Single Agent Mode)
为特定用户群体提供简化的智能体访问体验:
- 后端通过环境变量配置模板智能体
- 登录时自动为用户复制模板 bot
- 前端根据配置显示简化的侧边栏入口

后端变更:
- 添加 single_agent_bot_id 字段到 agent_user 表
- settings.py 添加单智能体模式配置
- 登录接口返回 single_agent 配置

环境变量:
- SINGLE_AGENT_MODE: 是否启用单智能体模式
- TEMPLATE_BOT_ID: 模板智能体 ID
- TEMPLATE_BOT_NAME: 模板智能体名称

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-26 08:05:18 +08:00
3 changed files with 181 additions and 7 deletions

View File

@ -0,0 +1,15 @@
-- ============================================================
-- Single Agent Mode Migration
-- 为用户表添加单智能体 bot_id 字段
-- ============================================================
-- 添加 single_agent_bot_id 字段
ALTER TABLE agent_user
ADD COLUMN IF NOT EXISTS single_agent_bot_id UUID;
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_agent_user_single_agent_bot
ON agent_user(single_agent_bot_id) WHERE single_agent_bot_id IS NOT NULL;
-- 添加注释
COMMENT ON COLUMN agent_user.single_agent_bot_id IS '单智能体模式下用户的专属智能体ID';

View File

@ -17,6 +17,7 @@ from pydantic import BaseModel
from agent.db_pool_manager import get_db_pool_manager
from utils.fastapi_utils import extract_api_key_from_auth
from utils.new_api_proxy import get_new_api_proxy
from utils.settings import SINGLE_AGENT_MODE, TEMPLATE_BOT_ID, TEMPLATE_BOT_NAME
logger = logging.getLogger('app')
@ -55,6 +56,105 @@ def copy_skills_folder(source_bot_id: str, target_bot_id: str) -> bool:
logger.error(f"Failed to copy skills folder: {e}")
return False
async def get_or_create_single_agent_bot(user_id: str, pool):
"""
获取或创建用户的单智能体 bot
Args:
user_id: 用户 ID
pool: 数据库连接池
Returns:
dict: { enabled: bool, bot_name: str, bot_id: str } { enabled: False }
"""
if not SINGLE_AGENT_MODE:
return {"enabled": False}
async with pool.connection() as conn:
async with conn.cursor() as cursor:
# 检查用户是否已有 single_agent_bot_id
await cursor.execute("""
SELECT single_agent_bot_id
FROM agent_user
WHERE id = %s
""", (user_id,))
row = await cursor.fetchone()
user_bot_id = row[0] if row and row[0] else None
# 如果用户没有 bot_id自动复制模板
if not user_bot_id:
if not TEMPLATE_BOT_ID:
logger.warning("TEMPLATE_BOT_ID not configured")
return {"enabled": False}
try:
# 获取模板 bot
await cursor.execute("""
SELECT id, name, settings
FROM agent_bots
WHERE id = %s AND is_published = TRUE
""", (TEMPLATE_BOT_ID,))
template = await cursor.fetchone()
if not template:
logger.warning(f"Template bot {TEMPLATE_BOT_ID} not found")
return {"enabled": False}
template_id, template_name, template_settings = template
settings = template_settings if template_settings else {}
# 创建新 bot
new_bot_id = str(uuid.uuid4())
# 复制设置(不复制知识库)
new_settings = {
'model_id': settings.get('model_id'),
'language': settings.get('language', 'zh'),
'avatar_url': settings.get('avatar_url'),
'description': settings.get('description'),
'suggestions': settings.get('suggestions'),
'system_prompt': settings.get('system_prompt'),
'enable_memori': settings.get('enable_memori', False),
'enable_thinking': settings.get('enable_thinking', False),
'tool_response': settings.get('tool_response', False),
'skills': settings.get('skills'),
}
# 插入新 bot
await cursor.execute("""
INSERT INTO agent_bots (name, bot_id, owner_id, settings, copied_from)
VALUES (%s, %s, %s, %s, %s)
RETURNING id
""", (TEMPLATE_BOT_NAME, new_bot_id, user_id, json.dumps(new_settings), template_id))
new_row = await cursor.fetchone()
user_bot_id = new_row[0]
# 更新用户的 single_agent_bot_id
await cursor.execute("""
UPDATE agent_user
SET single_agent_bot_id = %s
WHERE id = %s
""", (user_bot_id, user_id))
# 复制 skills 文件夹
copy_skills_folder(str(template_id), str(new_bot_id))
await conn.commit()
logger.info(f"Created single agent bot {user_bot_id} for user {user_id}")
except Exception as e:
logger.error(f"Failed to copy single agent bot: {e}")
return {"enabled": False}
return {
"enabled": True,
"bot_name": TEMPLATE_BOT_NAME,
"bot_id": str(user_bot_id)
}
# ============== Admin 配置 ==============
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD = "Admin123" # 生产环境应使用环境变量
@ -335,6 +435,7 @@ class UserLoginResponse(BaseModel):
email: Optional[str] = None
is_admin: bool = False
expires_at: str
single_agent: Optional[dict] = None # 单智能体模式配置
class UserVerifyResponse(BaseModel):
@ -940,6 +1041,39 @@ async def migrate_add_marketplace_fields():
logger.info("Marketplace fields migration completed")
async def migrate_single_agent_mode():
"""
添加单智能体模式相关字段到 agent_user
"""
pool = get_db_pool_manager().pool
async with pool.connection() as conn:
async with conn.cursor() as cursor:
# 检查 single_agent_bot_id 字段是否存在
await cursor.execute("""
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'agent_user' AND column_name = 'single_agent_bot_id'
""")
has_single_agent_bot_id = await cursor.fetchone()
if not has_single_agent_bot_id:
logger.info("Adding single_agent_bot_id column to agent_user table")
await cursor.execute("""
ALTER TABLE agent_user
ADD COLUMN single_agent_bot_id UUID
""")
await cursor.execute("""
CREATE INDEX IF NOT EXISTS idx_agent_user_single_agent_bot
ON agent_user(single_agent_bot_id) WHERE single_agent_bot_id IS NOT NULL
""")
logger.info("Single agent mode migration completed")
else:
logger.info("single_agent_bot_id column already exists")
await conn.commit()
async def init_bot_manager_tables():
"""
初始化 Bot Manager 相关的所有数据库表
@ -953,6 +1087,8 @@ async def init_bot_manager_tables():
await migrate_bot_owner_and_shares()
# 3. Marketplace 字段迁移
await migrate_add_marketplace_fields()
# 4. Single Agent Mode 字段迁移
await migrate_single_agent_mode()
# SQL 表创建语句
tables_sql = [
@ -1372,7 +1508,19 @@ async def get_bot_settings(bot_uuid: str, authorization: Optional[str] = Header(
api_key=None
)
# 处理 dataset_ids将字符串转换为数组
# 处理 dataset_ids
# 单智能体模式:加载用户的所有知识库
# 普通模式:从 settings 读取
if SINGLE_AGENT_MODE:
await cursor.execute("""
SELECT dataset_id
FROM user_datasets
WHERE user_id = %s
ORDER BY created_at DESC
""", (user_id,))
user_datasets = await cursor.fetchall()
dataset_ids = [row[0] for row in user_datasets] if user_datasets else []
else:
dataset_ids = settings.get('dataset_ids')
if dataset_ids and isinstance(dataset_ids, str):
dataset_ids = [id.strip() for id in dataset_ids.split(',') if id.strip()]
@ -2195,13 +2343,17 @@ async def user_login(request: UserLoginRequest):
await conn.commit()
# 获取单智能体配置
single_agent_config = await get_or_create_single_agent_bot(str(user_id), pool)
return UserLoginResponse(
token=token,
user_id=str(user_id),
username=username,
email=email,
is_admin=is_admin or False,
expires_at=expires_at.isoformat()
expires_at=expires_at.isoformat(),
single_agent=single_agent_config
)

View File

@ -113,3 +113,10 @@ NEW_API_TIMEOUT = int(os.getenv("NEW_API_TIMEOUT", "30"))
# New API 管理员密钥(用于同步用户等管理操作,可选)
NEW_API_ADMIN_KEY = os.getenv("NEW_API_ADMIN_KEY", "")
# ============================================================
# Single Agent Mode Configuration
# ============================================================
SINGLE_AGENT_MODE = os.getenv("SINGLE_AGENT_MODE", "true") == "true"
TEMPLATE_BOT_ID = os.getenv("TEMPLATE_BOT_ID", "403a2b63-88e4-4db1-b712-8dcf31fc98ea")
TEMPLATE_BOT_NAME = os.getenv("TEMPLATE_BOT_NAME", "智能助手")