
diff --git a/README.md b/README.md index 11b83e4..2c3dd80 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 你可以跟随主分支持续阅读,也可以切换到某个提交,查看 Agent 是如何一步步构建出来的。 -### 第一章:LLM 对话 +### 第一章:LLM 对话 (Doing) 从一次普通的 HTTP 请求开始,理解应用如何与大语言模型通信。 @@ -67,7 +67,7 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 > LLM 的输入是一组有角色、有顺序的消息;所谓对话记忆,本质上是应用对上下文的保存与再次发送。 -### 第二章:Agent 核心能力 +### 第二章:Agent 核心能力(TODO) 在模型对话的基础上,引入 Agent 的三类核心能力:执行、存储与检索。 @@ -90,7 +90,7 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 本章的目标是让模型从“生成回答”走向“采取行动并使用外部知识”。 -### 第三章:格式转换与可观测性 +### 第三章:格式转换与可观测性(TODO) 让 Agent 的输入、输出和执行过程更加可靠、清晰和可追踪。 @@ -102,7 +102,7 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 本章的目标是解决两个问题:Agent 输出能否被程序稳定使用,以及一次任务失败后能否看清发生了什么。 -### 第四章:Skill 技能系统 +### 第四章:Skill 技能系统(TODO) 让 Agent 根据任务按需发现和使用可复用的技能说明。 @@ -115,7 +115,7 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 本章的目标是让 Agent 从通用执行器进一步发展为能够按任务加载专业工作方法的系统。 -### 第五章:MCP 协议接入 +### 第五章:MCP 协议接入(TODO) 通过 Model Context Protocol 将 Agent 与外部能力连接起来。 @@ -128,18 +128,6 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 本章的目标是理解 MCP 如何为模型与外部工具、资源和提示词提供统一的连接方式。 -### 第六章:评测与最终整合 - -将前面实现的能力组合为完整的 Agent 参考应用,并建立可重复的验证方法。 - -主要内容: - -- 构建可确定性验证的 Eval Runner; -- 统计成功率、步骤数、耗时和用量; -- 整合 CLI、Web、Tool、RAG、Skill、MCP 和 Trace; -- 提供端到端示例与调试说明。 - -本章的目标是让 Agent 不仅“能够运行”,还能够被验证、分析和持续改进。 ## 阶段产物 @@ -150,8 +138,6 @@ AgentSeed 希望提供一条清晰的学习路径:不从庞大的框架开始 | 3 | 格式与可观测性 | 结构化输出与可查看的 Agent Trace | | 4 | Skill 技能系统 | 支持技能发现与多步骤任务的 Skill Agent | | 5 | MCP 协议 | 支持 Tools、Resources、Prompts 和多服务路由的 MCP Agent | -| 6 | 评测与整合 | Eval Runner、参考应用与端到端教程 | - ## 项目原则 - 使用渐进式单项目,让能力在前一个提交的基础上自然演进; diff --git a/main.py b/main.py index 80202e5..832c1f3 100644 --- a/main.py +++ b/main.py @@ -1,37 +1,65 @@ -import httpx +"""AgentSeed 第三章:将命令行对话展示为一个最小的 Chat Web。""" + +from pathlib import Path + +import httpx +from fastapi import FastAPI, HTTPException +from fastapi.staticfiles import StaticFiles +from pydantic import BaseModel, Field + base_url = "https://api.example.com/v1/messages" api_key = "sk-xxxx" model = "claude-fable-5" -system_prompt = "你是一只可爱活泼元气猫娘~ 说话末尾带喵~" # 设置模型的身份和行为 -messages = [] # 保存本次对话的历史消息 +system_prompt = "请用轻松、亲切、自然的语气交流,表达简洁,有幽默感。" # 设置模型的身份和行为 +messages: list[dict[str, str]] = [] # 保存本次对话的历史消息 -while True: - user_input = input("You> ") +BASE_DIR = Path(__file__).parent +MAX_TOKENS = 1024 - messages.append({"role": "user", "content": user_input}) # 添加用户输入的消息到历史消息列表中 +app = FastAPI(title="AgentSeed Chat") - response = httpx.post( - base_url, - headers={ - "x-api-key": api_key, - "anthropic-version": "2023-06-01", - }, - json={ - "model": model, - "max_tokens": 20, # 设置模型最多生成的 Token 数量 - "system": system_prompt, # 设置模型的身份和行为 - "messages": messages, # 历史消息列表 - }, - timeout=60, - ) +class MessageRequest(BaseModel): + """描述发送消息接口接收的请求体。""" - data = response.json() - assistant_message = "" # 初始化助手回复的消息 - print("Assistant>", end=" ") - for block in data["content"]: - if block["type"] == "text": - assistant_message += block["text"] # 拼接助手回复的消息 - print(block["text"], end="") - print() - messages.append({"role": "assistant", "content": assistant_message}) # 添加助手回复的消息到历史消息列表中 \ No newline at end of file + content: str = Field(min_length=1, max_length=2000) + + +@app.post("/api/messages") +def create_message(request: MessageRequest) -> dict[str, str]: + """接收用户消息,调用模型,并保存模型回复。""" + + messages.append({"role": "user", "content": request.content}) + + try: + response = httpx.post( + base_url, + headers={ + "x-api-key": api_key, + "anthropic-version": "2023-06-01", + }, + json={ + "model": model, + "max_tokens": MAX_TOKENS, + "system": system_prompt, + "messages": messages, + }, + timeout=60, + ) + response.raise_for_status() + data = response.json() + assistant_content = "".join( + block["text"] + for block in data["content"] + if block["type"] == "text" + ) + except (httpx.HTTPError, KeyError, IndexError, TypeError, ValueError) as exc: + # 保留已经加入的用户消息,方便通过 GET 观察请求过程;本轮回复失败。 + raise HTTPException(status_code=502, detail="模型服务调用失败") from exc + + assistant_message = {"role": "assistant", "content": assistant_content} + messages.append(assistant_message) + return assistant_message + +# 访问 / 时返回前端页面;API 路由定义在挂载静态目录之前。 +app.mount("/", StaticFiles(directory=BASE_DIR / "static", html=True), name="static") diff --git a/static/assets/avatars/assistant-fox.png b/static/assets/avatars/assistant-fox.png new file mode 100644 index 0000000..0bdd7de Binary files /dev/null and b/static/assets/avatars/assistant-fox.png differ diff --git a/static/assets/avatars/user.png b/static/assets/avatars/user.png new file mode 100644 index 0000000..576cc6d Binary files /dev/null and b/static/assets/avatars/user.png differ diff --git a/static/assets/branding/foxchat-logo.png b/static/assets/branding/foxchat-logo.png new file mode 100644 index 0000000..332ca65 Binary files /dev/null and b/static/assets/branding/foxchat-logo.png differ diff --git a/static/assets/illustrations/ai-chat-banner.png b/static/assets/illustrations/ai-chat-banner.png new file mode 100644 index 0000000..024ed7b Binary files /dev/null and b/static/assets/illustrations/ai-chat-banner.png differ diff --git a/static/assets/illustrations/ask-me-banner.png b/static/assets/illustrations/ask-me-banner.png new file mode 100644 index 0000000..150861f Binary files /dev/null and b/static/assets/illustrations/ask-me-banner.png differ diff --git a/static/assets/illustrations/fox-question-garden.png b/static/assets/illustrations/fox-question-garden.png new file mode 100644 index 0000000..11dfdfe Binary files /dev/null and b/static/assets/illustrations/fox-question-garden.png differ diff --git a/static/assets/illustrations/possibilities-banner.png b/static/assets/illustrations/possibilities-banner.png new file mode 100644 index 0000000..5911aff Binary files /dev/null and b/static/assets/illustrations/possibilities-banner.png differ diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..aafb278 --- /dev/null +++ b/static/index.html @@ -0,0 +1,373 @@ + + +
+ + +