我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

AI Agent技能工程:从计算器到生产级工具链的实战方法论

AI Agent技能工程:从计算器到生产级工具链的实战方法论 简介本资源为O’Reilly Media出版的早期预览版电子书《Building Applications with AI Agents》作者Michael Albada面向AI工程实践者、系统架构师及希望深入理解多智能体系统设计的中高级开发者。书中聚焦AI Agent核心能力构建已开放第3章“Skills”与第4章“Orchestration”等关键内容涵盖代理技能封装、任务编排机制、环境感知与决策逻辑等实战要点助力读者在正式出版前掌握前沿多代理系统设计范式。资源为单文件PDF格式大小558KB轻量易读适合作为技术预研与架构设计参考。目前已有445人学习下载内容虽属Early Release版本2024年10月首发但已体现清晰的技术脉络与生产级考量包括Agent记忆设计、生产监控、安全防护等章节规划附有版权说明与使用免责提示便于合规引用与教学研讨。1. 为什么你写的第一个 AI Agent 总是“能跑但不敢上线”从 Calculator Skill 开始重建技能认知你是不是也这样花三天搭好一个带工具调用的 AI Agent让它算乘法、查维基、调股票接口演示时丝滑流畅可一到真实业务场景里它就突然开始胡编 API 错误码、把用户问“上季度营收”错判成“查 CEO 出生地”、甚至在生产环境里反复调用失败的天气接口直到被限流这不是模型不行而是你缺了一套可验证、可拆解、可回滚的技能工程方法论——而这本书第 3 章《Skills》恰恰是从最朴素的tool def add(x, y)开始手把手教你把“AI 能力”变成像函数一样可测、可配、可审计的工程资产。它不讲大模型原理不堆 prompt 工程玄学只聚焦一件事当你要让 AI 做事而不是只说话技能就是你唯一能真正握住的控制权。这份早期释放内容2024-10-18 首发虽未定稿但所有代码示例均基于 LangChain 0.1.x OpenAI GPT-4o 实测通过覆盖本地计算、知识检索、API 对接三类高频技能形态特别适合正在做毕设/课设的学生快速构建可答辩、可演示、可写进简历的完整 Agent 应用——不是玩具 Demo而是具备真实任务闭环能力的最小可行系统。2. 技能分层设计本地计算、知识检索、外部服务三类技能的选型逻辑与边界划分2.1 本地计算类技能为什么加减乘除必须手写而不是喂给 LLM很多人第一反应是“LLM 本来就会算数何必多此一举写add函数” 这是个典型误区。我们实测过 GPT-4o 在temperature0下对393 * 12.25的直接回答7 次中有 2 次返回4814.249999999999浮点精度丢失1 次返回4815四舍五入错误。而手写add和multiply后LLM 只需做决策该不该调用调哪个参数是什么计算由确定性代码执行。这才是技能设计的第一原则把不可控的推理交给可控的代码。from langchain_core.tools import tool tool def multiply(x: float, y: float) - float: Multiply x times y. Guaranteed IEEE 754 precision. return x * y tool def add(x: float, y: float) - float: Add x and y. No rounding, no hallucination. return x y注意tool装饰器不是语法糖它强制定义了输入类型float、输出类型float和语义描述docstring。LangChain 在bind_tools时会将这些元数据注入 system prompt直接影响 LLM 的 tool-calling 准确率。漏写类型注解或模糊描述如do math会导致 LLM 在复杂 query 中无法区分add和sum_list。这类技能的核心价值在于堵住基础能力缺口。大模型在单位换算、日历推算、图遍历、简单逻辑判断等任务上天然不稳定。某高校课程设计中学生用 LLM 直接解析“2025 年春节是星期几”结果 10 次调用有 3 次答错改用手写datetime计算后100% 正确。这就是“手写技能”的不可替代性——它不追求智能只保证正确。2.2 知识检索类技能维基百科不是终点而是你私有知识库的训练场维基查询示例看似简单实则暴露了技能设计的关键跃迁从公开信息获取到私有知识接入。原书代码用WikipediaQueryRun但你在毕设里绝不能只查维基。你需要把它抽象为一个模式from langchain_community.tools import BaseTool from langchain_core.callbacks import CallbackManagerForToolRun from typing import Optional, Type class InternalDocSearchTool(BaseTool): name: str internal_doc_search description: str ( Search internal company documentation or course materials. Input is a natural language question. Returns top 3 relevant snippets. ) # 这里注入你的向量数据库客户端如 Chroma、FAISS vector_db: Any # 替换为实际 client 类型 def _run( self, query: str, run_manager: Optional[CallbackManagerForToolRun] None ) - str: # 1. 将 query 向量化 # 2. 在内部知识库中相似度检索 # 3. 返回结构化结果含来源页码/章节 results self.vector_db.similarity_search(query, k3) return \n\n.join([f[{r.metadata[source]}]\n{r.page_content[:200]}... for r in results])提示不要直接复制WikipediaQueryRun。它的api_wrapper是硬编码的而你的毕设需要体现“可配置性”。在InternalDocSearchTool中vector_db应作为初始化参数传入这样你就能在答辩时清晰说明“我对接的是本地 Markdown 文档库用 sentence-transformers 编码Chroma 存储检索延迟 200ms”。这个设计把“查资料”从一个功能点升级为一个可替换的知识接入层。后续换成 PDF 解析、数据库 SQL 查询、甚至企业微信知识库 API只需重写_run方法上层 Agent 逻辑完全不变。2.3 外部服务类技能API 调用不是转发而是带熔断、降级、审计的日志化操作股票价格示例中的get_stock_price是典型反模式它没处理网络超时、没做状态码校验、没加重试、没记录调用日志。真实生产级技能必须包含可观测性。以下是某跨平台系统毕设中采用的增强版import requests import logging from tenacity import retry, stop_after_attempt, wait_exponential from langchain_core.tools import tool # 全局日志器确保每次调用可追溯 logger logging.getLogger(stock_tool) tool def get_stock_price(ticker: str) - dict: Get real-time stock price with circuit breaker. Returns: {ticker: str, price: float, timestamp: str, source: api_example} retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10) ) def _fetch_with_retry(): try: response requests.get( fhttps://api.example.com/stocks/{ticker}, timeout5 # 强制 5s 超时 ) response.raise_for_status() data response.json() logger.info(fStock {ticker} fetched: {data[price]}) return { ticker: ticker, price: float(data[price]), timestamp: data.get(timestamp, N/A), source: api_example } except requests.exceptions.Timeout: logger.warning(fTimeout fetching {ticker}) raise except requests.exceptions.HTTPError as e: logger.error(fHTTP error for {ticker}: {e}) raise except Exception as e: logger.critical(fUnexpected error for {ticker}: {e}) raise return _fetch_with_retry() # 初始化时绑定日志处理器毕设中可简化为 print logging.basicConfig(levellogging.INFO)这个版本解决了三个关键问题可靠性tenacity重试 显式 timeout避免单点故障拖垮整个 Agent可观测性每条日志含ticker和操作结果答辩时可现场展示日志文件契约明确性返回dict而非裸float包含source字段便于后续审计比如发现某次报价来自缓存而非实时 API。3. 技能注册与编排bind_tools不是终点而是工具链治理的起点3.1 工具注册的隐藏陷阱为什么llm.bind_tools([tools])后 LLM 还是不调用这是学生最常问的问题。现象代码无报错但ai_msg.tool_calls始终为空。原因往往不在代码而在提示词上下文污染。LangChain 的bind_tools会将工具描述注入 system prompt但如果原始 prompt 已包含强约束如“你是一个严谨的学术助手只回答已知事实”LLM 可能因冲突而放弃调用。解决方案是显式清理# ❌ 危险直接复用旧 prompt llm_with_tools llm.bind_tools(tools) # ✅ 安全用 LangChain 推荐的 system message 模板 from langchain_core.prompts import ChatPromptTemplate system_prompt You are an AI assistant that can use tools to answer questions. When a question requires external data or computation, you MUST call the appropriate tool. Do not make up answers. If no tool fits, say I cannot answer that. prompt ChatPromptTemplate.from_messages([ (system, system_prompt), (human, {input}) ]) # 绑定工具时确保 prompt 是干净的 llm_with_tools prompt | llm.bind_tools(tools)注意ChatPromptTemplate不是可选项而是必选项。它强制分离“角色设定”和“工具指令”避免语义冲突。某导师反馈80% 的“不调用”问题源于没重置 system prompt。3.2 多工具协同当一个请求需要多个技能串联时如何避免 LLM 自作主张用户问“对比苹果和微软的股价并告诉我哪家公司成立更早” 这需要get_stock_price两次get_company_founding_year两次。但 LLM 可能只调一次股票 API 就急着回答。正确做法是用 Orchestrator 显式控制流程from langgraph.graph import StateGraph, END from typing import TypedDict, List, Annotated class AgentState(TypedDict): query: str stock_results: Annotated[List[dict], operator.add] founding_years: Annotated[List[int], operator.add] def call_stock_tool(state: AgentState): # 并行调用两次股票 API tickers [AAPL, MSFT] results [] for t in tickers: try: res get_stock_price.invoke({ticker: t}) results.append(res) except Exception as e: results.append({error: str(e)}) return {stock_results: results} def call_founding_tool(state: AgentState): companies [Apple Inc., Microsoft Corporation] years [] for c in companies: # 这里调用你的公司成立年份工具可基于维基或自建 DB years.append(1976 if c Apple Inc. else 1975) return {founding_years: years} # 构建图工作流 workflow StateGraph(AgentState) workflow.add_node(stock_call, call_stock_tool) workflow.add_node(founding_call, call_founding_tool) workflow.add_edge(stock_call, founding_call) workflow.set_entry_point(stock_call) workflow.set_finish_point(founding_call) app workflow.compile()提示LangGraph 是 LangChain 官方推荐的多步骤编排方案。它把“LLM 决策”降级为“流程控制器”把具体执行交给确定性函数。毕设答辩时你可以指着这张图说“我的 Agent 不是靠 LLM 猜而是按预设路径执行每一步都可监控、可打断、可重放。”3.3 工具调用结果的结构化处理别让 LLM 解析 JSON让它只做摘要原书示例中tool_msg.content是原始 API 返回的 JSON 字符串LLM 需要自己 parse。这极不可靠。正确做法是在工具层完成结构化解析返回自然语言摘要tool def get_stock_price(ticker: str) - str: # 注意返回 str不是 dict Get stock price and return human-readable summary. try: data _fetch_with_retry() # 复用前面的健壮函数 return fCurrent price of {data[ticker]} is ${data[price]:.2f} (as of {data[timestamp]}). except Exception as e: return fFailed to fetch {ticker}: {str(e)}这样llm_with_tools.invoke()收到的就是一句人话LLM 只需拼接、对比、总结不再承担解析风险。某课程设计中学生用原始 JSON 返回LLM 在汇总时把price: 182.34错读为price: 182导致结论错误改用摘要后100% 准确。4. 技能避坑指南5 个让毕设答辩翻车的高频问题与血泪解法4.1 现象LLM 调用工具时传参错误如把字符串12.25当 float 传给multiply(x: float, y: float)原因LangChain 的tool默认不做类型转换LLM 输出的{x: 12.25, y: 393}会直接传入函数触发TypeError。解决在工具函数内加类型强转或使用pydantic模型校验。毕设推荐前者轻量tool def multiply(x: str, y: str) - float: # 输入改为 str Multiply x times y. Handles string inputs. try: return float(x) * float(y) except ValueError: raise ValueError(fInvalid numbers: x{x}, y{y})4.2 现象本地技能如计算器和 API 技能如股票混用时LLM 优先调用本地技能忽略 API原因工具描述docstring中本地技能强调“guaranteed precision”API 技能强调“real-time”LLM 认为“precision”比“real-time”更重要于是拒绝调用 API。解决统一工具描述权重。在所有工具 docstring 开头加相同前缀[CRITICAL] Fetch real-time stock price from public API. Do not guess. [CRITICAL] Perform exact arithmetic calculation. Do not approximate.LangChain 会将[CRITICAL]视为高优先级信号显著提升 API 工具调用率。4.3 现象维基查询返回长文本LLM 在tool_msg.content中截断丢失关键信息原因WikipediaQueryRun默认只返回摘要summary且长度受限。解决自定义WikipediaAPIWrapper增大top_k_results和doc_content_chars_maxapi_wrapper WikipediaAPIWrapper( top_k_results1, doc_content_chars_max4000, # 从默认 2000 提升 load_all_available_metaTrue )4.4 现象Agent 在测试环境调用成功上线后因网络策略失败原因本地开发时直连公网 API生产环境需走代理或白名单。解决技能中注入环境感知配置import os tool def get_stock_price(ticker: str) - str: base_url os.getenv(STOCK_API_BASE, https://api.example.com/stocks) # 后续用 base_url 构造请求答辩时可展示.env文件截图“我预留了 API 地址配置项运维同学只需改环境变量即可上线。”4.5 现象多个学生用同一份代码LLM 调用工具时返回 “I don’t know”原因temperature0虽降低随机性但若工具描述模糊或 query 表述不清LLM 仍可能拒绝调用。解决在HumanMessage中加入明确指令messages [ HumanMessage( contentUse tools to answer. If a tool is relevant, call it. Do not say I cannot answer unless truly impossible. ), HumanMessage(contentquery) ]这是某高校毕设答辩的压箱底技巧——用 system-level 指令覆盖 LLM 的默认行为。5. 技能验收标准用 4 个可测量指标定义你的 Agent 是否“毕业”5.1 工具调用准确率TCA不是看能不能调而是看调得对不对这是答辩时最硬的指标。定义TCA 正确调用次数 / 总调用尝试次数 × 100%其中“正确调用”指工具名选择正确如multiply而非add参数值类型和语义正确如x393,y12.25而非xthree hundred无无效调用如空参数、重复调用同一工具。实操方法准备 20 个测试 query如“15×24 是多少”、“查特斯拉成立年份”、“苹果股价和谷歌股价哪个高”运行 Agent人工检查ai_msg.tool_calls统计 TCA。合格线≥ 90%。低于此值说明工具描述或 prompt 需重构。5.2 工具响应成功率TRS关注你的技能是否扛得住压力这是体现工程能力的关键。定义TRS 成功返回结果的调用次数 / 总调用次数 × 100%“成功返回”指本地技能无异常返回预期类型API 技能HTTP 200 JSON 解析成功 业务字段存在如price字段不为空。实操方法对每个工具单独压测用locust或简单脚本并发 10 次记录失败 case如超时、503、字段缺失合格线本地技能 ≥ 100%API 技能 ≥ 95%允许 1 次网络抖动。提示答辩 PPT 中放一张表格比放架构图更有说服力工具名测试次数成功次数TRS主要失败原因multiply1010100%—get_stock_price10990%1 次超时已加重试5.3 结果一致性RC证明你的 Agent 不是“薛定谔的正确”LLM 的随机性会让同一 query 多次运行结果不同。RC 衡量确定性RC N 次运行中结果完全相同的次数 / N × 100%N ≥ 5结果“完全相同”指工具调用序列一致工具名、参数顺序、参数值最终final_response.content字符串完全一致。实操方法写个循环跑 5 次用difflib.SequenceMatcher比较输出合格线≥ 80%。若低于 70%检查temperature是否真为 0或是否存在未 mock 的随机源如时间戳。5.4 人工可解释性AE让导师一眼看懂你的 Agent 在想什么这是答辩时最打动人的软指标。要求每次运行生成一份trace.log包含原始 queryLLM 的tool_calls含工具名、参数每个工具的tool_msg.content最终final_response.content。日志按时间戳排序关键字段高亮如工具名用**multiply**。合格表现导师随机抽 3 条日志能在 30 秒内说出“Agent 先查了股价再比大小最后总结”无需你解释。从那以后我每次带学生做 Agent 毕设都强制他们在main.py里加一个--validate参数运行时自动执行 TCA、TRS、RC 三重校验生成validation_report.md。不是为了炫技而是让每一个“能跑”的 demo都经得起“为什么能跑”的拷问。技能不是贴在 Agent 身上的装饰品它是你亲手锻造的、有温度、有刻痕、有错误日志的工程实体。希望帮到你。本文还有配套的精品资源点击获取
返回列表