让模型能干活,而不只是说话
模型算不对数、查不了今天的天气、发不了邮件——它只能生成文字。
但如果我们告诉它"你可以让我帮你调这几个函数",它就能决定什么时候调、传什么参数,然后我们替它执行,把结果再喂回去。这就是 Agent。
定义工具
from langchain_core.tools import tool
@tool
def add(a: int, b: int) -> int:
"""计算两个整数的和。"""
return a + b
@tool
def get_weather(city: str) -> str:
"""查询指定城市的当前天气。city 用中文城市名,如 北京。"""
return f"{city}:晴,22 度"
@tool 装饰器会把函数名、参数类型、文档字符串一起打包发给模型。
所以:
- 文档字符串不是注释,是给模型看的说明书。 写清楚"什么时候该用它"、参数是什么格式。写得含糊,模型就会在错误的时机调用它。
- 类型注解必须写。 模型靠它知道该传什么类型。
绑定并调用
llm_with_tools = llm.bind_tools([add, get_weather])
resp = llm_with_tools.invoke("北京天气怎么样?")
print(resp.tool_calls)
# [{'name': 'get_weather', 'args': {'city': '北京'}, 'id': 'call_abc'}]
注意:模型并没有执行函数,它只是说"我想调用 get_weather,参数是北京"。执行是你的事。
完整的一轮长这样:
from langchain_core.messages import HumanMessage, ToolMessage
messages = [HumanMessage("北京天气怎么样?")]
ai_msg = llm_with_tools.invoke(messages)
messages.append(ai_msg)
tools_by_name = {"add": add, "get_weather": get_weather}
for call in ai_msg.tool_calls:
result = tools_by_name[call["name"]].invoke(call["args"])
messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))
final = llm_with_tools.invoke(messages)
print(final.content) # 北京现在是晴天,22 度。
五步:问 → 模型说要调工具 → 你执行 → 把结果塞回消息列表 → 模型基于结果作答。
Agent 框架(LangGraph 等)做的就是把这五步包成一个循环,让模型可以连续调用多个工具直到得出答案。但底层就是上面这段代码,没有魔法。
三条必须守住的安全红线
Agent 是大模型应用里风险最高的部分,因为它真的会执行动作。
1. 绝不把无限制的执行能力给模型。
@tool
def run_sql(sql: str) -> str:
"""执行任意 SQL。""" # ← 绝对不要这样
模型可能被提示词注入诱导,生成 DROP TABLE。正确做法是收窄工具:提供 query_order_by_id(order_id: str),而不是"执行任意 SQL"。工具的权限要按最小必要原则设计,就像给实习生开数据库账号一样。
2. 有副作用的操作必须人工确认。 发邮件、付款、删数据、发布内容——让模型生成方案,人点确认。不可逆的动作永远不要全自动。
3. 加上限。 循环次数上限、超时、单次调用工具数上限。没有上限的 Agent 循环可以在你睡觉时烧掉整月预算。
什么时候不该用 Agent
流程固定就别用 Agent。
"翻译→润色→存档"这种确定流程,直接写三步链。它更快、更便宜、结果可预测、出错好排查。Agent 的价值在于"事先不知道需要哪几步"的场景。
工程上的常见教训是:一半的 Agent 项目其实应该是一条固定的链。先用链把问题解决掉,确实解决不了再上 Agent。
你已经学完了这条路线
回头看,你现在掌握的是一条完整的能力链:调用模型(第 2 课)→ 管理提示词(第 3 课)→ 拿到结构化输出(第 4 课)→ 维持对话(第 5 课)→ 接入私有知识(第 6 课)→ 执行动作(第 7 课)。
这六块拼起来,就是一个企业级 AI 应用的骨架。
接下来值得投入的方向:LangGraph(把 Agent 的循环显式建模成状态图,可控性远高于黑盒 Agent)、评估(建一套测试集,量化每次改动的效果——没有评估的提示词调优就是在赌博)、可观测性(LangSmith 之类,把每次调用的输入输出都记下来,否则线上问题无从查起)。
10 秒自测
为什么工具函数的文档字符串这么重要?
因为它是发给模型的说明书,模型靠它判断什么时候该调用这个工具、参数该传什么。写得含糊,模型就会乱调或漏调。