Unity C# 代码审查 Agent 开发记录(Memory、流式输出与部署)
【学习笔记】Unity C# 代码审查 Agent 开发记录:第四阶段(Memory、流式输出与部署)
前言
按照个人学习计划,第四阶段(原计划 4.20-4.26)的任务是让 Agent 具备多轮对话记忆、流式输出,并部署到公网。实际执行中因事推迟了几天,于 4.29-5.4 完成。本文记录这一阶段的实现过程、代码要点及踩坑记录。
实际完成时间线:
| 原计划日期 | 任务 | 实际完成日期 |
|---|---|---|
| 4.20 | LangChain Memory 学习 | 4.29 |
| 4.21 | 对话历史管理接口 | 5.2 |
| 4.22 | 前端聊天界面 | 5.2 |
| 4.23 | 流式输出 Agent 思考过程 | 5.4 |
| 4.24 | 部署后端到 Railway | 5.4 |
| 4.25 | 部署前端到 Netlify | 5.4 |
一、LangChain Memory 与多轮对话
要让 Agent 支持多轮对话,需要将之前的对话历史保存下来,并在每次请求时一并发送给 LLM。
1. 数据模型
在 models.py 中新增请求模型,添加 session_id 字段用于区分不同会话:
class AgentMemoryRequest(BaseModel):
input: str = Field(..., description="用户输入")
session_id: str = Field(default="default", description="会话ID")
2. 会话存储
在 agent_api.py 中,使用字典管理多用户会话历史:
session_store: Dict[str, list] = {}
def get_session_history(session_id: str) -> list:
if session_id not in session_store:
session_store[session_id] = []
return session_store[session_id]
def add_message_to_session(session_id: str, message):
session_store.setdefault(session_id, []).append(message)
3. Memory 接口
新建 /agent/memory 接口,将历史消息拼接到当前消息前发送给 LLM,并在收到回复后保存本轮对话:
@app.post("/agent/memory")
async def agent_with_memory(request: AgentMemoryRequest):
history = get_session_history(request.session_id)
# 拦截逻辑:只有全新会话且不含代码关键词时才拦截
if not history and not is_code_review_intent(request.input):
return UnifiedResponse(success=True, code=200, message="非代码请求已拦截", ...)
messages = [SystemMessage(content="你是专业的 Unity C# 代码审查助手...")]
messages.extend(history)
messages.append(HumanMessage(content=request.input))
result = await agent_graph.ainvoke({"messages": messages})
add_message_to_session(request.session_id, HumanMessage(content=request.input))
add_message_to_session(request.session_id, result["messages"][-1])
return UnifiedResponse(success=True, data={"output": result["messages"][-1].content})
测试:先用 "public int age;" 开启会话,再问 "刚才那个字段有什么问题?",Agent 能准确复述之前的审查内容。
二、对话历史管理接口
为了方便管理,增加了查看和清除历史的接口:
GET /agent/memory/history?session_id=xxx:返回指定会话的历史记录DELETE /agent/memory/history?session_id=xxx:清除指定会话历史
实现中需要注意:GET 和 DELETE 没有请求体,参数通过 Query 传递。例如:
@app.get("/agent/memory/history")
async def get_memory_history(session_id: str = Query(..., description="会话ID")):
history = get_session_history(session_id)
# ... 构造返回数据
三、流式输出(SSE)
问题:ainvoke 需要等待 Agent 完全执行完毕才返回,用户体验不佳。希望实现类似 ChatGPT 的打字机效果。
方案:使用 LangGraph 的 astream_events 方法,并通过 FastAPI 的 StreamingResponse 实现 SSE。
后端:
@app.post("/agent/memory/stream")
async def agent_with_memory_stream(request: AgentMemoryRequest):
# ... 准备 messages ...
async def stream_events():
async for event in agent_graph.astream_events({"messages": messages}, version="v2"):
kind = event.get("event")
if kind == "on_chat_model_stream":
chunk = event["data"]["chunk"]
yield f"data:{json.dumps({'type':'text','content':chunk.content})}\n\n"
elif kind == "on_tool_start":
yield f"data: {json.dumps({'type':'tool_start','content':f'调用工具: {event["name"]}...'})}\n\n"
# ... 其他事件处理 ...
return StreamingResponse(stream_events(), media_type="text/event-stream")
前端:使用 fetch + ReadableStream 逐块解析 SSE 数据,动态更新消息气泡。
const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解析 data: 行,更新 UI
}
四、前后端部署
后端 Railway:
- 编写
requirements.txt和runtime.txt(指定 Python 3.12) - 推送代码到 GitHub
- 在 Railway 新建 Web Service,设置 Start Command
uvicorn main:app --host 0.0.0.0 --port 10000 - 添加环境变量
DEEPSEEK_API_KEY - 获得公网地址
前端 Netlify:
- 修改
index.html中API_BASE为 Railway 地址 - 将项目导入 Netlify,自动部署
- 注意:
runtime.txt会使 Netlify 误判为 Python 项目,需加入.gitignore
五、小结
这一阶段完成了:
- ✅ Memory 多轮对话
- ✅ 历史管理接口
- ✅ 前端聊天界面
- ✅ 流式输出
- ✅ 后端 Railway + 前端 Netlify 部署
项目已可公网访问,具备实际演示能力。后续将继续优化提示词和管理 Token 消耗。
更多推荐


所有评论(0)