Agent 是什么

Agent = LLM + 工具 + 循环

普通调用:你问一次,它答一次。 Agent:它能自己决定"我要先查搜索引擎,再读文件,最后总结"——多步推理 + 工具使用 + 反思

ReAct:思考 → 行动 → 观察的循环

Thought: 我需要查一下今天天气
Action: get_weather(city="北京")
Observation: 晴 25 度
Thought: 已知天气,可以回答用户了
Final Answer: 北京今天晴,25 度

LLM 在每一步输出 Thought 和 Action,外部代码执行 Action 拿到 Observation,再反馈给 LLM。

自己写一个最小 Agent(80 行)

import json
from anthropic import Anthropic

client = Anthropic()

# 工具实现
def get_weather(city: str) -> str:
    return f"{city} 晴 25 度"

def search_web(query: str) -> str:
    return f"关于 {query} 的搜索结果:[模拟]"

def calculate(expression: str) -> str:
    # ⚠ 演示用:eval 即使禁了 builtins 也能被精心构造的字符串绕过沙箱。
    #    生产请用 ast.parse 自己写计算器,或用 simpleeval / numexpr 等库。
    try:
        return str(eval(expression, {"__builtins__": {}}))
    except Exception as e:
        return f"计算错误: {e}"

TOOLS = {"get_weather": get_weather, "search_web": search_web, "calculate": calculate}

# 工具描述
TOOL_SCHEMAS = [
    {
        "name": "get_weather",
        "description": "查询某城市天气",
        "input_schema": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
    {
        "name": "search_web",
        "description": "搜索网络信息",
        "input_schema": {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"],
        },
    },
    {
        "name": "calculate",
        "description": "计算数学表达式",
        "input_schema": {
            "type": "object",
            "properties": {"expression": {"type": "string"}},
            "required": ["expression"],
        },
    },
]


def run_agent(user_input: str, max_steps: int = 5):
    messages = [{"role": "user", "content": user_input}]

    for step in range(max_steps):
        msg = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=1024,
            tools=TOOL_SCHEMAS,
            messages=messages,
        )

        # 没有工具调用 → 终止
        if msg.stop_reason == "end_turn":
            for b in msg.content:
                if b.type == "text":
                    return b.text

        # 有工具调用 → 执行 → 反馈
        messages.append({"role": "assistant", "content": msg.content})
        results = []
        for b in msg.content:
            if b.type == "tool_use":
                func = TOOLS[b.name]
                result = func(**b.input)
                print(f"[{b.name}]({b.input}) → {result}")
                results.append({
                    "type": "tool_result",
                    "tool_use_id": b.id,
                    "content": result,
                })
        messages.append({"role": "user", "content": results})

    return "(达到最大步数)"


print(run_agent("北京今天天气?另外帮我算一下 12345 × 6789"))

LLM 会自己决定先调 get_weather 还是 calculate,调几次工具,最后总结。

加点"记忆"

简单做法:把对话历史塞进 messages 即可。 高级做法:用向量库存历史,按相关度检索("长期记忆")。

class Memory:
    def __init__(self, kb):
        self.kb = kb              # 向量库
        self.recent = []          # 最近 N 轮

    def add(self, text):
        self.kb.add(text)
        self.recent.append(text)
        if len(self.recent) > 10:
            self.recent.pop(0)

    def relevant(self, query):
        return self.kb.search(query, k=3)

每次对话开头注入 relevant(query) + recent——这就是 ChatGPT 的"记忆"功能本质。

Agent 的三大坑

1. 死循环

LLM 反复调同一个工具——加 max_steps 兜底。

2. 工具描述写得不清

LLM 调错参数 / 选错工具——把 description 写得超清楚(带例子)。

3. 任务太大不分解

让 Agent 一步做"建一个完整网站"——它会迷路。分阶段 + 子 agent

框架推荐

写自己的 Agent → 80 行就够(上面那种)。 要多 agent 协作 → 看看 CrewAI / AutoGen / LangGraph。 不复杂场景 → 直接 SDK 自己写。

Claude Agent SDK

Anthropic 出了官方 Agent SDK,专门做这个:

pip install claude-agent-sdk

把上面的 80 行变成 5 行——Tool use 循环 / 错误处理 / 流式输出 全包好。

下一篇讲微调。