目录
[一、环境与工程(对应 Maven/Gradle)](#一、环境与工程(对应 Maven/Gradle))
[二、基础语法:和 Java 不同的 7 个坑](#二、基础语法:和 Java 不同的 7 个坑)
[1. 缩进即作用域(没有 {})](#1. 缩进即作用域(没有 {}))
[2. 变量是引用,但不可变类型行为像值类](#2. 变量是引用,但不可变类型行为像值类)
[3. 没有 ++ / for(i=0;i < a=""> <>](#3. 没有 ++ / for(i=0;i < a=""> <>)
[4. 字符串:f-string 取代 String.format](#4. 字符串:f-string 取代 String.format)
[5. 字典 = HashMap,但语法是字面量](#5. 字典 = HashMap,但语法是字面量)
[6. 函数是一等公民(可当参数传,类似 Java 8 lambda)](#6. 函数是一等公民(可当参数传,类似 Java 8 lambda))
[7. 真值判断:空容器、0、None 都是 False](#7. 真值判断:空容器、0、None 都是 False)
[三、面向对象:和 Java OO 的对照](#三、面向对象:和 Java OO 的对照)
[私有:没有真正的 private(呼应你上一个问题)](#私有:没有真正的 private(呼应你上一个问题))
[接口 / 抽象类](#接口 / 抽象类)
[继承(支持多继承 + MRO)](#继承(支持多继承 + MRO))
[四、类型系统:dataclass 和 Pydantic(agent 开发核心)](#四、类型系统:dataclass 和 Pydantic(agent 开发核心))
[dataclass ------ 对应 Java record(轻量、快)](#dataclass —— 对应 Java record(轻量、快))
[Pydantic ------ 对应 Java record + 校验(agent 开发首选)](#Pydantic —— 对应 Java record + 校验(agent 开发首选))
[try-with-resources → with 语句](#try-with-resources → with 语句)
[六、异步:async/await(对应 CompletableFuture / WebFlux)](#六、异步:async/await(对应 CompletableFuture / WebFlux))
[七、装饰器(对应 Java 注解 + AOP)](#七、装饰器(对应 Java 注解 + AOP))
[八、Agent 核心:一次完整的最小可用示例](#八、Agent 核心:一次完整的最小可用示例)
前言约定:每条都给 Java 对照 + 能跑的示例。你只需抓住"和 Java 不一样的地方",相同的地方直接跳过。
一、环境与工程(对应 Maven/Gradle)
| Java 概念 | Python 对应 | 说明 |
|---|---|---|
| JDK | Python 解释器 | 用 3.10+(agent 生态最低要求) |
| Maven/Gradle | uv 或 poetry | 别用裸 pip,依赖管理会乱 |
pom.xml |
pyproject.toml |
依赖与项目元信息都在这 |
.m2 本地仓库 |
虚拟环境 .venv |
每个项目隔离依赖 |
| Maven Central | PyPI | uv add anthropic |
最快上手(推荐 uv):
# 安装 uv(一次)
pip install uv
# 建项目
uv init my-agent
cd my-agent
uv add anthropic # 类似 mvn install + 写 pom
uv run python main.py # 在隔离环境里跑
关键差异:Python 没有编译期类型检查。类型注解(
x: int)只是提示,运行时不校验。要真校验得靠 Pydantic(下面会讲)。
二、基础语法:和 Java 不同的 7 个坑
速查对照表
| 对象 | Python 规范 | 示例 | Java 对比 |
|---|---|---|---|
| 变量、函数 | snake_case 蛇形 |
user_name |
userName 驼峰 |
| 类 | PascalCase 大驼峰 |
ChatAgent |
一样 |
| 常量 | UPPER_SNAKE |
MAX_RETRY |
一样 |
| 模块(文件) | lowercase / lower_with_underscore |
agent_utils.py |
--- |
| 包(目录) | lowercase,短、无下划线 |
myagent |
全小写 |
| 私有成员 | 前缀 _ |
_internal |
无约定俗成 |
| 名字冲突/强制私有 | 前缀 __ |
__private |
--- |
| 类型变量 | T / TypeVar 单大写 |
T, T_co |
<T> |
⚠️ 最大的坑 :Java 习惯用
camelCase写变量名(userName),Python 一律snake_case(user_name)。IDE 会标黄。
1. 缩进即作用域(没有 {})
python
# Java: if (x > 0) { ... }
# Python: 靠缩进
def check(x):
if x > 0:
print("正数") # 4 空格缩进 = 在 if 内
print("总会执行") # 不缩进 = if 外
2. 变量是引用,但不可变类型行为像值类
python
a = [1, 2, 3]
b = a # b 和 a 指向同一个 list(类似 Java 引用)
b.append(4)
print(a) # [1, 2, 3, 4] ------ a 也变了!
# 想要副本:
b = a.copy() # 浅拷贝
b = a[:] # 等价写法
3. 没有 ++ / for(i=0;i<n;i++)
python
# Java: for (int i = 0; i < 3; i++)
for i in range(3):
print(i) # 0, 1, 2
# 遍历集合(Java: for (String s : list))
for item in ["a", "b", "c"]:
print(item)
4. 字符串:f-string 取代 String.format
python
name = "agent"
count = 3
# Java: String.format("Hello %s, count=%d", name, count)
print(f"Hello {name}, count={count}") # f-string,最常用
print(f"{count:03d}") # 003,格式化
5. 字典 = HashMap,但语法是字面量
python
# Java: Map<String, Integer> map = new HashMap<>();
d = {"a": 1, "b": 2}
d["c"] = 3
print(d.get("z", 0)) # 不存在返回默认值 0,类似 getOrDefault
6. 函数是一等公民(可当参数传,类似 Java 8 lambda)
python
# Java: list.stream().map(x -> x * 2).collect(...)
nums = [1, 2, 3]
doubled = [x * 2 for x in nums] # 列表推导,最 Pythonic
doubled = list(map(lambda x: x * 2, nums)) # 也能这么写,但不推荐
7. 真值判断:空容器、0、None 都是 False
python
# Java: if (list != null && !list.isEmpty())
lst = []
if lst: # 非空为真,无需判空
print("有元素")
if not lst:
print("空") # 打印这个
三、面向对象:和 Java OO 的对照
类与构造器
python
# Java:
# public class User {
# private String name; private int age;
# public User(String name, int age) { ... }
# }
class User:
def __init__(self, name: str, age: int): # 构造器;self = this
self.name = name
self.age = age
u = User("bot", 3)
print(u.name)
self必须显式写第一个参数(Java 的this是隐式的)。这是新手最容易忘的。
私有:没有真正的 private(呼应你上一个问题)
python
class Agent:
def __init__(self):
self.name = "bot" # public
self._config = {} # 约定私有(单下划线)
self.__secret = "key" # 名称改写(双下划线)→ 实际叫 _Agent__secret
# 没有 private 关键字,全靠约定。你上一题的报错就是这个。
接口 / 抽象类
python
from abc import ABC, abstractmethod
# Java: interface Tool { String run(String input); }
class Tool(ABC): # ABC = Abstract Base Class
@abstractmethod
def run(self, input: str) -> str:
...
class SearchTool(Tool):
def run(self, input: str) -> str: # 必须实现
return f"searching {input}"
还有更轻的 Protocol(结构化类型,类似 TypeScript interface / Go 接口,不要求显式继承)------agent 框架里很常见:
python
from typing import Protocol
class ToolLike(Protocol):
def run(self, input: str) -> str: ... # 只定义形状,不继承也能匹配
继承(支持多继承 + MRO)
python
class Base:
def greet(self):
return "base"
class Mixin:
def log(self):
print("logged")
class Agent(Base, Mixin): # 多继承,Java 不支持
pass
a = Agent()
a.greet() # base
a.log() # logged
四、类型系统:dataclass 和 Pydantic(agent 开发核心)
Agent 开发要大量定义"消息结构 / 工具参数 / API 响应"。Java 用 record / POJO,Python 用这两个。
dataclass ------ 对应 Java record(轻量、快)
python
from dataclasses import dataclass
# Java: public record User(String name, int age) {}
@dataclass
class User:
name: str
age: int = 18 # 默认值
u = User("bot")
print(u) # User(name='bot', age=18) ------ 自动 __repr__
Pydantic ------ 对应 Java record + 校验(agent 开发首选)
python
from pydantic import BaseModel, Field
class ToolArgs(BaseModel):
query: str = Field(..., min_length=1, max_length=100)
limit: int = Field(10, ge=1, le=50) # ge=大于等于,le=小于等于
# 自动校验 + 报错(运行时真正校验,这是和 dataclass 的关键区别)
args = ToolArgs(query="hello", limit=5)
print(args.model_dump()) # {'query': 'hello', 'limit': 5}
# ToolArgs(query="") # ❌ 校验失败抛 ValidationError
# ToolArgs(query="x", limit=0) # ❌ limit 必须 >=1
为什么 agent 开发离不开 Pydantic: LLM 返回的是 JSON,你要把它映射成 Python 对象并校验。几乎所有 agent 框架(LangChain、Anthropic SDK 的结构化输出)底层都是 Pydantic。
五、异常与资源管理
try-with-resources → with 语句
python
# Java:
# try (var br = new BufferedReader(new FileReader("f.txt"))) { ... }
with open("data.txt", "r", encoding="utf-8") as f: # 退出 with 自动关闭
content = f.read()
# 这里 f 已关闭,无需 finally
异常体系
python
# Java: try { ... } catch (IOException e) { ... } finally { ... }
try:
result = 10 / 0
except ZeroDivisionError as e: # 捕获具体异常
print(f"除零: {e}")
except (ValueError, TypeError): # 多个异常一起捕获
print("类型错误")
except Exception as e: # 兜底(类似 catch Throwable)
print(f"其他: {e}")
else:
print("无异常时执行")
finally:
print("总执行")
Python 异常不要求"声明或捕获"(没有 checked exception)。这更灵活,但也意味着你得主动列出可能出错的点,不能靠编译器提醒。
六、异步:async/await(对应 CompletableFuture / WebFlux)
Agent 经常要并发调多个工具或 LLM,异步是必修。
python
import asyncio
import anthropic
# Java CompletableFuture:
# CompletableFuture.supplyAsync(() -> callA())
# .thenCombine(CompletableFuture.supplyAsync(() -> callB()), ...)
async def call_llm(prompt: str) -> str:
client = anthropic.AsyncAnthropic() # 异步客户端
resp = await client.messages.create( # await = 阻塞等待,不占线程
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return resp.content[0].text
async def main():
# 并发执行多个(类似 CompletableFuture.allOf)
results = await asyncio.gather(
call_llm("写一句诗"),
call_llm("写一句歌词"),
)
print(results)
asyncio.run(main()) # 启动事件循环(入口必须这么调)
三条铁律:
async def定义的函数必须用await调用,不能直接call()。await只能在async def内部。- 事件循环入口:脚本顶层用
asyncio.run(main())。
七、装饰器(对应 Java 注解 + AOP)
Python 装饰器是真正的函数包装,不像 Java 注解只是标记。
python
import time
from functools import wraps
# Java AOP @Around 思路:在方法前后插逻辑
def log_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} 耗时 {time.time()-start:.2f}s")
return result
return wrapper
@log_time # 等价于 search = log_time(search)
def search(q):
time.sleep(0.5)
return f"结果: {q}"
search("hello") # 打印: search 耗时 0.50s
Agent 框架大量用装饰器注册工具,例如 Anthropic SDK 的
@beta_tool。把它理解成"声明这个函数是个工具 + 自动生成参数 schema"。
八、Agent 核心:一次完整的最小可用示例
下面用 Anthropic 官方 SDK 做一个带工具调用的 agent loop------这是 agent 开发的本质:LLM 决定调什么工具 → 你执行 → 把结果喂回去 → 循环直到完成。
python
# pip install anthropic
import anthropic
client = anthropic.Anthropic() # 读 ANTHROPIC_API_KEY 环境变量
# 1) 定义工具(LLM 看得懂的"函数签名")
tools = [{
"name": "get_weather",
"description": "查询某城市天气",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"},
},
"required": ["city"],
},
}]
# 2) 实际执行工具的 Python 函数
def get_weather(city: str) -> str:
# 真实场景里这里调天气 API
return f"{city} 今天 25°C 晴"
# 3) Agent 主循环
def run_agent(user_input: str):
messages = [{"role": "user", "content": user_input}]
while True: # agent loop
resp = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=tools,
messages=messages,
)
# 4) LLM 想调工具 → stop_reason == "tool_use"
if resp.stop_reason == "tool_use":
# 把 assistant 这轮(含 tool_use 块)加回历史
messages.append({"role": "assistant", "content": resp.content})
# 执行所有被请求的工具,把结果作为 user 消息回传
tool_results = []
for block in resp.content:
if block.type == "tool_use":
result = get_weather(**block.input) # ** 解包字典为参数
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
})
messages.append({"role": "user", "content": tool_results})
# 继续 while,让 LLM 看到工具结果后决定下一步
else:
# 5) 不再需要工具 → end_turn,输出最终回答
for block in resp.content:
if block.type == "text":
print(block.text)
return
run_agent("北京天气怎么样?")
这段就是 agent 的全部内核。不管框架多花哨,本质都是这个 while 循环。理解了它,LangChain / LangGraph / Anthropic Tool Runner 都只是这个循环的不同封装。
九、生态地图(别全学,按需挑)
| 层 | 工具 | 类比 | 何时用 |
|---|---|---|---|
| LLM SDK | anthropic | 直接调 API | 最底层、最可控,先掌握这个 |
| Agent 框架 | LangGraph | 工作流引擎 | 多步骤、带状态的复杂 agent |
| Agent 框架 | LangChain | Spring 全家桶 | 功能多但重,不建议入门 |
| Web 框架 | FastAPI | Spring Boot | 把 agent 包成 HTTP 服务 |
| 数据校验 | Pydantic | Bean Validation | 必学,FastAPI/agent 都依赖它 |
| 包管理 | uv | Maven | 必学 |
| 测试 | pytest | JUnit | 语法最简,assert 即测试 |