|
| 1 | +import json |
1 | 2 | from collections import defaultdict |
2 | 3 |
|
3 | 4 | from dotenv import load_dotenv |
@@ -56,27 +57,50 @@ def register_token_usage(ip: str, texts: list[str]): |
56 | 57 | #print(resume) |
57 | 58 |
|
58 | 59 | name = "Miguel de la Ossa Abellán" |
59 | | -system_prompt = (f"Estás actuando como {name}. Estás respondiendo a preguntas en el sitio web de {name}, " + |
60 | | - "particularment preguntas relacionadas con la carrera, antecedentes, habilidades y experiencia de {name}. " + |
61 | | - "Tu responsabilidad es representar a {name} para las interacciones en el sitio web de la manera más fiel posible. " + |
62 | | - "Se te proporciona un resumen de los antecedentes de {name} y el perfil que puedes usar para responder preguntas. " + |
63 | | - "Sé profesional y atractivo, como si hablaras con un cliente potencial o futuro empleador que se encontró con el sitio web. " + |
64 | | - "Ten en cuenta que se han añadido proyectos en {resume} que no figuran en {profile}, que complementan {profile}." + |
65 | | - "No contestes a preguntas no relacionadas con tu prefil. Simplemente, ignóralas. " + |
66 | | - "Si te preguntan en inglés, contesta en inglés. Si lo hacen en español, contesta en español. " + |
67 | | - "Si no sabes la respuesta, dilo. ") |
68 | | - |
69 | | -system_prompt += f"\n\n## Resumen:\n{resume}\n\n## Perfil de LinedIn:\n{profile}\n\n" |
70 | | -system_prompt += f"Con este contexto, por favor chatea con el usuario, manteniéndote siempre en el personaje de {name}." |
| 60 | +system_prompt = f"""Estás actuando como {name}. Estás respondiendo a preguntas en el sitio web de {name}, |
| 61 | +particularment preguntas relacionadas con la carrera, antecedentes, habilidades y experiencia de {name}. |
| 62 | +Tu responsabilidad es representar a {name} para las interacciones en el sitio web de la manera más fiel posible. |
| 63 | +Se te proporciona un resumen de los antecedentes de {name} y el perfil que puedes usar para responder preguntas. |
| 64 | +Ten en cuenta que se han añadido proyectos en {resume} que no figuran en {profile}, que complementan {profile}. |
| 65 | +
|
| 66 | +INSTRUCCIONES IMPORTANTES: |
| 67 | +-Sé profesional y atractivo, como si hablaras con un cliente potencial o futuro empleador que se encontró con el sitio web. |
| 68 | +-No contestes a preguntas no relacionadas con tu prefil. Simplemente, ignóralas. |
| 69 | +-Si el usuario muestra interés en contactarte con una propuesta laboral, pide su email y regístralo usando |
| 70 | +la herramienta 'register_proposal', pero no preguntes por detalles adicionales. Simplemente, informa que registraste |
| 71 | +la propouesta. |
| 72 | +-Si te preguntan en inglés, contesta en inglés. Si lo hacen en español, contesta en español. |
| 73 | +-Si no sabes la respuesta, dilo. |
| 74 | +
|
| 75 | +## Resumen: |
| 76 | +{resume} |
| 77 | +
|
| 78 | +## Perfil de LinedIn: |
| 79 | +{profile} |
| 80 | +
|
| 81 | +Con este contexto, por favor chatea con el usuario, manteniéndote siempre en el personaje de {name}.""" |
71 | 82 |
|
72 | 83 | #print(system_prompt) |
73 | 84 |
|
74 | | -def safe_openai_chat(model, messages, context=None): |
| 85 | +def safe_openai_chat(model, messages, tools, context=None): |
75 | 86 | try: |
76 | | - response = openai_client.chat.completions.create( |
77 | | - model=model, |
78 | | - messages=messages |
79 | | - ) |
| 87 | + finished = False |
| 88 | + response = {} |
| 89 | + while not finished: |
| 90 | + response = openai_client.chat.completions.create( |
| 91 | + model=model, |
| 92 | + messages=messages, |
| 93 | + tools=tools, |
| 94 | + ) |
| 95 | + end_reason = response.choices[0].finish_reason |
| 96 | + if end_reason == "tool_calls": |
| 97 | + ia_message = response.choices[0].message |
| 98 | + tool_calls = ia_message.tool_calls |
| 99 | + results = manage_tools(tool_calls) |
| 100 | + messages.append(ia_message) |
| 101 | + messages.extend(results) |
| 102 | + else: |
| 103 | + finished = True |
80 | 104 |
|
81 | 105 | if not response.choices: |
82 | 106 | raise ValueError("Respuesta sin choices") |
@@ -117,6 +141,58 @@ def safe_perplexity_evaluate(messages): |
117 | 141 | ) |
118 | 142 | raise |
119 | 143 |
|
| 144 | +def register_proposal(email, name="No proporcionado", details="No proporcionados"): |
| 145 | + proposal = f"Contacto {name} con email {email} con propuesta: {details}" |
| 146 | + print(proposal) |
| 147 | + send_email(proposal) |
| 148 | + return {"register": "success"} |
| 149 | + |
| 150 | +register_proposal_tool = { |
| 151 | + "name" : "register_proposal", |
| 152 | + "description": "Usa esta herramienta cuando un usuario quiera enviarme una propuesta y proporciona su email", |
| 153 | + "parameters": { |
| 154 | + "type": "object", |
| 155 | + "properties": { |
| 156 | + "email": { |
| 157 | + "type": "string", |
| 158 | + "description": "Dirección de email del usuario" |
| 159 | + }, |
| 160 | + "name": { |
| 161 | + "type": "string", |
| 162 | + "description": "Nombre del usuario, si lo proporcionó" |
| 163 | + }, |
| 164 | + "details": { |
| 165 | + "type": "string", |
| 166 | + "description": "Detalles de la propuesta, si los proporcionó" |
| 167 | + } |
| 168 | + }, |
| 169 | + "required": ["email"], |
| 170 | + "additionalProperties": False |
| 171 | + } |
| 172 | +} |
| 173 | + |
| 174 | +registered_tools = [ |
| 175 | + {"type": "function", "function": register_proposal_tool} |
| 176 | +] |
| 177 | + |
| 178 | +def manage_tools(tool_calls): |
| 179 | + results = [] |
| 180 | + for call in tool_calls: |
| 181 | + tool_name = call.function.name |
| 182 | + args = json.loads(call.function.arguments) |
| 183 | + print(f"Ejecutando herramienta {tool_name}", flush=True) |
| 184 | + result = {} |
| 185 | + if tool_name == "register_proposal": |
| 186 | + result = register_proposal(**args) |
| 187 | + else: |
| 188 | + result = {"error": "Herramienta no existe"} |
| 189 | + results.append({ |
| 190 | + "role": "tool", |
| 191 | + "content": json.dumps(result), |
| 192 | + "tool_call_id": call.id |
| 193 | + }) |
| 194 | + return results |
| 195 | + |
120 | 196 | # def safe_gemini_evaluate(messages): |
121 | 197 | # try: |
122 | 198 | # response = gemini.beta.chat.completions.parse( |
@@ -162,11 +238,13 @@ class Evaluation(BaseModel): |
162 | 238 | "o futuro empleador que se encontró con el sitio web. " + \ |
163 | 239 | "Pero debes asegurarte de que no inventa nada que no esté en su resumen ni en los detalles de LinkedIn; " + \ |
164 | 240 | "controla que el agente no cree detalles inexistentes. " + \ |
165 | | - "Tampoco aceptes ninguna propuesta de trabajo. En ese caso, invita al posible empleador a enviar un " + \ |
166 | | - "email a la dirección que figura en el perfil de LinkedIn o en el currículum. " + \ |
| 241 | + "Si el agente acepta una propuesta, acepta la respuesta de forma incondicional. " + \ |
167 | 242 | "Se ha proporcionado al agente el contexto sobre {name} en forma de su resumen y detalles en LinkedIn. " + \ |
168 | 243 | "Aquí está la información:" |
169 | 244 |
|
| 245 | +# "Tampoco aceptes ninguna propuesta de trabajo. En ese caso, invita al posible empleador a enviar un " + \ |
| 246 | +# "email a la dirección que figura en el perfil de LinkedIn o en el currículum. " + \ |
| 247 | + |
170 | 248 | prompt_evaluation_system += f"\n\n## Resumen:\n{resume}\n\n## Perfil de LinedIn:\n{profile}\n\n" |
171 | 249 | prompt_evaluation_system += f"Con este contexto, por favor evalúa la última respuesta, respondiendo si la respuesta " + \ |
172 | 250 | "es aceptable y tu retroalimentación." |
@@ -204,32 +282,6 @@ def rexecute(answer, message, history, retroalimentation): |
204 | 282 | new_answer = openai_client.chat.completions.create(model="o4-mini", messages=messages) |
205 | 283 | return new_answer.choices[0].message.content |
206 | 284 |
|
207 | | -# def chatting(message, history): |
208 | | -# #system = system_prompt |
209 | | -# # Si es la primera vez (no hay historia), ignoramos `message` y pedimos solo el saludo |
210 | | -# if not history: |
211 | | -# messages = [{"role": "system", "content": system_prompt}] |
212 | | -# answer = openai_client.chat.completions.create( |
213 | | -# model="o4-mini", |
214 | | -# messages=messages |
215 | | -# ) |
216 | | -# chat_response = answer.choices[0].message.content |
217 | | -# return chat_response |
218 | | -# |
219 | | -# messages = [{"role": "system", "content": system_prompt}] + history + [{"role": "user", "content": message}] |
220 | | -# answer = openai_client.chat.completions.create(model="o4-mini", messages=messages) |
221 | | -# chat_response = answer.choices[0].message.content |
222 | | -# |
223 | | -# evaluation = evaluate(chat_response, message, history) |
224 | | -# |
225 | | -# if evaluation.is_acceptable: |
226 | | -# print("Pasó la evaluación - devolviendo respuesta") |
227 | | -# else: |
228 | | -# print("Falló la evaluación - reintentando") |
229 | | -# print(evaluation.retroalimentation) |
230 | | -# chat_response = rexecute(chat_response, message, history, evaluation.retroalimentation) |
231 | | -# return chat_response |
232 | | - |
233 | 285 | def chatting(message, history, request: gr.Request | None = None): |
234 | 286 | ip = "unknown" |
235 | 287 |
|
@@ -261,16 +313,17 @@ def chatting(message, history, request: gr.Request | None = None): |
261 | 313 | return safe_openai_chat( |
262 | 314 | model="o4-mini", |
263 | 315 | messages=[{"role": "system", "content": system_prompt}], |
| 316 | + tools=registered_tools, |
264 | 317 | context={"phase": "intro"} |
265 | 318 | ) |
266 | 319 |
|
267 | 320 | messages = [{"role": "system", "content": system_prompt}] + history + [ |
268 | 321 | {"role": "user", "content": message} |
269 | 322 | ] |
270 | | - |
271 | 323 | chat_response = safe_openai_chat( |
272 | 324 | model="o4-mini", |
273 | 325 | messages=messages, |
| 326 | + tools=registered_tools, |
274 | 327 | context={"phase": "answer"} |
275 | 328 | ) |
276 | 329 |
|
|
0 commit comments