AI Agent,AI 智能体如何开发?
对此,我制定了一个路线:
- LLM API 单次调用,实现一问一答
- LLM API 循环调用,实现循环问答
- 实现 Chatbot
- 实现 Agent
今天先讲讲如何实现一问一答
Chapter 1. LLM API
以 Python 环境中调用 DeepSeek 为例
其实大模型的调用最底层只需要三行代码:
# 导入 requests 模块importrequests# 发送请求response=requests.post(url="https://api.deepseek.com/chat/completions",headers={"Authorization":"Bearer your_api_key"},json={"model":"deepseek-v4-flash","messages":[{"role":"user","content":"你好"}],},)# 打印响应print(response.json())如此运行一次程序便能实现与大模型的一问一答。
大模型只能一问一答,这是日后开发 Chatbot 和 Agent 的核心、关键!
为了安全起见,我一般会把密钥写入环境变量。如果你的密钥也和我一样写入了电脑环境里,或者当前终端的临时环境中,那么你可以用下面的方式来使用你的密钥:
# 导入模块importosimportrequests# 读取环境变量中的密钥api_key=os.getenv("DEEPSEEK_API_KEY")# 发送请求response=requests.post(url="https://api.deepseek.com/chat/completions",headers={"Authorization":f"Bearer{api_key}"},json={"model":"deepseek-v4-flash","messages":[{"role":"user","content":"你好"}],},)# 打印响应print(response.json())现在我们可以通过 API 调用,得到了自己的 AI 助手。虽然它现在还很简陋,但是恭喜你已经获得了大模型的能力。那么接下来的开发路径便产生了分支。
我首先选择实现终端输入,代码如下:
# 导入模块importosimportrequests# 读取环境变量中的密钥api_key=os.getenv("DEEPSEEK_API_KEY")# 终端输入question=input("请输入问题:")# 发送请求response=requests.post(url="https://api.deepseek.com/chat/completions",headers={"Authorization":f"Bearer{api_key}"},json={"model":"deepseek-v4-flash","messages":[{"role":"user","content":question}],},)# 打印响应print(response.json())达成了终端输入成就之后,解除了输入的限制,总算像那么点回事了。但是眼下大模型的回答似乎有点奇怪……
这和咱们平时用的AI,对话起来似乎很不一样啊,有太多“杂乱无章”的东西。没关系,我们耐下心,来一一拆解它们。换换行,让其中的细节更加明显一些:
{'id':'33fa70d1-b89c-4b87-b658-d4bd13c187f4','object':'chat.completion','created':1788102644,'model':'deepseek-v4-flash','choices':[{'index':0,'message':{'role':'assistant','content':'你好!我是 DeepSeek,由深度求索公司开发的 AI 助手。\n\n我可以帮你做很多事情,比如:\n\n- **回答问题**:涵盖知识、生活、科技、学习等各种领域\n- **写作与创作**:文章、文案、代码、方案等\n- **分析总结**:帮你整理信息、提炼重点\n- **编程辅助**:写代码、调试、讲解技术概念\n\n我的一些特点:\n\n- **免费使用**:目前完全免费,没有收费计划\n- **支持长文本**:上下文窗口达到 1M,可以一次性处理超长内容\n- **文件上传**:支持上传图片、PDF、Word、Excel、PPT 等文件,我会读取其中的文字信息帮你处理\n- **联网搜索**:需要时可以手动开启联网功能获取实时信息\n- **语音输入**:App 端支持语音交互\n\n我的知识截止日期是 2025 年 5 月。虽然我不支持多模态视觉识别(比如识别图片内容),但可以读取图片中的文字。\n\n有什么我可以帮你的吗?尽管问我吧!😊','reasoning_content':'We need answer in Chinese. Need introduce myself. As AI assistant from DeepSeek. Mention capabilities, free, knowledge cutoff, file upload, context. Keep concise.'},'logprobs':None,'finish_reason':'stop'}],'usage':{'prompt_tokens':85,'completion_tokens':263,'total_tokens':348,'prompt_tokens_details':{'cached_tokens':0},'completion_tokens_details':{'reasoning_tokens':34},'prompt_cache_hit_tokens':0,'prompt_cache_miss_tokens':85},'system_fingerprint':'a26a7955944dc5c60445bff77fac9c8e'}蛙趣!这行数可真多啊!没关系,我们先关注我们最关心的东西——像我们平时使用的 AI 对话中的回答的部分在哪?
'content':'你好!我是 DeepSeek,由深度求索公司开发的 AI 助手。\n\n我可以帮你做很多事情,比如:\n\n- **回答问题**:涵盖知识、生活、科技、学习等各种领域\n- **写作与创作**:文章、文案、代码、方案等\n- **分析总结**:帮你整理信息、提炼重点\n- **编程辅助**:写代码、调试、讲解技术概念\n\n我的一些特点:\n\n- **免费使用**:目前完全免费,没有收费计划\n- **支持长文本**:上下文窗口达到 1M,可以一次性处理超长内容\n- **文件上传**:支持上传图片、PDF、Word、Excel、PPT 等文件,我会读取其中的文字信息帮你处理\n- **联网搜索**:需要时可以手动开启联网功能获取实时信息\n- **语音输入**:App 端支持语音交互\n\n我的知识截止日期是 2025 年 5 月。虽然我不支持多模态视觉识别(比如识别图片内容),但可以读取图片中的文字。\n\n有什么我可以帮你的吗?尽管问我吧!😊',content的中文意思就是“内容”。很显然这段代码本质上是一个键值对(key value pair)。键和值都是字符串类型的。那么右边的这部分字符串,便是 AI 回答的内容了。既是现在已经把回答内容单独取出来了,可是这个内容本身还是看起来很奇怪。我们使用代码放大器🔍把它再放大一点,聚焦其中的细节。
'你好!我是 DeepSeek,由深度求索公司开发的 AI 助手。\n\n我可以帮你做很多事情,比如:\n\n- **回答问题**:涵盖知识、生活、科技、学习等各种领域\n- **写作与创作**:文章、文案、代码、方案等\n- **分析总结**:帮你整理信息、提炼重点\n- **编程辅助**:写代码、调试、讲解技术概念\n\n我的一些特点:\n\n- **免费使用**:目前完全免费,没有收费计划\n- **支持长文本**:上下文窗口达到 1M,可以一次性处理超长内容\n- **文件上传**:支持上传图片、PDF、Word、Excel、PPT 等文件,我会读取其中的文字信息帮你处理\n- **联网搜索**:需要时可以手动开启联网功能获取实时信息\n- **语音输入**:App 端支持语音交互\n\n我的知识截止日期是 2025 年 5 月。虽然我不支持多模态视觉识别(比如识别图片内容),但可以读取图片中的文字。\n\n有什么我可以帮你的吗?尽管问我吧!😊'通篇出现了明显的重复结构——成对的双星号,如果你有使用 Markdown 的经验,那么你不难回想到这是 Markdown 的文字加粗语法,继而我们不妨大胆猜测——这段字符串是 Markdown 格式的文本。但是如何直接复制粘贴到.md文件中的话,只能得到以下效果:
那是因为,这段文本其实不是严格意义上的 Markdown 文本,而是一段带转义符的 Markdown 文本。\n表示换行,-表示列表项。那么怎样才能把它转化为一个常见的 Markdown 文本呢?想要转化,首先我们需要搞清楚这段内容与我们前面得到的响应之间的关系,毕竟我们总不能每次都从一坨大模型返回的结果中挑出这一小部分再拿去转化呀,我们希望我们的程序能够自动进行解析。
我们重新看一眼大模型返回的内容,不过,content对应的值我们前面研究过了,这一次可以把它忽略掉,避免我们的注意力分散,减少我们的认知压力:
{'id':'33fa70d1-b89c-4b87-b658-d4bd13c187f4','object':'chat.completion','created':1788102644,'model':'deepseek-v4-flash','choices':[{'index':0,'message':{'role':'assistant','content':'','reasoning_content':'We need answer in Chinese. Need introduce myself. As AI assistant from DeepSeek. Mention capabilities, free, knowledge cutoff, file upload, context. Keep concise.'},'logprobs':None,'finish_reason':'stop'}],'usage':{'prompt_tokens':85,'completion_tokens':263,'total_tokens':348,'prompt_tokens_details':{'cached_tokens':0},'completion_tokens_details':{'reasoning_tokens':34},'prompt_cache_hit_tokens':0,'prompt_cache_miss_tokens':85},'system_fingerprint':'a26a7955944dc5c60445bff77fac9c8e'}'content': ''是一个键值对,那么什么地方会有键值对呢?没错,字典!哪里有字典呢?根据就近原则,我们逐行向上打量。找到了!message是一个字典。
'message':{}这个message也不一般,也是一个键,所以一定存在着更大的字典,不过我们当前先不管,先管好眼前——这个字典是怎样的呢?
'message':{'role':'assistant','content':'','reasoning_content':''}message,中文“消息”的意思。它此时作为一个字典,包含三个元素,role,content,reasoning_content。前面初步分析过了content,现在我们先看看与它最相似的reasoning_content是什么吧?reasoning content,中文意思是“推理内容”。它对应的就是大模型的推理思也就是常听说的思维链(CoT,Chain of Thought1)。
'reasoning_content':'We need answer in Chinese. Need introduce myself. As AI assistant from DeepSeek. Mention capabilities, free, knowledge cutoff, file upload, context. Keep concise.'最后一个元素是role,role的中文含义是“角色”,对应的值assistant则是“助手”的意思。逐一解析了message的细节之后,我们再回过头去重新打量一下message的整体:
'message':{'role':'assistant','content':'','reasoning_content':''}有没有发现它的形象似曾相识——
"messages":[{"role":"user","content":"你好"}]"messages":[{"role":"user","content":question}]相似之中,又有所不同。大模型的响应结果中,message的值是一个字典,而我们前面在请求的时候,message是一个列表。这其中自有玄机,后面我们很快就会用到这个细微的差别。
现在我们知道了content是message字典中的一个键值对,那么如何获取它的值呢?这个方法也就随即确定了:
message['content']只不过,这个message也是一个字典元素,单靠上面那样子还不足以达到访问回答内容的目的。我么还需要继续分析一下大模型的响应结果。message所属于的字典在哪呢?
{'index':0,'message':{},'logprobs':None,'finish_reason':'stop'}这个花括号括{}起来的部分,便是message所在的字典,而这个字典又位于数组中。
'choices':[{'index':0,'message':{},'logprobs':None,'finish_reason':'stop'}],并且这个数组只有一个元素,就是message所在的这个字典。因此我们可以这样访问回答内容;
[0]['message']['content']而这个数组呢,又是键choices所对应的值,
{'id':'33fa70d1-b89c-4b87-b658-d4bd13c187f4','object':'chat.completion','created':1788102644,'model':'deepseek-v4-flash','choices':[],...}choices同样位于一个更大的字典中,而这个字典是谁呢?这个字典就出现在我们用来打印大模型响应结果的那句指令之中——
print(response.json())即response.json()。
至此,我们总算理清楚了程序自动访问回答内容的指令:
response.json()['choices'][0]['message']['content']而如果你想要在终端输出,你只需在外面套一层print()即可。
# 导入模块importosimportrequests# 读取环境变量中的密钥api_key=os.getenv("DEEPSEEK_API_KEY")# 终端输入question=input("请输入问题:")# 发送请求response=requests.post(url="https://api.deepseek.com/chat/completions",headers={"Authorization":f"Bearer{api_key}"},json={"model":"deepseek-v4-flash","messages":[{"role":"user","content":question}],},)# 打印响应中的回答内容print(response.json()['choices'][0]['message']['content'])
以上便是今日的开发 or 学习笔记——如何在 Python 环境中调用 LLM API 进行一问一答。
明天我们将学习如何流式输出,让 AI 的回答一个字一个字、一个词一个词蹦出,就仿佛是在一边思考一边回复一样。以及如何存储对话消息记录,为持续对话提供关键的基石。
Chain-of-Thought Prompting Elicits Reasoning in Large Language Models ↩︎