【手搓 Agent 第1关】建立最小 Agent(上):代码前置基础
在 Stage 0 我们完成了 Agent 基础认知与工程选型,现在正式进入实操编码阶段。在第一阶段,我们计划搭建最小的 Agent Loop 完成以下目标:
本篇为 Stage 1 上篇,聚焦搭建 Agent 的底层前置能力:手把手封装 LLM 基础调用接口,实现多轮对话上下文记忆、自定义系统角色提示词,最后掌握强制模型输出标准 JSON 结构化数据的方案。
一、基础对话与结构化输出
1. 用 API 调用大模型进行简单对话
先创建.env环境变量文件。
代码主体
import os
from openai import OpenAI
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# 获取 OpenAI API Key
API_KEY = os.getenv("LLM_API_KEY")
Model_ID = os.getenv("LLM_MODEL_ID")
Base_URL = os.getenv("LLM_BASE_URL")
# 初始化 OpenAI 客户端
client = OpenAI(api_key=API_KEY, base_url=Base_URL)
def generate_response(prompt):
"""
使用 OpenAI API 生成响应
:param prompt: 用户输入的提示
:return: 模型生成的响应
"""
try:
response = client.chat.completions.create(
model=Model_ID,
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content.strip()
except Exception as e:
print(f"Error generating response: {e}")
return "抱歉,生成响应时发生错误。"
快速测试是否可用
# 示例用法
if __name__ == "__main__":
user_prompt = "你好,回复我调试成功"
response = generate_response(user_prompt)
print("模型响应:", response)
想要手动输入交互对话
# 示例用法
if __name__ == "__main__":
print("AI对话程序,输入 quit 结束对话")
while True:
user_prompt = input("你:")
if user_prompt == "quit":
print("对话结束")
break
response = generate_response(user_prompt)
print("AI:", response, "\n")
示例输出
你:你好,我叫阿言
AI: 很高兴认识你,阿言!今天过得怎么样?有没有什么想和我聊聊的,或者需要我帮忙的吗?😊
你:你还记得我叫什么吗
AI: 你好!每次对话对我来说都是新的开始,所以我暂时无法记住你的名字~不过如果你愿意告诉我,我会很乐意在**当前对话**中称呼你哦!😊 你叫什么名字呢?
此时大模型无法记住上下文,所以我们需要加入上下文模块。
2. 加入上下文功能
为了令大模型能记住上下文,我们需要设置chat_history来存储之前的对话。
代码修改
- 初始化 OpenAI 客户端后,加入全局列表存储完整对话历史
chat_history = [ ]。 - 修改
generate_response函数定义,每一轮次先在对话历史中加入用户 input:chat_history.append({"role": "user", "content": prompt}),再在 AI 回复后加入 AI 的回复:chat_history.append({"role": "assistant", "content": response.choices[0].message.content.strip()}。
完整代码演示
chat_history = [ ]
def generate_response(prompt):
"""
使用 OpenAI API 生成响应
:param prompt: 用户输入的提示
:return: 模型生成的响应
"""
try:
chat_history.append({"role": "user", "content": prompt})
response = client.chat.completions.create(
model=Model_ID,
messages=chat_history
)
ai_response = response.choices[0].message.content.strip()
chat_history.append({"role": "assistant", "content": ai_response})
return ai_response
except Exception as e:
print(f"Error generating response: {e}")
return "抱歉,生成响应时发生错误。"
示例输出
你:你好,我叫阿言
AI: 你好呀,阿言!👋 很高兴认识你~我是你的智能助手。今天有什么想聊的,或者需要我帮忙的地方吗?不管是回答问题、查资料,还是随便聊聊,随时都可以告诉我哦!😊
你:你还记得我叫什么吗?
AI: 当然记得呀,你叫**阿言**!😊咱们刚才才见面的~ 今天过得怎么样?有什么想聊的或者需要我帮忙的吗?
3. 加入系统提示词
Agent 一般都要有一个系统提示词,来规定 AI 的行为模式。可以通过定义系统提示词,让大模型在读取 message 信息时获得系统提示词。这个提示词只需要在一开始(即初始化 OpenAI 客户端后)定义一次,然后将其放入对话历史储存。
代码主体
# 定义系统提示词
system_prompt = {
"role": "system",
"content": "你是一个有帮助的AI助手,叫小爱。请根据用户的输入提供有用的信息和建议。"
}
# 全局列表存储完整对话历史,实现上下文
chat_history = [system_prompt]
示例输出
你:你好,我叫阿言,你叫什么名字
AI: 你好呀,阿言!很高兴认识你~👋我叫**小爱**,是你的智能助手。无论是解答疑惑、提供建议,还是日常聊天,我都在这里随时待命哦!今天有什么我可以帮你的吗?😊
4. 结构化输出
为什么希望 LLM 返回 JSON 格式?
| 优势 | 具体说明 |
|---|---|
| 结构化处理 | 程序可直接用json.loads()解析,无需复杂正则提取,避免字符串解析错误 |
| 数据集成 | 可直接导入数据库、作为 API 响应或工作流中间数据,无缝对接下游系统 |
| 类型安全 | 明确字段定义与类型,避免数据类型混淆(如数字 vs 字符串) |
| 结果一致性 | 强制固定字段(如thought+final_answer),消除自由文本的不确定性 |
| 智能体适配 | 完美支持工具调用、多轮对话状态管理等 AI Agent 场景 |
| 错误可检测 | JSON 格式错误可被程序立即捕获,便于重试或修复 |
JSON 格式输出的简单方法
- 系统提示词修改
"role": "system",
"content": "你是一个专业的数据格式化助手。请严格遵循以下规则:\
1. 仅输出符合 RFC 8259 标准的纯 JSON,无任何多余文字、解释、注释。\
2. 禁止使用 Markdown 代码块、禁止加 ```json 标记。\
3. 字段名称、类型严格按照要求,字符串用双引号,禁止尾随逗号。\
4. 只返回 JSON,不回答任何其他问题。"
- 写入函数硬性要求输出格式
response_format={"type": "json_object"}
加入位置:
response = client.chat.completions.create(
model=Model_ID,
response_format={"type": "json_object"},
messages=chat_history
)
二、本篇总结 & 下期预告
本篇我们完成了纯大模型对话、上下文管理、结构化输出三大基础能力,但当前模型仅能纯文本回答,没有执行外部操作的能力,还不能称之为真正的 Agent。
下一篇 Stage 1(中),我们将为模型拓展核心实操能力,详解工具规范定义、Schema 配置、工具调用解析与结果回传逻辑,让大模型拥有「动手执行任务」的能力。

浙公网安备 33010602011771号