Files
socaichat/doc/plan/backend-plan.md
T
gongzhiyongandClaude Sonnet 4.6 c6ca9dc126 fix: restore workspace components accidentally dropped from git index
workspace/ files existed on disk but were not included in previous
incremental commit, causing git to record them as deleted. Re-adding
all workspace card components, AgentWorkspace, ActivityTimeline, and
WorkspaceCardRenderer to properly track them.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 03:09:41 +08:00

9.9 KiB
Raw Blame History

so-c-chat-clone 后端建设计划

Context

基于 gpthd.md 的 18 项功能方案和 EXTERNAL_SERVICES.md 中的 10 个已接入外部服务,从零构建 backend/ 目录下的企业级对话 Agent 后端。前端 backend/ 目前为空,mock 数据需替换为真实 API。

核心约束:

  • 前端代码未经明确指定不允许修改
  • Azure 资源只能操作 AuthData 和 Operation 两个资源组
  • CI/CD 由用户自行创建,Agent 只负责推代码到 GitHub

前端改动(已授权): GeminiInput.tsx 的 onSubmit 扩展参数,将 activeTools 和 selectedModel 传给后端。工具是否实际调用由 LangGraph ReAct Agent 自行判断;如果 Agent 决定不使用某个工具,必须在回复中向用户说明原因。视觉交互完全不变。


第一阶段:基础工程 + 对话核心

目标

完成可运行的后端骨架,实现真实 LLM 对话,替换前端 simulateAIResponse()。

文件结构

backend/
├── pyproject.toml          # 依赖管理
├── .env.example            # 环境变量模板
├── .gitignore              # Python 忽略规则
├── app/
│   ├── main.py             # Litestar 入口,路由注册,CORS
│   ├── config.py           # 环境变量读取(pydantic-settings)
│   ├── schemas.py          # 请求/响应 Pydantic 模型
│   ├── graph/
│   │   ├── state.py        # LangGraph MessagesState 定义
│   │   ├── nodes.py        # call_model 节点
│   │   └── builder.py      # StateGraph 构建,compile with checkpointer
│   ├── store/
│   │   └── postgres.py     # AsyncPostgresSaver 初始化,conversations/messages 表
│   └── api/
│       ├── chat.py         # POST /api/chat/stream(SSE)
│       ├── conversations.py # GET/POST/PATCH/DELETE /api/conversations
│       └── health.py       # GET /health

关键实现

LangGraph 基础图(graph/builder.py):

from langgraph.graph import StateGraph, MessagesState
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver

graph = StateGraph(MessagesState)
graph.add_node("agent", call_model_node)
graph.set_entry_point("agent")
graph.set_finish_point("agent")
app_graph = graph.compile(checkpointer=AsyncPostgresSaver.from_conn_string(DATABASE_URL))

SSE 流式输出(api/chat.py):

async def stream_chat(request: ChatRequest) -> ServerSentEvent:
    async for event in app_graph.astream_events(
        {"messages": [HumanMessage(content=request.message)]},
        config={"configurable": {"thread_id": request.conversation_id}},
        version="v2"
    ):
        if event["event"] == "on_chat_model_stream":
            yield ServerSentEvent(data=event["data"]["chunk"].content)

模型参数(nodes.py):

  • model=flash → max_tokens=500, temperature=0.2
  • model=pro → max_tokens=4096, temperature=0.3

依赖:

litestar[standard] uvicorn
langchain langchain-openai langgraph
langgraph-checkpoint-postgres
asyncpg sqlalchemy[asyncio]
pydantic-settings python-dotenv httpx

数据库表(PostgreSQL):

  • conversations(id, title, created_at, updated_at)
  • messages(id, conversation_id, role, content, created_at)
  • LangGraph checkpointer 自动建表

完成后推 GitHub,同步前端改动(已授权):

  • GeminiInput.tsx:onSubmit: (tools: string[], model: string) => void
  • GeminiChat.tsx:handleSend 接收 tools/model,传入 /api/chat/stream
  • Agent system prompt 中注明:若决定不调用用户选中的工具,必须在回复中说明原因

第二阶段:Tool 接入(KB + 工单 + LLM 路由)

目标

接入内部知识库和工单系统,LangGraph ReAct Agent 自动路由工具调用。

新增文件

backend/app/
├── tools/
│   ├── kb.py          # kb_search_tool → KB_AGENT /api/v1/search
│   └── tickets.py     # ticket_list_tool, ticket_detail_tool, ticket_summary_tool
├── graph/
│   └── builder.py     # 升级为 create_react_agent,绑定 tools
└── api/
    └── tickets.py     # GET /api/tickets/summary, /api/tickets, /api/tickets/{id}

工具封装示例(tools/kb.py):

@tool
async def kb_search(query: str) -> str:
    """检索企业内部知识库"""
    resp = await client.post(KB_AGENT_URL + KB_AGENT_SEARCH_PATH,
        json={"query": query, "top": 5, "search_mode": "hybrid"},
        headers={"api-key": KB_AGENT_API_KEY})
    results = resp.json()["results"]
    return "\n\n".join(f"【{r['title']}】\n{r['content']}" for r in results)

动态 Tool 绑定(根据前端传入 tools 参数):

ALL_TOOLS = {"knowledge": kb_search, "search": web_search, ...}
active = [ALL_TOOLS[t] for t in request.tools if t in ALL_TOOLS]
agent = create_react_agent(llm, active, checkpointer=checkpointer)

工单接口: 代理转发 Gongdan API,返回字段对齐前端 TicketData(id/title/status/priority/createdAt)

完成后推 GitHub。


第三阶段:外部搜索 + Redis 缓存

目标

接入 Jina MCP/v1 搜索链路(Search + Reader + Rerank),Redis 缓存热点结果。

新增文件

backend/app/
├── tools/
│   └── search.py      # web_search_tool(Jina Search → Reader → Rerank)
└── cache/
    └── redis.py       # Redis 客户端,搜索结果 TTL 缓存

Jina 接入(优先测试 /v1,备选 MCP SSE):

# 搜索
POST https://s.jina.ai/  Authorization: Bearer JINA_API_KEY

# 读取全文
GET https://r.jina.ai/{url}  Authorization: Bearer JINA_API_KEY

# 重排
POST https://api.jina.ai/v1/rerank
  model: jina-reranker-v2-base-multilingual

按 model 深度区分:

model top timeout Reader Rerank
flash 3 8s 跳过 跳过
pro 10 20s 并发 top 5

Redis 配置(EXTERNAL_SERVICES.md):

oper.redis.cache.windows.net:6380
password=bY8ZNwyJX60UwN5NPqnl6HRODfTV0efkDAzCaF1PrOU=,ssl=True

缓存 key:search:{query_hash}:{model},TTL=300s

完成后推 GitHub。


第四阶段:文档生成 + 沙盒 + 附件 + 异步任务

目标

接入 Doc Creator Agent、Daytona Sandbox、Azure Blob Storage、Azure Service Bus。

新增文件

backend/app/
├── tools/
│   ├── document.py    # doc_generate_tool → Doc Creator Agent
│   └── sandbox.py     # sandbox_run_tool → Daytona API
├── storage/
│   └── blob.py        # Azure Blob Storage 上传/下载
└── tasks/
    └── bus.py         # Azure Service Bus 异步任务派发/消费

Doc Creator(tools/document.py):

@tool
async def generate_document(prompt: str) -> str:
    """生成 Word/PPT/Excel 文档,返回下载链接"""
    output_type = detect_doc_type(prompt)  # ppt/table/word
    resp = await client.post(DOC_AGENT_URL,
        json={"prompt": prompt, "output_type": output_type},
        headers={"Authorization": f"Bearer {DOC_AGENT_KEY}"})
    data = resp.json()
    return f"📄 [{data['title']}]({data['file_url']})"

Daytona(tools/sandbox.py): 创建 workspace → 上传代码 → 执行 → 获取 stdout/stderr → 销毁

Azure Blob Storage(EXTERNAL_SERVICES.md):

AccountName=authdatablol
AccountKey=sm3ysR0zAmS9OLti...

用于保存附件、Sandbox 产物、文档生成结果。

Azure Service Bus(EXTERNAL_SERVICES.md):

Endpoint=sb://databus.servicebus.windows.net/

文档生成、深度搜索等长任务通过 Service Bus 异步化,主链路先返回"任务受理"。

完成后推 GitHub,通知用户配置 CI/CD。


环境变量清单(backend/.env.example)

# Azure OpenAI
AZURE_OPENAI_ENDPOINT=
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_API_VERSION=2025-04-01-preview
AZURE_OPENAI_DEPLOYMENT=gpt-5.4

# KB Agent
KB_AGENT_URL=https://agnetdoc-cve0guf5h8eggmej.southeastasia-01.azurewebsites.net
KB_AGENT_API_KEY=
KB_AGENT_SEARCH_PATH=/api/v1/search

# Jina
JINA_API_KEY=

# Daytona
DAYTONA_API_KEY=
DAYTONA_API_URL=https://app.daytona.io/api

# Doc Agent
DOC_AGENT_URL=http://doc-creator-agent-b0d02105-a557fe.taijiagnet.com
DOC_AGENT_KEY=

# Gongdan
GONGDAN_API_BASE=https://gongdan-b5fzbtgteqd5gzfb.eastasia-01.azurewebsites.net
GONGDAN_API_KEY=

# PostgreSQL
DATABASE_URL=postgresql+asyncpg://azuredb:PASSWORD@dataope.postgres.database.azure.com:5432/soc?ssl=require

# Redis
REDIS_URL=rediss://:bY8ZNwyJX60UwN5NPqnl6HRODfTV0efkDAzCaF1PrOU=@oper.redis.cache.windows.net:6380

# Azure Blob Storage
AZURE_STORAGE_CONNECTION_STRING=

# Azure Service Bus
AZURE_SERVICE_BUS_CONNECTION_STRING=

Azure 资源操作约束

所有 az 命令必须带 --resource-group Operation 或 --resource-group AuthData,否则停止执行。


GitHub 推送节奏

每完成一个阶段:

git pull origin main
git add backend/
git commit -m "feat(backend): 阶段N - ..."
git push origin main

CI/CD 由用户自行在 GitHub Actions 中配置,Agent 不创建 workflow 文件。


验证方式

第一阶段验证:

cd backend && uvicorn app.main:app --port 8000 --reload
curl -N http://localhost:8000/api/chat/stream \
  -X POST -H "Content-Type: application/json" \
  -d '{"message":"你好","conversation_id":"conv-test","tools":[],"model":"flash"}'
# 期望:SSE 流式返回 AI 回复

第二阶段验证:

curl http://localhost:8000/api/tickets
# 期望:真实工单数据数组,字段对齐前端 TicketData

第三阶段验证:

curl -N http://localhost:8000/api/chat/stream \
  -d '{"message":"查一下最新的 AI 新闻","tools":["search"],"model":"pro",...}'
# 期望:回复引用真实搜索结果

第四阶段验证:

curl -N http://localhost:8000/api/chat/stream \
  -d '{"message":"帮我生成一份PPT方案","tools":["document"],...}'
# 期望:回复包含文档下载链接