Agent应用开发零基础入门:核心概念、环境配置与首次LLM调用

【L1阶段总结】Agent应用开发零基础入门:我是怎么从0开始理解的

这篇文章适合谁

如果你是Java后端开发,想转型Agent应用开发,但不知道从哪里下手------这篇文章是我的学习笔记,记录我从"听不懂Agent是什么"到"能跑通第一次LLM调用"的全过程。

不是百科全书式整理,是我自己消化后的理解,所以会有我的疑问和思考过程。


一、Agent到底是什么?

1.1 第一次听到"Agent",我在想什么

听到"Agent"这个词,我脑子里第一个反应是:这是什么?特工?电影里的AI机器人?

后来听到"AI Agent"这个词,又想:是不是就是接入了AI的系统?比如加了个GPT-4的系统?

如果你也有类似想法,恭喜,你和我一开始一样------理解跑偏了


1.2 普通微服务是怎么工作的

在解释Agent之前,先想清楚一个普通Java微服务是怎么处理请求的:

复制代码
用户发起请求
    ↓
Controller 接收(@RequestMapping)
    ↓
Service 处理(写死的业务逻辑)
    ↓
数据库查数据 / 调用其他微服务
    ↓
返回结果

这个流程的特点是什么?每一步都是程序员写死的 。你写 if (status == "已发货") { refund() },它就永远这样执行,不会自己判断。

1.3 Agent的本质:让模型决定下一步做什么

好,现在换个思路:

复制代码
用户:"我想退货"
    ↓
LLM(大脑)分析:用户想要退货,需要先查订单状态
    ↓
LLM决定:调用"查询订单"工具,参数是订单号A12345
    ↓
拿到订单状态:已发货,可以退款
    ↓
LLM决定:调用"执行退款"工具
    ↓
返回:"退款成功"

核心区别在这里

  • 普通微服务:程序员写死 if-else,代码决定下一步
  • Agent:LLM(大模型)自己推理出下一步该做什么

自测一下:Agent和普通微服务的本质区别是什么?花10秒钟自己想一下,再往下看。

我的理解:Agent有一个"大脑"(LLM),它能根据用户输入自己决定调用什么工具、怎么组合,而普通微服务所有逻辑都是写死在代码里的。


二、Agent核心四件套:我花了一整天才理清楚

刚开始学的时候,我听到四个词:LLM、System Prompt、Function Calling、Memory。一脸懵。后来用Java类比,一下就通了。

2.1 第一件:LLM------Agent的大脑

类比 :想象你的Spring Boot应用里内置了一个超级 Drools 规则引擎

Drools你知道,用规则描述业务逻辑。但Drools需要你预先把所有规则一条一条写出来。LLM不一样------你用自然语言描述,它自己学会规则,而且能处理你没见过的场景。

2.2 第二件:System Prompt------Agent的配置文件

类比 :这就像Spring的 application.yml

yaml 复制代码
# application.yml 定义了一个UserService的行为边界
spring:
  application:
    name: user-service
  datasource:
    url: jdbc:mysql://...
    hibernate:
      ddl-auto: validate

System Prompt的作用一模一样:定义这个Agent是谁、能做什么、不能做什么、输出格式是什么

python 复制代码
# System Prompt示例:定义一个"代码审查Agent"
system_prompt = """
你是一个资深Java架构师,负责代码审查。

【你能做什么】
- 发现性能问题(N+1查询、内存泄漏)
- 发现安全隐患(SQL注入、硬编码密码)
- 发现并发缺陷(线程安全)

【你不能做什么】
- 不纠结格式问题(空行、命名风格)
- 不回答非代码相关问题
- 每次最多指出5个问题,聚焦最重要

【输出格式】
每个问题:位置 → 原因 → 建议方案
"""

2.3 第三件:Function Calling------Agent调用工具的方式

类比 :这就像Spring Cloud的 @FeignClient

用Feign Client调用其他微服务,你需要:

  1. 定义接口
  2. 声明方法签名
  3. 写好URL和参数

Function Calling类似,你需要:

  1. 定义函数签名(名称、描述、参数)
  2. 告诉LLM"你有这些工具可用"
  3. LLM自己决定该调哪个、参数填什么
python 复制代码
# 定义两个工具
functions = [
    {
        "name": "查询订单",
        "description": "根据订单ID查询订单详情",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "订单ID"}
            },
            "required": ["order_id"]
        }
    },
    {
        "name": "执行退款",
        "description": "对指定订单执行退款",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"},
                "amount": {"type": "number"}
            },
            "required": ["order_id", "amount"]
        }
    }
]

# 用户说:"我想退货"
# LLM自动推理:先查订单 → 判断可退 → 执行退款
# 你不需要写 if-else,LLM自己决定

2.4 第四件:Memory------Agent的记忆

类比:这就像 Redis(短期记忆)+ MySQL(长期记忆)。

python 复制代码
memory = {
    # 短期记忆:当前对话,类似Redis
    # 用户刚说了什么,Agent正在处理什么
    "short_term": [
        {"role": "user", "content": "我要查订单"},
        {"role": "assistant", "content": "请问订单号是多少?"},
        {"role": "user", "content": "A12345"},
    ],

    # 长期记忆:历史积累,类似MySQL
    # 用户偏好、公司政策、过往交互记录
    "long_term": [
        {"type": "policy", "content": "退货政策:收货7天内可退"},
        {"type": "history", "content": "用户A12345历史订单5笔,退货2次"},
    ]
}

四件套串联起来

复制代码
用户输入
    ↓
LLM接收 + System Prompt约束(你是谁/能做什么)
    ↓
LLM推理:"用户想退货,需要先查订单状态"
    ↓
Function Calling执行工具(调用查询订单API)
    ↓
结果写回Memory
    ↓
LLM根据结果决定下一步(执行退款)
    ↓
返回结果给用户

三、环境搭建:我踩过的那些坑

3.1 搭建过程我走了弯路

我的机器上其实早就装了Python 3.13.7 ,安装在 D:\AAAProgram Files\Python\。但我一开始不知道,装了另一个Python到默认位置,后来清理了很久。

教训 :安装软件之前,一定要先检查 D:\AAAProgram Files\ 等常见自定义安装目录 ,不能只靠 where python 查PATH。

3.2 最终环境状态

组件 版本 安装位置 备注
Python 3.13.7 D:\AAAProgram Files\Python\python\ 用户原有安装
Java 17.0.17 LTS 系统默认 Spring Boot要求满足
Node.js v20.19.0 系统默认 JS开发用
DeepSeek API deepseek-chat - Key已配置

3.3 DeepSeek API Key:我选它的原因

我选DeepSeek有三个理由:

  1. 便宜:¥0.1/千Token输入,¥0.28/千Token输出,注册送额度
  2. 国内可访问:不需要梯子
  3. 中文支持好:比OpenAI API在国内的体验好

申请地址:https://platform.deepseek.com/

3.4 pip清华镜像配置

因为在国内,直接用pip安装包会非常慢。需要配置国内镜像:

powershell 复制代码
python -m pip install pip --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

四、第一次LLM调用:我的第一个"Hello World"

4.1 先搞懂Token是什么

API按Token计费,不是按字数。简单记:

  • 英文:1 Token ≈ 4个字符
  • 中文:1 Token ≈ 1-2个汉字

一个中等句子 ≈ 20-50 tokens。不需要精确计算,但要有量级概念,否则发一条请求几十块钱就没了(夸张了,但确实要心里有数)。

4.2 我的第一个Python版本

python 复制代码
# -*- coding: utf-8 -*-
# 设定文件编码为utf-8,防止中文乱码

# 导入HTTP请求库,用于发送网络请求调用大模型接口
import httpx
# 导入系统环境变量模块,用于安全读取密钥
import os

# 优先从系统环境变量读取API密钥,没有则使用默认备用密钥
# 避免密钥硬编码泄露,提升安全性
API_KEY = os.environ.get("你的key的名称", "你的key")
# DeepSeek 对话模型官方接口请求地址
url = "https://api.deepseek.com/chat/completions"

# 构造请求头信息
headers = {
    # 指定请求体数据格式为JSON
    "Content-Type": "application/json",
    # 接口身份鉴权,Bearer模式携带密钥
    "Authorization": f"Bearer {API_KEY}"
}

# 构造请求体核心参数
payload = {
    # 指定调用的模型:deepseek-chat 通用对话模型
    "model": "deepseek-chat",
    # 对话消息列表,遵循大模型标准对话格式
    "messages": [
        # role角色:user用户,content为用户提问内容
        {"role": "user", "content": "用一句话解释什么是Agent"}
    ],
    # 限制模型最大输出token数量,控制回复长度
    "max_tokens": 200,
    # 温度系数:控制回答随机性,0严谨固定,1发散创意
    "temperature": 0.7,
    # 非流式返回,一次性返回完整结果(默认关闭)
    # "stream": False
}

# 发送POST请求,传入地址、请求头、json参数,设置30秒超时时间
response = httpx.post(url, headers=headers, json=payload, timeout=30)
# 主动抛出HTTP错误,4xx/5xx请求异常直接报错
response.raise_for_status()
# 将接口返回的json字符串转为字典,方便解析
result = response.json()

"""
接口返回的 JSON 长这样(简化版)
{
    "id": "xxx",
    "object": "chat.completion",
    "created": 123456,
    "model": "deepseek-chat",
    "choices": [          <-- 第一层:choices 是一个【列表】
        {                 <-- 列表里的第 0 个元素(第一个)
            "index": 0,
            "message": {  <-- 第二层:message 是一个【字典】
                "role": "assistant",
                "content": "Agent就是能自主思考、调用工具完成任务的AI智能体"  <-- 你要的内容
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {...}
}
"""
# 解析提取大模型返回的回答内容
reply = result["choices"][0]["message"]["content"]
# 获取本次调用的token消耗统计数据
usage = result["usage"]

# 打印AI回复内容
print(f"回复: {reply}")
# 打印详细token消耗:输入、输出、总消耗,用于计费和调试
print(f"消耗: 输入={usage['prompt_tokens']} | 输出={usage['completion_tokens']} | 总={usage['total_tokens']}")

运行结果

复制代码
回复: Agent是一种能够感知环境、自主决策并执行动作的智能系统...
Token消耗: 输入=18 | 输出=45

几个参数我是这样理解的

  • model:用哪个模型,就像选择用哪个JDK版本
  • max_tokens:限制输出长度,防止LLM废话太多
  • temperature:0=固定输出(适合测试),1=随机(适合创意写作)

4.3 我的第一个Java版本

Java没有内置的OpenAI Client,但Java 11的HttpClient够用:

java 复制代码
package com.agentstudy;

import java.net.URI;
import java.net.http.*;
import java.util.*;

public class FirstLLMCall {

    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("DEEPSEEK_API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            System.out.println("请设置 DEEPSEEK_API_KEY 环境变量");
            return;
        }

        // 构建请求体
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("model", "deepseek-chat");
        payload.put("max_tokens", 200);
        payload.put("temperature", 0.7);

        List<Map<String, String>> messages = new ArrayList<>();
        messages.add(Map.of(
            "role", "user",
            "content", "用一句话解释什么是Agent"
        ));
        payload.put("messages", messages);

        // 发送请求
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.deepseek.com/chat/completions"))
            .header("Content-Type", "application/json")
            .header("Authorization", "Bearer " + apiKey)
            .POST(HttpRequest.BodyPublishers.ofString(toJson(payload)))
            .build();

        HttpResponse<String> response = client.send(request,
            HttpResponse.BodyHandlers.ofString());

        System.out.println("状态码: " + response.statusCode());
        System.out.println("响应: " + response.body());
    }

    // 简单JSON序列化(演示用,生产环境用Jackson)
    static String toJson(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder("{");
        var it = map.entrySet().iterator();
        while (it.hasNext()) {
            var e = it.next();
            sb.append("\"").append(e.getKey()).append("\":");
            Object v = e.getValue();
            if (v instanceof String s) {
                sb.append("\"").append(s.replace("\\", "\\\\").replace("\"", "\\\"")).append("\"");
            } else if (v instanceof List l) {
                sb.append("[");
                for (int i = 0; i < l.size(); i++) {
                    if (i > 0) sb.append(",");
                    Map<?, ?> m = (Map<?, ?>) l.get(i);
                    sb.append("{\"role\":\"").append(m.get("role"))
                      .append("\",\"content\":\"").append(m.get("content")).append("\"}");
                }
                sb.append("]");
            } else {
                sb.append(v);
            }
            if (it.hasNext()) sb.append(",");
        }
        sb.append("}");
        return sb.toString();
    }
}

运行:

bash 复制代码
javac -encoding UTF-8 com/agentstudy/FirstLLMCall.java
java -cp . com.agentstudy.FirstLLMCall

4.4 流式输出是什么

普通调用:等LLM全部生成完 → 返回

流式调用:LLM边生成边返回 → 就像打字机,一个字一个字出来

python 复制代码
# ========== Python 流式调用 ==========
import httpx

API_KEY = "你的key"  # 硬编码测试

payload = {
    "model": "deepseek-chat",
    "messages": [{"role": "user", "content": "写一个快排算法"}],
    "max_tokens": 500,
    "stream": True  # 开启流式
}

"""
with语法 
它帮你自动做两件事:
1. 打开资源
2. 用完自动关闭资源(不用你手动写关闭代码)
eg:
with 打开这个连接 as 给它起个名字叫 resp:
    在里面使用它  
"""


# with 打开流式请求
with httpx.stream("POST",  "https://api.deepseek.com/chat/completions",
                   headers={"Authorization": f"Bearer {API_KEY}"}, # 身份认证
                   json=payload, timeout=60) as resp: # 把问题发过去,并接收服务器的返回
    # 在里面使用它
    for line in resp.iter_lines(): # "服务器一段一段发回来的数据流" 这就是流式输出的核心。服务器边生成、边分段发,源源不断推送碎片化数据流,一段一段传给客户端。
        if line.startswith("data: "): # 只处理以 data: 开头的行 (服务器发回来的数据流,只有以 data: 开头的才是真正内容)
            data = line[6:] # 去掉前面的 data: ,拿到真正数据
            if data == "[DONE]":
                break
            import json
            chunk = json.loads(data) # 把 JSON 字符串转成 Python 字典
            delta = chunk["choices"][0]["delta"].get("content", "")
            if delta:
                print(delta, end="", flush=True)

五、System Prompt:我被这个词吓到了,其实很简单

5.1 System Prompt是什么

System Prompt的本质就是三个字:定规矩

就像新员工入职,公司给你一个职位说明书

  • 你是谁?(角色)
  • 你做什么不做什么?(职责范围)
  • 你汇报格式是什么?(输出格式)

System Prompt就是LLM的职位说明书。

5.2 我见过的反面教材

复制代码
"你是一个AI助手,帮我回答问题。"

这个System Prompt的问题:

  • "AI助手"是谁?Siri?ChatGPT?没有边界
  • 回答格式?长度?风格?全部未知
  • LLM自由发挥,结果完全不可控

5.3 正面教材:三层结构

第一层:角色设定

复制代码
你是一个资深Java后端架构师,拥有10年以上Spring Boot/微服务开发经验,
专注于性能调优、并发安全、JVM原理。

第二层:行为约束(禁区)

复制代码
【绝对禁区】
- 不回答与Java/后端无关的问题
- 不给出未经生产验证的"黑科技"
- 不在回复开头加"当然可以"/"很高兴为你服务"

第三层:输出格式

复制代码
【输出格式】
- 技术问题:问题 → 原因 → 解决方案,分点列出
- 代码示例:必须标注JDK版本,附关键注释
- 结论必须明确,禁止"视情况而定"
python 复制代码
import os, httpx

API_KEY = os.environ.get("DEEPSEEK_API_KEY", "sk-ed091521af1949809be4a740f7abc237")
url = "https://api.deepseek.com/chat/completions"

# system_prompt = """[把你的System Prompt粘贴在这里]"""
system_prompt = """[
你是一个专业的代码审查工程师,擅长发现Java代码中的:
- 性能问题(N+1查询、内存泄漏、慢查询)
- 安全隐患(SQL注入、硬编码密码、XSS)
- 并发缺陷(线程安全、资源竞争)
- 设计缺陷(违反SOLID原则、过度封装)

审查规则:
1. 严重问题(P0)必须指出:安全漏洞、内存泄漏、数据一致性风险
2. 一般问题(P1)建议指出:性能优化点、代码可读性
3. 格式问题(P2)忽略:不指出变量命名、注释格式、空行问题
4. 每次审查最多指出5个P0/P1问题(聚焦最重要)

输出格式:
## 审查结论
- 整体评级:[A/B/C/D](A=可直接合入,D=必须修改)
- 严重程度分布:P0:X个 | P1:X个 | P2:X个(忽略)

## 详细问题(按严重程度排序)
### [P0] 问题标题
- 位置:文件路径:行号 / 接口名
- 问题:简洁描述
- 原因:为什么这是个问题
- 建议:具体怎么改

## 可选优化建议(非强制)
(列出1-2个P2级建议,不强制要求修改)

]
"""

test_question = """以下代码有无安全问题?
```java
String sql = "SELECT * FROM users WHERE id = " + userId;
stmt.executeQuery(sql);
```"""

for i in range(3):
 resp = httpx.post(url, headers={
  "Authorization": f"Bearer {API_KEY}",
  "Content-Type": "application/json"
 }, json={
  "model": "deepseek-chat",
  "messages": [
   {"role": "system", "content": system_prompt},
   {"role": "user", "content": test_question}
  ],
  "max_tokens": 500,
  "temperature": 0  # 温度=0,测试一致性
 }, timeout=30)

 result = resp.json()
 print(f"=== 第{i + 1}次回答 ===")
 print(result["choices"][0]["message"]["content"])
 print()

5.4 怎么验证System Prompt好不好

temperature=03次相同问题,然后检查:

  1. 一致性:3次回答核心结论是否一致(不能忽左忽右)
  2. 约束遵守:是否遵守了你设的禁区
  3. 聚焦程度:是否只回答了你定义的范围

六、Java后端类比总结(快速查阅)

Agent概念 Java对应 一句话理解
Agent 带大脑的微服务 主动决策,不只是接请求
LLM Drools规则引擎(超强版) 自然语言写规则,模型自己推理
System Prompt application.yml 定义行为边界
Function Calling @FeignClient 模型决定调哪个接口
Skill/Tool Remote Service 封装好的外部能力
Memory Redis+MySQL 短期Redis,长期DB
RAG Elasticsearch+Service 检索+生成,类似搜索+推荐
Multi-Agent 微服务集群 Agent间通信≈RPC调用
Agent Framework Spring Boot Starter 脚手架,降低搭建成本

七、学习反思:我踩过的坑不希望你再踩

坑1:安装前没扫描自定义目录

只查PATH导致我装了两次Python。正确做法:

装任何软件前,先检查 D:\AAAProgram Files\C:\Program Files\ 等常见路径。

坑2:pip命令找不到

解决方案:用 python -m pip 代替。

坑3:PowerShell中文输出乱码

涉及中文时,用Python脚本处理,不用PowerShell管道操作。

坑4:把Agent想得太复杂

一开始我觉得Agent要懂很多AI理论才能学。其实:会用API调用LLM、会写Prompt、会用Java调用HttpClient,就够了。


八、下一步学什么

L1完成,接下来是 L2 进阶

  • 2.1 Prompt工程体系(Zero-shot/Few-shot/CoT)
  • 2.2 Function Calling(工具调用)
  • 2.3 Agent框架入门(LangChain/Lang4j)
  • 2.4 Memory机制(对话记忆)

最后留一个问题给你思考

如果让你设计一个"技术文档翻译Agent",把英文文档翻译成中文并保持专业术语,你会怎么设计它的System Prompt?三层结构怎么写?

想好了吗?想好了就完成了L1的最后一个考核 ✅

相关推荐
曹牧1 小时前
Java Web:DispatcherServlet
java·开发语言·前端
直奔標竿1 小时前
Java开发者AI转型第二十三课!Spring AI个人知识库实战(二):异步ETL流水线搭建与避坑指南
java·人工智能·spring boot·后端·spring
Lyyaoo.1 小时前
TreadLocal和TreadLocalMap
android·java·redis
【 】4231 小时前
从迭代器到生成器
python·迭代器·生成器
AC赳赳老秦1 小时前
网安工程师提效:用 OpenClaw 实现漏洞扫描报告生成、安全巡检自动化、日志合规审计
java·开发语言·前端·javascript·python·deepseek·openclaw
你数过天上的星星吗1 小时前
Python学习笔记二(函数、类与对象)
笔记·python·学习
四维迁跃1 小时前
如何排查SQL存储过程死锁_分析死锁日志与索引优化
jvm·数据库·python
m0_741173331 小时前
如何检测SQL注入风险_利用模糊测试技术发现漏洞
jvm·数据库·python
xcbrand1 小时前
餐饮品牌全案公司哪家可靠
运维·python