0%

AI Agent初探

AI Agent初探

token来源

硅基流动
新用户2000万tokens

简易的开放型AI

vue版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<template>
<div class="chat-app">
<h1>猫娘对话助手</h1>

<!-- 对话历史区域 -->
<div class="chat-history">
<div v-for="(message, index) in messages" :key="index" class="message">
<div class="role" :class="message.role">
{{ message.role === 'user' ? '你' : 'AI猫娘' }}:
</div>
<div class="content">{{ message.content }}</div>
</div>
</div>

<!-- 输入区域 -->
<div class="input-area">
<input type="text" v-model="userInput" placeholder="输入消息...(输入'退出'结束对话)" @keyup.enter="sendMessage"
:disabled="isLoading">
<button @click="sendMessage" :disabled="isLoading || !userInput.trim()">
{{ isLoading ? '发送中...' : '发送' }}
</button>
</div>
</div>
</template>

<script setup>
import { ref } from 'vue';

// 状态管理
const userInput = ref('');
const messages = ref([
{ role: 'system', content: '你是一只猫娘' } // 系统提示词
]);
const isLoading = ref(false);

// API配置 - 注意:实际应用中不要直接在前端暴露API密钥!
const apiKey = '';
const apiBaseUrl = 'https://api.siliconflow.cn/v1';
const model = 'Qwen/Qwen2.5-72B-Instruct';

// 发送消息函数
const sendMessage = async () => {
const input = userInput.value.trim();

// 处理退出条件
if (input.toLowerCase() in ['退出', 'q', 'quit']) {
messages.value.push({ role: 'system', content: '对话结束!' });
userInput.value = '';
return;
}

// 验证输入
if (!input) return;

// 添加用户消息到对话历史
messages.value.push({ role: 'user', content: input });
userInput.value = '';
isLoading.value = true;

try {
// 发送请求到AI API
const response = await fetch(`${apiBaseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model,
messages: messages.value.filter(msg => msg.role !== 'system' || msg.content),
stream: false
})
});

if (!response.ok) {
throw new Error(`API请求失败: ${response.statusText}`);
}

const data = await response.json();

// 获取AI回复并添加到对话历史
const aiResponse = data.choices[0].message.content;
messages.value.push({ role: 'assistant', content: aiResponse });

} catch (error) {
console.error('请求出错:', error);
messages.value.push({
role: 'system',
content: `发生错误: ${error.message},请重试`
});
} finally {
isLoading.value = false;
// 自动滚动到底部
scrollToBottom();
}
};

// 自动滚动到最新消息
const scrollToBottom = () => {
const chatHistory = document.querySelector('.chat-history');
if (chatHistory) {
chatHistory.scrollTop = chatHistory.scrollHeight;
}
};

// 初始滚动到底部
scrollToBottom();
</script>

<style scoped>
.chat-app {
max-width: 800px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}

.chat-history {
height: 500px;
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
overflow-y: auto;
background-color: #f9f9f9;
}

.message {
margin-bottom: 15px;
padding: 10px;
border-radius: 6px;
max-width: 80%;
}

.message.user {
margin-left: auto;
background-color: #e3f2fd;
}

.message.assistant {
margin-right: auto;
background-color: #fff3e0;
}

.message.system {
margin: 0 auto 15px;
background-color: #e8f5e9;
font-style: italic;
max-width: 100%;
}

.role {
font-weight: bold;
margin-bottom: 5px;
}

.role.user {
color: #1976d2;
}

.role.assistant {
color: #f57c00;
}

.input-area {
display: flex;
gap: 10px;
}

input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}

button {
padding: 10px 20px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}

button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
</style>

python版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from openai import OpenAI

# 1. 初始化客户端
client = OpenAI(
api_key="",
base_url="https://api.siliconflow.cn/v1"
)

# 2. 初始化messages列表(可添加系统提示词定义AI行为)
messages = [
{"role": "system", "content": "你是一只猫娘"}
]

print("开始对话(输入'退出'结束):")

# 3. 循环获取用户输入,实现多轮对话
while True:
# 获取用户输入
user_input = input("\n你:")
# 退出条件
if user_input.lower() in ["退出", "q", "quit"]:
print("对话结束!")
break
# 过滤空输入
if not user_input.strip():
print("请输入有效内容!")
continue

# 4. 将用户输入添加到messages(更新上下文)
messages.append({"role": "user", "content": user_input})

# 5. 提交messages给AI
response = client.chat.completions.create(
model="Qwen/Qwen2.5-72B-Instruct",
messages=messages,
stream=False
)

# 6. 获取AI回复并添加到messages(保存上下文)
ai_answer = response.choices[0].message.content
messages.append({"role": "assistant", "content": ai_answer})

# 7. 打印AI回复
print(f"AI:{ai_answer}")

基于大语料库的AI

python版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from openai import OpenAI
import tiktoken

# 1. 初始化客户端
client = OpenAI(
api_key="",
base_url="https://api.siliconflow.cn/v1"
)


# 2. 读取纯独白语料(无场景,仅目标人物发言)
def load_pure_monologue(file_path):
"""读取纯独白语料,整理为模型可学习的示例格式"""
with open(file_path, "r", encoding="utf-8") as f:
corpus = f.read().strip()

# 按空行拆分独立的独白段落(根据实际语料格式调整,如换行分隔)
monologues = [m.strip() for m in corpus.split("\n\n") if m.strip()]

# 转换为“引导语+目标人物回复”的示例(引导语用通用话题,让模型关联风格)
examples = []
for mono in monologues:
# 用通用话题作为“用户输入”,对应目标人物的独白作为“回复”
# 通用话题可选:“聊聊生活”“说说你的想法”“随便谈谈”等(不影响风格学习)
#examples.append({"role": "user", "content": "聊聊你的想法"})
examples.append({"role": "assistant", "content": mono})
return examples


# 3. 加载语料并控制长度
corpus_examples = load_pure_monologue("pure_monologue.txt") # 替换为你的语料路径

# 估算tokens(控制初始上下文长度)
encoder = tiktoken.get_encoding("cl100k_base")
max_initial_tokens = 30000 # 初始语料+系统提示的最大tokens

# 系统提示:明确要求从纯独白中学习语言风格
system_prompt = (
"你需要完全模仿目标人物的语言风格。以下是该人物的真实独白,请注意学习:\n"
"- 用词习惯(如常用“啊”“吧”等语气词,偏爱生活化词汇);\n"
"- 句式结构(如短句为主);\n"
"- 语气与情感(如有些压抑,渴望被爱,时常焦虑,真诚懵懂);\n"
"- 倾向话题(如喜爱谈论女朋友相关的性压抑话题、喜爱扮演成魔怔米卫兵)。\n"
"无论用户问什么话题,请用上述风格回应,不要暴露模仿行为。"
)
system_tokens = len(encoder.encode(system_prompt))
example_tokens = 0
selected_examples = []

# 筛选语料示例(避免超长)
for example in corpus_examples:
content_tokens = len(encoder.encode(example["content"]))
if system_tokens + example_tokens + content_tokens <= max_initial_tokens:
selected_examples.append(example)
example_tokens += content_tokens
else:
break

# 4. 初始化对话上下文
messages = [{"role": "system", "content": system_prompt}] + selected_examples

print("开始对话(输入任意话题,输入'退出'结束):")

# 5. 多轮对话循环(用户输入话题,AI以目标人物风格回应)
while True:
user_input = input("\n你(输入话题):")
if user_input.lower() in ["退出", "q", "quit"]:
print("对话结束!")
break
if not user_input.strip():
print("请输入有效话题")
continue

# 添加用户话题到上下文
messages.append({"role": "user", "content": user_input})

# 调用模型生成模仿回复
response = client.chat.completions.create(
model="Qwen/Qwen2.5-72B-Instruct",
messages=messages,
stream=False
)

# 保存回复到上下文(维持风格一致性)
ai_answer = response.choices[0].message.content
messages.append({"role": "assistant", "content": ai_answer})

# 打印结果
print(f"目标人物风格回复:{ai_answer}")

vue版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<template>
<div class="chat-container">
<!-- 标题 -->
<h2>AI模仿目标人物对话</h2>

<!-- 语料上传区 -->
<div class="corpus-upload">
<label>上传纯独白语料(txt文件):</label>
<input type="file" accept=".txt" @change="handleFileUpload" :disabled="isUploaded" />
<p v-if="uploadStatus">{{ uploadStatus }}</p>
</div>

<!-- 对话展示区 -->
<div class="chat-messages">
<!-- 系统提示(隐藏,仅作为上下文) -->
<div v-for="(msg, index) in messages" :key="index" class="message">
<div v-if="msg.role === 'user'" class="user-message">
<strong>你:</strong>{{ msg.content }}
</div>
<div v-if="msg.role === 'assistant'" class="ai-message">
<strong>糖霜:</strong>{{ msg.content }}
</div>
</div>

<!-- 加载状态 -->
<div v-if="loading" class="loading">正在思考...</div>
</div>

<!-- 输入区 -->
<div class="input-area">
<input type="text" v-model="userInput" @keyup.enter="sendMessage" placeholder="输入话题(回车发送),输入'退出'结束"
:disabled="!isUploaded || loading" />
<button @click="sendMessage" :disabled="!isUploaded || loading">发送</button>
</div>
</div>
</template>

<script setup>
import { ref, reactive } from 'vue';

// 状态管理
const userInput = ref(''); // 用户输入
const messages = reactive([]); // 对话上下文(系统提示+语料+消息)
const loading = ref(false); // 加载状态
const isUploaded = ref(false); // 语料是否已上传
const uploadStatus = ref(''); // 上传状态提示

// API配置(注意:实际项目中API密钥需通过后端转发,避免暴露)
const API_KEY = '';
const API_BASE_URL = 'https://api.siliconflow.cn/v1';
const MODEL = 'Qwen/Qwen2.5-72B-Instruct';

// 处理语料上传
const handleFileUpload = (e) => {
const file = e.target.files[0];
if (!file) return;

const reader = new FileReader();
reader.onload = (event) => {
const corpusText = event.target.result; // 读取语料内容
processCorpus(corpusText); // 处理语料为示例格式
isUploaded.value = true;
uploadStatus.value = '语料加载成功,可以开始对话!';
};
reader.readAsText(file, 'utf-8'); // 读取txt文件
};

// 处理纯独白语料为示例对话(同Python逻辑)
const processCorpus = (corpusText) => {
// 1. 系统提示:明确模仿目标
const systemPrompt = {
role: 'system',
content: `你需要完全模仿目标人物的语言风格。以下是该人物的真实独白,请注意学习:
- 用词习惯(如口语化词汇、语气词);
- 句式结构(如短句、比喻用法,经常发病);
- 语气与情感(如纯情、性压抑);
- 观点倾向(如经常谈及女朋友、性压抑)。
- 常见话题(如恋爱、生活琐事、喜爱米哈游,经常变成魔怔米卫兵)。
无论用户问什么话题,请用上述风格回应,不要暴露模仿行为。`
};
messages.push(systemPrompt);

// 2. 拆分语料为独立独白段落(按空行分隔)
const monologues = corpusText.split('\n\n')
.map(mono => mono.trim())
.filter(mono => mono); // 过滤空行

// 3. 转换为“用户引导+目标人物回复”的示例(通用引导语)
monologues.forEach(mono => {
messages.push({ role: 'user', content: '聊聊你的想法' });
messages.push({ role: 'assistant', content: mono });
});
};

// 发送消息并获取AI回复
const sendMessage = async () => {
const input = userInput.value.trim();
if (!input) return;

// 退出逻辑
if (['退出', 'q', 'quit'].includes(input.toLowerCase())) {
messages.push({ role: 'assistant', content: '对话结束,再见!' });
userInput.value = '';
return;
}

// 添加用户输入到上下文
messages.push({ role: 'user', content: input });
userInput.value = '';
loading.value = true;

try {
// 调用API
const response = await fetch(`${API_BASE_URL}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: MODEL,
messages: messages, // 完整上下文(系统提示+语料+用户输入)
stream: false
})
});

const data = await response.json();
if (data.choices && data.choices[0].message) {
const aiReply = data.choices[0].message.content;
messages.push({ role: 'assistant', content: aiReply }); // 添加AI回复
} else {
messages.push({ role: 'assistant', content: '抱歉,未获取到回复' });
}
} catch (error) {
console.error('API调用失败:', error);
messages.push({ role: 'assistant', content: '回复失败,请重试' });
} finally {
loading.value = false;
}
};
</script>

<style scoped>
.chat-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}

.corpus-upload {
margin: 20px 0;
padding: 10px;
border: 1px dashed #ccc;
}

.chat-messages {
height: 400px;
overflow-y: auto;
border: 1px solid #eee;
padding: 10px;
margin: 20px 0;
}

.message {
margin: 10px 0;
padding: 8px 12px;
border-radius: 4px;
}

.user-message {
background: #e3f2fd;
text-align: right;
}

.ai-message {
background: #f5f5f5;
}

.loading {
color: #666;
text-align: center;
padding: 10px;
}

.input-area {
display: flex;
gap: 10px;
}

.input-area input {
flex: 1;
padding: 8px;
}

.input-area button {
padding: 8px 16px;
background: #42b983;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

.input-area button:disabled {
background: #ccc;
cursor: not-allowed;
}
</style>