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>
9.9 KiB
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.2model=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) => voidGeminiChat.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"],...}'
# 期望:回复包含文档下载链接