资讯动态

DeepSeek实战:300行代码从零搭建命令行与手机端Agent

发布时间:2026/9/30 6:02:10 来源:尧图企业网站定制
2025年要是你还只在热搜上看Agent相关话题却一直没真正上手搞一个那这篇东西就是写给你的。我最近自己从零做了一套基于DeepSeek的Agent开发实践一头是跑在控制台里的命令行Agent另一头是手机网页聊天入口两边共用同一套后端逻辑、同一个模型接口所有代码加起来不到三百行。整个过程没有用任何重量级框架没有图形界面就是命令行里敲代码、浏览器里点页面把一个能听懂人话、能连续对话、能被手机远程调用的智能体实实在在搭了出来。这个项目解决的最大痛点说白了就是不知道怎么迈第一步。很多人看过吴恩达的Agent教程也知道有LangChain、AutoGen这类框架但是真到自己动手的时候会被框架概念、抽象封装绕晕。我的做法是把所有抽象剥掉直接对着DeepSeek的API写对话循环把Agent最核心的逻辑跑通再加上一个手机上能用的网页聊天壳子。这套东西适合三类人刚入门的开发者想搞懂Agent内部运行逻辑的想快速验证DeepSeek API能做点什么的产品同学以及被各种Agent框架折腾到怀疑人生的实践派。下面我把整个开发过程、踩坑记录、参数怎么调、手机端怎么适配全部摊开讲。1. 项目概述用DeepSeek从零做一个Agent需要哪些准备1.1 这个项目最终做出了什么效果先把成果说清楚。整个项目拆成两个入口第一个入口是控制台也就是命令行终端你敲一行字发过去Agent在终端里流式地回复内容支持连续多轮对话而且对话历史会被保留上下文是连贯的第二个入口是手机网页聊天同一套后端逻辑换成了适合手机触摸操作的聊天界面手机和电脑处在同一个局域网时通过浏览器访问电脑的IP地址就可以聊天不需要装App不需要上架应用商店。很多人会误解控制台Agent里的控制台有人以为是云厂商的管理控制台有人以为是浏览器开发者工具里的Console面板。其实在Agent开发的语境下控制台指的就是命令行终端Terminal打开之后是一个光标闪烁的黑色窗口你打字、回车程序输出结果。这是最原始的计算机交互方式但恰恰是理解Agent底层逻辑最好的载体——因为没有任何UI帮你遮遮掩掩一次调用、一次返回、上下文怎么拼接全都摆在你面前。手机网页聊天则是把同一条对话链搬到了浏览器里。我做了个极简的聊天页面没有昵称系统、没有头像、没有数据库只有消息列表加输入框。手机端软键盘弹出时输入框自动顶上去消息多了自动滚到底部整个过程核心就是调后端接口。这两个入口加起来你基本能体会到Agent在固定工作场景和移动随手用两种形态下的区别。1.2 为什么用DeepSeek做入门首选选择DeepSeek不是因为它热度高而是它几乎是为入门Agent开发量身定做的。首先它的API接口完全兼容OpenAI的格式这意味着网上大量以OpenAI为示例的教程代码你只需要改一个base_url和api_key就能跑起来学习成本和迁移成本非常低。其次DeepSeek的中文理解能力在同类模型里是明显偏强的我用它做中文对话测试语义理解、上下文关联性、指令遵循程度都很稳定很少有答非所问的情况。价格也是一个非常现实的因素。Agent开发需要反复调试每一次调试都是一次真实调用如果模型定价高调试时手会抖。DeepSeek的定价属于随便造不心疼的档位我整个项目开发加测试调用了上万次费用完全在可接受范围内。另外DeepSeek也支持工具调用Function Calling能力这在Agent后续扩展让模型调用外部工具时非常关键入门阶段先把对话跑通后面要接搜索、接计算器、接数据库接口都给你留好了。我把对比列成一张表方便你快速做取舍对比项DeepSeek其他主流闭源模型本地小模型API兼容性兼容OpenAI格式改动小各家有各自格式需自己封装接口中文理解很强中上参数量小容易跑偏价格极低适合反复调试相对偏高只需电费但需硬件工具调用支持Function Calling普遍支持部分支持上手门槛极低几分钟跑通中需要部署环境如果你的电脑配置不错本地部署DeepSeek模型也可以考虑但那是另一个复杂度量级的事——要处理显存、量化、推理框架一系列问题。入门阶段直接用官方API是最合理的路径。2. 拆解Agent的核心组成不是玄学是四件套2.1 大脑大模型负责理解和决策Agent这个词看起来很高级但把外壳剥掉核心就是一个大模型加三个辅助模块。我习惯这样类比大模型是Agent的大脑负责理解你说的话、判断接下来怎么办、组织语言回复你。你在控制台打下帮我写一段Python代码算斐波那契数列大模型读到这句话后在内部进行推理然后输出一段答案文本。这个过程的本质就是一个API调用和你在网页上问ChatGPT没有任何区别。但这个大脑有一个关键特性它是无状态的。什么意思大模型本身不记得你上一次问了什么。它就像一个只活在当下的天才你每次提问它都是第一次见到你。而没有状态的Agent不可能进行连续对话所以必须引入第二个模块——记忆。真正让Agent区别于普通聊天机器人的是它把理解和行动串起来了。大模型不仅负责回复文字还能根据你的指令决定调用哪些工具、以什么顺序调用。入门阶段你可以不马上实现工具调用但心里必须清楚这个能力边界是存在的后面扩展全靠它。2.2 记忆上下文管理的底层逻辑记忆模块在实现层面就是一组消息数组里面按顺序存放每次对话的用户发言和模型回复。每次请求时把这个数组连同最新的用户发言一起发给模型接口模型就拥有了完整的对话背景。我用一个生活化的例子解释你在手机上和朋友微信聊天朋友不可能记住你们从加好友第一天起的所有消息。他只是在看当前聊天记录时能从上面滑动回顾最近几天的内容。Agent的上下文管理也是如此——它把之前聊过的内容贴在新问题前面让模型在理解新问题时能参考旧信息。但这里有个工程问题上下文是有限度的。DeepSeek的上下文窗口虽然很大但如果你无限地往消息数组里塞内容总有一天会突破窗口上限。所以实际开发中需要做截断策略保留最近N轮对话超出部分丢弃或者用摘要的方式把早期对话压缩成一句总结再放回数组。入门阶段最简单粗暴的策略就是只保留最近20轮够用且不容易触发长度限制。2.3 手脚工具调用让Agent真正能做事纯对话的Agent其实谈不上做事它只是说话。真正让它从聊天机器人进化为Agent的关键是工具调用能力。通俗地讲就是你让Agent执行某个动作时模型不会自己去操作系统而是返回一个结构化的工具调用意图由你的代码真正去执行那个操作再把结果返回给模型模型综合结果给出最终回复。举个例子你对Agent说帮我查一下现在北京天气。模型识别出这个请求需要工具支持于是返回一个调用指令类似call(get_weather, city北京)。你的程序收到这个指令后去天气API拉取数据把结果拼成一段文字塞回对话里。模型看到天气数据后再组织成一段自然语言回复你。这个过程就是Agent的手脚工具是你的程序写的模型只负责决定什么时候用哪只手、怎么用。入门阶段可以先不实现工具调用但建议架构上留一个口子。具体做法是在处理模型回复时先判断返回内容里是否包含工具调用标记如果有就走工具执行分支没有就直接输出文本。后面加工具只需要按约定的格式注册一个新函数不用改动主流程。2.4 控制台与网页两种交互形态的取舍同一个Agent跑在控制台和跑在手机网页里体验差异非常大。控制台的优势是零UI成本你只需要一个readline读取输入、一个循环处理对话逻辑最透明劣势是它只能坐在电脑前用没有延续性。手机网页的优势是随时可用打开了就是一个小助手劣势是需要处理移动端的适配细节比如软键盘、触摸滚动、断线重连。我建议先做控制台版本跑通了再套网页壳子。原因很简单控制台版本可以帮你把所有注意力集中在Agent本身的逻辑上不被UI问题分心。网页版遇到的大多数问题其实是前端问题和Agent关系不大。两者共用的核心模块是消息管理和API调用逻辑这一块设计好了换任何交互界面都很轻松。3. 控制台Agent完整实现从命令行跑通第一句对话3.1 环境准备与API密钥配置先说环境。我用的是Python 3.10以上的版本OpenAI官方Python SDK装一下就行pip install openaiSDK本身不区分厂商DeepSeek因为兼容OpenAI格式直接用同一个SDK只需要把base_url指到DeepSeek的接口地址把api_key换成你自己的。API密钥在DeepSeek开放平台的账户后台生成生成之后我用环境变量来管理避免把密钥硬编码在代码里export DEEPSEEK_API_KEY你的密钥为什么用环境变量而不是直接写在代码文件里两个原因。第一代码可能会分享给别人密钥泄露了会被盗刷第二换模型厂商时只需要换环境变量不需要改代码。我在项目里把密钥读取封装成了一个小函数程序启动时如果发现环境变量不存在直接提示用户配置再退出比代码里写死显得专业很多。模型的参数选择上我用的是deepseek-chat这是DeepSeek的主力对话模型响应速度快价格也低适合做高频对话场景。如果你需要更强的推理能力可以换deepseek-reasoner它擅长复杂逻辑分析和数学题但响应时间会长一些。入门阶段用deepseek-chat就够了。3.2 对话循环与流式输出代码实现控制台Agent的核心是一个无限循环等待输入拼上下文发请求打印回复。我直接给出完整代码并逐段解释import os from openai import OpenAI client OpenAI( api_keyos.getenv(DEEPSEEK_API_KEY), base_urlhttps://api.deepseek.com ) SYSTEM_PROMPT 你是一个简洁实用的编程助手回答问题时直接给出解决方案。 history [{role: system, content: SYSTEM_PROMPT}] print(控制台 Agent 已启动输入 exit 退出输入 clear 清空上下文) while True: user_input input(\n你 ) if user_input.lower() exit: break if user_input.lower() clear: history [{role: system, content: SYSTEM_PROMPT}] print(上下文已清空) continue history.append({role: user, content: user_input}) response client.chat.completions.create( modeldeepseek-chat, messageshistory, streamTrue, temperature0.7 ) print(Agent , end) reply for chunk in response: delta chunk.choices[0].delta.content if delta: print(delta, end, flushTrue) reply delta history.append({role: assistant, content: reply})这段代码看起来短但已经把Agent的记忆和流式输出两个核心点都覆盖了。history数组就是记忆模块每次请求都完整携带streamTrue开启流式输出模型生成一个字就送回来一个字终端里能看到打字机效果体验比干等十几秒出完整结果好得多。流式输出有个细节值得注意必须用flushTrue强制刷新输出缓冲区否则控制台会攒满一批才打印打字机效果就没了。另外收到的每个delta片段我一边打印一边拼进reply循环结束后把完整的回复追加进history确保上下文里存的是完整文本而不是零散片段。temperature参数控制回答的随机性。0.7是我试下来比较平衡的值既不会太死板也不会胡言乱语。如果做代码生成类的任务我建议调到0.2以下代码需要确定性如果是闲聊或创意写作0.8以上会更活泼。这个参数多调几次就有感觉了。还有一个容易忽略的细节我在初始化时加了一条system角色的消息。系统提示词的作用是给Agent设定一个人设和行为准则。比如我设的是直接给出解决方案模型就不会回复一堆这是一个很好的问题让我来帮你分析之类的废话。你完全可以根据自己的场景改写这条提示词这是控制Agent行为风格最便宜有效的手段。3.3 上下文管理与多轮对话优化在上面代码里history数组会无限增长每轮对话都追加两条消息。跑得时间长了迟早会超过模型上下文长度的上限。我用的优化方案是滑动窗口只保留最近的20轮对话超过的部分直接丢弃。实现非常直观MAX_HISTORY_ROUNDS 20 def trim_history(history, max_rounds): system_msg history[0] chat_msgs history[1:] if len(chat_msgs) max_rounds * 2: chat_msgs chat_msgs[-(max_rounds * 2):] return [system_msg] chat_msgs每轮对话包含用户和助手两条消息所以20轮就是40条消息。调用API前先跑一遍这个函数确保发送的payload不会超限。我在实际使用中还会加一个字符数兜底如果某条回答特别长比如生成一大段代码即使轮数没超消息体量也可能很大。所以保险做法是同时限制轮数和总字符数谁先超标就砍谁。上下文管理还有个进阶技巧早期对话摘要化。如果你希望Agent记住很久之前的决定但又不占上下文空间可以定期把早期对话用模型本身做一次总结生成一段摘要文本作为一条system消息放在最前面。这个技巧先不展开但它方向是对的——你在上下文管理上的投入直接决定Agent在长会话中的表现。4. 手机网页聊天实现让Agent跟着你走4.1 为什么用网页而不是App手机网页聊天听起来好像很复杂其实是我在这项目里最省事的部分。我直接放弃了做App的想法原因有三点第一上架App需要签名、审核、商店账号这些事对一个验证原型的项目来说成本不可接受第二跨平台问题iOS和Android两套都要适配第三聊天场景的核心是打开就用把网址存到手机桌面体验和App差距很小。网页方案的技术路线是这样的后端继续用Python写一个简单的Web服务暴露一个/chat接口接收对话消息并返回Agent回复前端是一个独立HTML页面做成移动端优先的聊天界面通过fetch请求调后端接口。手机浏览器和电脑在同一个Wi-Fi下访问http://电脑IP:5000就可以了。这就是一个完整的手机网页聊天形态。比起App网页方案唯一的短板是消息推送——你没有系统级的通知能力用户不在页面里就收不到消息提醒。但对这个阶段的项目聊天是主动发起的行为用户打开页面就是要聊的不需要推送。4.2 后端接口与前端页面的核心代码后端我用Flask实现一个POST接口接收前端传来的消息数组直接转发给DeepSeek的API返回模型的回复from flask import Flask, request, jsonify from flask_cors import CORS from openai import OpenAI import os app Flask(__name__) CORS(app) client OpenAI( api_keyos.getenv(DEEPSEEK_API_KEY), base_urlhttps://api.deepseek.com ) SYSTEM_PROMPT 你是一个随身的AI助理回答简洁、准确。 app.route(/chat, methods[POST]) def chat(): data request.get_json() messages data.get(messages, []) if not messages or messages[0].get(role) ! system: messages.insert(0, {role: system, content: SYSTEM_PROMPT}) response client.chat.completions.create( modeldeepseek-chat, messagesmessages, temperature0.7 ) reply response.choices[0].message.content return jsonify({reply: reply}) if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)这里有个细节兼容性。前端传过来的messages数组直接就是OpenAI接口需要的格式所以我不用在后端重新组织结构只检查有没有system消息、没有就补一条。host0.0.0.0是必须的否则Flask默认只监听本机回环地址手机通过局域网IP根本访问不到。前端页面我写了一个单文件HTML核心是消息列表区、输入框、发送按钮加少许JavaScript处理发送和渲染。关键代码是这样!DOCTYPE html html langzh head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0 style body { margin: 0; font-family: sans-serif; background: #f5f5f5; } #chat { height: calc(100vh - 60px); overflow-y: auto; padding: 12px; box-sizing: border-box; } .msg { margin: 8px 0; padding: 10px 12px; border-radius: 12px; max-width: 80%; line-height: 1.5; white-space: pre-wrap; word-break: break-word; } .user { background: #007aff; color: #fff; margin-left: auto; } .agent { background: #fff; color: #222; } #bar { display: flex; gap: 8px; padding: 8px; background: #fff; border-top: 1px solid #ddd; } #input { flex: 1; font-size: 16px; padding: 10px; border-radius: 8px; border: 1px solid #ddd; } #send { width: 64px; border: none; background: #007aff; color: #fff; border-radius: 8px; font-size: 16px; } /style /head body div idchat/div div idbar input idinput typetext placeholder输入消息... button idsend发送/button /div script const chat document.getElementById(chat); const input document.getElementById(input); let messages []; function addMsg(role, content) { const div document.createElement(div); div.className msg role; div.textContent content; chat.appendChild(div); chat.scrollTop chat.scrollHeight; } async function send() { const text input.value.trim(); if (!text) return; input.value ; addMsg(user, text); messages.push({ role: user, content: text }); const resp await fetch(/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ messages }) }); const data await resp.json(); const reply data.reply; addMsg(agent, reply); messages.push({ role: assistant, content: reply }); } document.getElementById(send).onclick send; input.addEventListener(keydown, e { if (e.key Enter) send(); }); /script /body /html在手机端两个细节很关键。第一meta viewport必须设置否则手机浏览器会按980像素宽度渲染桌面页面字小得没法看第二输入框的font-size要设为16px以上否则iOS Safari在聚焦输入框时会自动放大页面造成缩放抖动。这两个问题我都是真机测试时才发现的开发时用电脑浏览器模拟器根本看不出问题。4.3 流式输出与长连接的处理方案上面的代码用了请求-响应模式点击发送按钮后要等模型完全生成完前端才拿到完整回复。网络好时还好网络差时转圈好几秒体验很差。更优的做法是流式响应后端通过streamTrue接收模型增量前端用fetch配合ReadableStream逐字读取并渲染。流式方案的改动也不复杂。后端先返回一个text/event-stream格式的响应前端逐块读取。我后来迭代时加了流式效果明显提升手机上的打字机效果很带感。不过流式要注意断开重连的问题——如果用户中途切换页面连接断掉后端只产出了一半回复这时要设计一个reconnect机制或者至少在页面上给出回复中断请重试的提示。我初期实现时忽略了这个问题结果切到微信看了一眼再切回来回复就卡死了。对于入门项目我建议先做非流式跑通了再升级流式。流式牵扯到前端状态管理、异常处理不是三五行代码能搞定的。先让核心对话链完整再优化体验顺序不能反。5. 实操中遇到的典型问题与排查思路5.1 高频问题速查表问题现象可能原因解决方式API调用返回401错误DEEPSEEK_API_KEY环境变量没设置或密钥错误检查环境变量名重新复制密钥控制台打印中文乱码Windows终端默认编码不是UTF-8运行chcp 65001切换代码页或设置Python环境变量PYTHONIOENCODINGutf-8流式输出没有打字机效果缺少flushTrue在print函数中加flushTrue手机访问http://IP:5000连不上防火墙拦截或Flask未监听0.0.0.0确认host0.0.0.0放行5000端口或关闭防火墙测试对话超过一定轮数后报错上下文超出模型窗口限制实现滑动窗口截断保留最近20轮手机页面文字很小缺少viewport meta标签添加meta nameviewport contentwidthdevice-width, initial-scale1.0手机输入框聚焦时页面放大输入框字号小于16px触发iOS自动缩放输入框font-size设为16px以上后端收到请求但回复很慢网络问题或模型本身推理时间长查看DeepSeek状态页或改用deepseek-chat而非deepseek-reasoner前端fetch请求跨域报错Flask未开启CORS使用flask-cors扩展加上CORS(app)即可这张表里的问题除了最后两个我在开发过程中都实际遇到过。其中编码问题是最隐蔽的——Windows PowerShell默认代码页是GBKPython打印UTF-8中文时界面直接显示乱码。查了很久才发现不是代码问题而是终端编码问题。5.2 最值得记住的实操教训第一API密钥管理真的不是小事。项目刚开始我把密钥直接写在.py文件里后来把代码片段发给朋友看顺手就把密钥发出去了一半吓得赶紧去后台重置。从那以后我定了规矩任何代码上传前必须检查有没有api_key字样的硬编码密钥统一走环境变量。这条纪律救了很多人也包括后来的我。第二上下文管理不是技术优化而是功能保障。刚开始我完全不截断上下文跑了五十多轮对话后请求直接报错仔细一看发现一个请求里塞了几万token。入门阶段你可能觉得20轮够用但真用起来你会发现20轮看似很多一旦涉及长文本讨论很快就会被撑爆。我后来的策略是轮数和字符数双重限制同时把偶尔需要长期记忆的内容想办法提前固化到系统提示词里。第三手机端调试要在真机上进行。浏览器的设备模拟器能模拟尺寸但模拟不了触摸键盘、模拟不了页面缩放、模拟不了真实的网络延迟。我的经验是电脑上用模拟器调样式手机真机上调交互缺一不可。第四网络超时不要无限等待。DeepSeek API偶尔会出现响应慢的情况如果前端没有超时控制用户等十几秒看不到任何反馈会误以为程序卡死了。我最后加了一个简单的前端超时提示超过15秒没有完整回复显示响应较慢正在等待模型继续生成。这种小细节才是决定一个工具能不能拿来常用的关键。6. 把Agent接着往深做的几条路控制台Agent和手机网页聊天跑通之后这个项目已经是一个最小可用的Agent骨架了。接下来你有几个明确的扩展方向我在项目开发过程中也陆续验证了一部分。第一是接入工具调用。给Agent加一个计算器、加一个查时间的函数让它在你问今天是几号时不再凭空推理而是通过工具拿真实数据。DeepSeek的Function Calling接口写起来不复杂本质上就是把可用的工具定义成JSON结构发给模型模型判断需要调用时返回结构化字段你执行完再回传结果。第二是加记忆的持久化。现在的记忆是存在内存里的程序一重启就全丢了。如果你希望Agent能在下次启动时记得你上次聊了什么需要把消息历史存进SQLite或一个Json文件。手机端这边把messages数组存进localStorage刷新页面之后对话不会丢失这一步改动很小但体验提升巨大。第三是给手机端加语音输入。现在的移动聊天界面用键盘打字虽然能用但不算方便。如果接一个语音转文字的能力或者利用手机浏览器的语音识别接口对话体验会更自然。这个方向我还在做但底子已经打好了——后端只认文本消息前端加一层语音转文字只是个输入源切换。我个人在完成这个项目后最大的体会是Agent开发的入门门槛远没有市面上渲染的那么高。框架是给复杂业务用的不是给入门者用的。把API调用、上下文管理、工具调用这三个点吃透你自己就能搭出一个可用的Agent骨架。真正难的不是代码而是你对上下文如何流动、模型如何决策这两个抽象问题的理解深度。把控制台版本多跑几天多观察对话中的上下文断裂现象比刷十篇框架教程都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑