LangChain Agent 实战接入需要先看清适用场景和关键步骤,避免只记结论却忽略实际限制。
上一篇我们论证了 AI 产品为什么需要内置本地 Agent !
现在我们来聚焦"怎么做" —— 从架构设计到代码接入,输出我们的落地方案。
五层职责:
| 层级 | 技术选择 | 职责 |
|---|---|---|
| UI 交互层 | React + 流式 Hook | 对话界面、审批卡片、结果展示 |
| 基础 Agent 层 | LangChain createAgent() | 通用问答与工具调用循环 |
| 工作流编排层 | LangGraph StateGraph | 复杂任务、检查点、中断恢复 |
| 工具接入层 | 自研 Tool Adapter | 屏蔽数据源、本地命令、第三方差异 |
| 宿主层 | Electron Main + Local Service | 密钥安全、进程管理、随包发布 |
新建 ai-agent-server workspace,基于 Hono 框架提供 HTTP 服务:
// apps/ai-agent-server/src/server.tsimport { Hono } from'hono';import { serve } from'@hono/node-server';exportconstcreateAiAgentServer = async (config) => {const app = newHono();// 健康检查app.get('/health', (c) => c.json({ status: 'ready', model: config.model }));// 问答接口(Step 2 实现)registerChatRoutes(app, config);const server = serve({ fetch: app.fetch, hostname: config.host, port: config.port });return { origin: `http://<equation>{config.host}:</equation>{server.address().port}`, close: () => ... };};
关键点:
127.0.0.1:39321,只监听本地最小 Agent 接入——先做普通问答,工具后续再加:
// apps/ai-agent-server/src/agent/createHomeAiQaAgent.tsimport { createAgent } from'langchain';import { ChatOpenAI } from'@langchain/openai';exportconstcreateHomeAiQaAgent = (config) => {const model = newChatOpenAI({apiKey: config.apiKey,model: config.model,temperature: 0.2,configuration: { baseURL: config.baseUrl },});returncreateAgent({model,tools: [],// 先不加工具,二期再扩展systemPrompt: '你是本地 AI 助手...',name: 'home_qa_agent',});};
SSE 流式接口:
// apps/ai-agent-server/src/routes/chat.tsexportconstregisterChatRoutes = (app, config) => {const agent = createHomeAiQaAgent(config);app.post('/api/agent', async (c) => {const body = await c.req.json();const stream = await agent.stream({ messages: body.messages },{ encoding: 'text/event-stream', streamMode: ['values', 'updates', 'messages'] },);returnnewResponse(stream, {headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },});});};
让 Agent 服务随应用自动启动。开发态用 pnpm dev,打包态用 fork:
// electron/src/main/processManager/aiAgentProcess/index.tsexportconststartAiAgentProcess = () => {if (app.isPackaged) {// 打包态:fork 打包后的 js 文件aiAgentProcess = childProcess.fork(path.join(__dirname, 'aiAgentServer.js'), []);} else {// 开发态:pnpm 启动 dev serveraiAgentProcess = childProcess.spawn('pnpm', ['--filter', 'ai-agent-server', 'dev'], { env: { ...process.env } });}};
前端通过 LinkService 获取运行时状态:
// 前端调用const runtime = awaitwindow.LinkService.request('v1/aiAgent/getRuntime');// runtime = { ready: true, status: 'ready', baseUrl: 'http://127.0.0.1:39321' }
右侧副面板使用 LangGraph SDK 的 useStream Hook:
// apps/src/features/home/components/AiQaPanel/index.tsximport { FetchStreamTransport, useStream } from'@langchain/langgraph-sdk/react';constAiQaPanel = ({ runtime }) => {const transport = useMemo(() =>newFetchStreamTransport({apiUrl: `${runtime.baseUrl}/api/agent`,}), [runtime.baseUrl]);const stream = useStream({ transport });return (<section><header><h2>AI 问答</h2></header>{/* 消息列表 */}<div>{stream.messages.map(renderMessage)}</div>{/* 输入框 */}<inputvalue={input}onChange={(e) => setInput(e.target.value)} /><buttononClick={() => stream.submit({ messages: [{ type: 'human', content: input }] })}>发送</button></section>);};
Agent 一旦能执行本地命令,就必须有权限审批。推荐三层模型:
| 风险等级 | 典型操作 | 默认策略 |
|---|---|---|
low | 读笔记、读线程上下文 | 自动执行 |
medium | 新建文档、导出 PDF | 首次确认,可记住 |
high | Shell 命令、发消息 | 必须逐次审批 |
critical | 删除文件、批量外发 | 默认拒绝 |
用 LangGraph 的 interrupt() 实现审批中断:
// Agent 调用高风险工具时,interrupt 等待用户审批// 用户点击"允许"后,resume 继续执行
前端渲染审批卡片:
<ApprovalCardtitle="读取文件"reason="需要读取首页实现文件"riskLevel="low"onApprove={() =>AiAgentApis.approveRequest(requestId)}onDeny={() =>AiAgentApis.denyRequest(requestId)}/>
这是 Agent 和普通 Chat 最大的差异之一。
基于 thread_id 的状态持久化,保存当前会话的:
用 LangGraph 的 checkpointer 实现,开发阶段用 MemorySaver,生产用 PostgresSaver。
跨会话持久保存:
关键判断:不是所有内容都该记住。只保存三类:用户显式要求记住的、高频重复出现对后续有帮助的、权限与偏好类配置。
简单任务(普通问答、单工具调用)→ 直接用 Agent ReAct 循环。
复杂任务(多步骤、有审批、多阶段产物)→ 用 LangGraph StateGraph 显式编排。推荐的任务编排层次:
| 层次 | 作用 |
|---|---|
| Intent Classifier | 识别意图:问答 / 查询 / 生成 / 执行 / 工作流 |
| Planner | 拆解目标为可执行步骤 |
| Supervisor | 调度子袋里、审批、重试 |
| Executor | 执行工具或子图 |
| Verifier | 检查结果是否完整 |
| 错误类型 | 示例 | 处理 |
|---|---|---|
model_error | 超时、429 | 自动重试 / 模型降级 |
tool_error | 参数错误 | 让 Agent 自修复 |
permission_error | 审批被拒 | 中断提示用户 |
sandbox_error | 路径非法 | 终止并诊断 |
LangChain Agent 接入的核心,不是"接个模型"这么简单。从业务层面看,需要:
但更核心的是:Agent 只是一套执行、编排任务的框架,如何让大模型更好的工作。离不开我们的工程化:
这些,将是更深远的课题!