feat: virtual company module, team projects, PWA dishes app, and startup scripts overhaul

- Add company module (3-tier org, CEO planning, parallel departments)
- Add company orchestrator, knowledge extractor, presets, scheduler
- Add company API endpoints, models, and frontend views
- Add 今天吃啥 PWA app (69 dishes, real images, offline support)
- Add team_projects output directory structure
- Add unified manage.ps1 for service lifecycle
- Add Windows startup guide v1.0
- Add TTS troubleshooting doc
- Update frontend (AgentChat UX overhaul, new views)
- Update backend (voice engine fix, multi-tenant, RBAC)
- Remove deprecated startup scripts and old docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 22:37:56 +08:00
parent 3483c6b3be
commit f2e65a8fbb
259 changed files with 39239 additions and 3148 deletions

View File

@@ -136,8 +136,10 @@ class ChatRequest(BaseModel):
message: str
session_id: Optional[str] = None
model: Optional[str] = None
model_config_id: Optional[str] = Field(default=None, description="使用已保存的模型配置(含 API Key")
temperature: Optional[float] = None
max_iterations: Optional[int] = None
max_tokens: Optional[int] = Field(default=None, description="单次回复最大 token 数")
streamlined: bool = Field(default=False, description="启用工具结果流式美化")
prompt_sections_enabled: bool = Field(default=True, description="启用系统提示词分层装配")
system_prompt_override: Optional[str] = Field(default=None, description="覆盖 Agent 的 System Prompt")
@@ -355,17 +357,20 @@ async def chat_bare(
"""无需 Agent 配置,使用默认设置直接对话。"""
uid = current_user.id
bare_scope = f"{uid}:__bare__" if uid else "__bare__"
mc = _resolve_model_config(db, req.model_config_id, uid)
llm_kwargs = _apply_model_config(dict(
model=req.model or (
"gpt-4o-mini" if settings.OPENAI_API_KEY and settings.OPENAI_API_KEY != "your-openai-api-key"
else "deepseek-v4-flash"
),
temperature=req.temperature or 0.7,
max_iterations=req.max_iterations or 10,
max_tokens=req.max_tokens,
), mc)
config = AgentConfig(
name="bare_agent",
system_prompt="你是一个有用的AI助手。请使用可用工具来帮助用户完成任务。",
llm=AgentLLMConfig(
model=req.model or (
"gpt-4o-mini" if settings.OPENAI_API_KEY and settings.OPENAI_API_KEY != "your-openai-api-key"
else "deepseek-v4-flash"
),
temperature=req.temperature or 0.7,
max_iterations=req.max_iterations or 10,
),
llm=AgentLLMConfig(**llm_kwargs),
user_id=uid,
memory_scope_id=bare_scope,
memory=AgentMemoryConfig(
@@ -421,17 +426,20 @@ async def chat_bare_stream(
"""无需 Agent 配置,使用默认设置直接对话(流式 SSE"""
uid = current_user.id
bare_scope = f"{uid}:__bare__" if uid else "__bare__"
mc = _resolve_model_config(db, req.model_config_id, uid)
llm_kwargs = _apply_model_config(dict(
model=req.model or (
"gpt-4o-mini" if settings.OPENAI_API_KEY and settings.OPENAI_API_KEY != "your-openai-api-key"
else "deepseek-v4-flash"
),
temperature=req.temperature or 0.7,
max_iterations=req.max_iterations or 10,
max_tokens=req.max_tokens,
), mc)
config = AgentConfig(
name="bare_agent",
system_prompt="你是一个有用的AI助手。请使用可用工具来帮助用户完成任务。",
llm=AgentLLMConfig(
model=req.model or (
"gpt-4o-mini" if settings.OPENAI_API_KEY and settings.OPENAI_API_KEY != "your-openai-api-key"
else "deepseek-v4-flash"
),
temperature=req.temperature or 0.7,
max_iterations=req.max_iterations or 10,
),
llm=AgentLLMConfig(**llm_kwargs),
user_id=uid,
memory_scope_id=bare_scope,
memory=AgentMemoryConfig(
@@ -476,7 +484,7 @@ async def chat_with_agent(
agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent 不存在")
if agent.user_id and agent.user_id != current_user.id and current_user.role != "admin" and not agent.is_public:
if agent.user_id and agent.user_id != current_user.id and not current_user.has_permission("agent:chat_any") and not agent.is_public:
raise HTTPException(status_code=403, detail="无权访问该 Agent")
# 从 Agent 配置构建 Runtime
@@ -506,18 +514,20 @@ async def chat_with_agent(
memory_cfg = _build_memory_config_from_node(agent_node_cfg)
if getattr(agent, "parent_agent_id", None):
memory_cfg.parent_agent_id = agent.parent_agent_id
mc = _resolve_model_config(db, req.model_config_id, uid)
llm_kwargs = _apply_model_config(dict(
provider=agent_node_cfg.get("provider", "openai"),
model=req.model or agent_node_cfg.get("model", "gpt-4o-mini"),
temperature=req.temperature or float(agent_node_cfg.get("temperature", 0.7)),
max_iterations=req.max_iterations or int(agent_node_cfg.get("max_iterations", 10)),
max_tokens=req.max_tokens or agent_node_cfg.get("max_tokens"),
plan_mode_enabled=bool(agent_node_cfg.get("plan_mode_enabled", False)),
plan_approval_required=bool(agent_node_cfg.get("plan_approval_required", True)),
), mc)
config = AgentConfig(
name=agent.name,
system_prompt=system_prompt,
llm=AgentLLMConfig(
provider=agent_node_cfg.get("provider", "openai"),
model=req.model or agent_node_cfg.get("model", "gpt-4o-mini"),
temperature=req.temperature or float(agent_node_cfg.get("temperature", 0.7)),
max_iterations=req.max_iterations or int(agent_node_cfg.get("max_iterations", 10)),
# 计划模式 (P2)
plan_mode_enabled=bool(agent_node_cfg.get("plan_mode_enabled", False)),
plan_approval_required=bool(agent_node_cfg.get("plan_approval_required", True)),
),
llm=AgentLLMConfig(**llm_kwargs),
tools=AgentToolConfig(
include_tools=agent_node_cfg.get("tools", []),
exclude_tools=agent_node_cfg.get("exclude_tools", []),
@@ -577,7 +587,7 @@ async def chat_with_agent_stream(
agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not agent:
raise HTTPException(status_code=404, detail="Agent 不存在")
if agent.user_id and agent.user_id != current_user.id and current_user.role != "admin" and not agent.is_public:
if agent.user_id and agent.user_id != current_user.id and not current_user.has_permission("agent:chat_any") and not agent.is_public:
raise HTTPException(status_code=403, detail="无权访问该 Agent")
wc = agent.workflow_config or {}
@@ -603,18 +613,20 @@ async def chat_with_agent_stream(
memory_cfg = _build_memory_config_from_node(agent_node_cfg)
if getattr(agent, "parent_agent_id", None):
memory_cfg.parent_agent_id = agent.parent_agent_id
mc = _resolve_model_config(db, req.model_config_id, uid)
llm_kwargs = _apply_model_config(dict(
provider=agent_node_cfg.get("provider", "openai"),
model=req.model or agent_node_cfg.get("model", "gpt-4o-mini"),
temperature=req.temperature or float(agent_node_cfg.get("temperature", 0.7)),
max_iterations=req.max_iterations or int(agent_node_cfg.get("max_iterations", 10)),
max_tokens=req.max_tokens or agent_node_cfg.get("max_tokens"),
plan_mode_enabled=bool(agent_node_cfg.get("plan_mode_enabled", False)),
plan_approval_required=bool(agent_node_cfg.get("plan_approval_required", True)),
), mc)
config = AgentConfig(
name=agent.name,
system_prompt=system_prompt,
llm=AgentLLMConfig(
provider=agent_node_cfg.get("provider", "openai"),
model=req.model or agent_node_cfg.get("model", "gpt-4o-mini"),
temperature=req.temperature or float(agent_node_cfg.get("temperature", 0.7)),
max_iterations=req.max_iterations or int(agent_node_cfg.get("max_iterations", 10)),
# 计划模式 (P2)
plan_mode_enabled=bool(agent_node_cfg.get("plan_mode_enabled", False)),
plan_approval_required=bool(agent_node_cfg.get("plan_approval_required", True)),
),
llm=AgentLLMConfig(**llm_kwargs),
tools=AgentToolConfig(
include_tools=agent_node_cfg.get("tools", []),
exclude_tools=agent_node_cfg.get("exclude_tools", []),
@@ -901,7 +913,38 @@ async def search_messages(
return SearchMessagesResponse(messages=result, total=total)
def _find_agent_node_config(nodes: list) -> Dict[str, Any]:
def _resolve_model_config(db: Session, model_config_id: Optional[str], user_id: str) -> Optional[Dict[str, Any]]:
"""从数据库加载模型配置,返回 {provider, model, api_key, base_url}。"""
if not model_config_id:
return None
from app.models.model_config import ModelConfig
mc = db.query(ModelConfig).filter(
ModelConfig.id == model_config_id,
ModelConfig.user_id == user_id,
).first()
if not mc:
raise HTTPException(status_code=404, detail="模型配置不存在")
return {
"provider": mc.provider,
"model": mc.model_name,
"api_key": mc.api_key,
"base_url": mc.base_url,
}
def _apply_model_config(llm_kwargs: Dict[str, Any], mc: Optional[Dict[str, Any]]) -> Dict[str, Any]:
"""将模型配置应用到 LLM 参数中。"""
if not mc:
return llm_kwargs
if mc.get("provider"):
llm_kwargs["provider"] = mc["provider"]
if mc.get("model"):
llm_kwargs["model"] = mc["model"]
if mc.get("api_key"):
llm_kwargs["api_key"] = mc["api_key"]
if mc.get("base_url"):
llm_kwargs["base_url"] = mc["base_url"]
return llm_kwargs
"""从工作流节点列表中查找第一个 agent 类型或 llm 类型的节点配置。"""
if not nodes:
return {}