Skip to content
成为赞助商

AI

2026年01月

基础入门

  • Agent 智能体
    • 核心:任务规划和自主执行,可通过工具调用操作实现交互,实现最终成果的交付
    • 由 大语言模型LLM、一组可供模型使用的工具、提供指令的提示prompt、Tool Executor执行器 构成
    • 执行过程:遵循 ReAct 模式(Reasoning + Acting)
      • 思考:模型分析任务,接收输入与相关历史记忆构建上下文
      • 行动:选择并调用工具
      • 观察:获取工具返回结果
      • 循环:重复直到完成任务
  • Skills Agent = Skills架构的Agent【通用的Agent扩展包】
    • 25年10月由Anthropic公司发布Claude Skills,不久后 Agent Skills作为开放标准发布
    • Agent可通过加载不同的 Skills 包,拥有不同的专业知识、工具使用技能、稳定完成任务
    • Skills 是模块化能力,扩展Agent,每个Skills都打包了 LLM指令、元数据、可选资源(脚本、模版等),在Agent需要时自动使用
  • Multi-Agent 多智能架构
    • 用户问题-> Main Agent -> 选择分发给不同的Agent分别处理 -> 输出
    • 适合业务复杂、工具较多时,进行拆分为 多智能体架构
  • 场景分析
    • Skills Agent :任务逻辑线形、可序列化(如:数据分析、文档生成、标准业务流程)
    • Supervisor模式的多Agent
      • 需并行、对抗或辩论(如:红蓝军对抗、模拟辩论、创意风暴)- 单个Agent无法在同一上下文同时模拟持有对立观点的多个角色
      • 需调用不同特性的模型(如:文生图SD、代码生成Claude)- Skills架构通常基于单个骨干模型,无法解决模型异构问题
      • 涉及隐私或信息隔离 - Skills共享全局上下文,会破坏信息隔离的基本规则
    • 分层Skills 或转向多Agent:技能库规模庞大,超过50个 - 避免触及认知过载极限,可采用先分领域路由、在选技能的分层策略
  • 其他备注
    • MCP:Anthropic主导发布的一个开放的、通用的、有共识的协议标准;用于让 AI 大模型与不同数据源和工具无缝交互
对比维度Skills AgentMulti-Agent
核心范式单体智能体 SAS,通过能力扩展多智能体系统 MAS,通过任务分解与协作
工作流程单一Agent根据任务自主规划并调用一个或多个技能Supervisor接受任务,识别意图,分配给特定子Agent执行,并可能协调多个子Agent
上下文管理渐进式披露:仅预加载技能描述,需要时再加载技能详情,上下文共享;软隔离/动态隔离上下文隔离:每个子Agent拥有独立的上下文,通过状态State在Agent间传递关键信息;完全隔离
通信成本低,所有思考发生在单个模型内部,无额外网络往返高,每次子agent的调用都是一次独立的模型调用,产生额外Token消耗和延迟
优势高效、简洁、低延迟;适合逻辑线性、可序列化任务专业化、容错率高、适合复杂任务协作;处理不同模型、并行意见和隐私隔离的任务
劣势存在“认知过载”的物理极限,技能数量过多会导致性能急剧下降架构复杂,通信和协调开销大,开发和运维成本高

工业级Agent

  • 大模型:各种类型的大模型 、 大模型的推理框架
    • Ollama 为个人使用,不属于企业级,存在大量问题
  • 工具
    • Function Calling
    • MCP协议
    • 自定义工具
    • 人工介入
  • 上下文工程
    • 上下文摘要
    • 上下文嵌入

image-20260203105042531image-20260203105236252

LangChain-TS

25年10月发布LangChain1.0 与 LangGraph1.0,标志着AI Agent开发进入工程化阶段;用于构建大模型驱动应用的开源框架,通过标准化接口将大模型预言LLM与外部工具、数据存储、工作流逻辑等组件串联,实现快速构建具备“思考、记忆、行动”能力的智能体 Agent

  • LangChain:提供标准的工具调用架构、供应商无关设计和可插拔的中间件系统,高效构建通用agent,构建在LangGraph之上;
  • LangGraph:底层运行时框架,专为需要长期运行、可控且高定制化的生产级智能体设计
  • DeepAgents:
sh
# https://docs.langchain.com/oss/javascript/langchain/install
pnpm i langchain @langchain/core # 安装LangChain包(注意Node版本要求)

# LangChain提供数百个LLM和数千个其他【Integrations 集成】 。它们存在于独立的提供程序包中
# 国内外各种大模型都兼容,可直接使用ChatOpenAI的类调用,差异在更细粒度的控制
pnpm install @langchain/openai # Installing the OpenAI integration
pnpm install @langchain/anthropic # Installing the Anthropic integration

经验总结

  • initChatModel 配置模型

  • createAgent 创建agent

    • 配置系统提示词 systemPrompt
    • 配置结构化输出 responseFormat
    • 配置记忆 checkpointer
    • 创建并配置 tools
  • 调用agent

    • 方式:invoke标准化输出 、 stream流式输出

标准输出 invoke()

方法行为适用场景
invoke()等待工具完全执行完毕后返回结果快速操作、不需要实时反馈
stream() + streamMode: "updates"实时接收工具内的 writer 更新长时间操作、需要进度反馈
typescript
/*
标准化参数:ts中采用驼峰命名法,py中采用_分割
- model 模型名称
- apiKey 大模型供应商的API密钥
- temperature 【按官方推荐】采样温度,控制生成文本的随机性,值越高,生成的文本越随机
- maxTokens 生成的最大令牌token数
- maxRetries 请求重试的最大次数
- timeout 超时时间
- configuration: {
        baseURL: process.env.ALIBAILIAN_BASE_URL, // 发送请求的端点
    }
*/


/*
调用输出 invoke()
在模型生成完整响应后返回单个 AIMessage
*/
// 一条消息
const response = await model.invoke("Why do parrots have colorful feathers?");
console.log(response);

// 消息列表/对话历史
// 每个消息都有一个角色,模型使用该角色来指示谁在对话中发送了消息
const conversation = [
  { role: "system", content: "You are a helpful assistant that translates English to French." },
  { role: "user", content: "Translate: I love programming." },
  { role: "assistant", content: "J'adore la programmation." },
  { role: "user", content: "Translate: I love building applications." },
];
const response = await model.invoke(conversation);
console.log(response);  // AIMessage("J'adore créer des applications.")




/*
速率限制 rate
厂商可能限制指定时间段内的调用次数,超过速率限制可能收到供应商错误响应
为管理速率限制,initChatModel 可在模型集成初始化期间提供参数配置,以控制发出请求的速率【https://docs.langchain.com/langsmith/rate-limiting】
*/

流式输出 stream()

  • 返回多个 AIMessageChunk 对象,每个对象包含输出文本的一部分

  • 包含 深度思考过程输出

  • streamMode

    • 场景适配
      • 聊天机器人 UImessages 单独使用(实时显示,打字效果
      • 工具进度展示custom 单独使用(文件上传、数据处理等长时间操作,只需要及时反馈进度数据
      • 调试/开发阶段updates 单独使用(追踪 Agent 执行流程
      • 生产环境推荐 — 组合模式( ["custom", 'messages']
    模式描述返回格式主要场景
    updates流式输出 Agent 每一步的状态更新{ nodeName: { messages: [...] } }调试、状态追踪、UI 步骤展示【不常用】
    messages流式输出 LLM 生成的 token[token, metadata] 元组实时响应、用户体验核心
    custom流式输出工具内的自定义数据(通过 writerwriter 发送的任意数据工具进度、长时间操作反馈
ts
/** agent.stream  streamMode 详解 **/
import z from "zod";
import { tool, createAgent } from "langchain";
import { LangGraphRunnableConfig } from "@langchain/langgraph";
import { model } from '../../test/llm';

// 支持流式输出的工具
const longRunningTask = tool(
    async (input, config: LangGraphRunnableConfig) => {
        const writer = config.writer;

        // 发送进度更新
        writer?.(`开始处理任务 ${input.taskId}...`);
        await new Promise(resolve => setTimeout(resolve, 1000));

        writer?.(`任务 ${input.taskId} 进度:50%`);
        await new Promise(resolve => setTimeout(resolve, 1000));

        writer?.(`任务 ${input.taskId} 完成!`);

        return `任务 ${input.taskId} 执行成功`;
    },
    {
        name: "long_running_task",
        description: "执行长时间运行的任务",
        schema: z.object({
            taskId: z.string().describe("任务 ID"),
        }),
    }
);

const agent = createAgent({
    model: model,
    tools: [longRunningTask],
});

async function main() {
    console.log("🚀 开始执行...\n");
    // 多个模式时,返回 [mode, chunk] 元组
    for await (const [mode, chunk] of await agent.stream(
        { messages: [{ role: "user", content: "执行任务 abc123" }] },
        { streamMode: ["custom", 'messages'] } // 支持单个模式"xxx",也支持多个模式 ["updates","custom"]
    )) {

        if (mode === "custom") {
            console.log(`⏳ [进度] ${chunk}`); // custom 模式:chunk 接收 writer 发送的字符串
        }
        else if (mode === "messages") {
            // messages 模式:chunk 是 [token, metadata] 元组
            const [token, metadata] = chunk;
            if (token.content) {
                // LLM 生成的文本内容
                process.stdout.write(`${token.content}`);
            }
        }
    }
    console.log("✅ 执行完成!");
}

main();
typescript
import { deepseekModel } from "./llm";

async function main() {
    try {
        for await (const chunk of await deepseekModel.stream('现在是26年2月初,我这周末想从杭州自驾去南浔古镇及周边玩1天,请生成一份攻略,最好告诉我这个季节的百间楼最佳观赏时间!')) {
            // console.log(chunk);
            // 深度思考过程输出
            if (chunk.additional_kwargs?.reasoning_content) {
                const content = typeof chunk.additional_kwargs?.reasoning_content === 'string'
                    ? chunk.additional_kwargs?.reasoning_content
                    : String(chunk.additional_kwargs?.reasoning_content);
                process.stdout.write(content);
            }

            if (chunk.content) {
                // 确保 content 转换为字符串,支持 AIMessageChunk 类型
                const content = typeof chunk.content === 'string'
                    ? chunk.content
                    : String(chunk.content);
                process.stdout.write(content);
            }
        }
        console.log('\n\n流式输出完成!');
    } catch (error) {
        console.error('发生错误:', error);
    }
}

main();

结构化输出

  • providerStrategy(供应商策略)

  • 模型提供商原生支持结构化输出时(如 OpenAI、xAI/Grok、Gemini、Anthropic/Claude)

  • 最可靠的方法,因为模型提供商强制执行 schema

  • 部分模型供应商不支持,可用工具调用策略实现:如deepSeek-R1 不支持工具调用不支持结构化输出

  • toolStrategy(工具调用策略)

    • 模型不支持原生结构化输出,但支持工具调用(大多数现代模型)
typescript
// 供应商策略
import * as z from "zod";
import { createAgent, providerStrategy } from "langchain";
import { createStructuredModel } from './llm'

// 1. 定义 schema
const ContactInfo = z.object({
    name: z.string().describe("电影名称"),
    year: z.string().describe("发行年份"),
    director: z.string().describe("导演姓名"),
});
// 2. 创建模型
const model = await createStructuredModel();
// 3. 创建 Agent
const agent = createAgent({
    model,
    tools: [],
    responseFormat: providerStrategy(ContactInfo) // 提供商策略
});

// 4. 调用
const result = await agent.invoke({
    messages: [{
        "role": "user",
        "content": "提供电影《夏洛特烦恼》的详细信息"
    }]
});

console.log(result.structuredResponse);
// { director: '闫非、彭大魔', name: '夏洛特烦恼', year: '2015' }
typescript
// 工具调用策略
import * as z from "zod";
import { createAgent, toolStrategy } from "langchain";
import { createUniversalModel } from './llm'
const ProductReview = z.object({
    name: z.string().describe("电影名称"),
    year: z.string().describe("发行年份"),
    director: z.string().describe("导演姓名"),
});
// 2. 创建模型
const model = await createUniversalModel();
const agent = createAgent({
    model,
    tools: [],
    responseFormat: toolStrategy(ProductReview)
});

try {
    const result = await agent.invoke({
        "messages": [{
            "role": "user",
            "content": "提供电影《夏洛特烦恼》的详细信息"
        }]
    });

    console.log(result.structuredResponse);
    // { name: '夏洛特烦恼', year: '2015', director: '闫非、彭大魔' }
} catch (e) {
    console.log(e);
}

Agent项目

  • Agent(智能体) = LLM(大脑)+ Tools(手脚)
  • 执行过程
    1. 请求到达 → wrapModelCall (所有中间件)
    2. 模型调用
    3. 模型响应 → afterModelCall (所有中间件)
    4. 工具调用 → wrapToolCall (特定工具的中间件)
    5. 工具执行
    6. 工具响应 → afterToolCall (特定工具的中间件)
中间件 middleware
  • 专用中间件 LangChain 提供的一个快捷方式,本质是通用中间件的应用

    • dynamicSystemPromptMiddleware({...})- 专用于动态系统提示词
  • 通用中间件 createMiddleware({...}):执行遵循特定的生命周期和顺序、完全自定义逻辑

    • wrapModelCall - 模型调用前

      • 在请求发送给LLM模型之前执行
      • 可以修改请求参数、权限控制、动态过滤/追加工具、添加上下文等
    • wrapToolCall - 工具调用前

      • 在Agent决定调用某个具体工具之前执行
      • 可以修改工具参数、验证权限、记录日志等
      • 典型用途:参数验证、调用频率限制、工具执行前的数据预处理
    • afterModelCall - 模型调用后

      • 在LLM模型返回响应后执行
      • 可以处理模型输出、添加后处理逻辑等
      • 典型用途:响应内容过滤、敏感信息脱敏、日志记录
    • afterToolCall - 工具调用后

      • 在工具执行完成并返回结果后执行
      • 可以处理工具返回值、记录执行结果等
      • 典型用途:结果格式化、错误处理、执行结果验证
  • 常见中间件类型

    • 权限控制中间件:基于用户角色过滤可用工具、验证操作权限
    • 日志记录中间件:记录请求和响应、追踪工具调用链路
    • 缓存中间件:缓存模型响应、缓存工具调用结果
    • 验证中间件:验证输入参数、验证输出格式
    • 监控中间件:性能监控、错误追踪
ts
// 通用中间件 createMiddleware
// 动态工具中间件 
const dynamicToolMiddleware = createMiddleware({
    name: "DynamicToolMiddleware", // 自定义名称
    wrapModelCall: (request, handler) => {
        // 添加动态工具到请求中
        return handler({
            ...request,
            tools: [...request.tools, calculateTip],
        });
    },
    wrapToolCall: (request, handler) => {
        // 处理动态工具的执行
        if (request.toolCall.name === "calculate_tip") {
            return handler({ ...request, tool: calculateTip });
        }
        return handler(request);
    },
});


// 专用中间件 dynamicSystemPromptMiddleware
// 动态系统提示中间件
import { createAgent, dynamicSystemPromptMiddleware } from "langchain";
import * as z from "zod";

// 1. 定义上下文 Schema
const contextSchema = z.object({
  userRole: z.enum(["expert", "beginner"]),
});

// 2. 创建一个 Agent,支持动态提示词
const agent = createAgent({
  model,
  tools: [],
  contextSchema,  // 定义上下文类型
  middleware: [
    dynamicToolMiddleware, // 应用中间件
    dynamicSystemPromptMiddleware<z.infer<typeof contextSchema>>(
      (state, runtime) => {
        // 根据上下文动态生成提示词
        const userRole = runtime.context.userRole || "user";
        
        if (userRole === "expert") {
          return "你是一个专家助手。提供详细的技术性回答。";
        } else if (userRole === "beginner") {
          return "你是一个新手助手。用简单的语言解释概念,避免专业术语。";
        }
        return "你是一个有用的助手。";
      }
    ),
  ],
});

// 3. 使用同一个 Agent,通过 context 切换行为
const result1 = await agent.invoke(
  { messages: [{ role: "user", content: "解释机器学习" }] },
  { context: { userRole: "expert" } }  // 专家模式
);

const result2 = await agent.invoke(
  { messages: [{ role: "user", content: "解释机器学习" }] },
  { context: { userRole: "beginner" } }  // 新手模式
);
工具 tools

Agent 的手脚,让它能够执行实际操作,定义后,由LLM自主选择工具并使用,实现数据库查询、调用API、计算逻辑等,最终再以动态提示词形式交给LLM执行输出

  • 常见功能
    • 异步工具:调用API、数据库查询
    • 上下文
  • 注意
    • 某些模型提供程序对包含空格或特殊字符的名称有问题,或者拒绝包含空格或特殊字符的名称;
    • 使用字母数字字符、下划线和连字符有助于提高提供程序之间的兼容性;例如,web_search而不是Web Search
ts
/* 定义tool 工具函数 + 配置对象*/
const myTool = tool(
  // 工具函数(必需)
  (args, config) => {
    // args: 从 Agent 传入的参数,由当前tool的schema定义
    // config: 运行时配置(context, store, writer 等)
    // - context 上下文内容,调用agent时传入的内容 如{ context: { userRole: "user", userId: "002" } }
);
    return "工具执行结果";
  },
  // 2️⃣ 配置对象(必需)
  {
    name: "工具名称",              // 工具的唯一标识
    description: "工具描述",        // 告诉 Agent 当前工具的功能
    schema: z.object({...})        // 定义参数类型和说明
  }
);

/* 使用示例 */
import * as z from "zod";
import { tool } from "langchain";

// 工具函数 + 配置对象
const searchDatabase = tool(
  // 第一个参数:工具函数(执行逻辑)
  ({ query, limit, user, tags, settings }) => `找到 ${limit} 条关于 '${query}' 的结果`,
  // 第二个参数:配置对象
  {
    name: "search_database",
    description: "搜索客户数据库中匹配查询条件的记录",
    schema: z.object({
      query: z.string().describe("搜索关键词"),
      limit: z.number().describe("返回的最大结果数"),
      
      // 进阶类型
      // 对象
      user: z.object({
        id: z.string(),
        name: z.string().describe("用户姓名"),
        email: z.string().email().describe("邮箱地址"),
      }),
      // 数组
      tags: z.array(z.string()).describe("用户标签"),
      // 枚举
      role: z.enum(["user", "admin", "guest"]).describe("用户角色"),
      // 可选参数
      bio: z.string().optional().describe("个人简介"),
      // 带默认值
      limit: z.number().default(10).describe("返回结果数量限制"),
    }),
  }
);
ts
// ========================================
// 2. 异步工具
// ========================================
import z from 'zod'
import { createStructuredModel, } from '../../test/llm'
import { createMiddleware, createAgent, tool } from 'langchain'
const model = await createStructuredModel()

const users = [
    { name: '张三', age: 20, sex: '男' },
    { name: '李四', age: 25, sex: '女' },
    { name: '王五', age: 30, sex: '男' },
    { name: '赵六', age: 35, sex: '女' },
]
const userSearch = tool(async ({ name }: { name?: string }) => {
    if (!name) {
        return '请提供用户姓名'
    }
    // 可以直接发起 await fetch网络请求,获取user信息
    const user = users.find(user => user.name === name)
    if (!user) {
        return '用户不存在'
    }
    return `用户信息:${name},年龄:${user.age},性别:${user.sex}`
},
    {
        name: 'userSearch',
        description: '搜索用户信息',
        schema: z.object({
            name: z.string().optional().describe("用户姓名"),
        })
    })


// 真实的 API 调用工具
const fetchNews = tool(
  async ({ topic, limit = 5 }) => {
    try {
      const response = await fetch(
        `https://api.example.com/news?topic=${topic}&limit=${limit}`
      );
      
      if (!response.ok) {
        throw new Error(`API 请求失败:${response.status}`);
      }
      
      const data = await response.json();
      return data.articles || [];
    } catch (error) {
      console.error("获取新闻失败:", error);
      return `获取新闻失败:${error.message}`;
    }
  },
  {
    name: "fetch_news",
    description: "从新闻 API 获取指定主题的最新新闻",
    schema: z.object({
      topic: z.string().describe("新闻主题,如:科技、财经、体育"),
      limit: z.number().default(5).min(1).max(20).describe("返回新闻数量"),
    }),
  }
);

const agent = createAgent({
    model: model,
    tools: [userSearch, fetchNews],
    middleware: []
})

async function main() {
    for await (const chunk of await agent.stream({ messages: [{ role: 'user', content: '查询一下李四的信息' }] })) {

        // 处理模型请求(AI 的回复)
        if (chunk.model_request) {
            for (const message of chunk.model_request.messages) {
                // 输出推理内容(如果有)
                const reasoningContent = message.additional_kwargs?.reasoning_content;
                if (reasoningContent) {
                    const content = typeof reasoningContent === 'string'
                        ? reasoningContent
                        : String(reasoningContent);
                    process.stdout.write(content);
                }

                // 输出最终回复内容
                if (message.content) {
                    const content = typeof message.content === 'string'
                        ? message.content
                        : String(message.content);
                    process.stdout.write(content);
                }
            }
        }

        // 如果需要显示工具执行结果(可选)
        // if (chunk.tools) {
        //     for (const message of chunk.tools.messages) {
        //         console.log('[工具执行结果]', message.content);
        //     }
        // }
    }
}


main()
ts
// ========================================
// 3. 带权限控制的工具
// ========================================
const contextSchema = z.object({
    userRole: z.enum(["admin", "user"]),
});

// 为 config 参数标注具体的 `ToolRuntime` 类型
type AgentRuntime = ToolRuntime<unknown, { userRole: string }>;
const deleteUser = tool(
    async ({ userId }, config: AgentRuntime) => {
        const { userRole } = config.context;

        if (userRole !== "admin") {
            throw new Error("权限不足:只有管理员可以删除用户");
        }

        await new Promise(resolve => setTimeout(resolve, 300));
        delete database[userId];
        return `成功删除用户 ${userId}`;
    },
    {
        name: "delete_user",
        description: "删除用户(需要管理员权限)",
        schema: z.object({
            userId: z.string().describe("要删除的用户 ID"),
        }),
    }
);


// ========================================
// 4. 带长期内存的工具
// ========================================
const store = new InMemoryStore();

const saveNote = tool(
    async ({ userId, note }) => {
        console.log(`[saveNote] 被调用,userId=${userId}, note=${note}`);  // 调试日志
        const existing: any = (await store.get(["notes"], userId)) || { notes: [] }; // 先获取现有配置
        existing.notes.push({ content: note, timestamp: Date.now() }); 	// 合并新配置
        await store.put(["notes"], userId, existing); // 保存
        return `✅ 笔记已成功保存。用户ID:${userId},笔记内容:${note}`;
    },
    {
        name: "save_note",
        description: "保存用户笔记到持久化存储。当用户要求保存/记录笔记时必须使用此工具。",
        schema: z.object({
            userId: z.string().describe("用户ID"),
            note: z.string().describe("笔记内容"),
        }),
    }
);

const getNotes = tool(
    async ({ userId }) => {
        console.log(`[getNotes] 被调用,userId=${userId}`);  // 调试日志
        // 返回{ value: { notes: [...] }, key: "001" }需要 .value
        // console.log(`[getNotes] 处理后 data:`, data.value?.notes);
      	const data: any = await store.get(["notes"], userId);
      	if (!data.value?.notes || data.value.notes.length === 0) {
          return `用户 ${user_id} 暂无笔记`;
        }

        // 返回格式化的字符串(重要!)
        const notesList = data.notes
          .map((n, i) => `${i + 1}. ${n.content}`)
          .join("\n");

        return `用户 ${user_id} 的笔记:\n${notesList}`;
    },
    {
        name: "get_notes",
        description: "获取用户笔记",
        schema: z.object({
            userId: z.string().describe("用户ID"),
        }),
    }
);

// ========================================
// 创建 Agent
// ========================================
async function main() {
    try {
        const model = await createUniversalModel();

        const agent = createAgent({
            model,
            tools: [calculator, getUser, deleteUser, saveNote, getNotes],
            contextSchema,
            store,
            systemPrompt: `你是一个全能助手。
                你可以:
                1. 计算数学表达式
                2. 查询用户信息
                3. 管理用户笔记
                4. 删除用户(仅管理员可用)`
        });

        console.log("=".repeat(60));
        console.log("🛠️  完整工具演示");
        console.log("=".repeat(60));

        // 测试 1: 计算器
        console.log("\n📊 测试计算器:");
        const result1 = await agent.invoke({
            messages: [{ role: "user", content: "计算 (100 + 50) * 2" }]
        });
        console.log(result1.messages[result1.messages.length - 1].content);

        // 测试 2: 查询用户
        console.log("\n👤 测试查询用户:");
        const result2 = await agent.invoke({
            messages: [{ role: "user", content: "查询用户 001 的信息" }]
        });
        console.log(result2.messages[result2.messages.length - 1].content);

       // 测试 3: 保存并获取【长期存储】的笔记
        console.log("\n📝 测试保存笔记:");
        const result3 = await agent.invoke({
            messages: [{ role: "user", content: "帮userId为'001'的用户保存笔记:'今天天气不错'" }]
        });
        console.log(result3.messages[result3.messages.length - 1].content);
      
        console.log("\n📝 获取保存的笔记:");
        const result0 = await agent.invoke({
            messages: [{ role: "user", content: "获取userId为'001'的笔记" }]
        });
        console.log(result0.messages[result0.messages.length - 1].content);

        // 测试 4: 删除用户(管理员)
        console.log("\n🔧 测试删除用户(管理员):");
        const result4 = await agent.invoke(
            { messages: [{ role: "user", content: "删除用户 002" }] },
            { context: { userRole: "admin" } }
        );
        console.log(result4.messages[result4.messages.length - 1].content);

        // 测试 5: 删除用户失败(普通用户)
        console.log("\n❌ 测试删除用户失败(普通用户):");
        const result5 = await agent.invoke(
            { messages: [{ role: "user", content: "删除用户 001" }] },
            { context: { userRole: "user" } }
        );
        console.log(result5.messages[result5.messages.length - 1].content);

    } catch (error) {
        console.error("发生错误:", error);
    }
}

main();
上下文

在调用时传递的不可变配置数据,用于在对话期间不应更改的用户ID、会话详细信息或特定于应用程序的设置

ts
import * as z from "zod";
import { tool, ToolRuntime } from "langchain";

// 定义上下文 Schema
const contextSchema = z.object({
  user_name: z.string(),
  user_id: z.string(),
  api_key: z.string(),
});

// 访问上下文的工具
const getUserName = tool(
  (_, config: ToolRuntime) => {
    // 从 config.context 获取上下文
    return `你好,${config.context.user_name}!`;
  },
  {
    name: "get_user_name",
    description: "获取用户名称",
    schema: z.object({}),  // 无需参数
  }
);

// 使用上下文
import { createAgent } from "langchain";
import { createUniversalModel } from '../test/llm';

const agent = createAgent({
  model: await createUniversalModel(),
  tools: [getUserName],
  contextSchema,  // 定义上下文类型
});

// 调用时传入上下文
const result = await agent.invoke(
  { messages: [{ role: "user", content: "你是谁?" }] },
  { context: { user_name: "张三", user_id: "123", api_key: "secret" } }
);
长期记忆

与状态(短期记忆)不同,保存到存储中的数据在未来的会话中仍然可用;

特性短期记忆长期存储-内存/程序结束丢失
生命周期单次会话跨会话持久化
用途对话历史、上下文用户数据、偏好设置、知识库
访问方式自动注入 state通过工具主动读写
核心方法说明示例
put(namespace命名空间, key, value)存储数据store.put(["users"], "001", { name: "张三" })
get(namespace, key)获取数据store.get(["users"], "001")
delete(namespace, key)删除数据store.delete(["users"], "001")
search(prefix)搜索数据store.search(["users"])
存储类型适用场景
InMemoryStore适用于开发测试环境、临时数据,仅存在于内存中,程序重启后数据丢失
Redis高性能、分布式场景
PostgreSQL结构化数据、复杂查询
MongoDB文档型数据、灵活 schema
sh
# Store 使用 **namespace + key** 的模式组织数据
store
├── namespace: ["users"]          ← 第一层:命名空间(数组形式)
   ├── key: "001" value 第二层:键值对
   └── key: "002" value
├── namespace: ["notes"]
   └── key: "001" value
└── namespace: ["settings"]
    └── key: "global" value
ts
// 长期内存 store
import { tool, createAgent } from "langchain";
import { InMemoryStore } from "@langchain/langgraph";  // 1.导入InMemoryStore
import * as z from "zod";
import { createUniversalModel } from '../test/llm';

// 初始化InMemoryStore - 创建内存存储
const store = new InMemoryStore();

// 保存用户信息工具
const saveUserInfo = tool(
  async ({ userId, name, age, email }) => {
    // 保存到 store(跨会话持久化)
    await store.put(["users"], userId, { name, age, email });
    return `成功保存用户信息:${name}`;
  },
  {
    name: "save_user_info",
    description: "保存用户信息到数据库",
    schema: z.object({
      userId: z.string().describe("用户 ID"),
      name: z.string().describe("用户姓名"),
      age: z.number().describe("用户年龄"),
      email: z.string().email().describe("用户邮箱"),
    }),
  }
);

// 获取用户信息工具
const getUserInfo = tool(
  async ({ userId }) => {
    // 从 store 读取
    const value = await store.get(["users"], userId);
    if (!value) {
      return `未找到用户 ${userId} 的信息`;
    }
    return JSON.stringify(value);
  },
  {
    name: "get_user_info",
    description: "获取用户信息",
    schema: z.object({
      userId: z.string().describe("用户 ID"),
    }),
  }
);

const agent = createAgent({
  model: await createUniversalModel(),
  tools: [saveUserInfo, getUserInfo],
  store,  // 传入 store
});

// 第一次会话:保存用户信息
await agent.invoke({
  messages: [{
    role: "user",
    content: "保存用户信息:ID abc123,姓名 张三,年龄 25,邮箱 zhangsan@example.com"
  }]
});

// 第二次会话(可以跨会话):获取用户信息
const result = await agent.invoke({
  messages: [{
    role: "user",
    content: "获取用户 abc123 的信息"
  }]
});
console.log(result);
Stream writer
  • LangChain中tool的功能,支持在工具执行过程中实时流式传输更新,及时提供进度反馈
    • 如耗时 30 秒的数据库查询,原始只有空白界面,
    • 但Stream writer可实时告知用户
      • "正在连接数据库..."
      • "正在执行查询..."
      • "查询完成,正在处理结果..."
  • 通过 config.writer 来发送自定义更新
    • 需要为 config 参数标注 ToolRuntime 类型
    • 使用 if (writer) 检查 writer 是否存在
    • writer("消息内容") 直接调用即可发送更新
ts
import * as z from "zod";
import { tool, ToolRuntime } from "langchain";


const getWeather = tool(
  ({ city }, config: ToolRuntime) => {
    const writer = config.writer;

    // 在工具执行过程中流式发送自定义更新
    if (writer) {
      writer(`正在查询城市: ${city}`);
      writer(`已获取 ${city} 的数据`);
    }

    return `${city} 总是阳光明媚!`;
  },
  {
    name: "get_weather",
    description: "获取指定城市的天气信息。",
    schema: z.object({
      city: z.string(),
    }),
  }
);
其他工具
联网搜索
ts

2024年

https://www.bilibili.com/video/BV1R6P7eVEtd/?spm_id_from=333.1007.top_right_bar_window_history.content.click

  • 论文阅读:
    • LoRA 开山论文:2021 年 Microsoft Research 提出,首次提出了通过低秩矩阵分解的方式来进行部分参数微调,极大推动了 AI 技术在多行业的广泛落地应用:LoRA: Low-Rank Adaptation of Large Language Models
    • 大语言模型开山论文:2017 年 Google Brain 团队发布,标志着 Transformer 架构的提出,彻底改变了自然语言处理(NLP)领域,标志着大语言模型时代的开始:Attention Is All You Need

模型部署及价值

  • Ollama
    • 模型部署
  • Dify
    • 案例:中铁水务客服助理
  • ChatBox AI
    • 智能对话的界面
  • MaxKB
    • 私有知识库 - RAG检索增强生成
    • 函数库 - 工作流编排
    • 案例:中铁水务客服助理
  • 其他
    • 嵌入到企业官网,以及微信公众号、企业微信、钉钉、飞书、OA 等系统

未来思考

  • 能做什么?

    现有大模型的底层实现,依靠大量资金实力,借助头部企业实现,如deepseek、通义千问...

    当前我们迫切需要使用,实现模型落地应用开发

    业务数据来源(mysql、hive、hbase)

  • 和原有的业务生态系统打通

    • LLM、一条完整的工具链
    • 大公司 - 通用应用开发中...
  • 中小企业:细分领域

    • 业务数据 -> 数据清洗(复杂)

      • rag - 数据向量处理 - 业务数据量不是特别大的场景
      • sft - 人工处理 标注数据 - 训练业务模型
    • 训练自己的代码开发助手(微调)

技术概念

  • 微调:将知识喂给模型,模型消化吸收成为参数的一部分
  • 强化学习:根据人类反馈,调整结果,生成个性化答案
  • 检索增强生成:给予模型-额外的知识来源

三种训练方式

  • SFT 有监督微调
    • 通过提供人工标注的数据,进一步训练 预训练模型,让模型能够更加准确地处理特定领域的任务
    • 除此之外,还有无监督微调、自监督微调,常见为有监督微调
  • 强化学习 RLHF
    • DPO
      • 核心思想: 通过 人类对比选择,直接优化生成模型,使其产生更符合用户需求的结果,调整幅度大
    • PPO 【Proximal Policy Optimization】
      • 核心思想:通过 奖励信号,渐进式调整模型的行为策略,调整幅度小
  • 检索增强生成 RAG
    • 将外部信息检索与文本生成结合,帮助模型在生成答案时,实时获取外部信息和最新信息

微调还是RAG ?

  • 微调
    • 适合:拥有非常充足的数据
    • 能够直接提升模型固有能力,无需依赖外部数据,反应速度更快、更稳定
  • RAG
    • 适合:非常少的数据量,数据需要动态更新
    • 每次回答前需耗时检索知识库,回答质量依赖于知识库的质量

有监督微调 SFT

  • 通过提供人工标注的数据,进一步训练预训练模型,让模型能够更加精准地处理特定领域的任务

    • 人工标注的数据
    json
    // 如:分类系统
    {"image_path": "path/image1.jpg", "label": "SpongeBobSquarePants"}
    {"image_path": "path/image2.jpg", "label": "PatrickStar"}
    // 如:对话系统
    {
        "instruction": "请问你是谁",
        "input": "",
        "output": "您好,我是蟹堡王的神奇海螺,很高兴为您服务!我可以回答关于蟹堡王和汉堡制作的任何问题,您有什么需要帮助的吗?"
    },
    • 预训练模型(基座模型) 指已经在大量数据上训练过的模型,也就是我们微调前需要预先下载的开源模型。它具备了较为通用的知识和能力,能够解决一些常见的任务,可以在此基础上进行进一步的微调(fine-tuning)以适应特定的任务或领域
    • 微调算法的分类
      • 全参数微调(Full Fine-Tuning)
        • 对整个预训练模型进行微调,会更新所有参数。
        • 优点:因为每个参数都可以调整,通常能得到最佳的性能;能够适应不同任务和场景
        • 缺点:需要较大的计算资源并且容易出现过拟合
      • 部分参数微调(Partial Fine-Tuning)
        • 只更新模型的部分参数(例如某些层或模块)
        • 优点:减少了计算成本;减少过拟合风险;能够以较小的代价获得较好的结果
        • 缺点:可能无法达到最佳性能
        • 最著名算法:LoRA
LoRA 微调算法
  • 微调常见实现框架
    • 初学者如何对大模型进行微调?
    • Llama-Factory:由国内北航开源的低代码大模型训练框架,可以实现零代码微调,简单易学,功能强大,且目前热度很高,建议新手从这个开始入门
    • transformers.Trainer:由 Hugging Face 提供的高层 API,适用于各种 NLP 任务的微调,提供标准化的训练流程和多种监控工具,适合需要更多定制化的场景,尤其在部署和生产环境中表现出色
    • DeepSpeed:由微软开发的开源深度学习优化库,适合大规模模型训练和分布式训练,在大模型预训练和资源密集型训练的时候用得比较多

能做的事

知识库搭建

访客总数 总访问量统计始于2024.10.29